improve dashboard

This commit is contained in:
pa
2026-03-13 11:52:09 +09:00
parent 044c1a42d4
commit 0135d9bb29
6 changed files with 41 additions and 22 deletions
-1
View File
@@ -96,7 +96,6 @@
"delete": "Delete" "delete": "Delete"
}, },
"toolbar": { "toolbar": {
"editing": "Editing Dashboard",
"name_placeholder": "Dashboard Name", "name_placeholder": "Dashboard Name",
"icon_placeholder": "Icon Class (Optional)" "icon_placeholder": "Icon Class (Optional)"
}, },
+9 -3
View File
@@ -28,7 +28,7 @@
:dashboard-id="id" :dashboard-id="id"
:is-editing="true" :is-editing="true"
@update-panel="handleUpdatePanel" @update-panel="handleUpdatePanel"
@remove-row="handleRemoveRow" /> @remove-panel="handleRemovePanel" />
<div <div
class="mt-auto flex min-h-[80px] flex-1 items-center justify-center rounded-md border-2 border-dashed border-muted-foreground/20 text-muted-foreground transition-colors hover:border-primary/40 hover:bg-primary/5" class="mt-auto flex min-h-[80px] flex-1 items-center justify-center rounded-md border-2 border-dashed border-muted-foreground/20 text-muted-foreground transition-colors hover:border-primary/40 hover:bg-primary/5"
@@ -161,8 +161,14 @@
showAddRowOptions.value = false; showAddRowOptions.value = false;
}; };
const handleRemoveRow = (rowIndex) => { const handleRemovePanel = (rowIndex, panelIndex) => {
editRows.value.splice(rowIndex, 1); const row = editRows.value[rowIndex];
if (!row) return;
if (row.panels.length <= 1) {
editRows.value.splice(rowIndex, 1);
} else {
row.panels.splice(panelIndex, 1);
}
}; };
const handleUpdatePanel = (rowIndex, panelIndex, panelKey) => { const handleUpdatePanel = (rowIndex, panelIndex, panelKey) => {
@@ -1,9 +1,8 @@
<template> <template>
<div class="flex items-center gap-2 rounded-md border bg-card px-3 py-2"> <div class="flex items-center gap-2 rounded-md border bg-card px-3 py-2">
<span class="text-sm font-medium text-muted-foreground">{{ t('dashboard.toolbar.editing') }}</span>
<Input <Input
:model-value="name" :model-value="name"
:placeholder="t('dashboard.name_placeholder')" :placeholder="t('dashboard.toolbar.name_placeholder')"
class="mx-2 h-7 max-w-[200px] text-sm" class="mx-2 h-7 max-w-[200px] text-sm"
@update:model-value="emit('update:name', $event)" /> @update:model-value="emit('update:name', $event)" />
<div class="flex gap-2"> <div class="flex gap-2">
@@ -1,6 +1,14 @@
<template> <template>
<div class="relative flex min-h-0 flex-1 overflow-hidden rounded-md border bg-card"> <div class="relative flex min-h-0 flex-1 overflow-hidden rounded-md border bg-card">
<template v-if="isEditing"> <template v-if="isEditing">
<Button
v-if="showRemove"
variant="ghost"
size="icon-sm"
class="absolute right-1 top-1 z-20"
@click="emit('remove')">
<X class="size-4" />
</Button>
<div class="flex w-full min-h-0 flex-col gap-2 p-3"> <div class="flex w-full min-h-0 flex-col gap-2 p-3">
<div class="flex flex-1 items-center justify-center gap-2 text-xs text-muted-foreground"> <div class="flex flex-1 items-center justify-center gap-2 text-xs text-muted-foreground">
<i v-if="panelIcon" :class="panelIcon" class="text-base" /> <i v-if="panelIcon" :class="panelIcon" class="text-base" />
@@ -13,7 +21,7 @@
</template> </template>
<template v-else-if="panelKey && panelComponent"> <template v-else-if="panelKey && panelComponent">
<div class="h-full w-full overflow-y-auto"> <div class="dashboard-panel h-full w-full overflow-y-auto">
<component :is="panelComponent" /> <component :is="panelComponent" />
</div> </div>
</template> </template>
@@ -32,6 +40,7 @@
<script setup> <script setup>
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { X } from 'lucide-vue-next';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@@ -48,10 +57,14 @@
isEditing: { isEditing: {
type: Boolean, type: Boolean,
default: false default: false
},
showRemove: {
type: Boolean,
default: false
} }
}); });
const emit = defineEmits(['select']); const emit = defineEmits(['select', 'remove']);
const { t } = useI18n(); const { t } = useI18n();
const selectorOpen = ref(false); const selectorOpen = ref(false);
@@ -87,3 +100,13 @@
selectorOpen.value = false; selectorOpen.value = false;
}; };
</script> </script>
<style scoped>
.dashboard-panel :deep(.x-container) {
height: 100%;
margin: 0;
border: none;
border-radius: 0;
background: transparent;
}
</style>
@@ -9,16 +9,10 @@
:key="panelIndex" :key="panelIndex"
:panel-key="panelKey" :panel-key="panelKey"
:is-editing="true" :is-editing="true"
:show-remove="true"
:class="panelEditClass" :class="panelEditClass"
@select="(key) => emit('update-panel', rowIndex, panelIndex, key)" /> @select="(key) => emit('update-panel', rowIndex, panelIndex, key)"
@remove="emit('remove-panel', rowIndex, panelIndex)" />
<Button
variant="ghost"
size="icon-sm"
class="absolute right-1 top-2 z-20 bg-background/80"
@click="emit('remove-row', rowIndex)">
<X class="size-4" />
</Button>
</div> </div>
<ResizablePanelGroup <ResizablePanelGroup
@@ -43,9 +37,7 @@
<script setup> <script setup>
import { computed } from 'vue'; import { computed } from 'vue';
import { X } from 'lucide-vue-next';
import { Button } from '@/components/ui/button';
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'; import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable';
import DashboardPanel from './DashboardPanel.vue'; import DashboardPanel from './DashboardPanel.vue';
@@ -69,7 +61,7 @@
} }
}); });
const emit = defineEmits(['update-panel', 'remove-row']); const emit = defineEmits(['update-panel', 'remove-panel']);
const isVertical = computed(() => props.row.direction === 'vertical'); const isVertical = computed(() => props.row.direction === 'vertical');
+2 -2
View File
@@ -126,7 +126,7 @@
<Spinner class="text-2xl" /> <Spinner class="text-2xl" />
</div> </div>
<template v-else-if="searchWorldResults.length > 0"> <template v-else-if="searchWorldResults.length > 0">
<ItemGroup class="grid grid-cols-5 gap-3"> <ItemGroup class="grid gap-3" style="grid-template-columns: repeat(auto-fill, minmax(180px, 1fr))">
<Item <Item
v-for="world in searchWorldResults" v-for="world in searchWorldResults"
:key="world.id" :key="world.id"
@@ -200,7 +200,7 @@
<Spinner class="text-2xl" /> <Spinner class="text-2xl" />
</div> </div>
<template v-else-if="searchAvatarPage.length > 0"> <template v-else-if="searchAvatarPage.length > 0">
<ItemGroup class="grid grid-cols-5 gap-3"> <ItemGroup class="grid gap-3" style="grid-template-columns: repeat(auto-fill, minmax(180px, 1fr))">
<Item <Item
v-for="avatar in searchAvatarPage" v-for="avatar in searchAvatarPage"
:key="avatar.id" :key="avatar.id"