admin firs step
This commit is contained in:
174
frontend/admin/components/TileWrapper.vue
Normal file
174
frontend/admin/components/TileWrapper.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<v-card
|
||||
:color="tileColor"
|
||||
variant="tonal"
|
||||
class="h-100 d-flex flex-column tile-wrapper"
|
||||
:class="{ 'draggable-tile': draggable }"
|
||||
>
|
||||
<!-- Drag Handle -->
|
||||
<div v-if="draggable" class="drag-handle d-flex align-center justify-center pa-2" @mousedown.prevent>
|
||||
<v-icon icon="mdi-drag-vertical" size="small" class="text-disabled"></v-icon>
|
||||
</div>
|
||||
|
||||
<!-- Tile Header -->
|
||||
<v-card-title class="d-flex align-center justify-space-between pa-3 pb-0">
|
||||
<div class="d-flex align-center">
|
||||
<v-icon :icon="tileIcon" class="mr-2"></v-icon>
|
||||
<span class="text-subtitle-1 font-weight-bold">{{ tile.title }}</span>
|
||||
</div>
|
||||
<div class="d-flex align-center">
|
||||
<!-- RBAC Badge -->
|
||||
<v-chip size="small" :color="accessLevelColor" class="text-caption mr-1">
|
||||
{{ accessLevelText }}
|
||||
</v-chip>
|
||||
<!-- Visibility Toggle -->
|
||||
<v-btn
|
||||
v-if="showVisibilityToggle"
|
||||
icon
|
||||
size="x-small"
|
||||
variant="text"
|
||||
@click="toggleVisibility"
|
||||
:title="tile.preference?.visible ? 'Hide tile' : 'Show tile'"
|
||||
>
|
||||
<v-icon :icon="tile.preference?.visible ? 'mdi-eye' : 'mdi-eye-off'"></v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-card-title>
|
||||
|
||||
<!-- Tile Content Slot -->
|
||||
<v-card-text class="flex-grow-1 pa-3">
|
||||
<slot>
|
||||
<!-- Default content if no slot provided -->
|
||||
<p class="text-body-2">{{ tile.description }}</p>
|
||||
</slot>
|
||||
</v-card-text>
|
||||
|
||||
<!-- Tile Footer Actions -->
|
||||
<v-card-actions class="mt-auto pa-3 pt-0">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
v-if="showActionButton"
|
||||
variant="text"
|
||||
size="small"
|
||||
:prepend-icon="actionIcon"
|
||||
@click="handleTileClick"
|
||||
>
|
||||
{{ actionText }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useTileStore } from '~/stores/tiles'
|
||||
import type { TilePermission } from '~/composables/useRBAC'
|
||||
|
||||
interface Props {
|
||||
tile: TilePermission
|
||||
draggable?: boolean
|
||||
showVisibilityToggle?: boolean
|
||||
showActionButton?: boolean
|
||||
actionIcon?: string
|
||||
actionText?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
draggable: true,
|
||||
showVisibilityToggle: true,
|
||||
showActionButton: true,
|
||||
actionIcon: 'mdi-open-in-new',
|
||||
actionText: 'Open'
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
click: [tile: TilePermission]
|
||||
toggleVisibility: [tileId: string, visible: boolean]
|
||||
}>()
|
||||
|
||||
const tileStore = useTileStore()
|
||||
|
||||
// Tile color based on ID
|
||||
const tileColor = computed(() => {
|
||||
const colors: Record<string, string> = {
|
||||
'ai-logs': 'indigo',
|
||||
'financial-dashboard': 'green',
|
||||
'salesperson-hub': 'orange',
|
||||
'user-management': 'blue',
|
||||
'service-moderation-map': 'teal',
|
||||
'gamification-control': 'purple',
|
||||
'system-health': 'red'
|
||||
}
|
||||
return colors[props.tile.id] || 'surface'
|
||||
})
|
||||
|
||||
// Tile icon based on ID
|
||||
const tileIcon = computed(() => {
|
||||
const icons: Record<string, string> = {
|
||||
'ai-logs': 'mdi-robot',
|
||||
'financial-dashboard': 'mdi-chart-line',
|
||||
'salesperson-hub': 'mdi-account-tie',
|
||||
'user-management': 'mdi-account-group',
|
||||
'service-moderation-map': 'mdi-map',
|
||||
'gamification-control': 'mdi-trophy',
|
||||
'system-health': 'mdi-heart-pulse'
|
||||
}
|
||||
return icons[props.tile.id] || 'mdi-view-dashboard'
|
||||
})
|
||||
|
||||
// Access level indicator
|
||||
const accessLevelColor = computed(() => {
|
||||
if (props.tile.minRank && props.tile.minRank > 5) return 'warning'
|
||||
if (props.tile.requiredRole?.includes('admin')) return 'error'
|
||||
return 'success'
|
||||
})
|
||||
|
||||
const accessLevelText = computed(() => {
|
||||
if (props.tile.minRank) return `Rank ${props.tile.minRank}+`
|
||||
if (props.tile.requiredRole?.length) return props.tile.requiredRole[0]
|
||||
return 'All'
|
||||
})
|
||||
|
||||
// Methods
|
||||
function handleTileClick() {
|
||||
emit('click', props.tile)
|
||||
}
|
||||
|
||||
function toggleVisibility() {
|
||||
const newVisible = !props.tile.preference?.visible
|
||||
tileStore.toggleTileVisibility(props.tile.id)
|
||||
emit('toggleVisibility', props.tile.id, newVisible)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tile-wrapper {
|
||||
position: relative;
|
||||
transition: box-shadow 0.2s ease-in-out, transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.tile-wrapper:hover {
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 24px;
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
cursor: grab;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.drag-handle:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.draggable-tile {
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user