salix-front/src/components/ui/CardSummary.vue

208 lines
5.8 KiB
Vue

<script setup>
import { ref, computed, watch, onBeforeMount } from 'vue';
import { useRoute } from 'vue-router';
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import { useArrayData } from 'src/composables/useArrayData';
import { isDialogOpened } from 'src/filters';
import VnMoreOptions from './VnMoreOptions.vue';
const props = defineProps({
url: {
type: String,
default: '',
},
filter: {
type: Object,
default: null,
},
entityId: {
type: [Number, String],
default: null,
},
dataKey: {
type: String,
default: '',
},
moduleName: {
type: String,
default: null,
},
});
const emit = defineEmits(['onFetch']);
const route = useRoute();
const isSummary = ref();
const arrayData = useArrayData(props.dataKey, {
url: props.url,
filter: props.filter,
skip: 0,
});
const { store } = arrayData;
const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
const isLoading = ref(false);
defineExpose({
entity,
fetch,
});
onBeforeMount(async () => {
isSummary.value = String(route.path).endsWith('/summary');
await fetch();
watch(props, async () => await fetch());
});
async function fetch() {
store.url = props.url;
store.filter = props.filter ?? {};
isLoading.value = true;
const { data } = await arrayData.fetch({ append: false, updateRouter: false });
emit('onFetch', Array.isArray(data) ? data[0] : data);
isLoading.value = false;
}
</script>
<template>
<div class="summary container">
<QCard class="cardSummary">
<SkeletonSummary v-if="!entity || isLoading" />
<template v-if="entity && !isLoading">
<div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
<slot name="header-left">
<router-link
v-if="isDialogOpened()"
class="header link"
:to="{
name: `${moduleName ?? route.meta.moduleName}Summary`,
params: { id: entityId || entity.id },
}"
>
<QIcon name="open_in_new" color="white" size="sm" />
</router-link>
<span v-else></span>
</slot>
<slot name="header" :entity="entity" dense>
<VnLv :label="`${entity.id} -`" :value="entity.name" />
</slot>
<span class="row no-wrap">
<slot name="header-right" :entity="entity" />
<VnMoreOptions v-if="$slots.menu && isDialogOpened()">
<template #menu="{ menuRef }">
<slot name="menu" :entity="entity" :menu-ref="menuRef" />
</template>
</VnMoreOptions>
</span>
</div>
<div class="summaryBody row q-mb-md">
<slot name="body" :entity="entity" />
</div>
</template>
</QCard>
</div>
</template>
<style lang="scss">
.summary.container {
display: flex;
justify-content: center;
}
.cardSummary {
width: 100%;
max-height: 70vh;
.summaryHeader {
text-align: center;
font-size: 20px;
display: flex;
justify-content: space-between;
}
.summaryBody {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
gap: 10px;
padding: 10px;
background-color: var(--vn-section-color);
> .q-card.vn-one {
flex: 1;
}
> .q-card.vn-two {
flex: 40%;
}
> .q-card.vn-three {
flex: 75%;
}
> .q-card.vn-max {
flex: 100%;
}
> .q-card {
width: 100%;
background-color: var(--vn-section-color);
padding: 7px;
font-size: 16px;
min-width: 275px;
box-shadow: none;
.vn-label-value {
&.negative > .value span {
color: $alert;
}
display: flex;
flex-direction: row;
margin-top: 2px;
.label {
color: var(--vn-label-color);
width: 9em;
overflow: hidden;
white-space: wrap;
text-overflow: ellipsis;
margin-right: 10px;
flex-grow: 0;
flex-shrink: 0;
}
.value {
color: var(--vn-text-color);
overflow: hidden;
}
}
.header {
color: $primary;
font-weight: bold;
margin-bottom: 10px;
font-size: 20px;
display: inline-block;
}
.header.link:hover {
color: lighten($primary, 20%);
}
.q-checkbox {
& .q-checkbox__label {
color: var(--vn-text-color);
}
& .q-checkbox__inner {
color: var(--vn-label-color);
}
}
}
}
@media (max-width: $breakpoint-xs) {
.summaryBody {
padding: 0;
}
}
}
</style>
<style lang="scss" scoped>
.summaryHeader .vn-label-value {
display: flex;
flex-direction: row;
}
.summaryHeader {
color: $white;
}
</style>