Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add part of dashboard #1012

Merged
merged 10 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 13 additions & 17 deletions src/components/va-charts/VaChart.vue
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
<template>
<!-- @vue-ignore -->
<Component :is="chartComponent" ref="chart" :chart-data="data" :chart-options="chartOptions" class="va-chart" />
<component :is="chartComponent" :chart-data="data" :chart-options="chartOptions" class="va-chart" />
</template>

<script lang="ts" setup>
import { computed, ref } from 'vue'
import type { TChartOptions } from 'vue-chartjs/dist/types'
<script lang="ts" setup generic="T extends 'line' | 'bar' | 'bubble' | 'doughnut' | 'pie'">
import { computed } from 'vue'
import type { TChartOptions, TChartData } from 'vue-chartjs/dist/types'
import { defaultConfig, chartTypesMap } from './vaChartConfigs'
import { TChartData } from '../../data/types'

const props = defineProps<{
data: TChartData
options?: TChartOptions<'line' | 'bar' | 'bubble' | 'doughnut' | 'pie'>
type: keyof typeof chartTypesMap
data: TChartData<T>
options?: TChartOptions<T>
type: T
}>()

const chart = ref()
const chartComponent = chartTypesMap[props.type]

const chartComponent = computed(() => chartTypesMap[props.type])

