General UI Refinements (#1678)

Does the following:
- Adjusts the shade of red buttons and banners to increase readability
and reduce eye strain

![image](https://github.com/user-attachments/assets/7f741a9e-dc1e-4394-b87d-580e189245b1)

![image](https://github.com/user-attachments/assets/b23202f1-4cf6-46c1-aca5-2455a09259cd)

- Cleans up factory reset and camera deletion modals

![image](https://github.com/user-attachments/assets/e6564732-d578-43da-bc83-729ec6fdbc5e)

![image](https://github.com/user-attachments/assets/9c5a1cba-f4fd-47ea-811c-abbabe5fa3a4)

- Removes matchCamerasOnlyByPath as it is no longer used and throws
errors in the console

![image](https://github.com/user-attachments/assets/77043993-26a2-4de4-8e98-702e7f285dc6)

- Limits the criteria to flag a camera mismatch in Camera Matching to
only what is necessary based on camera type and highlights differences
in table properties (testing on this is appreciated)

![image](https://github.com/user-attachments/assets/cfbd96c1-09dd-414a-8177-693fc054b26f)

- Only displays both saved vs. current info in camera matching if there
is a difference between the two

![image](https://github.com/user-attachments/assets/6223ffc8-4cff-464f-8b54-720c3222a5d5)

- Some general code cleanup (reduced unnecessary padding/margin/row-col
statements, style="display:flex;" -> class="d-flex", etc.
- Moves Compact Mode button to the bottom away from all the menu items
(cleaner imo, open to thoughts)
- Establishes a general spacing format for cards and pages and applies
this to existing cards and pages to create a consistent look and feel to
the UI (e.g. keeping things in line and less erratic spacing/placement
of UI elements)

![image](https://github.com/user-attachments/assets/1ab0ca4b-303e-436d-97b3-da72d46c4fcb)

![image](https://github.com/user-attachments/assets/82ba9e53-f854-4309-bc00-7b5d0bad58b7)

![image](https://github.com/user-attachments/assets/18aa6ca4-e6fa-4125-8a0a-e6a007a0337d)

![image](https://github.com/user-attachments/assets/77043993-26a2-4de4-8e98-702e7f285dc6)


- Delete protection for camera matching modules
- Anti-backend-spam for activate/deactivate/delete modules to hopefully
prevent any odd behavior from button spamming
- Enforces a common camera stream size on camera matching view (NEEDS
MORE TESTING)

![image](https://github.com/user-attachments/assets/9032783d-1edf-4c6e-ba7b-00e5f20280df)

https://private-user-images.githubusercontent.com/29715865/400783758-dc99c151-b8a7-4367-a173-74c2fc5b2666.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzYyNTc3NzEsIm5iZiI6MTczNjI1NzQ3MSwicGF0aCI6Ii8yOTcxNTg2NS80MDA3ODM3NTgtZGM5OWMxNTEtYjhhNy00MzY3LWExNzMtNzRjMmZjNWIyNjY2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAxMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMTA3VDEzNDQzMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWMwOWM1MDc2ZTVlOWZhM2MxYjAwZjAyZTc2MTYyZTk1ZTVmOGFhZmVkMzlmODRlZTk1ODVlOTk2ZGQzZmM0Y2EmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.ovtRnObwbkEfljr9d5fqaory0nH91LWJSSkmrUUe_4Y
This commit is contained in:
Devon Doyle
2025-01-07 08:45:39 -05:00
committed by GitHub
parent fa2034d30b
commit 484e8d4298
34 changed files with 1098 additions and 965 deletions

View File

@@ -1,15 +1,10 @@
<script setup lang="ts">
import { PVCameraInfo } from "@/types/SettingTypes";
const { camera, showTitle } = defineProps({
const { camera } = defineProps({
camera: {
type: PVCameraInfo,
required: true
},
showTitle: {
type: Boolean,
required: false,
default: true
}
});
@@ -29,12 +24,6 @@ const cameraInfoFor: any = (camera: PVCameraInfo) => {
<template>
<div>
<div v-if="showTitle === true">
<h3 v-if="camera.PVUsbCameraInfo" class="mb-3">USB Camera Info</h3>
<h3 v-if="camera.PVCSICameraInfo" class="mb-3">CSI Camera Info</h3>
<h3 v-if="camera.PVFileCameraInfo" class="mb-3">File Camera Info</h3>
</div>
<v-simple-table dense :style="{ backgroundColor: 'var(--v-primary-base)' }">
<tbody>
<tr v-if="cameraInfoFor(camera).dev !== undefined && cameraInfoFor(camera).dev !== null">
@@ -45,6 +34,13 @@ const cameraInfoFor: any = (camera: PVCameraInfo) => {
<td>Name:</td>
<td>{{ cameraInfoFor(camera).name }}</td>
</tr>
<tr>
<td>Type:</td>
<td v-if="camera.PVUsbCameraInfo" class="mb-3">USB Camera</td>
<td v-else-if="camera.PVCSICameraInfo" class="mb-3">CSI Camera</td>
<td v-else-if="camera.PVFileCameraInfo" class="mb-3">File Camera</td>
<td v-else>Unidentified Camera Type</td>
</tr>
<tr v-if="cameraInfoFor(camera).baseName !== undefined && cameraInfoFor(camera).baseName !== null">
<td>Base Name:</td>
<td>{{ cameraInfoFor(camera).baseName }}</td>

View File

@@ -1,12 +1,13 @@
<script setup lang="ts">
import { PVCameraInfo } from "@/types/SettingTypes";
import _ from "lodash";
const { saved, matched } = defineProps({
const { saved, current } = defineProps({
saved: {
type: PVCameraInfo,
required: true
},
matched: {
current: {
type: PVCameraInfo,
required: true
}
@@ -28,58 +29,95 @@ const cameraInfoFor = (camera: PVCameraInfo): any => {
<template>
<div>
<h3 v-if="saved.PVUsbCameraInfo" class="mb-3">USB Camera Info</h3>
<h3 v-if="saved.PVCSICameraInfo" class="mb-3">CSI Camera Info</h3>
<h3 v-if="saved.PVFileCameraInfo" class="mb-3">File Camera Info</h3>
<v-simple-table dense :style="{ backgroundColor: 'var(--v-primary-base)' }">
<tbody>
<tr>
<th></th>
<th>Saved</th>
<th>Matched</th>
<th>Current</th>
</tr>
<tr v-if="cameraInfoFor(saved).dev !== undefined && cameraInfoFor(saved).dev !== null">
<tr
v-if="cameraInfoFor(saved).dev !== undefined && cameraInfoFor(saved).dev !== null"
:class="cameraInfoFor(saved).dev !== cameraInfoFor(current).dev ? 'mismatch' : ''"
>
<td>Device Number:</td>
<td>{{ cameraInfoFor(saved).dev }}</td>
<td>{{ cameraInfoFor(matched).dev }}</td>
<td>{{ cameraInfoFor(current).dev }}</td>
</tr>
<tr v-if="cameraInfoFor(saved).name !== undefined && cameraInfoFor(saved).name !== null">
<tr
v-if="cameraInfoFor(saved).name !== undefined && cameraInfoFor(saved).name !== null"
:class="cameraInfoFor(saved).name !== cameraInfoFor(current).name ? 'mismatch' : ''"
>
<td>Name:</td>
<td>{{ cameraInfoFor(saved).name }}</td>
<td>{{ cameraInfoFor(matched).name }}</td>
<td>{{ cameraInfoFor(current).name }}</td>
</tr>
<tr v-if="cameraInfoFor(saved).baseName !== undefined && cameraInfoFor(saved).baseName !== null">
<tr
v-if="cameraInfoFor(saved).baseName !== undefined && cameraInfoFor(saved).baseName !== null"
:class="cameraInfoFor(saved).baseName !== cameraInfoFor(current).baseName ? 'mismatch' : ''"
>
<td>Base Name:</td>
<td>{{ cameraInfoFor(saved).baseName }}</td>
<td>{{ cameraInfoFor(matched).baseName }}</td>
<td>{{ cameraInfoFor(current).baseName }}</td>
</tr>
<tr v-if="cameraInfoFor(saved).vendorId !== undefined && cameraInfoFor(saved).vendorId !== null">
<tr>
<td>Type:</td>
<td v-if="saved.PVUsbCameraInfo" class="mb-3">USB Camera</td>
<td v-else-if="saved.PVCSICameraInfo" class="mb-3">CSI Camera</td>
<td v-else-if="saved.PVFileCameraInfo" class="mb-3">File Camera</td>
<td v-else>Unidentified Camera Type</td>
<td v-if="current.PVUsbCameraInfo" class="mb-3">USB Camera</td>
<td v-else-if="current.PVCSICameraInfo" class="mb-3">CSI Camera</td>
<td v-else-if="current.PVFileCameraInfo" class="mb-3">File Camera</td>
<td v-else>Unidentified Camera Type</td>
</tr>
<tr
v-if="cameraInfoFor(saved).vendorId !== undefined && cameraInfoFor(saved).vendorId !== null"
:class="cameraInfoFor(saved).vendorId !== cameraInfoFor(current).vendorId ? 'mismatch' : ''"
>
<td>Vendor ID:</td>
<td>{{ cameraInfoFor(saved).vendorId }}</td>
<td>{{ cameraInfoFor(matched).vendorId }}</td>
<td>{{ cameraInfoFor(current).vendorId }}</td>
</tr>
<tr v-if="cameraInfoFor(saved).productId !== undefined && cameraInfoFor(saved).productId !== null">
<tr
v-if="cameraInfoFor(saved).productId !== undefined && cameraInfoFor(saved).productId !== null"
:class="cameraInfoFor(saved).productId !== cameraInfoFor(current).productId ? 'mismatch' : ''"
>
<td>Product ID:</td>
<td>{{ cameraInfoFor(saved).productId }}</td>
<td>{{ cameraInfoFor(matched).productId }}</td>
<td>{{ cameraInfoFor(current).productId }}</td>
</tr>
<tr v-if="cameraInfoFor(saved).path !== undefined && cameraInfoFor(saved).path !== null">
<tr
v-if="cameraInfoFor(saved).path !== undefined && cameraInfoFor(saved).path !== null"
:class="cameraInfoFor(saved).path !== cameraInfoFor(current).path ? 'mismatch' : ''"
>
<td>Path:</td>
<td style="word-break: break-all">{{ cameraInfoFor(saved).path }}</td>
<td style="word-break: break-all">{{ cameraInfoFor(matched).path }}</td>
<td style="word-break: break-all">{{ cameraInfoFor(current).path }}</td>
</tr>
<tr v-if="cameraInfoFor(saved).otherPaths !== undefined && cameraInfoFor(saved).otherPaths !== null">
<tr
v-if="cameraInfoFor(saved).otherPaths !== undefined && cameraInfoFor(saved).otherPaths !== null"
:class="!_.isEqual(cameraInfoFor(saved).otherPaths, cameraInfoFor(current).otherPaths) ? 'mismatch' : ''"
>
<td>Other Paths:</td>
<td>{{ cameraInfoFor(saved).otherPaths }}</td>
<td>{{ cameraInfoFor(matched).otherPaths }}</td>
<td>{{ cameraInfoFor(current).otherPaths }}</td>
</tr>
<tr v-if="cameraInfoFor(saved).uniquePath !== undefined && cameraInfoFor(saved).uniquePath !== null">
<tr
v-if="cameraInfoFor(saved).uniquePath !== undefined && cameraInfoFor(saved).uniquePath !== null"
:class="cameraInfoFor(saved).uniquePath !== cameraInfoFor(current).uniquePath ? 'mismatch' : ''"
>
<td>Unique Path:</td>
<td style="word-break: break-all">{{ cameraInfoFor(saved).uniquePath }}</td>
<td style="word-break: break-all">{{ cameraInfoFor(matched).uniquePath }}</td>
<td style="word-break: break-all">{{ cameraInfoFor(current).uniquePath }}</td>
</tr>
</tbody>
</v-simple-table>
</div>
</template>
<style scoped>
.mismatch {
background: #39a4d546 !important;
}
</style>

View File

@@ -48,26 +48,24 @@ const handleKeydown = ({ key }) => {
</script>
<template>
<div>
<v-row dense align="center">
<v-col :cols="labelCols || 12 - inputCols">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<div class="d-flex">
<v-col :cols="labelCols || 12 - inputCols" class="d-flex align-center pl-0">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="inputCols">
<v-text-field
v-model="localValue"
dark
dense
color="accent"
:placeholder="placeholder"
:disabled="disabled"
:error-messages="errorMessage"
:rules="rules"
class="mt-1 pt-2"
@keydown="handleKeydown"
/>
</v-col>
</v-row>
<v-col :cols="inputCols" class="d-flex align-center pr-0">
<v-text-field
v-model="localValue"
dark
dense
color="accent"
:placeholder="placeholder"
:disabled="disabled"
:error-messages="errorMessage"
:rules="rules"
hide-details="auto"
@keydown="handleKeydown"
/>
</v-col>
</div>
</template>

View File

@@ -31,26 +31,24 @@ const localValue = computed({
</script>
<template>
<div>
<v-row dense align="center">
<v-col :cols="labelCols">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col>
<v-text-field
v-model="localValue"
dark
class="mt-0 pt-0"
hide-details
single-line
color="accent"
type="number"
style="width: 70px"
:step="step"
:disabled="disabled"
:rules="rules"
/>
</v-col>
</v-row>
<div class="d-flex">
<v-col :cols="labelCols" class="d-flex pl-0 align-center">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col class="pr-0">
<v-text-field
v-model="localValue"
dark
class="mt-0 pt-0"
hide-details
single-line
color="accent"
type="number"
style="width: 70px"
:step="step"
:disabled="disabled"
:rules="rules"
/>
</v-col>
</div>
</template>

View File

@@ -29,23 +29,21 @@ const localValue = computed({
</script>
<template>
<div>
<v-row dense align="center">
<v-col :cols="12 - inputCols">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="inputCols">
<v-radio-group v-model="localValue" row dark :mandatory="true">
<v-radio
v-for="(radioName, index) in list"
:key="index"
color="#ffd843"
:label="radioName"
:value="index"
:disabled="disabled"
/>
</v-radio-group>
</v-col>
</v-row>
<div class="d-flex">
<v-col :cols="12 - inputCols" class="d-flex align-center pl-0">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="inputCols" class="d-flex align-center pr-0">
<v-radio-group v-model="localValue" row dark :mandatory="true" hide-details="auto">
<v-radio
v-for="(radioName, index) in list"
:key="index"
color="#ffd843"
:label="radioName"
:value="index"
:disabled="disabled"
/>
</v-radio-group>
</v-col>
</div>
</template>

View File

@@ -58,61 +58,59 @@ const checkNumberRange = (v: string): boolean => {
</script>
<template>
<div>
<v-row dense align="center">
<v-col :cols="12 - sliderCols">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="sliderCols">
<v-range-slider
v-model="localValue"
:max="max"
:min="min"
:disabled="disabled"
hide-details
class="align-center"
dark
:color="inverted ? 'rgba(255, 255, 255, 0.2)' : 'accent'"
:track-color="inverted ? 'accent' : undefined"
thumb-color="accent"
:step="step"
>
<template #prepend>
<v-text-field
:value="localValue[0]"
dark
color="accent"
class="mt-0 pt-0"
hide-details
single-line
:max="max"
:min="min"
:step="step"
:rules="[checkNumberRange]"
type="number"
style="width: 60px"
@input="(v) => changeFromSlot(v, 0)"
/>
</template>
<template #append>
<v-text-field
:value="localValue[1]"
dark
color="accent"
class="mt-0 pt-0"
hide-details
single-line
:max="max"
:min="min"
:step="step"
:rules="[checkNumberRange]"
type="number"
style="width: 60px"
@input="(v) => changeFromSlot(v, 1)"
/>
</template>
</v-range-slider>
</v-col>
</v-row>
<div class="d-flex">
<v-col :cols="12 - sliderCols" class="d-flex align-center pl-0">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="sliderCols" class="pr-0">
<v-range-slider
v-model="localValue"
:max="max"
:min="min"
:disabled="disabled"
hide-details
class="align-center"
dark
:color="inverted ? 'rgba(255, 255, 255, 0.2)' : 'accent'"
:track-color="inverted ? 'accent' : undefined"
thumb-color="accent"
:step="step"
>
<template #prepend>
<v-text-field
:value="localValue[0]"
dark
color="accent"
class="mt-0 pt-0"
hide-details
single-line
:max="max"
:min="min"
:step="step"
:rules="[checkNumberRange]"
type="number"
style="width: 60px"
@input="(v) => changeFromSlot(v, 0)"
/>
</template>
<template #append>
<v-text-field
:value="localValue[1]"
dark
color="accent"
class="mt-0 pt-0"
hide-details
single-line
:max="max"
:min="min"
:step="step"
:rules="[checkNumberRange]"
type="number"
style="width: 60px"
@input="(v) => changeFromSlot(v, 1)"
/>
</template>
</v-range-slider>
</v-col>
</div>
</template>

View File

@@ -49,24 +49,28 @@ const items = computed<SelectItem[]>(() => {
</script>
<template>
<div>
<v-row dense align="center">
<v-col :cols="12 - selectCols">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="selectCols">
<v-select
v-model="localValue"
:items="items"
item-text="name"
item-value="value"
item-disabled="disabled"
dark
color="accent"
item-color="secondary"
:disabled="disabled"
/>
</v-col>
</v-row>
<div class="d-flex">
<v-col :cols="12 - selectCols" class="d-flex align-center pl-0">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="selectCols" class="d-flex align-center pr-0">
<v-select
v-model="localValue"
:items="items"
item-text="name"
item-value="value"
item-disabled="disabled"
dark
color="accent"
item-color="secondary"
:disabled="disabled"
hide-details="auto"
/>
</v-col>
</div>
</template>
<style>
.v-select {
padding-top: 0px;
}
</style>

View File

@@ -45,47 +45,45 @@ const localValue = computed({
</script>
<template>
<div>
<v-row dense align="center">
<v-col :cols="12 - sliderCols - 1">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="sliderCols">
<v-slider
v-model="localValue"
dark
class="align-center"
:max="max"
:min="min"
hide-details
color="accent"
:disabled="disabled"
:step="step"
append-icon="mdi-menu-right"
prepend-icon="mdi-menu-left"
@click:append="localValue += step"
@click:prepend="localValue -= step"
/>
</v-col>
<v-col :cols="1">
<v-text-field
:value="localValue"
dark
color="accent"
:max="max"
:min="min"
:disabled="disabled"
class="mt-0 pt-0"
hide-details
single-line
type="number"
style="width: 45px"
:step="step"
:hide-spin-buttons="true"
@keyup.enter="localValue = $event.target.value"
@blur="localValue = $event.target.value"
/>
</v-col>
</v-row>
<div class="d-flex">
<v-col :cols="12 - sliderCols - 1" class="pl-0 d-flex align-center">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="sliderCols">
<v-slider
v-model="localValue"
dark
class="align-center"
:max="max"
:min="min"
hide-details
color="accent"
:disabled="disabled"
:step="step"
append-icon="mdi-menu-right"
prepend-icon="mdi-menu-left"
@click:append="localValue += step"
@click:prepend="localValue -= step"
/>
</v-col>
<v-col :cols="1" class="pr-0">
<v-text-field
:value="localValue"
dark
color="accent"
:max="max"
:min="min"
:disabled="disabled"
class="mt-0 pt-0"
hide-details
single-line
type="number"
style="width: 100%"
:step="step"
:hide-spin-buttons="true"
@keyup.enter="localValue = $event.target.value"
@blur="localValue = $event.target.value"
/>
</v-col>
</div>
</template>

View File

@@ -11,11 +11,13 @@ const props = withDefaults(
disabled?: boolean;
labelCols?: number;
switchCols?: number;
dense?: boolean;
}>(),
{
disabled: false,
labelCols: 2,
switchCols: 8
switchCols: 8,
dense: false
}
);
@@ -30,14 +32,17 @@ const localValue = computed({
</script>
<template>
<div>
<v-row dense align="center">
<v-col :cols="12 - switchCols || labelCols">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="switchCols || 12 - labelCols">
<v-switch v-model="localValue" dark :disabled="disabled" color="#ffd843" />
</v-col>
</v-row>
<div class="d-flex">
<v-col :cols="12 - switchCols || labelCols" class="d-flex align-center pl-0">
<tooltipped-label :tooltip="tooltip" :label="label" />
</v-col>
<v-col :cols="switchCols || 12 - labelCols" class="d-flex align-center pr-0">
<v-switch v-model="localValue" dark :disabled="disabled" color="#ffd843" hide-details="auto" class="pb-1" />
</v-col>
</div>
</template>
<style>
.v-input--selection-controls {
margin-top: 0px;
}
</style>