Skip to content

Commit

Permalink
Merge pull request #17 from MarceloVichar/feature/company-dash
Browse files Browse the repository at this point in the history
dash client
  • Loading branch information
MarceloVichar authored Jan 24, 2024
2 parents 5b8d3d6 + e1bf9b3 commit ba80623
Show file tree
Hide file tree
Showing 18 changed files with 740 additions and 7 deletions.
91 changes: 91 additions & 0 deletions components/app/company/dashboard/FiltersForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<template>
<VeeForm v-slot="{ meta }" class="flex justify-end items-center gap-2 w-full" @submit="onSubmit">
<VeeField
v-slot="{ field, errors }"
v-model="mutableForm.startDate"
name="startDate"
:rules="() => ({ required: true, minDate: minDate, maxDate: mutableForm.endDate })"
>
<CustomDatePicker
v-bind="field"
v-model="field.value"
required
class="w-full md:w-56"
:max-date="mutableForm.endDate || null"
placeholder="Data inicial"
label="A partir de"
:errors="errors"
/>
</VeeField>
<VeeField
v-slot="{ field, errors }"
v-model="mutableForm.endDate"
name="endDate"
:rules="() => ({ required: true, minDate: mutableForm.startDate, maxDate: maxDate })"
>
<CustomDatePicker
v-bind="field"
v-model="field.value"
required
class="w-full md:w-56"
:min-date="mutableForm.startDate || null"
:max-date="maxDate"
end-of-day
placeholder="Data final"
label="Até"
:errors="errors"
/>
</VeeField>
<button
:disabled="!meta.valid || !datesAreValid"
type="submit"
class="btn btn-primary"
>
Buscar
</button>
</VeeForm>
</template>

<script setup>
import CustomDatePicker from '~/components/shared/form/CustomDatePicker.vue';
const props = defineProps({
modelValue: {
type: Object,
required: true,
},
})
const emit = defineEmits(['submit', 'update:modelValue'])
const form = _cloneDeep(props.modelValue)
const mutableForm = reactive(form)
watch(
() => props.modelValue,
(newValue) => {
Object.assign(mutableForm, newValue)
},
{ immediate: true, deep: true },
)
const onSubmit = () => {
if (props.sending) return
emit('update:modelValue', mutableForm)
emit('submit')
}
const minDate = computed(() => {
return mutableForm.endDate ? useDayjs()(mutableForm.endDate).subtract(31, 'day').toISOString() : undefined
})
const maxDate = computed(() => {
return mutableForm.startDate ? useDayjs()(mutableForm.startDate).add(31, 'day').toISOString() : undefined
})
const datesAreValid = computed(() => {
const startDate = useDayjs()(mutableForm.startDate)
const endDate = useDayjs()(mutableForm.endDate)
return startDate.diff(endDate, 'day') <= 31 && endDate.diff(startDate, 'day') <= 31
})
</script>
64 changes: 64 additions & 0 deletions components/app/company/dashboard/events/EventInfosPieChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<template>
<div class="grid w-full p-2 overflow-hidden bg-base-100 shadow rounded-lg">
<p class="text-sm">
Ocupação em eventos
</p>
<VChart :option="chartOptions" class="h-72" />
</div>
</template>

<script setup>
import VChart from 'vue-echarts';
const props = defineProps({
availableSeats: {
type: Number,
required: true,
},
occupiedSeats: {
type: Number,
required: true,
},
});
const chartOptions = ref({
tooltip: {
trigger: 'item',
},
series: [
{
radius: ['50%', '90%'],
name: 'Lugares',
type: 'pie',
avoidLabelOverlap: true,
emphasis: {
label: {
show: true,
fontSize: 16,
},
},
label: {
show: false,
position: 'center',
},
data: [],
color: ['#10B981', '#F59E0B'],
},
],
});
watch(() => props.availableSeats, () => setChartData());
watch(() => props.occupiedSeats, () => setChartData());
onMounted(
() => setChartData(),
)
const setChartData = () => {
chartOptions.value.series[0].data = [
{value: props.availableSeats, name: 'Disponíveis'},
{value: props.occupiedSeats, name: 'Ocupados'},
];
};
</script>
53 changes: 53 additions & 0 deletions components/app/company/dashboard/events/EventsInfos.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<template>
<div>
<span>Eventos</span>
<div v-if="pending" class="flex justify-center items-center py-12">
<Loader />
</div>
<div v-else class="grid gap-4">
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
<Card
title="Total de eventos"
type="info"
:text="data?.totalEvents"
class="sm:col-span-2 lg:col-span-1"
/>
<Card title="Total de lugares" type="warning" :text="data?.totalSeats" />
<Card title="Lugares disponíveis" type="success" :text="data?.totalAvailableSeats" />
</div>
<div class="grid lg:grid-cols-3 gap-4 w-full">
<EventsPerDayBarChart class="lg:col-span-2" :events="data?.datesWithEvents" />
<EventSalesPieChart
:available-seats="data?.totalAvailableSeats"
:occupied-seats="data?.totalSeats - data?.totalAvailableSeats"
/>
</div>
</div>
</div>
</template>