const chartOptions = computed(() => ({
...defaultConfig,
const chartOptions = computed<TChartOptions<T>>(() => ({
...(defaultConfig as any),
...props.options,
}))
</script>

<style lang="scss">
.va-chart {
width: 100%;
height: 100%;
min-width: 100%;
min-height: 100%;
display: flex;
align-items: center;
justify-content: center;
Expand All @@ -41,7 +37,7 @@ const chartOptions = computed(() => ({
canvas {
width: 100%;
height: auto;
min-height: 320px;
min-height: 20px;
}
}
</style>
45 changes: 44 additions & 1 deletion src/components/va-charts/chart-types/LineChart.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<template>
<Line :chart-data="props.chartData" :chart-options="chartOptions" />
<Line ref="chart" :chart-data="computedChartData" :chart-options="chartOptions" />
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import { Line } from 'vue-chartjs'
import type { TChartOptions } from 'vue-chartjs/dist/types'
import {
Expand All @@ -17,11 +18,53 @@ import {
Filler,
} from 'chart.js'
import { TLineChartData } from '../../../data/types'
import { computed } from 'vue'
import { useColors } from 'vuestic-ui/web-components'

ChartJS.register(Title, Tooltip, Legend, LineElement, LinearScale, PointElement, CategoryScale, Filler)

const chart = ref<typeof Line>()

const props = defineProps<{
chartData: TLineChartData
chartOptions?: TChartOptions<'line'>
}>()

const ctx = computed(() => {
if (!chart.value) {
return null
}

return chart.value.chart.ctx as CanvasRenderingContext2D
})

const { setHSLAColor, getColor } = useColors()

const colors = ['primary', 'success', 'danger', 'warning']

const computedChartData = computed(() => {
if (!ctx.value) {
return props.chartData
}

const makeGradient = (bg: string) => {
const gradient = ctx.value!.createLinearGradient(0, 0, 0, 70)
gradient.addColorStop(0, setHSLAColor(bg, { a: 0.3 }))
gradient.addColorStop(1, setHSLAColor(bg, { a: 0.05 }))
return gradient
}

const datasets = props.chartData.datasets.map((dataset, index) => {
const color = getColor(colors[index % colors.length])

return {
...dataset,
fill: true,
backgroundColor: makeGradient(color),
borderColor: color,
}
})

return { ...props.chartData, datasets }
})
</script>
15 changes: 8 additions & 7 deletions src/components/va-charts/vaChartConfigs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineAsyncComponent } from 'vue'
import { defineAsyncComponent, markRaw } from 'vue'

const DEFAULT_FONT_FAMILY = "'Inter', sans-serif"

Expand Down Expand Up @@ -57,6 +57,7 @@ export const defaultConfig = {
}

export const doughnutConfig = {
cutout: '80%',
scales: {
x: {
display: false,
Expand Down Expand Up @@ -97,10 +98,10 @@ export const doughnutConfig = {
}

export const chartTypesMap = {
pie: defineAsyncComponent(() => import('./chart-types/PieChart.vue')),
doughnut: defineAsyncComponent(() => import('./chart-types/DoughnutChart.vue')),
bubble: defineAsyncComponent(() => import('./chart-types/BubbleChart.vue')),
line: defineAsyncComponent(() => import('./chart-types/LineChart.vue')),
bar: defineAsyncComponent(() => import('./chart-types/BarChart.vue')),
'horizontal-bar': defineAsyncComponent(() => import('./chart-types/HorizontalBarChart.vue')),
pie: markRaw(defineAsyncComponent(() => import('./chart-types/PieChart.vue'))),
doughnut: markRaw(defineAsyncComponent(() => import('./chart-types/DoughnutChart.vue'))),
bubble: markRaw(defineAsyncComponent(() => import('./chart-types/BubbleChart.vue'))),
line: markRaw(defineAsyncComponent(() => import('./chart-types/LineChart.vue'))),
bar: markRaw(defineAsyncComponent(() => import('./chart-types/BarChart.vue'))),
'horizontal-bar': markRaw(defineAsyncComponent(() => import('./chart-types/HorizontalBarChart.vue'))),
}
76 changes: 76 additions & 0 deletions src/components/va-timeline-item.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<template>
<tr class="va-timeline-item">
<td class="va-timeline-item__icon-cell">
<div class="va-timeline-item__icon">
<VaIcon name="schedule" size="22px" color="backgroundBorder" />
</div>
</td>
<td class="va-timeline-item__content-cell">
<div class="va-timeline-item__content">
<slot />
</div>
</td>
<td class="va-timeline-item__date-cell">
<slot name="date">
{{ $props.date }}
</slot>
</td>
</tr>
</template>

<script setup lang="ts">
defineProps({
date: {
type: String,
default: '',
},
})
</script>

<style lang="scss" scoped>
.va-timeline-item {
display: table-row;

&__icon-cell {
vertical-align: top;
height: 1px;
padding-right: 1rem;
}

&__icon {
width: 24px;
position: relative;
display: inline-flex;
justify-content: center;
flex-direction: column;
align-items: center;
height: 100%;

&::after {
content: '';
width: 2px;
height: 100%;
background: var(--va-background-border);
}
}

&__content {
margin-bottom: 1rem;
}

&__date-cell {
vertical-align: top;
color: var(--va-secondary);
text-wrap: nowrap;
padding-left: 0.5rem;
}

&:last-child {
.va-timeline-item__icon {
&::after {
background: transparent;
}
}
}
}
</style>
16 changes: 0 additions & 16 deletions src/data/charts/lineChartData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,7 @@ export const lineChartData: TLineChartData = {
datasets: [
{
label: 'Monthly Earnings',
fill: false,
lineTension: 0.1,
backgroundColor: 'rgba(75,192,192,0.4)',
borderColor: 'rgba(75,192,192,1)',
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
pointBorderColor: 'rgba(75,192,192,1)',
pointBackgroundColor: '#fff',
pointBorderWidth: 1,
pointHoverRadius: 5,
pointHoverBackgroundColor: 'rgba(75,192,192,1)',
pointHoverBorderColor: 'rgba(220,220,220,1)',
pointHoverBorderWidth: 2,
pointRadius: 1,
pointHitRadius: 10,
data: [65, 59, 80, 81, 56, 55, 40], // Random values
},
],
Expand Down
42 changes: 20 additions & 22 deletions src/data/geo.json
Original file line number Diff line number Diff line change
Expand Up @@ -31593,6 +31593,26 @@
"geometry": {
"type": "Polygon",
"coordinates": [
[
[33.435988094713366, 45.971917370797485],
[33.69946184910907, 46.219572831556434],
[34.41040172853718, 46.005162391728845],
[34.73201738827845, 45.96566573176062],
[34.861792128174045, 45.76818243191957],
[35.01265897004737, 45.73772519982549],
[35.02078779474607, 45.65121898048466],
[35.51000857925311, 45.40999339454612],
[36.52999799983019, 45.46998973243717],
[36.334712762199274, 45.11321564389402],
[35.239999220528205, 44.93999624285175],
[33.882511020652885, 44.361478583344194],
[33.32642093276013, 44.564877020844904],
[33.546924269349404, 45.03477081967486],
[32.4541744321055, 45.327466132176085],
[32.63080447767919, 45.51918569597899],
[33.58816206231842, 45.85156850848023],
[33.435988094713366, 45.971917370797485]
],
[
[31.78599244755525, 52.1016775699397],
[32.15944000000013, 52.06125000000014],
Expand Down Expand Up @@ -32951,28 +32971,6 @@
[-177.663575, 71.13277],
[-178.69378, 70.89302]
]
],
[
[
[33.435988094713366, 45.971917370797485],
[33.69946184910907, 46.219572831556434],
[34.41040172853718, 46.005162391728845],
[34.73201738827845, 45.96566573176062],
[34.861792128174045, 45.76818243191957],
[35.01265897004737, 45.73772519982549],
[35.02078779474607, 45.65121898048466],
[35.51000857925311, 45.40999339454612],
[36.52999799983019, 45.46998973243717],
[36.334712762199274, 45.11321564389402],
[35.239999220528205, 44.93999624285175],
[33.882511020652885, 44.361478583344194],
[33.32642093276013, 44.564877020844904],
[33.546924269349404, 45.03477081967486],
[32.4541744321055, 45.327466132176085],
[32.63080447767919, 45.51918569597899],
[33.58816206231842, 45.85156850848023],
[33.435988094713366, 45.971917370797485]
]
]
]
}
Expand Down
12 changes: 7 additions & 5 deletions src/data/pages/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ export type Sorting = {
export const getProjects = async (options: Sorting & Pagination) => {
await sleep(1000)

const projects = projectsDb.map((project) => ({
...project,
project_owner: usersDb.find((user) => user.id === project.project_owner)! as (typeof usersDb)[number],
team: usersDb.filter((user) => project.team.includes(user.id)) as (typeof usersDb)[number][],
}))
const projects = projectsDb
.slice((options.page - 1) * options.perPage, options.page * options.perPage)
.map((project) => ({
...project,
project_owner: usersDb.find((user) => user.id === project.project_owner)! as (typeof usersDb)[number],
team: usersDb.filter((user) => project.team.includes(user.id)) as (typeof usersDb)[number][],
}))

if (options.sortBy && options.sortingOrder) {
projects.sort((a, b) => {
Expand Down
44 changes: 27 additions & 17 deletions src/pages/admin/dashboard/Dashboard.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
<script lang="ts" setup>
import RevenueUpdates from './cards/RevenueReport.vue'
import ProjectTable from './cards/ProjectTable.vue'
import RevenueByLocationMap from './cards/RevenueByLocationMap.vue'
import DataSection from './DataSection.vue'
import YearlyBreakup from './cards/YearlyBreakup.vue'
import MonthlyEarnings from './cards/MonthlyEarnings.vue'
import RegionRevenue from './cards/RegionRevenue.vue'
import Timeline from './cards/Timeline.vue'
</script>

<template>
<h1 class="h1">Dashboard</h1>
<section class="grid grid-cols-8 gap-4">
<RevenueUpdates class="col-span-5" />
<div class="col-span-3 flex">
<YearlyBreakup />
<section class="flex flex-col gap-4">
<div class="flex flex-col sm:flex-row gap-4">
<RevenueUpdates class="w-full sm:w-2/3" />
<div class="flex flex-col gap-4 w-full sm:w-1/3">
<YearlyBreakup />
<MonthlyEarnings />
</div>
</div>
<DataSection />
<div class="flex flex-col md:flex-row gap-4">
<RevenueByLocationMap class="w-full md:w-3/5" />
<RegionRevenue class="w-full md:w-2/5" />
</div>
<div class="flex flex-col md:flex-row gap-4">
<ProjectTable class="w-full md:w-1/2" />
<Timeline class="w-full md:w-1/2" />
</div>
<DataSection class="col-span-8" />
<RevenueByLocationMap class="col-span-5" />
<div class="col-span-3">todo</div>
<ProjectTable class="col-span-4" />
<div class="col-span-4">todo</div>
</section>
</template>

<script lang="ts" setup>
import RevenueUpdates from './RevenueUpdates.vue'
import ProjectTable from './ProjectTable.vue'
import RevenueByLocationMap from './RevenueByLocationMap.vue'
import DataSection from './DataSection.vue'
import YearlyBreakup from './YearlyBreakup.vue'
</script>
Loading