mirror of
https://github.com/unraid/api.git
synced 2026-01-24 01:18:39 -06:00
implement 'move to folder'
This commit is contained in:
7
web/components.d.ts
vendored
7
web/components.d.ts
vendored
@@ -105,7 +105,6 @@ declare module 'vue' {
|
||||
SsoButtons: typeof import('./src/components/sso/SsoButtons.vue')['default']
|
||||
SsoProviderButton: typeof import('./src/components/sso/SsoProviderButton.vue')['default']
|
||||
Status: typeof import('./src/components/UpdateOs/Status.vue')['default']
|
||||
TableRenderer: typeof import('./src/components/JsonForms/TableRenderer.vue')['default']
|
||||
'TestThemeSwitcher.standalone': typeof import('./src/components/TestThemeSwitcher.standalone.vue')['default']
|
||||
'TestUpdateModal.standalone': typeof import('./src/components/UpdateOs/TestUpdateModal.standalone.vue')['default']
|
||||
'ThemeSwitcher.standalone': typeof import('./src/components/ThemeSwitcher.standalone.vue')['default']
|
||||
@@ -116,12 +115,11 @@ declare module 'vue' {
|
||||
UCard: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Card.vue')['default']
|
||||
UCheckbox: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue')['default']
|
||||
UContextMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/ContextMenu.vue')['default']
|
||||
UDrawer: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue')['default']
|
||||
UDropdownMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenu.vue')['default']
|
||||
UFormField: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/FormField.vue')['default']
|
||||
UIcon: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default']
|
||||
UInput: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default']
|
||||
UNavigationMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/NavigationMenu.vue')['default']
|
||||
UModal: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue')['default']
|
||||
UnraidToaster: typeof import('./src/components/UnraidToaster.vue')['default']
|
||||
Update: typeof import('./src/components/UpdateOs/Update.vue')['default']
|
||||
UpdateExpiration: typeof import('./src/components/Registration/UpdateExpiration.vue')['default']
|
||||
@@ -129,11 +127,10 @@ declare module 'vue' {
|
||||
UpdateIneligible: typeof import('./src/components/UpdateOs/UpdateIneligible.vue')['default']
|
||||
'UpdateOs.standalone': typeof import('./src/components/UpdateOs.standalone.vue')['default']
|
||||
UptimeExpire: typeof import('./src/components/UserProfile/UptimeExpire.vue')['default']
|
||||
URadioGroup: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/RadioGroup.vue')['default']
|
||||
USelectMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue')['default']
|
||||
'UserProfile.standalone': typeof import('./src/components/UserProfile.standalone.vue')['default']
|
||||
USwitch: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue')['default']
|
||||
UTable: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Table.vue')['default']
|
||||
UTabs: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue')['default']
|
||||
'WanIpCheck.standalone': typeof import('./src/components/WanIpCheck.standalone.vue')['default']
|
||||
'WelcomeModal.standalone': typeof import('./src/components/Activation/WelcomeModal.standalone.vue')['default']
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
import { computed, h, ref, resolveComponent } from 'vue';
|
||||
import { useMutation } from '@vue/apollo-composable';
|
||||
|
||||
import { Button } from '@unraid/ui';
|
||||
// removed unused Button import
|
||||
import { GET_DOCKER_CONTAINERS } from '@/components/Docker/docker-containers.query';
|
||||
import { CREATE_DOCKER_FOLDER } from '@/components/Docker/docker-create-folder.mutation';
|
||||
import { DELETE_DOCKER_ENTRIES } from '@/components/Docker/docker-delete-entries.mutation';
|
||||
import { MOVE_DOCKER_ENTRIES_TO_FOLDER } from '@/components/Docker/docker-move-entries.mutation';
|
||||
import { SET_DOCKER_FOLDER_CHILDREN } from '@/components/Docker/docker-set-folder-children.mutation';
|
||||
import { ContainerState } from '@/composables/gql/graphql';
|
||||
|
||||
import type {
|
||||
@@ -31,6 +34,7 @@ const UBadge = resolveComponent('UBadge');
|
||||
const UInput = resolveComponent('UInput');
|
||||
const UDropdownMenu = resolveComponent('UDropdownMenu');
|
||||
const UContextMenu = resolveComponent('UContextMenu');
|
||||
const UModal = resolveComponent('UModal');
|
||||
|
||||
function formatPorts(container?: DockerContainer | null): string {
|
||||
if (!container) return '';
|
||||
@@ -266,7 +270,7 @@ const columns = computed<TableColumn<TreeRow>[]>(() => {
|
||||
items,
|
||||
size: 'md',
|
||||
ui: {
|
||||
content: 'overflow-x-hidden z-10',
|
||||
content: 'overflow-x-hidden z-50',
|
||||
item: 'bg-transparent hover:bg-transparent focus:bg-transparent border-0 ring-0 outline-none shadow-none data-[state=checked]:bg-transparent',
|
||||
},
|
||||
},
|
||||
@@ -305,37 +309,220 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const { mutate: createFolderMutation, loading: creating } = useMutation(CREATE_DOCKER_FOLDER);
|
||||
const { mutate: moveEntriesMutation, loading: moving } = useMutation(MOVE_DOCKER_ENTRIES_TO_FOLDER);
|
||||
const { mutate: deleteEntriesMutation, loading: deleting } = useMutation(DELETE_DOCKER_ENTRIES);
|
||||
const { mutate: setFolderChildrenMutation } = useMutation(SET_DOCKER_FOLDER_CHILDREN);
|
||||
|
||||
async function handleCreateFolder() {
|
||||
const name = window.prompt('New folder name');
|
||||
if (!name) return;
|
||||
const childrenIds = treeData.value
|
||||
.flatMap(function collect(row): TreeRow[] {
|
||||
if (row.type === 'container') return [row];
|
||||
return (row.children || []).flatMap(collect);
|
||||
})
|
||||
.filter((r) => rowSelection.value[r.id])
|
||||
.map((r) => r.id);
|
||||
if (childrenIds.length === 0) return;
|
||||
await createFolderMutation(
|
||||
{ name, childrenIds },
|
||||
const moveOpen = ref(false);
|
||||
const selectedFolderId = ref<string>('');
|
||||
const pendingMoveSourceIds = ref<string[]>([]);
|
||||
const expandedFolders = ref<Set<string>>(new Set());
|
||||
const renamingFolderId = ref<string>('');
|
||||
const renameValue = ref<string>('');
|
||||
const newTreeFolderName = ref<string>('');
|
||||
|
||||
const rootFolderId = computed<string>(() => props.organizerRoot?.id || '');
|
||||
|
||||
type FolderNode = { id: string; name: string; children: FolderNode[] };
|
||||
|
||||
function buildFolderOnlyTree(entry?: ResolvedOrganizerFolder | null): FolderNode | null {
|
||||
if (!entry) return null;
|
||||
const folders: FolderNode[] = [];
|
||||
for (const child of entry.children || []) {
|
||||
if ((child as ResolvedOrganizerEntry).__typename === 'ResolvedOrganizerFolder') {
|
||||
const sub = buildFolderOnlyTree(child as ResolvedOrganizerFolder);
|
||||
if (sub) folders.push(sub);
|
||||
}
|
||||
}
|
||||
return { id: entry.id, name: entry.name, children: folders };
|
||||
}
|
||||
|
||||
const folderTree = computed<FolderNode | null>(() => buildFolderOnlyTree(props.organizerRoot));
|
||||
|
||||
type FlatFolderRow = { id: string; name: string; depth: number; hasChildren: boolean };
|
||||
|
||||
function flattenVisibleFolders(
|
||||
node: FolderNode | null,
|
||||
depth = 0,
|
||||
out: FlatFolderRow[] = []
|
||||
): FlatFolderRow[] {
|
||||
if (!node) return out;
|
||||
out.push({ id: node.id, name: node.name, depth, hasChildren: node.children.length > 0 });
|
||||
if (expandedFolders.value.has(node.id)) {
|
||||
for (const child of node.children) flattenVisibleFolders(child, depth + 1, out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const visibleFolders = computed<FlatFolderRow[]>(() => flattenVisibleFolders(folderTree.value));
|
||||
|
||||
const parentById = computed<Record<string, string>>(() => {
|
||||
const map: Record<string, string> = {};
|
||||
function walk(node?: ResolvedOrganizerFolder | null, parentId?: string) {
|
||||
if (!node) return;
|
||||
if (parentId) map[node.id] = parentId;
|
||||
for (const child of node.children || []) {
|
||||
if ((child as ResolvedOrganizerEntry).__typename === 'ResolvedOrganizerFolder') {
|
||||
walk(child as ResolvedOrganizerFolder, node.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(props.organizerRoot, undefined);
|
||||
return map;
|
||||
});
|
||||
|
||||
const folderChildrenIds = computed<Record<string, string[]>>(() => {
|
||||
const map: Record<string, string[]> = {};
|
||||
function walk(node?: ResolvedOrganizerFolder | null) {
|
||||
if (!node) return;
|
||||
map[node.id] = (node.children || []).map((c) => {
|
||||
const entry = c as ResolvedOrganizerEntry;
|
||||
return (entry as { id: string }).id;
|
||||
});
|
||||
for (const child of node.children || []) {
|
||||
if ((child as ResolvedOrganizerEntry).__typename === 'ResolvedOrganizerFolder') {
|
||||
walk(child as ResolvedOrganizerFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(props.organizerRoot);
|
||||
return map;
|
||||
});
|
||||
|
||||
function getSelectedEntryIds(): string[] {
|
||||
return Object.entries(rowSelection.value)
|
||||
.filter(([, selected]) => !!selected)
|
||||
.map(([id]) => id);
|
||||
}
|
||||
|
||||
function openMoveModal(ids?: string[]) {
|
||||
const sources = ids ?? getSelectedEntryIds();
|
||||
console.log('sources', sources);
|
||||
if (sources.length === 0) return;
|
||||
pendingMoveSourceIds.value = sources;
|
||||
selectedFolderId.value = rootFolderId.value || '';
|
||||
expandedFolders.value = new Set([rootFolderId.value]);
|
||||
renamingFolderId.value = '';
|
||||
renameValue.value = '';
|
||||
newTreeFolderName.value = '';
|
||||
moveOpen.value = true;
|
||||
}
|
||||
|
||||
async function confirmMove(close: () => void) {
|
||||
const ids = pendingMoveSourceIds.value;
|
||||
if (ids.length === 0) return;
|
||||
if (!selectedFolderId.value) return;
|
||||
await moveEntriesMutation(
|
||||
{ destinationFolderId: selectedFolderId.value, sourceEntryIds: ids },
|
||||
{
|
||||
refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }],
|
||||
awaitRefetchQueries: true,
|
||||
}
|
||||
);
|
||||
rowSelection.value = {};
|
||||
emit('created-folder');
|
||||
showToast('Moved to folder');
|
||||
close();
|
||||
}
|
||||
|
||||
async function handleCreateFolderInTree() {
|
||||
const name = newTreeFolderName.value.trim();
|
||||
if (!name) return;
|
||||
await createFolderMutation(
|
||||
{
|
||||
name,
|
||||
parentId: selectedFolderId.value || rootFolderId.value,
|
||||
childrenIds: pendingMoveSourceIds.value,
|
||||
},
|
||||
{
|
||||
refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }],
|
||||
awaitRefetchQueries: true,
|
||||
}
|
||||
);
|
||||
emit('created-folder');
|
||||
showToast('Folder created');
|
||||
newTreeFolderName.value = '';
|
||||
expandedFolders.value.add(selectedFolderId.value || rootFolderId.value);
|
||||
}
|
||||
|
||||
function startRenameFolder(id: string, currentName: string) {
|
||||
if (!id || id === rootFolderId.value) return;
|
||||
renamingFolderId.value = id;
|
||||
renameValue.value = currentName;
|
||||
}
|
||||
|
||||
async function commitRenameFolder(id: string) {
|
||||
const newName = renameValue.value.trim();
|
||||
if (!id || !newName || newName === id) {
|
||||
renamingFolderId.value = '';
|
||||
renameValue.value = '';
|
||||
return;
|
||||
}
|
||||
const parentId = parentById.value[id] || rootFolderId.value;
|
||||
const children = folderChildrenIds.value[id] || [];
|
||||
// 1) Create new folder with same children under same parent
|
||||
await createFolderMutation(
|
||||
{ name: newName, parentId, childrenIds: children },
|
||||
{ awaitRefetchQueries: true }
|
||||
);
|
||||
// 2) Clear old folder children to avoid cascading deletion of descendants
|
||||
await setFolderChildrenMutation({ folderId: id, childrenIds: [] }, { awaitRefetchQueries: true });
|
||||
// 3) Delete the now-empty old folder
|
||||
await deleteEntriesMutation(
|
||||
{ entryIds: [id] },
|
||||
{
|
||||
refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }],
|
||||
awaitRefetchQueries: true,
|
||||
}
|
||||
);
|
||||
renamingFolderId.value = '';
|
||||
renameValue.value = '';
|
||||
selectedFolderId.value = newName;
|
||||
showToast('Folder renamed');
|
||||
}
|
||||
|
||||
async function handleDeleteFolder() {
|
||||
const id = selectedFolderId.value;
|
||||
if (!id || id === rootFolderId.value) return;
|
||||
if (!confirm('Delete this folder? Contents will move to root.')) return;
|
||||
await deleteEntriesMutation(
|
||||
{ entryIds: [id] },
|
||||
{
|
||||
refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }],
|
||||
awaitRefetchQueries: true,
|
||||
}
|
||||
);
|
||||
selectedFolderId.value = rootFolderId.value;
|
||||
showToast('Folder deleted');
|
||||
}
|
||||
|
||||
function toggleExpandFolder(id: string) {
|
||||
const set = new Set(expandedFolders.value);
|
||||
if (set.has(id)) set.delete(id);
|
||||
else set.add(id);
|
||||
expandedFolders.value = set;
|
||||
}
|
||||
|
||||
// removed unused handleCreateFolder; creation handled in modal
|
||||
|
||||
function getSelectedContainerIds(): string[] {
|
||||
return treeData.value
|
||||
.flatMap(function collect(row): TreeRow[] {
|
||||
if (row.type === 'container') return [row];
|
||||
return (row.children || []).flatMap(collect);
|
||||
})
|
||||
.filter((r) => rowSelection.value[r.id])
|
||||
.map((r) => r.id);
|
||||
const collected = new Set<string>();
|
||||
|
||||
function collectContainers(row: TreeRow, includeAll: boolean): void {
|
||||
const isSelected = !!rowSelection.value[row.id];
|
||||
const shouldInclude = includeAll || isSelected;
|
||||
|
||||
if (row.type === 'container') {
|
||||
if (shouldInclude) collected.add(row.id);
|
||||
return;
|
||||
}
|
||||
// folder
|
||||
const children = row.children || [];
|
||||
const propagate = shouldInclude; // selecting a folder selects all descendants
|
||||
for (const child of children) collectContainers(child as TreeRow, propagate);
|
||||
}
|
||||
|
||||
for (const root of treeData.value) collectContainers(root, false);
|
||||
return Array.from(collected);
|
||||
}
|
||||
|
||||
declare global {
|
||||
@@ -377,7 +564,7 @@ const bulkItems = computed<DropdownMenuItems>(() => [
|
||||
label: 'Move to folder',
|
||||
icon: 'i-lucide-folder',
|
||||
as: 'button',
|
||||
onSelect: () => handleBulkAction('Move to folder'),
|
||||
onSelect: () => openMoveModal(),
|
||||
},
|
||||
{
|
||||
label: 'Start / Stop',
|
||||
@@ -401,7 +588,7 @@ function getRowActionItems(row: TreeRow): DropdownMenuItems {
|
||||
label: 'Move to folder',
|
||||
icon: 'i-lucide-folder',
|
||||
as: 'button',
|
||||
onSelect: () => handleRowAction(row, 'Move to folder'),
|
||||
onSelect: () => openMoveModal([row.id]),
|
||||
},
|
||||
{
|
||||
label: 'Start / Stop',
|
||||
@@ -436,7 +623,7 @@ function getRowActionItems(row: TreeRow): DropdownMenuItems {
|
||||
:items="bulkItems"
|
||||
size="md"
|
||||
:ui="{
|
||||
content: 'overflow-x-hidden z-10',
|
||||
content: 'overflow-x-hidden z-40',
|
||||
item: 'bg-transparent hover:bg-transparent focus:bg-transparent border-0 ring-0 outline-none shadow-none data-[state=checked]:bg-transparent',
|
||||
}"
|
||||
>
|
||||
@@ -467,5 +654,107 @@ function getRowActionItems(row: TreeRow): DropdownMenuItems {
|
||||
<div v-if="!loading && treeData.length === 0" class="py-8 text-center text-gray-500">
|
||||
No containers found
|
||||
</div>
|
||||
|
||||
<UModal
|
||||
v-model:open="moveOpen"
|
||||
title="Move to folder"
|
||||
:ui="{ footer: 'justify-end', overlay: 'z-50', content: 'z-50' }"
|
||||
>
|
||||
<template #body>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<UInput v-model="newTreeFolderName" placeholder="New folder name" class="flex-1" />
|
||||
<UButton
|
||||
size="sm"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
:disabled="!newTreeFolderName.trim()"
|
||||
@click="handleCreateFolderInTree"
|
||||
>Create</UButton
|
||||
>
|
||||
<UButton
|
||||
size="sm"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
:disabled="!selectedFolderId || selectedFolderId === rootFolderId"
|
||||
@click="handleDeleteFolder"
|
||||
>Delete</UButton
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="border-default rounded border">
|
||||
<div
|
||||
v-for="row in visibleFolders"
|
||||
:key="row.id"
|
||||
:data-id="row.id"
|
||||
class="flex items-center gap-2 px-2 py-1 hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||
>
|
||||
<UButton
|
||||
v-if="row.hasChildren"
|
||||
color="neutral"
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
icon="i-lucide-chevron-right"
|
||||
:class="expandedFolders.has(row.id) ? 'rotate-90' : ''"
|
||||
square
|
||||
@click="toggleExpandFolder(row.id)"
|
||||
/>
|
||||
<span v-else class="inline-block w-5" />
|
||||
|
||||
<input type="radio" :value="row.id" v-model="selectedFolderId" class="accent-primary" />
|
||||
|
||||
<div
|
||||
:style="{ paddingLeft: `calc(${row.depth} * 0.75rem)` }"
|
||||
class="flex min-w-0 flex-1 items-center gap-2"
|
||||
>
|
||||
<span class="i-lucide-folder text-gray-500" />
|
||||
<template v-if="renamingFolderId === row.id">
|
||||
<input
|
||||
v-model="renameValue"
|
||||
class="border-default bg-default flex-1 rounded border px-2 py-1"
|
||||
@keydown.enter.prevent="commitRenameFolder(row.id)"
|
||||
@keydown.esc.prevent="
|
||||
renamingFolderId = '';
|
||||
renameValue = '';
|
||||
"
|
||||
@blur="commitRenameFolder(row.id)"
|
||||
autofocus
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="truncate">{{ row.name }}</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<UDropdownMenu
|
||||
:items="[
|
||||
[
|
||||
{
|
||||
label: 'Rename',
|
||||
icon: 'i-lucide-pencil',
|
||||
as: 'button',
|
||||
onSelect: () => startRenameFolder(row.id, row.name),
|
||||
},
|
||||
],
|
||||
]"
|
||||
:ui="{ content: 'z-50' }"
|
||||
>
|
||||
<UButton color="neutral" variant="ghost" icon="i-lucide-more-vertical" square />
|
||||
</UDropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer="{ close }">
|
||||
<UButton color="neutral" variant="outline" @click="close">Cancel</UButton>
|
||||
<UButton
|
||||
:loading="moving || creating || deleting"
|
||||
:disabled="!selectedFolderId"
|
||||
@click="confirmMove(close)"
|
||||
>
|
||||
Confirm
|
||||
</UButton>
|
||||
</template>
|
||||
</UModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
34
web/src/components/Docker/docker-delete-entries.mutation.ts
Normal file
34
web/src/components/Docker/docker-delete-entries.mutation.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_DOCKER_ENTRIES = gql`
|
||||
mutation DeleteDockerEntries($entryIds: [String!]!) {
|
||||
deleteDockerEntries(entryIds: $entryIds) {
|
||||
version
|
||||
views {
|
||||
id
|
||||
name
|
||||
root {
|
||||
__typename
|
||||
... on ResolvedOrganizerFolder {
|
||||
id
|
||||
name
|
||||
type
|
||||
children {
|
||||
__typename
|
||||
... on ResolvedOrganizerFolder {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
... on OrganizerContainerResource {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
37
web/src/components/Docker/docker-move-entries.mutation.ts
Normal file
37
web/src/components/Docker/docker-move-entries.mutation.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const MOVE_DOCKER_ENTRIES_TO_FOLDER = gql`
|
||||
mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {
|
||||
moveDockerEntriesToFolder(
|
||||
destinationFolderId: $destinationFolderId
|
||||
sourceEntryIds: $sourceEntryIds
|
||||
) {
|
||||
version
|
||||
views {
|
||||
id
|
||||
name
|
||||
root {
|
||||
__typename
|
||||
... on ResolvedOrganizerFolder {
|
||||
id
|
||||
name
|
||||
type
|
||||
children {
|
||||
__typename
|
||||
... on ResolvedOrganizerFolder {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
... on OrganizerContainerResource {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const SET_DOCKER_FOLDER_CHILDREN = gql`
|
||||
mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {
|
||||
setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {
|
||||
version
|
||||
views {
|
||||
id
|
||||
name
|
||||
root {
|
||||
__typename
|
||||
... on ResolvedOrganizerFolder {
|
||||
id
|
||||
name
|
||||
type
|
||||
children {
|
||||
__typename
|
||||
... on ResolvedOrganizerFolder {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
... on OrganizerContainerResource {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -30,7 +30,10 @@ type Documents = {
|
||||
"\n mutation UpdateConnectSettings($input: JSON!) {\n updateSettings(input: $input) {\n restartRequired\n values\n }\n }\n": typeof types.UpdateConnectSettingsDocument,
|
||||
"\n query GetDockerContainers($skipCache: Boolean = false) {\n docker {\n id\n organizer(skipCache: $skipCache) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n }\n }\n }\n": typeof types.GetDockerContainersDocument,
|
||||
"\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.CreateDockerFolderDocument,
|
||||
"\n mutation DeleteDockerEntries($entryIds: [String!]!) {\n deleteDockerEntries(entryIds: $entryIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.DeleteDockerEntriesDocument,
|
||||
"\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.MoveDockerEntriesToFolderDocument,
|
||||
"\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n": typeof types.GetDockerContainerOverviewFormDocument,
|
||||
"\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.SetDockerFolderChildrenDocument,
|
||||
"\n query LogFiles {\n logFiles {\n name\n path\n size\n modifiedAt\n }\n }\n": typeof types.LogFilesDocument,
|
||||
"\n query LogFileContent($path: String!, $lines: Int, $startLine: Int) {\n logFile(path: $path, lines: $lines, startLine: $startLine) {\n path\n content\n totalLines\n startLine\n }\n }\n": typeof types.LogFileContentDocument,
|
||||
"\n subscription LogFileSubscription($path: String!) {\n logFile(path: $path) {\n path\n content\n totalLines\n }\n }\n": typeof types.LogFileSubscriptionDocument,
|
||||
@@ -78,7 +81,10 @@ const documents: Documents = {
|
||||
"\n mutation UpdateConnectSettings($input: JSON!) {\n updateSettings(input: $input) {\n restartRequired\n values\n }\n }\n": types.UpdateConnectSettingsDocument,
|
||||
"\n query GetDockerContainers($skipCache: Boolean = false) {\n docker {\n id\n organizer(skipCache: $skipCache) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n }\n }\n }\n": types.GetDockerContainersDocument,
|
||||
"\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.CreateDockerFolderDocument,
|
||||
"\n mutation DeleteDockerEntries($entryIds: [String!]!) {\n deleteDockerEntries(entryIds: $entryIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.DeleteDockerEntriesDocument,
|
||||
"\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.MoveDockerEntriesToFolderDocument,
|
||||
"\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n": types.GetDockerContainerOverviewFormDocument,
|
||||
"\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.SetDockerFolderChildrenDocument,
|
||||
"\n query LogFiles {\n logFiles {\n name\n path\n size\n modifiedAt\n }\n }\n": types.LogFilesDocument,
|
||||
"\n query LogFileContent($path: String!, $lines: Int, $startLine: Int) {\n logFile(path: $path, lines: $lines, startLine: $startLine) {\n path\n content\n totalLines\n startLine\n }\n }\n": types.LogFileContentDocument,
|
||||
"\n subscription LogFileSubscription($path: String!) {\n logFile(path: $path) {\n path\n content\n totalLines\n }\n }\n": types.LogFileSubscriptionDocument,
|
||||
@@ -188,10 +194,22 @@ export function graphql(source: "\n query GetDockerContainers($skipCache: Boole
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation DeleteDockerEntries($entryIds: [String!]!) {\n deleteDockerEntries(entryIds: $entryIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation DeleteDockerEntries($entryIds: [String!]!) {\n deleteDockerEntries(entryIds: $entryIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n"): (typeof documents)["\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user