Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 7 additions & 1 deletion frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<script setup>
import { inject, provide } from 'vue'
import { inject, provide, watchEffect } from 'vue'
import { RouterView } from 'vue-router'

import { useRequestsStateStore } from '@/stores/requestsState'
import { useThemeStore } from '@/stores/theme'
import NavigationBar from '@/components/NavigationBar.vue'
import RequestsState from '@/components/RequestsState.vue'

const axios = inject('axios')
const requestsState = useRequestsStateStore()
const theme = useThemeStore()

/**
* Universal data getter
Expand All @@ -27,6 +29,10 @@ async function getData(urlPath, ...params) {

// Provide data getter to whole app
provide('getData', getData)

watchEffect(() => {
document.documentElement.setAttribute('data-bs-theme', theme.theme)
})
</script>

<template>
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/assets/logo-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
6 changes: 4 additions & 2 deletions frontend/src/components/ActivityClassBadge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ defineProps(['value'])
</script>

<template>
<span v-if="value == 'off'" class="badge bg-dark-subtle">
<span v-if="value == 'off'" class="badge bg-dark-subtle text-reset">
<i class="fa fa-power-off me-2" aria-hidden="true"></i>Off
</span>
<span v-else-if="value == 'idle'" class="badge bg-dark-subtle border border-white">Idle</span>
<span v-else-if="value == 'idle'" class="badge bg-dark-subtle border border-secondary text-reset"
>Idle</span
>
<span v-else-if="value == 'light'" class="badge bg-info text-dark">Light</span>
<span v-else-if="value == 'medium'" class="badge bg-warning text-dark">Medium</span>
<span v-else-if="value == 'high'" class="badge bg-danger">High</span>
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/ActivityTimeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import {
CHART_SCALE_X_OPTIONS,
resampleTimedData,
setChartDatetimeRange,
themeTextColor,
} from '@/utils/commonCharts.js'
import { useThemeStore } from '@/stores/theme'

const themeStore = useThemeStore()

ChartJS.register(...registerables, annotationPlugin)
ChartJS.defaults.color = '#dee2e6'
ChartJS.defaults.borderColor = '#495057'

const props = defineProps({
Expand Down Expand Up @@ -44,12 +47,13 @@ const props = defineProps({
})

const CHART_UNITS = ['pkt', 'flw', 'B']
const CHART_COLORS = ['#0DCAF0', '#198754', '#FFC107']
const CHART_COLORS = ['#3498DB', '#198754', '#F39C12']

const DIRECTION_PREFIX = props.incomingDirection ? 'in_' : 'out_'

const chartOptions = computed(() => {
let scaleXOptions = { ...CHART_SCALE_X_OPTIONS }
scaleXOptions.ticks.color = themeTextColor(themeStore.isDark)

// Set the time range of the chart
setChartDatetimeRange(scaleXOptions, props.timePickerState.from, props.timePickerState.to)
Expand Down
19 changes: 16 additions & 3 deletions frontend/src/components/NavigationBar.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
<script setup>
import { RouterLink, useRoute } from 'vue-router'

import { useThemeStore } from '@/stores/theme'
import ThemeToggle from '@/components/ThemeToggle.vue'

const route = useRoute()
const theme = useThemeStore()
</script>

<template>
<nav class="navbar bg-body-tertiary navbar-expand-lg">
<div class="container">
<div class="navbar-brand">
<RouterLink :to="{ name: 'home' }" class="navbar-brand">
<img src="@/assets/logo.svg" alt="PANDDA" class="logo" />
<img
v-if="theme.theme === 'dark'"
src="@/assets/logo-white.svg"
alt="PANDDA"
class="logo"
/>
<img v-else src="@/assets/logo-black.svg" alt="PANDDA" class="logo" />
</RouterLink>
</div>
<button
Expand All @@ -19,8 +29,8 @@ const route = useRoute()
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse row" id="navbarSupportedContent">
<ul class="navbar-nav px-2">
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav px-2 me-auto">
<li class="nav-item">
<RouterLink
:to="{ name: 'home' }"
Expand All @@ -42,6 +52,9 @@ const route = useRoute()
</RouterLink>
</li>
</ul>
<div class="m-2">
<ThemeToggle />
</div>
</div>
</div>
</nav>
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/ObservationsTimeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import {
CHART_SCALE_X_OPTIONS,
resampleTimedData,
setChartDatetimeRange,
themeTextColor,
} from '@/utils/commonCharts.js'
import { useThemeStore } from '@/stores/theme'

const themeStore = useThemeStore()

ChartJS.register(...registerables, annotationPlugin)
ChartJS.defaults.color = '#dee2e6'
ChartJS.defaults.borderColor = '#495057'

const props = defineProps({
Expand Down Expand Up @@ -55,6 +58,7 @@ const props = defineProps({

const chartOptions = computed(() => {
let scaleXOptions = { ...CHART_SCALE_X_OPTIONS }
scaleXOptions.ticks.color = themeTextColor(themeStore.isDark)

// Set the time range of the chart
setChartDatetimeRange(scaleXOptions, props.timePickerState.from, props.timePickerState.to)
Expand All @@ -69,6 +73,7 @@ const chartOptions = computed(() => {
offset: true,
ticks: {
display: true,
color: themeTextColor(themeStore.isDark),
},
grid: {
display: false,
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/components/ThemeToggle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<button
type="button"
class="btn btn-dark"
:aria-label="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
:title="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
@click="theme.setTheme(isDark ? 'light' : 'dark')"
>
<span v-if="isDark" aria-hidden="true">☀️</span>
<span v-else aria-hidden="true">🌙</span>
</button>
</template>

<script setup>
import { computed } from 'vue'
import { useThemeStore } from '@/stores/theme'

const theme = useThemeStore()
const isDark = computed(() => theme.theme === 'dark')
</script>
33 changes: 33 additions & 0 deletions frontend/src/stores/theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

/**
* Tracking of the current theme (dark or light).
*/
export const useThemeStore = defineStore('theme', () => {
const STORAGE_KEY = 'theme'
const theme = ref(initialTheme())

/**
* Determine the initial theme mode based on local storage or system preference.
*/
function initialTheme() {
const saved = localStorage.getItem(STORAGE_KEY)
if (saved === 'dark' || saved === 'light') return saved
return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}

/**
* Sets the current theme.
*/
function setTheme(newTheme) {
theme.value = newTheme === 'dark' ? 'dark' : 'light'
localStorage.setItem(STORAGE_KEY, theme.value)
}

return {
theme: computed(() => theme.value),
isDark: computed(() => theme.value === 'dark'),
setTheme,
}
})
7 changes: 7 additions & 0 deletions frontend/src/utils/commonCharts.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export const CHART_COMMON_OPTIONS = {
maintainAspectRatio: false,
}

/**
* Gets text color based on current theme
*/
export function themeTextColor(isDark) {
return isDark ? '#DEE2E6' : '#212529'
}

/**
* Sets chart datetime range if both `dtFrom` and `dtTo` are provided
* @param {Object} chartScaleXOptions Options for the X axis
Expand Down