<script setup>
import Card from '~/components/shared/Card.vue';
import DashboardService from '~/services/api/company/dashboard/DashboardService';
import EventSalesPieChart from '~/components/app/company/dashboard/events/EventInfosPieChart.vue';
import EventsPerDayBarChart from '~/components/app/company/dashboard/events/EventsPerDayBarChart.vue';
import Loader from '~/components/shared/Loader.vue';
const dashboardService = new DashboardService();
const route = useRoute()
async function fetchEvents() {
if (!route.query.startDate || !route.query.endDate) {
return
}
return dashboardService.getEventsInfos(route.query)
.catch(() => {
useNotify('error', 'Ops! Ocorreu algum erro, tente novamente mais tarde.')
})
}
const {pending, data, refresh} = useLazyAsyncData('eventsInfos', await fetchEvents)
useRouteQueryWatcher(refresh)
</script>
63 changes: 63 additions & 0 deletions components/app/company/dashboard/events/EventsPerDayBarChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<template>
<div class="grid w-full p-2 overflow-hidden bg-base-100 shadow rounded-lg">
<p class="text-sm">
Eventos por dia
</p>
<VChart :option="chartOptions" class="h-72" />
</div>
</template>

<script setup>
import VChart from 'vue-echarts';
const props = defineProps({
events: {
type: Array,
required: true,
},
});
const chartOptions = ref({
tooltip: {
trigger: 'item',
axisPointer: {
type: 'shadow',
},
},
color: '#33C8B6',
xAxis: [
{
type: 'category',
data: [],
axisTick: {
alignWithLabel: true,
},
},
],
yAxis: [
{
type: 'value',
},
],
series: [
{
name: 'Eventos',
type: 'bar',
barWidth: '60%',
data: [],
},
],
});
watch(() => props.events, () => setChartData());
onMounted(
() => setChartData(),
)
const setChartData = () => {
chartOptions.value.xAxis[0].data = props.events?.map(event => useDayjs()(event?.date).format('DD/MM'))
chartOptions.value.series[0].data = props.events?.map(event => event?.count)
};
</script>
67 changes: 67 additions & 0 deletions components/app/company/dashboard/sales/SalesInfos.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<template>
<div>
<span>Vendas</span>
<div v-if="pending" class="flex justify-center items-center py-12">
<Loader />
</div>
<div v-else class="grid gap-4">
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
<Card
title="Total confirmado"
type="success"
:text="useFormattedRealMoney(confirmedSales?.amountCents || 0)"
class="sm:col-span-2 lg:col-span-1"
/>
<Card
title="Vendas confirmadas"
type="success"
:text="confirmedSales?.count || 0"
/>
<Card
title="Vendas pendentes"
type="info"
:text="pendingSales?.count || 0"
/>
</div>
<div class="grid lg:grid-cols-3 gap-4 w-full">
<SalesPerDayBarChart :sales="data?.salesByDate" class="lg:col-span-2" />
<SalesInfosPieChart :sales-by-status="data?.salesByStatus" />
</div>
</div>
</div>
</template>

<script setup>
import Card from '~/components/shared/Card.vue';
import DashboardService from '~/services/api/company/dashboard/DashboardService';
import Loader from '~/components/shared/Loader.vue';
import {SaleStatusEnum} from '~/enums/SaleStatusEnum';
import SalesInfosPieChart from '~/components/app/company/dashboard/sales/SalesInfosPieChart.vue';
import SalesPerDayBarChart from '~/components/app/company/dashboard/sales/SalesPerDayBarChart.vue';
const dashboardService = new DashboardService();
const route = useRoute()
async function fetchEvents() {
if (!route.query.startDate || !route.query.endDate) {
return
}
return dashboardService.getSalesInfos(route.query)
.catch(() => {
useNotify('error', 'Ops! Ocorreu algum erro, tente novamente mais tarde.')
})
}
const {pending, data, refresh} = useLazyAsyncData('salesInfo', await fetchEvents)
const confirmedSales = computed(() => {
return data.value?.salesByStatus?.find(sale => sale?.status === SaleStatusEnum.CONFIRMED)
})
const pendingSales = computed(() => {
return data.value?.salesByStatus?.find(sale => sale?.status === SaleStatusEnum.PENDING)
})
useRouteQueryWatcher(refresh)
</script>
Loading

0 comments on commit ba80623

Please sign in to comment.