From 6bad002f09302dea67ecd90e868585ea67fa9829 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:11:20 +0100 Subject: [PATCH 01/19] docs: insert JSDoc comment for the core package --- frontend/frontend-kit/core/src/logger.ts | 32 +++++---- .../frontend-kit/core/src/loggerColors.ts | 7 ++ .../frontend-kit/core/src/minMaxDownsample.ts | 69 ++++++++++++++++--- frontend/frontend-kit/core/src/types.ts | 40 +++++++++++ frontend/frontend-kit/core/src/websocket.ts | 43 +++++++++++- .../src/types/telemetry/telemetry.ts | 13 +--- 6 files changed, 170 insertions(+), 34 deletions(-) create mode 100644 frontend/frontend-kit/core/src/loggerColors.ts diff --git a/frontend/frontend-kit/core/src/logger.ts b/frontend/frontend-kit/core/src/logger.ts index 23f64cdbf..b6ff4f42e 100644 --- a/frontend/frontend-kit/core/src/logger.ts +++ b/frontend/frontend-kit/core/src/logger.ts @@ -1,24 +1,30 @@ -type LoggerModule = "testing-view" | "competition-view" | "core" | "ui"; - -const colors = { - "testing-view": "\x1b[36m", // Cyan - "competition-view": "\x1b[35m", // Magenta - core: "\x1b[33m", // Yellow - ui: "\x1b[32m", // Green - reset: "\x1b[0m", -}; +import { loggerColors } from "./loggerColors"; +import type { LoggerModule } from "./types"; +/** + * Creates a logger for a given module + * @param module - the module to log for (it's just a colored string that will be shown between `[` and `]` before the message itself) + * @returns a logger object with `log`, `warn`, and `error` methods + */ function createLogger(module: LoggerModule) { - const color = colors[module]; + const color = loggerColors[module]; const prefix = `[${module.toUpperCase()}]`; return { - log: console.log.bind(console, `${color}${prefix}${colors.reset}`), - warn: console.warn.bind(console, `${color}${prefix}${colors.reset}`), - error: console.error.bind(console, `${color}${prefix}${colors.reset}`), + // It's important to use `bind` here to correctly display log file path and line number in the console + // Otherwise, console prints will just point to this file + log: console.log.bind(console, `${color}${prefix}${loggerColors.reset}`), + warn: console.warn.bind(console, `${color}${prefix}${loggerColors.reset}`), + error: console.error.bind( + console, + `${color}${prefix}${loggerColors.reset}`, + ), }; } +/** + * Logger object with methods for each module + */ export const logger = { testingView: createLogger("testing-view"), competitionView: createLogger("competition-view"), diff --git a/frontend/frontend-kit/core/src/loggerColors.ts b/frontend/frontend-kit/core/src/loggerColors.ts new file mode 100644 index 000000000..c99f7d68d --- /dev/null +++ b/frontend/frontend-kit/core/src/loggerColors.ts @@ -0,0 +1,7 @@ +export const loggerColors = { + "testing-view": "\x1b[36m", // Cyan + "competition-view": "\x1b[35m", // Magenta + core: "\x1b[33m", // Yellow + ui: "\x1b[32m", // Green + reset: "\x1b[0m", +}; diff --git a/frontend/frontend-kit/core/src/minMaxDownsample.ts b/frontend/frontend-kit/core/src/minMaxDownsample.ts index 9b4cb8685..bfc7b41e3 100644 --- a/frontend/frontend-kit/core/src/minMaxDownsample.ts +++ b/frontend/frontend-kit/core/src/minMaxDownsample.ts @@ -1,21 +1,74 @@ -export const minMaxDownsample = (buffer: any[]) => { +import { TelemetryPacket, VariableValue } from "./types"; + +/** + * Helper to extract a numeric value for comparison. + * Handles both primitive numbers, booleans and { last, average } objects. + */ +const getNumericValue = ( + val: VariableValue | undefined, +): number | undefined => { + if (typeof val === "number") { + return val; + } + + if (typeof val === "boolean") { + return val ? 1 : 0; + } + + if ( + typeof val === "object" && + val !== null && + "last" in val && + "average" in val + ) { + return val.last; + } + + return undefined; +}; + +/** + * Downsamples a buffer of packets using the min-max algorithm.\ + * It considers only numeric variables, booleans and { last, average } object variables. + * + * The idea is to reduce the number of packets in the buffer by keeping only the min and max packets + * to prevent the app from freezing when there are too many packets. (Usually happens on start) + * + * @param buffer - array of packets to downsample, should contain at least 2 elements + * @returns downsampled buffer with only min and max packets from the original buffer (in chronological order) + */ +export const minMaxDownsample = (buffer: TelemetryPacket[]) => { + if (buffer.length < 2) return buffer; + let minIdx = 0; let maxIdx = 0; buffer.forEach((packet, i) => { const measurements = packet.measurementUpdates || {}; + + // At the beginning the initial champion is the first variable in the packet const firstKey = Object.keys(measurements)[0]; - const val = measurements[firstKey as keyof typeof measurements]; + if (!firstKey) return; + + const rawVal = measurements[firstKey]; + const val = getNumericValue(rawVal); + + const minVal = getNumericValue( + buffer[minIdx]?.measurementUpdates[firstKey], + ); + const maxVal = getNumericValue( + buffer[maxIdx]?.measurementUpdates[firstKey], + ); - if (typeof val === "number") { - if (val < (buffer[minIdx].measurementUpdates[firstKey] ?? Infinity)) - minIdx = i; - if (val > (buffer[maxIdx].measurementUpdates[firstKey] ?? -Infinity)) - maxIdx = i; + // Compare local min and max with the global champions + // If one of them is undefined, use Infinity or -Infinity respectively + if (val !== undefined) { + if (val < (minVal ?? Infinity)) minIdx = i; + if (val > (maxVal ?? -Infinity)) maxIdx = i; } }); - // 4. Return them in chronological order to maintain X-axis integrity + // Return them in chronological order to maintain X-axis integrity const result = minIdx < maxIdx ? [buffer[minIdx], buffer[maxIdx]] diff --git a/frontend/frontend-kit/core/src/types.ts b/frontend/frontend-kit/core/src/types.ts index fb7538dde..1c7db835c 100644 --- a/frontend/frontend-kit/core/src/types.ts +++ b/frontend/frontend-kit/core/src/types.ts @@ -1,4 +1,44 @@ +/** + * Options for the `onTopic` method of the `SocketService` class. + */ export interface TopicOptions { + /** + * The downsampling method to use. + */ downsample?: "min-max" | "none"; + /** + * The throttle time in milliseconds. + */ throttle?: number; } + +/** + * The value of a variable in a telemetry packet. + */ +export type VariableValue = + | { last: number; average: number } + | boolean + | string + | number; + +/** + * The variables of a telemetry packet. + */ +export type Variables = Record; + +/** + * A telemetry packet that arrives in high frequency.\ + * Don't confuse it with the `TelemetryCatalogItem` type. + */ +export interface TelemetryPacket { + count: number; + cycleTime: number; + hexValue: string; + id: number; + measurementUpdates: Variables; +} + +/** + * The modules that can be logged to. Used for the `logger` object. + */ +export type LoggerModule = "testing-view" | "competition-view" | "core" | "ui"; diff --git a/frontend/frontend-kit/core/src/websocket.ts b/frontend/frontend-kit/core/src/websocket.ts index b8bd59dbc..2d4005654 100644 --- a/frontend/frontend-kit/core/src/websocket.ts +++ b/frontend/frontend-kit/core/src/websocket.ts @@ -17,19 +17,39 @@ import { logger } from "./logger"; import { minMaxDownsample } from "./minMaxDownsample"; import { type TopicOptions } from "./types"; +/** + * Service for connecting to the WebSocket server and subscribing to topics + */ class SocketService { + /** + * Singleton instance that holds the WebSocket subject + */ private socketSource$ = new ReplaySubject>(1); + + /** + * Subject that holds the status of the WebSocket connection. + */ public status$ = new BehaviorSubject< "connected" | "disconnected" | "connecting" >("disconnected"); + /** + * Observable that emits the messages from the WebSocket server. + */ public messages$: Observable = this.socketSource$.pipe( switchMap((socket) => socket), shareReplay(1), ); + /** + * Disposable WebSocket connection object. Lives only as long as the connection is open. + */ private ws: WebSocketSubject | null = null; + /** + * Connects to the WebSocket server by creating a new connection object and pushing it to `socketSource$`. + * @param port - the port to connect to. Defaults to 4000. + */ connect(port: number = 4000) { if (this.ws) return; @@ -62,20 +82,34 @@ class SocketService { this.socketSource$.next(this.ws); } + /** + * Cleans up the WebSocket connection by setting the connection object to null and updating the status to "disconnected". + */ private cleanup() { this.ws = null; this.status$.next("disconnected"); } + /** + * Creates an observable that emits the messages from the WebSocket server for a given topic. + * @param topic - the topic to subscribe to. + * @param options - options for the observable. + + * Downsampling and throttling are supported. + * In case of downsampling, throttling option is used as the buffering time and defaults to 100ms. + * @returns an observable that emits the messages from the WebSocket server for a given topic. + */ onTopic(topic: string, options: TopicOptions = {}) { let pipe$ = this.messages$.pipe( filter((msg) => msg.topic === topic), map((msg) => msg.payload), ); + // Apply downsampling if requested if (options.downsample == "min-max") { pipe$ = pipe$.pipe( - bufferTime(options.throttle || 100), + // Apply buffering + bufferTime(options.throttle ?? 100), filter((buffer) => buffer.length > 0), concatMap((buffer) => { if (buffer.length <= 2) return from(buffer); @@ -89,6 +123,7 @@ class SocketService { ); } + // Apply throttling if requested if (options.throttle) { pipe$ = pipe$.pipe(throttleTime(options.throttle, asyncScheduler)); } @@ -96,6 +131,12 @@ class SocketService { return pipe$; } + /** + * Posts a message to the WebSocket server. If the connection is not established, an error is logged and the message is not sent. + * @param topic - the topic to post to. + * @param payload - the payload to post. + * // TODO: reference payloads definition file + */ post(topic: string, payload: any) { if (!this.ws) { logger.core.error("Cannot post: Socket not connected."); diff --git a/frontend/testing-view/src/types/telemetry/telemetry.ts b/frontend/testing-view/src/types/telemetry/telemetry.ts index bb879ba3d..3d1a1b091 100644 --- a/frontend/testing-view/src/types/telemetry/telemetry.ts +++ b/frontend/testing-view/src/types/telemetry/telemetry.ts @@ -1,15 +1,4 @@ -export type Variables = Record< - string, - { last: number; average: number } | boolean | string | number ->; - -export interface TelemetryPacket { - count: number; - cycleTime: number; - hexValue: string; - id: number; - measurementUpdates: Variables; -} +import type { TelemetryPacket } from "@workspace/core"; export type TelemetryData = Record; From 95ea2d0d34db669b58907e0fa2d44dba0f983425 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:12:00 +0100 Subject: [PATCH 02/19] docs: add JSDoc comments for basic components --- .../testing-view/src/components/AppModeRouter.tsx | 6 ++++++ frontend/testing-view/src/components/Error.tsx | 8 ++++++++ .../testing-view/src/components/ErrorBoundary.tsx | 6 ++++++ frontend/testing-view/src/components/Footer.tsx | 5 ++++- frontend/testing-view/src/components/Loading.tsx | 4 ++++ frontend/testing-view/src/hooks/useErrorHandler.ts | 12 +++++++++++- 6 files changed, 39 insertions(+), 2 deletions(-) diff --git a/frontend/testing-view/src/components/AppModeRouter.tsx b/frontend/testing-view/src/components/AppModeRouter.tsx index b62b2e01a..18148be81 100644 --- a/frontend/testing-view/src/components/AppModeRouter.tsx +++ b/frontend/testing-view/src/components/AppModeRouter.tsx @@ -6,6 +6,12 @@ interface AppModeRouterProps { children: React.ReactNode; } +/** + * This component works as a router + * and renders the appropriate page based on the app mode. + * + * Note: If mode is not loading or error, it renders the children normally + */ export const AppModeRouter = ({ children }: AppModeRouterProps) => { const appMode = useStore((s) => s.appMode); diff --git a/frontend/testing-view/src/components/Error.tsx b/frontend/testing-view/src/components/Error.tsx index ac4706b4d..fbec77e4b 100644 --- a/frontend/testing-view/src/components/Error.tsx +++ b/frontend/testing-view/src/components/Error.tsx @@ -5,10 +5,18 @@ import errorGif from "../assets/error.gif"; import { useStore } from "../store/store"; interface ErrorProps { + /** Optional error to display. Can be null or undefined. In this case component will show default error message */ error?: Error | null; + /** Optional component stack trace to display. Can be null or undefined. In this case component will not show anything */ componentStack?: string | null; } +/** + * Renders error page with the given error and component stack + * + * Displays an error message, optional component stack trace,\ + * and provides actions to reload the application or inspect the stack. + */ export const Error = ({ error: propError, componentStack }: ErrorProps) => { const storeError = useStore((s) => s.error); const error = propError || storeError; diff --git a/frontend/testing-view/src/components/ErrorBoundary.tsx b/frontend/testing-view/src/components/ErrorBoundary.tsx index d53a89989..a69c23611 100644 --- a/frontend/testing-view/src/components/ErrorBoundary.tsx +++ b/frontend/testing-view/src/components/ErrorBoundary.tsx @@ -13,6 +13,12 @@ interface State { componentStack?: string | null; } +/** + * This component works as a wrapper and + * catches and handles any unhandled errors and unhandled promise rejections. + * + * The idea is to prevent the app from crashing when an unhandled error occurs. + */ export class ErrorBoundary extends Component { constructor(props: Props) { super(props); diff --git a/frontend/testing-view/src/components/Footer.tsx b/frontend/testing-view/src/components/Footer.tsx index 24dd95fd1..6846cdb93 100644 --- a/frontend/testing-view/src/components/Footer.tsx +++ b/frontend/testing-view/src/components/Footer.tsx @@ -1,4 +1,7 @@ -const Footer = () => { +/** + * Renders a footer with the app name, current year and the copyright notice + */ +export const Footer = () => { const currentYear = new Date().getFullYear(); const dateRange = currentYear <= 2025 ? `${currentYear}` : `2025-${currentYear}`; diff --git a/frontend/testing-view/src/components/Loading.tsx b/frontend/testing-view/src/components/Loading.tsx index 3ad478f4f..698ebdc71 100644 --- a/frontend/testing-view/src/components/Loading.tsx +++ b/frontend/testing-view/src/components/Loading.tsx @@ -1,5 +1,8 @@ import loadingGif from "../assets/loading-monkey.gif"; +/** + * Renders a loading page with a monkey GIF, text and a glowing loading indicator + */ export const Loading = () => { return (
@@ -32,6 +35,7 @@ export const Loading = () => {

+ {/* Simple glowing loading indicator */}
diff --git a/frontend/testing-view/src/hooks/useErrorHandler.ts b/frontend/testing-view/src/hooks/useErrorHandler.ts index 7f7f3c475..60f14b26c 100644 --- a/frontend/testing-view/src/hooks/useErrorHandler.ts +++ b/frontend/testing-view/src/hooks/useErrorHandler.ts @@ -2,6 +2,12 @@ import { logger } from "@workspace/core"; import { useEffect } from "react"; import { useStore } from "../store/store"; +/** + * This hook listens for global errors and unhandled promises rejections + * and sets the app mode to "error" in global store's app slice. + * @returns a function to manually report errors + * * Note: returned function is supposed to be used in the ErrorBoundary component + */ export function useErrorHandler() { const setError = useStore((s) => s.setError); const setAppMode = useStore((s) => s.setAppMode); @@ -38,7 +44,11 @@ export function useErrorHandler() { }; }, [setError, setAppMode]); - // Return a function to manually report errors + /** + * This functions prints the error and the error info in console using logger + * and sets the error in global store's app slice. + * It is designed to be used in the ErrorBoundary component. + */ const reportError = (error: Error, ErrorInfo?: React.ErrorInfo) => { logger.testingView.error( `Error${ErrorInfo ? ` in ${ErrorInfo}` : ""}:`, From 2b3ac4a6b9ae2011856595b8b381d3e49759aa6a Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:44:34 +0100 Subject: [PATCH 03/19] docs: add comments to types/common --- .../testing-view/src/types/common/config.ts | 6 ++++ .../src/types/common/connection.ts | 6 ++++ .../testing-view/src/types/common/item.ts | 8 +++++ .../testing-view/src/types/common/logger.ts | 3 ++ .../testing-view/src/types/common/settings.ts | 32 +++++++++++++++++++ 5 files changed, 55 insertions(+) diff --git a/frontend/testing-view/src/types/common/config.ts b/frontend/testing-view/src/types/common/config.ts index a3e28858a..0f2a862fa 100644 --- a/frontend/testing-view/src/types/common/config.ts +++ b/frontend/testing-view/src/types/common/config.ts @@ -1,5 +1,11 @@ +/** + * Type of the field value in the config data. + */ export type ConfigField = string | string[] | number | boolean; +/** + * Configuration type used in app slice to store values from the form. + */ export interface ConfigData { [section: string]: { [field: string]: ConfigField; diff --git a/frontend/testing-view/src/types/common/connection.ts b/frontend/testing-view/src/types/common/connection.ts index 0259f91dc..56a4c4445 100644 --- a/frontend/testing-view/src/types/common/connection.ts +++ b/frontend/testing-view/src/types/common/connection.ts @@ -1,4 +1,10 @@ +/** + * One connection to the vehicle. + */ export interface Connection { + /** Name of the connection */ name: string; + + /** Whether the connection is established */ isConnected: boolean; } diff --git a/frontend/testing-view/src/types/common/item.ts b/frontend/testing-view/src/types/common/item.ts index decb1d8e4..bc0821fd7 100644 --- a/frontend/testing-view/src/types/common/item.ts +++ b/frontend/testing-view/src/types/common/item.ts @@ -1,5 +1,13 @@ +/** + * One item of the catalog (commands or telemetry). + */ export interface Item { + /** Unique (If firmware fixes it) ID of the item from ADJ */ id: number; + + /** Name of the item from ADJ */ name: string; + + /** Label of the item generated from the name on frontend startup */ label: string; } diff --git a/frontend/testing-view/src/types/common/logger.ts b/frontend/testing-view/src/types/common/logger.ts index d3f3c0d9a..0c364c272 100644 --- a/frontend/testing-view/src/types/common/logger.ts +++ b/frontend/testing-view/src/types/common/logger.ts @@ -1 +1,4 @@ +/** + * Status of the logger. + */ export type LoggerStatus = "standby" | "recording" | "loading" | "error"; diff --git a/frontend/testing-view/src/types/common/settings.ts b/frontend/testing-view/src/types/common/settings.ts index bca604420..bbe3e20df 100644 --- a/frontend/testing-view/src/types/common/settings.ts +++ b/frontend/testing-view/src/types/common/settings.ts @@ -1,3 +1,6 @@ +/** + * Type of the field component showed in the settings form and defined in the settings schema (configuration). + */ export type FieldType = | "text" | "number" @@ -6,21 +9,50 @@ export type FieldType = | "multi-checkbox" | "path"; +/** + * Generic props for a field component showed in the settings form.\ + * @template T - The type of the field value. + * **!IMPORTANT:** it is a type used for input component and possibly doesn't match the actual type of the field value.\ + * **E.g.** number field value T should be a string, but the actual type is number. + */ export interface FieldProps { + /** Field configuration */ field: SettingField; + /** Current value of the field */ value: T; + /** Function to handle the change of the field value */ onChange: (value: T) => void; } +/** + * One item of the settings schema from config.toml. + */ export interface SettingField { + /** Label of the field */ label: string; + + /** Type of the field */ type: FieldType; + + /** Options of the field */ options?: string[]; + + /** Placeholder of the field */ placeholder?: string; + + /** Path to the field in the config.toml configuration.\ + * **Note:** it should match config.toml section and variable name.\ + * **E.g.** `vehicle.boards` or `adj.branch`. */ path: string; } +/** + * One section of the settings from config.toml. + */ export interface SettingsSection { + /** Title of the section */ title: string; + + /** Fields of the section */ fields: SettingField[]; } From 9add7d5d200970f5ffb1818588ca784bd50beca1 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:51:26 +0100 Subject: [PATCH 04/19] docs: little tweaks --- frontend/frontend-kit/core/src/types.ts | 9 +++------ .../testing-view/src/components/Settings/TextField.tsx | 5 +++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/frontend-kit/core/src/types.ts b/frontend/frontend-kit/core/src/types.ts index 1c7db835c..e0346cc78 100644 --- a/frontend/frontend-kit/core/src/types.ts +++ b/frontend/frontend-kit/core/src/types.ts @@ -2,13 +2,10 @@ * Options for the `onTopic` method of the `SocketService` class. */ export interface TopicOptions { - /** - * The downsampling method to use. - */ + /** The downsampling method to use. */ downsample?: "min-max" | "none"; - /** - * The throttle time in milliseconds. - */ + + /** The throttle time in milliseconds. */ throttle?: number; } diff --git a/frontend/testing-view/src/components/Settings/TextField.tsx b/frontend/testing-view/src/components/Settings/TextField.tsx index ba14d6f5a..71969dfb6 100644 --- a/frontend/testing-view/src/components/Settings/TextField.tsx +++ b/frontend/testing-view/src/components/Settings/TextField.tsx @@ -2,6 +2,11 @@ import { Input } from "@workspace/ui/components/shadcn/input"; import { Label } from "@workspace/ui/components/shadcn/label"; import type { FieldProps } from "../../types/common/settings"; +/** + * Text field component for the settings form.\ + * Note: it is also used for numeric values and converted later to the correct type.\ + * Why: because empty numeric input displays 0 and not an empty string. + */ export const TextField = ({ field, value, onChange }: FieldProps) => (
From 65c82424705448b27a4494b35b4e4afe0744d493 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:31:36 +0100 Subject: [PATCH 05/19] docs: document types/data and some fixes related to type changes --- .../components/Testing/Filters/FilterItem.tsx | 4 +- .../RightSidebar/Tabs/CategoryItem.tsx | 4 +- .../RightSidebar/Tabs/StandardList.tsx | 4 +- .../Testing/RightSidebar/Tabs/Tab.tsx | 4 +- .../testing-view/src/hooks/useAppConfigs.ts | 2 +- frontend/testing-view/src/lib/utils.ts | 4 +- .../src/store/slices/catalogSlice.ts | 18 +++++--- .../src/store/slices/workspacesSlice.ts | 12 ++--- .../testing-view/src/types/common/item.ts | 7 ++- frontend/testing-view/src/types/data/board.ts | 34 +++++++++++++- .../src/types/data/commandCatalogItem.ts | 23 +++++++++- .../testing-view/src/types/data/message.ts | 45 +++++++++++++++++-- .../src/types/data/telemetryCatalogItem.ts | 17 ++++--- .../src/types/data/transformedBoards.ts | 23 +++++----- .../src/types/data/virtualization.ts | 3 ++ 15 files changed, 155 insertions(+), 49 deletions(-) diff --git a/frontend/testing-view/src/components/Testing/Filters/FilterItem.tsx b/frontend/testing-view/src/components/Testing/Filters/FilterItem.tsx index 9c949f0a5..c5e4dac6e 100644 --- a/frontend/testing-view/src/components/Testing/Filters/FilterItem.tsx +++ b/frontend/testing-view/src/components/Testing/Filters/FilterItem.tsx @@ -1,8 +1,8 @@ import { Badge, Checkbox } from "@workspace/ui"; -import type { Item } from "../../../types/common/item"; +import type { CatalogItem } from "../../../types/common/item"; interface FilterItemProps { - item: Item; + item: CatalogItem; isChecked: boolean; onToggle: () => void; } diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/CategoryItem.tsx b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/CategoryItem.tsx index fb7560dc8..7f72f782e 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/CategoryItem.tsx +++ b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/CategoryItem.tsx @@ -2,7 +2,7 @@ import { Collapsible, CollapsibleContent } from "@workspace/ui"; import { type ComponentType } from "react"; import { useShallow } from "zustand/shallow"; import { useStore } from "../../../../store/store"; -import type { Item } from "../../../../types/common/item"; +import type { CatalogItem } from "../../../../types/common/item"; import type { BoardName } from "../../../../types/data/board"; import type { SidebarTab } from "../../../../types/workspace/sidebar"; import { CategoryHeader } from "./CategoryHeader"; @@ -10,7 +10,7 @@ import { CategoryHeader } from "./CategoryHeader"; interface CategoryItemProps { category: BoardName; scope: SidebarTab; - ItemComponent: ComponentType<{ item: Item }>; + ItemComponent: ComponentType<{ item: CatalogItem }>; } export const CategoryItem = ({ diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx index cbedc7977..65b2614f2 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx +++ b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx @@ -1,5 +1,5 @@ import { type ComponentType } from "react"; -import type { Item } from "../../../../types/common/item"; +import type { CatalogItem } from "../../../../types/common/item"; import type { BoardName } from "../../../../types/data/board"; import type { SidebarTab } from "../../../../types/workspace/sidebar"; import { CategoryItem } from "./CategoryItem"; @@ -7,7 +7,7 @@ import { CategoryItem } from "./CategoryItem"; interface StandardListProps { scope: SidebarTab; categories: readonly BoardName[]; - ItemComponent: ComponentType<{ item: Item }>; + ItemComponent: ComponentType<{ item: CatalogItem }>; } export const StandardList = ({ diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Tab.tsx b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Tab.tsx index 4178663b6..f7660115e 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Tab.tsx +++ b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Tab.tsx @@ -1,6 +1,6 @@ import { type ComponentType } from "react"; import { useStore } from "../../../../store/store"; -import type { Item } from "../../../../types/common/item"; +import type { CatalogItem } from "../../../../types/common/item"; import type { BoardName } from "../../../../types/data/board"; import type { SidebarTab } from "../../../../types/workspace/sidebar"; import { EmptyTab } from "./EmptyTab"; @@ -12,7 +12,7 @@ interface TabProps { title: string; scope: SidebarTab; categories: readonly BoardName[]; - ItemComponent: ComponentType<{ item: Item }>; + ItemComponent: ComponentType<{ item: CatalogItem }>; virtualized?: boolean; } diff --git a/frontend/testing-view/src/hooks/useAppConfigs.ts b/frontend/testing-view/src/hooks/useAppConfigs.ts index 04853dfef..24ceb704b 100644 --- a/frontend/testing-view/src/hooks/useAppConfigs.ts +++ b/frontend/testing-view/src/hooks/useAppConfigs.ts @@ -1,6 +1,6 @@ import { useFetchConfig } from "@workspace/ui/hooks"; import { useEffect } from "react"; -import type { OrdersData, PacketsData } from "../types/data/transformedBoards"; +import type { OrdersData, PacketsData } from "../types/data/board"; const useAppConfigs = (isConnected: boolean) => { const backendUrl = import.meta.env.VITE_BACKEND_URL; diff --git a/frontend/testing-view/src/lib/utils.ts b/frontend/testing-view/src/lib/utils.ts index c657e7fb2..798855a52 100644 --- a/frontend/testing-view/src/lib/utils.ts +++ b/frontend/testing-view/src/lib/utils.ts @@ -2,7 +2,7 @@ import { acronyms } from "../constants/acronyms"; import { BOARD_NAMES } from "../constants/boards"; import { DEFAULT_WORKSPACES } from "../constants/defaultWorkspaces"; import { variablesBadgeClasses } from "../constants/variablesBadgeClasses"; -import type { Item } from "../types/common/item"; +import type { CatalogItem } from "../types/common/item"; import type { BoardName } from "../types/data/board"; import type { MessageTimestamp } from "../types/data/message"; import type { @@ -37,7 +37,7 @@ export const createEmptyFilter = (): TabFilter => { }; export const createFullFilter = ( - dataSource: Record, + dataSource: Record, ): TabFilter => { return BOARD_NAMES.reduce((acc, category) => { acc[category] = dataSource[category]?.map((item) => item.id) || []; diff --git a/frontend/testing-view/src/store/slices/catalogSlice.ts b/frontend/testing-view/src/store/slices/catalogSlice.ts index 0439d04b0..e2e60017f 100644 --- a/frontend/testing-view/src/store/slices/catalogSlice.ts +++ b/frontend/testing-view/src/store/slices/catalogSlice.ts @@ -1,23 +1,27 @@ import type { StateCreator } from "zustand"; -import type { Item } from "../../types/common/item"; +import type { CatalogItem } from "../../types/common/item"; import type { BoardName } from "../../types/data/board"; import type { Store } from "../store"; export interface CatalogSlice { // Commands catalog - commandsCatalog: Record; - setCommandsCatalog: (commandsCatalog: Record) => void; + commandsCatalog: Record; + setCommandsCatalog: ( + commandsCatalog: Record, + ) => void; // Telemetry catalog - telemetryCatalog: Record; - setTelemetryCatalog: (telemetryCatalog: Record) => void; + telemetryCatalog: Record; + setTelemetryCatalog: ( + telemetryCatalog: Record, + ) => void; } export const createCatalogSlice: StateCreator = ( set, ) => ({ - commandsCatalog: {} as Record, - telemetryCatalog: {} as Record, + commandsCatalog: {} as Record, + telemetryCatalog: {} as Record, setCommandsCatalog: (commandsCatalog) => set({ commandsCatalog }), setTelemetryCatalog: (telemetryCatalog) => set({ telemetryCatalog }), }); diff --git a/frontend/testing-view/src/store/slices/workspacesSlice.ts b/frontend/testing-view/src/store/slices/workspacesSlice.ts index 6705043c0..191b88eef 100644 --- a/frontend/testing-view/src/store/slices/workspacesSlice.ts +++ b/frontend/testing-view/src/store/slices/workspacesSlice.ts @@ -7,9 +7,9 @@ import { generateInitialFilters, getCatalogKey, } from "../../lib/utils"; -import type { Item } from "../../types/common/item"; +import type { CatalogItem } from "../../types/common/item"; import type { BoardName } from "../../types/data/board"; -import type { Measurement } from "../../types/data/telemetryCatalogItem"; +import type { Variable } from "../../types/data/telemetryCatalogItem"; import type { VirtualRow } from "../../types/data/virtualization"; import type { FilterScope, @@ -48,7 +48,7 @@ export interface WorkspacesSlice { getActiveWorkspaceId: () => string | null; // Internal helpers - getCatalog: (scope: FilterScope) => Record; + getCatalog: (scope: FilterScope) => Record; // Tabs (per workspace) activeTab: Record; @@ -65,7 +65,7 @@ export interface WorkspacesSlice { getActiveExpanded: (scope: FilterScope) => Set | undefined; // Getters for filtered items - getFilteredItems: (scope: FilterScope) => Item[]; + getFilteredItems: (scope: FilterScope) => CatalogItem[]; getFilteredItemsIds: (scope: FilterScope) => number[]; getFilteredItemsIdsByCategory: ( scope: FilterScope, @@ -74,7 +74,7 @@ export interface WorkspacesSlice { getFilteredItemsByCategory: ( scope: FilterScope, category: BoardName, - ) => Item[]; + ) => CatalogItem[]; // Stats getters getFilteredCount: (scope: FilterScope) => number; @@ -546,7 +546,7 @@ export const createWorkspacesSlice: StateCreator< // If the packet is expanded, add its variables/measurements if (get().isItemExpanded(scope, "packet", item.id)) { if ("measurements" in item) { - const variables = item.measurements as Measurement[]; + const variables = item.measurements as Variable[]; variables.forEach((m) => { rows.push({ type: "variable", diff --git a/frontend/testing-view/src/types/common/item.ts b/frontend/testing-view/src/types/common/item.ts index bc0821fd7..fd320b5ef 100644 --- a/frontend/testing-view/src/types/common/item.ts +++ b/frontend/testing-view/src/types/common/item.ts @@ -1,5 +1,5 @@ /** - * One item of the catalog (commands or telemetry). + * Raw item from the catalog (commands or telemetry) as it arrives from the backend. */ export interface Item { /** Unique (If firmware fixes it) ID of the item from ADJ */ @@ -7,7 +7,12 @@ export interface Item { /** Name of the item from ADJ */ name: string; +} +/** + * Item from the catalog (commands or telemetry) with my label. + */ +export interface CatalogItem extends Item { /** Label of the item generated from the name on frontend startup */ label: string; } diff --git a/frontend/testing-view/src/types/data/board.ts b/frontend/testing-view/src/types/data/board.ts index f1bfea21f..5d63ff878 100644 --- a/frontend/testing-view/src/types/data/board.ts +++ b/frontend/testing-view/src/types/data/board.ts @@ -1,15 +1,45 @@ import type { CommandCatalogItem } from "./commandCatalogItem"; import type { TelemetryCatalogItem } from "./telemetryCatalogItem"; +/** + * Name of a board, like LCU or HVBMS.\ + * I decided not to use array of string in case a new board is added in the future. + */ export type BoardName = string; + +/** + * Common properties of boards. + */ export interface BoardData { name: BoardName; } +/** + * Basically ADJ telemetry packets definition from the backend. + */ +export interface BoardPacketsData extends BoardData { + /** List of telemetry packets (also referred as packets in some places) */ + packets: TelemetryCatalogItem[]; +} + +/** + * Basically ADJ commands definition from the backend. + */ export interface BoardOrdersData extends BoardData { + /** List of commands (also referred as orders in some places) */ orders: CommandCatalogItem[]; } -export interface BoardPacketsData extends BoardData { - packets: TelemetryCatalogItem[]; +/** + * Final type of the result of the packets fetching. + */ +export interface PacketsData { + boards: BoardPacketsData[]; +} + +/** + * Final type of the result of the commands fetching. + */ +export interface OrdersData { + boards: BoardOrdersData[]; } diff --git a/frontend/testing-view/src/types/data/commandCatalogItem.ts b/frontend/testing-view/src/types/data/commandCatalogItem.ts index 0e0d2efad..923b2fae4 100644 --- a/frontend/testing-view/src/types/data/commandCatalogItem.ts +++ b/frontend/testing-view/src/types/data/commandCatalogItem.ts @@ -1,5 +1,8 @@ -import type { Item } from "../common/item"; +import type { CatalogItem, Item } from "../common/item"; +/** + * Params of a command. + */ export interface CommandParameter { kind: string; id: string; @@ -7,21 +10,37 @@ export interface CommandParameter { type: string; } +/** + * Numeric parameter has safe and warning ranges. + */ export interface NumericCommandParameter extends CommandParameter { safeRange: (number | null)[]; warningRange: (number | null)[]; } +/** + * Enum parameter has options. + */ export interface EnumCommandParameter extends CommandParameter { options: string[]; } +/** + * Map of parameter id to parameter type of a command.\ + * Each parameter can be either numeric or enum. + */ export interface CommandParameters { [key: string]: NumericCommandParameter | EnumCommandParameter; } +/** + * Definition of a command packet as it arrives from the backend. + */ export interface RawOrder extends Item { fields: CommandParameters; } -export type CommandCatalogItem = RawOrder; +/** + * Definition of a command catalog item as it arrives from the backend and my label. + */ +export type CommandCatalogItem = CatalogItem & RawOrder; diff --git a/frontend/testing-view/src/types/data/message.ts b/frontend/testing-view/src/types/data/message.ts index 25f7101fe..7d8c2998a 100644 --- a/frontend/testing-view/src/types/data/message.ts +++ b/frontend/testing-view/src/types/data/message.ts @@ -1,4 +1,15 @@ +import type { BoardName } from "./board"; + +/** + * Defines current moment in time.\ + * **Be careful** as there is no way to check if a timestamp is unique.\ + * Because seconds basically don't tell us anything in the context of high-frequency systems + * and counter only means uniqueness considering the same packet type + */ export interface MessageTimestamp { + /** Counter which is incremented by 1 every time packet of one type is generated,\ + * but it can be the same between different packet types + */ counter: number; second: number; minute: number; @@ -8,14 +19,42 @@ export interface MessageTimestamp { year: number; } +/** + * Possible payloads for a `ok`, `warning` and `fault` messages. + */ +export type DetailedPayload = + | { + kind: "LOWER_BOUND" | "UPPER_BOUND"; + data: { bound: number; value: number }; + } + | { kind: "OUT_OF_BOUNDS"; data: { bounds: [number, number]; value: number } } + | { kind: "EQUALS"; data: { value: number } } + | { kind: "NOT_EQUALS"; data: { want: number; value: number } } + | { + kind: "TIME_ACCUMULATION"; + data: { value: number; bound: number; timelimit: number }; + } + | { kind: "ERROR_HANDLER" | "WARNING"; data: string }; + +/** + * Definition of a MessagePacket as it arrives from the backend. + */ export interface MessagePacket { + /** Message type */ kind: "info" | "warning" | "fault" | "ok"; - payload: any; - board: string; + /** For `info` messages, the payload is a string. + * For `warning`, `fault` and `ok` messages, the payload is a DetailedPayload. + */ + payload: string | DetailedPayload; + board: BoardName; name: string; timestamp: MessageTimestamp; } +/** + * Message definition on frontend. + */ export interface Message extends MessagePacket { - id: string; // Generated on frontend for React keys + /** Unique message id generated on frontend for React keys */ + id: string; } diff --git a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts index b847e9f94..a2902be3c 100644 --- a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts +++ b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts @@ -1,5 +1,8 @@ -import type { Item } from "../common/item"; +import type { CatalogItem, Item } from "../common/item"; +/** + * Variable definition. Sometimes also called Measurement. This is the same thing. + */ export interface Variable { id: string; name: string; @@ -7,14 +10,18 @@ export interface Variable { units: string; } -export type Measurement = any; - +/** + * Definition of a TelemetryPacket as it arrives from the backend. + */ export interface RawPacket extends Item { hexValue: string; count: number; cycleTime: number; type: string; - measurements: Measurement[]; + measurements: Variable[]; } -export type TelemetryCatalogItem = RawPacket; +/** + * Definition of a telemetry catalog item as it arrives from the backend and my label. + */ +export type TelemetryCatalogItem = CatalogItem & RawPacket; diff --git a/frontend/testing-view/src/types/data/transformedBoards.ts b/frontend/testing-view/src/types/data/transformedBoards.ts index 6b9582734..38e9658ea 100644 --- a/frontend/testing-view/src/types/data/transformedBoards.ts +++ b/frontend/testing-view/src/types/data/transformedBoards.ts @@ -1,19 +1,18 @@ -import type { BoardName, BoardOrdersData, BoardPacketsData } from "./board"; +import type { BoardName } from "./board"; import type { CommandCatalogItem } from "./commandCatalogItem"; import type { TelemetryCatalogItem } from "./telemetryCatalogItem"; -// Packets fetching return data type -export interface PacketsData { - boards: BoardPacketsData[]; -} +/** + * Final result of the useBoardData hook and boards transformation.\ + * This is the format I actually use + */ +export interface TransformedBoards { + /** Map of board name to list of telemetry catalog items */ + telemetryCatalog: Record; -// Commands fetching return data type -export interface OrdersData { - boards: BoardOrdersData[]; -} + /** Map of board name to list of command catalog items */ + commandsCatalog: Record; -export interface TransformedBoards { - telemetryCatalog: Record; - commandsCatalog: Record; + /** Set of all available boards (not used for now) */ boards: Set; } diff --git a/frontend/testing-view/src/types/data/virtualization.ts b/frontend/testing-view/src/types/data/virtualization.ts index 4b57ae220..d6989ef33 100644 --- a/frontend/testing-view/src/types/data/virtualization.ts +++ b/frontend/testing-view/src/types/data/virtualization.ts @@ -1,3 +1,6 @@ +/** + * A row of data in a virtualized list. + */ export type VirtualRow = | { type: "board"; id: string; label: string; count: number } | { type: "packet"; id: number; data: any } From b154736c007228e52fabfbf4c9a95a195c8270cf Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:41:33 +0100 Subject: [PATCH 06/19] docs: some tweaks --- frontend/testing-view/src/types/data/telemetryCatalogItem.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts index a2902be3c..41fcd4bdb 100644 --- a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts +++ b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts @@ -11,14 +11,15 @@ export interface Variable { } /** - * Definition of a TelemetryPacket as it arrives from the backend. + * Definition of a telemetry packet as it arrives from the backend. */ export interface RawPacket extends Item { - hexValue: string; count: number; cycleTime: number; type: string; measurements: Variable[]; + /** Currently unused (always equals to "000000" placeholder) */ + hexValue: string; } /** From 38fb8a5a85e9128b1c88b04367e3b144ab779c8e Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:27:26 +0100 Subject: [PATCH 07/19] docs: document types/workspace and related inconviniences --- .../components/Testing/Charts/ChartLegend.tsx | 4 ++-- .../Testing/Charts/ChartSurface.tsx | 4 ++-- .../components/Testing/Charts/ChartsGrid.tsx | 2 +- .../Testing/Charts/TelemetryChart.tsx | 4 ++-- .../Testing/Charts/tooltipPlugin.ts | 4 ++-- .../src/components/Testing/DndOverlay.tsx | 2 +- .../src/mocks/chartsConfigurations.ts | 2 +- .../src/store/slices/workspacesSlice.ts | 18 ++++----------- .../src/types/data/telemetryCatalogItem.ts | 2 +- .../src/types/telemetry/telemetry.ts | 7 ++++++ .../src/types/workspace/charts.ts | 23 +++++++++++++++++-- .../src/types/workspace/filters.ts | 10 ++++++++ .../src/types/workspace/keyBinding.ts | 13 ++++++++--- .../src/types/workspace/sidebar.ts | 6 +++++ .../src/types/workspace/workspace.ts | 7 ++++++ 15 files changed, 78 insertions(+), 30 deletions(-) diff --git a/frontend/testing-view/src/components/Testing/Charts/ChartLegend.tsx b/frontend/testing-view/src/components/Testing/Charts/ChartLegend.tsx index d7446e061..83b22f66d 100644 --- a/frontend/testing-view/src/components/Testing/Charts/ChartLegend.tsx +++ b/frontend/testing-view/src/components/Testing/Charts/ChartLegend.tsx @@ -1,11 +1,11 @@ import { X } from "@workspace/ui/icons"; import { COLORS } from "../../../constants/chartsColors"; -import type { VariableSeries } from "../../../types/workspace/charts"; +import type { WorkspaceChartSeries } from "../../../types/workspace/charts"; import { ChartSettings } from "./ChartSettings"; interface ChartLegendProps { chartId: string; - series: VariableSeries[]; + series: WorkspaceChartSeries[]; disabledIndices: Set; onToggle: (index: number) => void; onRemove: (variable: string, index: number) => void; diff --git a/frontend/testing-view/src/components/Testing/Charts/ChartSurface.tsx b/frontend/testing-view/src/components/Testing/Charts/ChartSurface.tsx index f4a428b83..91fad0634 100644 --- a/frontend/testing-view/src/components/Testing/Charts/ChartSurface.tsx +++ b/frontend/testing-view/src/components/Testing/Charts/ChartSurface.tsx @@ -6,12 +6,12 @@ import uPlot from "uplot"; import { useShallow } from "zustand/shallow"; import { COLORS } from "../../../constants/chartsColors"; import { useStore } from "../../../store/store"; -import type { VariableSeries } from "../../../types/workspace/charts"; +import type { WorkspaceChartSeries } from "../../../types/workspace/charts"; import { createTooltipPlugin } from "./tooltipPlugin"; interface ChartSurfaceProps { chartId: string; - series: VariableSeries[]; + series: WorkspaceChartSeries[]; disabledIndices: Set; } diff --git a/frontend/testing-view/src/components/Testing/Charts/ChartsGrid.tsx b/frontend/testing-view/src/components/Testing/Charts/ChartsGrid.tsx index 0cd35e5d1..d6813db40 100644 --- a/frontend/testing-view/src/components/Testing/Charts/ChartsGrid.tsx +++ b/frontend/testing-view/src/components/Testing/Charts/ChartsGrid.tsx @@ -1,6 +1,6 @@ import { SortableContext, rectSortingStrategy } from "@dnd-kit/sortable"; import { cn } from "@workspace/ui/lib"; -import type { WorkspaceChartConfig } from "../../../store/slices/workspacesSlice"; +import type { WorkspaceChartConfig } from "../../../types/workspace/charts"; import { SortableChart } from "./SortableChart"; interface ChartsGridProps { diff --git a/frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx b/frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx index e66a7e7d6..0837ff857 100644 --- a/frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx +++ b/frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx @@ -3,13 +3,13 @@ import { cn } from "@workspace/ui/lib/utils"; import { useState } from "react"; import "uplot/dist/uPlot.min.css"; import { useStore } from "../../../store/store"; -import type { VariableSeries } from "../../../types/workspace/charts"; +import type { WorkspaceChartSeries } from "../../../types/workspace/charts"; import { ChartLegend } from "./ChartLegend"; import { ChartSurface } from "./ChartSurface"; interface TelemetryChartProps { id: string; - series: VariableSeries[]; + series: WorkspaceChartSeries[]; isDragging: boolean; isOver?: boolean; dragAttributes?: any; diff --git a/frontend/testing-view/src/components/Testing/Charts/tooltipPlugin.ts b/frontend/testing-view/src/components/Testing/Charts/tooltipPlugin.ts index 802e42043..f82a7e207 100644 --- a/frontend/testing-view/src/components/Testing/Charts/tooltipPlugin.ts +++ b/frontend/testing-view/src/components/Testing/Charts/tooltipPlugin.ts @@ -1,7 +1,7 @@ import { COLORS } from "../../../constants/chartsColors"; -import type { VariableSeries } from "../../../types/workspace/charts"; +import type { WorkspaceChartSeries } from "../../../types/workspace/charts"; -export const createTooltipPlugin = (series: VariableSeries[]) => { +export const createTooltipPlugin = (series: WorkspaceChartSeries[]) => { let tooltip: HTMLDivElement, header: HTMLDivElement, rows: HTMLDivElement[], diff --git a/frontend/testing-view/src/components/Testing/DndOverlay.tsx b/frontend/testing-view/src/components/Testing/DndOverlay.tsx index 345efdc29..4f3818560 100644 --- a/frontend/testing-view/src/components/Testing/DndOverlay.tsx +++ b/frontend/testing-view/src/components/Testing/DndOverlay.tsx @@ -1,7 +1,7 @@ import { DragOverlay } from "@dnd-kit/core"; import { Badge } from "@workspace/ui"; -import type { WorkspaceChartConfig } from "../../store/slices/workspacesSlice"; import type { DndActiveData } from "../../types/app/dndData"; +import type { WorkspaceChartConfig } from "../../types/workspace/charts"; import { TelemetryChart } from "./Charts/TelemetryChart"; interface DndOverlayProps { diff --git a/frontend/testing-view/src/mocks/chartsConfigurations.ts b/frontend/testing-view/src/mocks/chartsConfigurations.ts index 173f88306..7e0fed4b1 100644 --- a/frontend/testing-view/src/mocks/chartsConfigurations.ts +++ b/frontend/testing-view/src/mocks/chartsConfigurations.ts @@ -1,4 +1,4 @@ -import type { WorkspaceChartConfig } from "../store/slices/workspacesSlice"; +import type { WorkspaceChartConfig } from "../types/workspace/charts"; export const MOCK_CHARTS: Record = { "workspace-1": [ diff --git a/frontend/testing-view/src/store/slices/workspacesSlice.ts b/frontend/testing-view/src/store/slices/workspacesSlice.ts index 191b88eef..10a42271a 100644 --- a/frontend/testing-view/src/store/slices/workspacesSlice.ts +++ b/frontend/testing-view/src/store/slices/workspacesSlice.ts @@ -11,6 +11,11 @@ import type { CatalogItem } from "../../types/common/item"; import type { BoardName } from "../../types/data/board"; import type { Variable } from "../../types/data/telemetryCatalogItem"; import type { VirtualRow } from "../../types/data/virtualization"; +import type { + CheckboxState, + WorkspaceChartConfig, + WorkspaceChartSeries, +} from "../../types/workspace/charts"; import type { FilterScope, TabFilter, @@ -24,19 +29,6 @@ import type { import type { Workspace } from "../../types/workspace/workspace"; import type { Store } from "../store"; -export interface WorkspaceChartSeries { - packetId: number; - variable: string; -} - -export interface WorkspaceChartConfig { - id: string; - series: WorkspaceChartSeries[]; - historyLimit: number; -} - -export type CheckboxState = boolean | "indeterminate"; - export interface WorkspacesSlice { // Workspaces activeWorkspace: Workspace | null; diff --git a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts index 41fcd4bdb..a526962eb 100644 --- a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts +++ b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts @@ -18,7 +18,7 @@ export interface RawPacket extends Item { cycleTime: number; type: string; measurements: Variable[]; - /** Currently unused (always equals to "000000" placeholder) */ + /** Currently unused (always equals to "000000" placeholder on the backend) */ hexValue: string; } diff --git a/frontend/testing-view/src/types/telemetry/telemetry.ts b/frontend/testing-view/src/types/telemetry/telemetry.ts index 3d1a1b091..330c63722 100644 --- a/frontend/testing-view/src/types/telemetry/telemetry.ts +++ b/frontend/testing-view/src/types/telemetry/telemetry.ts @@ -1,5 +1,12 @@ import type { TelemetryPacket } from "@workspace/core"; +/** + * Map of telemetry packets per telemetry packetid. + */ export type TelemetryData = Record; +/** + * Full map of telemetry packets per telemetry packetid.\ + * This is not the same type as TelemetryData conceptually, but they match. + */ export type TelemetryState = Record; diff --git a/frontend/testing-view/src/types/workspace/charts.ts b/frontend/testing-view/src/types/workspace/charts.ts index 3a888b7f9..b1277bda0 100644 --- a/frontend/testing-view/src/types/workspace/charts.ts +++ b/frontend/testing-view/src/types/workspace/charts.ts @@ -1,4 +1,23 @@ -export type VariableSeries = { +/** + * Workspace chart series. Chosen "lines" to be showed. + */ +export interface WorkspaceChartSeries { packetId: number; variable: string; -}; +} + +/** + * Workspace chart configuration. + */ +export interface WorkspaceChartConfig { + id: string; + series: WorkspaceChartSeries[]; + /** Last n points to be kept in memory. The same thing as buffer size of the chart. */ + historyLimit: number; +} + +/** + * Checkbox state type.\ + * Indeterminate state is used when some items under the group are checked, but not all. + */ +export type CheckboxState = boolean | "indeterminate"; diff --git a/frontend/testing-view/src/types/workspace/filters.ts b/frontend/testing-view/src/types/workspace/filters.ts index 93cb06b86..303812832 100644 --- a/frontend/testing-view/src/types/workspace/filters.ts +++ b/frontend/testing-view/src/types/workspace/filters.ts @@ -1,8 +1,18 @@ import type { BoardName } from "../data/board"; import type { SidebarTab } from "./sidebar"; +/** + * Filter scope identifiers. + * **Be careful** don't confuse this one with sidebar tabs. + */ export type FilterScope = SidebarTab | "logs"; +/** + * Record of filtered items ids per board. + */ export type TabFilter = Record; +/** + * Record of filtered items ids per filter scope. + */ export type WorkspaceFilters = Record; diff --git a/frontend/testing-view/src/types/workspace/keyBinding.ts b/frontend/testing-view/src/types/workspace/keyBinding.ts index 39c7423d4..8458855ef 100644 --- a/frontend/testing-view/src/types/workspace/keyBinding.ts +++ b/frontend/testing-view/src/types/workspace/keyBinding.ts @@ -1,6 +1,13 @@ +/** + * Key binding definition. + */ export interface KeyBinding { - id: string; // UUID for this binding - commandId: number; // The command's ID - key: string; // The key (e.g., "1", "a") + /** UUID for this binding */ + id: string; + /** The command's ID */ + commandId: number; + /** The key (e.g., "1", "a") */ + key: string; + /** The parameters for this binding executed with the command */ parameters: Record; } diff --git a/frontend/testing-view/src/types/workspace/sidebar.ts b/frontend/testing-view/src/types/workspace/sidebar.ts index 040cc6509..aaad45f35 100644 --- a/frontend/testing-view/src/types/workspace/sidebar.ts +++ b/frontend/testing-view/src/types/workspace/sidebar.ts @@ -1,5 +1,11 @@ import type { FilterScope } from "./filters"; +/** + * Sidebar tab identifiers. + */ export type SidebarTab = "commands" | "telemetry"; +/** + * Record of expanded items per filter scope. + */ export type WorkspaceExpandedItems = Record>; diff --git a/frontend/testing-view/src/types/workspace/workspace.ts b/frontend/testing-view/src/types/workspace/workspace.ts index ea42e3d0b..bf835f3a1 100644 --- a/frontend/testing-view/src/types/workspace/workspace.ts +++ b/frontend/testing-view/src/types/workspace/workspace.ts @@ -1,8 +1,15 @@ import type { KeyBinding } from "./keyBinding"; +/** + * Workspace definition. + */ export interface Workspace { + /** The name of the workspace */ name: string; + /** The id of the workspace */ id: string; + /** The description of the workspace */ description: string; + /** The key bindings for the workspace */ keyBindings: KeyBinding[]; } From 46b937f59afa57f42b7b464b612d2cae99744064 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:00:30 +0100 Subject: [PATCH 08/19] refact: divide workspace slice into smaller slices --- .../RightSidebar/Sections/MessageItem.tsx | 14 +- .../Tabs/Telemetry/ChartPicker.tsx | 2 +- frontend/testing-view/src/hooks/useAppMode.ts | 2 +- .../src/hooks/useTransformedBoards.ts | 10 +- .../src/store/slices/chartsSlice.ts | 146 +++++ .../src/store/slices/filteringSlice.ts | 368 ++++++++++++ .../src/store/slices/workspacesSlice.ts | 527 +----------------- frontend/testing-view/src/store/store.ts | 13 +- 8 files changed, 552 insertions(+), 530 deletions(-) create mode 100644 frontend/testing-view/src/store/slices/chartsSlice.ts create mode 100644 frontend/testing-view/src/store/slices/filteringSlice.ts diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessageItem.tsx b/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessageItem.tsx index 8a7ffa8a1..8a35647ac 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessageItem.tsx +++ b/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessageItem.tsx @@ -82,12 +82,14 @@ export const MessageItem = ({ message }: MessageItemProps) => {
e.stopPropagation()}>
- {/* If it's a protection message, showing the sub-kind can be helpful */} - {message.payload?.kind && ( - - {message.payload.kind.replace("_", " ")} - - )} + {/* If it's a detailed message, showing the sub-kind can be helpful */} + {message.payload && + typeof message.payload === "object" && + message.payload.kind && ( + + {message.payload.kind.replace("_", " ")} + + )}

{renderMessageContent(message.payload)}

diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/ChartPicker.tsx b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/ChartPicker.tsx index ebfc0e697..39bae8636 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/ChartPicker.tsx +++ b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/ChartPicker.tsx @@ -8,7 +8,7 @@ import { DropdownMenuTrigger, } from "@workspace/ui"; import { Plus } from "@workspace/ui/icons"; -import type { WorkspaceChartConfig } from "../../../../../store/slices/workspacesSlice"; +import type { WorkspaceChartConfig } from "../../../../../types/workspace/charts"; interface ChartPickerProps { charts: WorkspaceChartConfig[]; diff --git a/frontend/testing-view/src/hooks/useAppMode.ts b/frontend/testing-view/src/hooks/useAppMode.ts index fa1feb36c..13eb13307 100644 --- a/frontend/testing-view/src/hooks/useAppMode.ts +++ b/frontend/testing-view/src/hooks/useAppMode.ts @@ -1,7 +1,7 @@ import { logger } from "@workspace/core"; import { useCallback, useEffect } from "react"; import { useStore } from "../store/store"; -import type { OrdersData, PacketsData } from "../types/data/transformedBoards"; +import type { OrdersData, PacketsData } from "../types/data/board"; export function useAppMode( packets: PacketsData | null, diff --git a/frontend/testing-view/src/hooks/useTransformedBoards.ts b/frontend/testing-view/src/hooks/useTransformedBoards.ts index 96c590837..c8fc04c40 100644 --- a/frontend/testing-view/src/hooks/useTransformedBoards.ts +++ b/frontend/testing-view/src/hooks/useTransformedBoards.ts @@ -1,6 +1,6 @@ import { useEffect } from "react"; import { useStore } from "../store/store"; -import type { OrdersData, PacketsData } from "../types/data/transformedBoards"; +import type { OrdersData, PacketsData } from "../types/data/board"; import { useBoardData } from "./useBoardData"; export function useTransformedBoards( @@ -12,7 +12,9 @@ export function useTransformedBoards( const setTelemetryCatalog = useStore((s) => s.setTelemetryCatalog); const setCommandsCatalog = useStore((s) => s.setCommandsCatalog); - const initializeTabFilters = useStore((s) => s.initializeTabFilters); + const initializeWorkspaceFilters = useStore( + (s) => s.initializeWorkspaceFilters, + ); useEffect(() => { if ( @@ -23,12 +25,12 @@ export function useTransformedBoards( setTelemetryCatalog(transformedBoards.telemetryCatalog); setCommandsCatalog(transformedBoards.commandsCatalog); - initializeTabFilters(); + initializeWorkspaceFilters(); }, [ transformedBoards, setTelemetryCatalog, setCommandsCatalog, - initializeTabFilters, + initializeWorkspaceFilters, ]); // Debug logs diff --git a/frontend/testing-view/src/store/slices/chartsSlice.ts b/frontend/testing-view/src/store/slices/chartsSlice.ts new file mode 100644 index 000000000..b0d21e02d --- /dev/null +++ b/frontend/testing-view/src/store/slices/chartsSlice.ts @@ -0,0 +1,146 @@ +import type { StateCreator } from "zustand"; +import { EMPTY_ARRAY } from "../../constants/emptyArray"; +import type { + WorkspaceChartConfig, + WorkspaceChartSeries, +} from "../../types/workspace/charts"; +import type { Store } from "../store"; + +export interface ChartsSlice { + /** Map of WorkspaceID -> List of Charts */ + charts: Record; + + setCharts: (charts: Record) => void; + getActiveWorkspaceCharts: () => WorkspaceChartConfig[]; + + addChart: (workspaceId: string) => string; + removeChart: (workspaceId: string, chartId: string) => void; + reorderCharts: ( + workspaceId: string, + oldIndex: number, + newIndex: number, + ) => void; + + addSeriesToChart: ( + workspaceId: string, + chartId: string, + series: WorkspaceChartSeries, + ) => void; + removeSeriesFromChart: ( + workspaceId: string, + chartId: string, + variable: string, + ) => void; + + /** Sets new buffer size or history limit for a chart. */ + setChartHistoryLimit: ( + workspaceId: string, + chartId: string, + newHistoryLimit: number, + ) => void; +} + +export const createChartsSlice: StateCreator = ( + set, + get, +) => ({ + // Telemetry Charts + charts: { + "workspace-1": [], + "workspace-2": [], + "workspace-3": [], + }, + + setCharts: (charts) => set({ charts }), + + getActiveWorkspaceCharts: () => { + const id = get().getActiveWorkspaceId(); + if (!id) return EMPTY_ARRAY as WorkspaceChartConfig[]; + return get().charts[id] || EMPTY_ARRAY; + }, + + // Future-proofing Actions + addChart: (workspaceId) => { + const newChartId = crypto.randomUUID(); + set((state) => ({ + charts: { + ...state.charts, + [workspaceId]: [ + ...(state.charts[workspaceId] || []), + { id: newChartId, series: [], historyLimit: 200 }, + ], + }, + })); + return newChartId; + }, + + removeChart: (workspaceId, chartId) => + set((state) => ({ + charts: { + ...state.charts, + [workspaceId]: (state.charts[workspaceId] || []).filter( + (c) => c.id !== chartId, + ), + }, + })), + + clearCharts: () => { + const activeWorkspaceId = get().getActiveWorkspaceId(); + if (!activeWorkspaceId) return; + + set((state) => ({ + charts: { + ...state.charts, + [activeWorkspaceId]: [], + }, + })); + }, + + reorderCharts: (workspaceId, oldIndex, newIndex) => { + if (oldIndex < 0 || newIndex < 0) return; + + set((state) => { + const charts = [...(state.charts[workspaceId] || [])]; + const [removed] = charts.splice(oldIndex, 1); + charts.splice(newIndex, 0, removed); + return { + charts: { + ...state.charts, + [workspaceId]: charts, + }, + }; + }); + }, + + addSeriesToChart: (workspaceId, chartId, series) => + set((state) => ({ + charts: { + ...state.charts, + [workspaceId]: (state.charts[workspaceId] || []).map((c) => + c.id === chartId ? { ...c, series: [...c.series, series] } : c, + ), + }, + })), + + removeSeriesFromChart: (workspaceId, chartId, variable) => + set((state) => ({ + charts: { + ...state.charts, + [workspaceId]: (state.charts[workspaceId] || []).map((c) => + c.id === chartId + ? { ...c, series: c.series.filter((s) => s.variable !== variable) } + : c, + ), + }, + })), + + setChartHistoryLimit: (workspaceId, chartId, newHistoryLimit) => + set((state) => ({ + charts: { + ...state.charts, + [workspaceId]: (state.charts[workspaceId] || []).map((c) => + c.id === chartId ? { ...c, historyLimit: newHistoryLimit } : c, + ), + }, + })), +}); diff --git a/frontend/testing-view/src/store/slices/filteringSlice.ts b/frontend/testing-view/src/store/slices/filteringSlice.ts new file mode 100644 index 000000000..e523e2517 --- /dev/null +++ b/frontend/testing-view/src/store/slices/filteringSlice.ts @@ -0,0 +1,368 @@ +import type { StateCreator } from "zustand"; +import { + createEmptyFilter, + createFullFilter, + generateInitialFilters, + getCatalogKey, +} from "../../lib/utils"; +import type { CatalogItem } from "../../types/common/item"; +import type { BoardName } from "../../types/data/board"; +import type { Variable } from "../../types/data/telemetryCatalogItem"; +import type { VirtualRow } from "../../types/data/virtualization"; +import type { CheckboxState } from "../../types/workspace/charts"; +import type { + FilterScope, + TabFilter, + WorkspaceFilters, +} from "../../types/workspace/filters"; +import type { + SidebarTab, + WorkspaceExpandedItems, +} from "../../types/workspace/sidebar"; +import type { Store } from "../store"; + +export interface FilteringSlice { + /** Sidebar Navigation */ + activeTab: Record; + getActiveTab: () => SidebarTab; + setActiveTab: (tab: SidebarTab) => void; + + filterDialog: { + isOpen: boolean; + scope: FilterScope | null; + }; + openFilterDialog: (scope: FilterScope) => void; + closeFilterDialog: () => void; + + /** Filter State */ + workspaceFilters: Record; + initializeWorkspaceFilters: () => void; + updateFilters: (scope: FilterScope, filters: TabFilter) => void; + getActiveFilters: (scope: FilterScope) => TabFilter | undefined; + + /** Filter Actions */ + selectAllFilters: (scope: FilterScope) => void; + clearFilters: (scope: FilterScope) => void; + toggleCategoryFilter: ( + scope: FilterScope, + category: BoardName, + checked: boolean, + ) => void; + toggleItemFilter: ( + scope: FilterScope, + category: BoardName, + id: number, + ) => void; + + /** Filter Queries */ + getCatalog: (scope: FilterScope) => Record; + getFilteredItems: (scope: FilterScope) => CatalogItem[]; + getFilteredItemsIds: (scope: FilterScope) => number[]; + getFilteredItemsIdsByCategory: ( + scope: FilterScope, + category: BoardName, + ) => number[]; + getFilteredItemsByCategory: ( + scope: FilterScope, + category: BoardName, + ) => CatalogItem[]; + + getFilteredCount: (scope: FilterScope) => number; + getFilteredCountByCategory: ( + scope: FilterScope, + category: BoardName, + ) => number; + getTotalCount: (scope: FilterScope) => number; + getSelectionState: (scope: FilterScope, category: BoardName) => CheckboxState; + + /** Virtualization & Expansion */ + expandedItems: Record; + isItemExpanded: ( + scope: SidebarTab, + type: string, + itemId: number | string, + ) => boolean; + toggleExpandedItem: ( + scope: SidebarTab, + type: string, + itemId: number | string, + ) => void; + getActiveExpanded: (scope: FilterScope) => Set | undefined; + getFlattenedRows: ( + scope: SidebarTab, + categories: readonly BoardName[], + ) => VirtualRow[]; +} + +export const createFilteringSlice: StateCreator< + Store, + [], + [], + FilteringSlice +> = (set, get) => ({ + // Tabs (per workspace) + activeTab: {}, + getActiveTab: () => { + const activeWorkspaceId = get().getActiveWorkspaceId(); + if (!activeWorkspaceId) return "commands"; + return get().activeTab[activeWorkspaceId] || "commands"; + }, + setActiveTab: (tab) => { + const activeWorkspaceId = get().getActiveWorkspaceId(); + if (!activeWorkspaceId) return; + + set((state) => ({ + activeTab: { ...state.activeTab, [activeWorkspaceId]: tab }, + })); + }, + + openFilterDialog: (scope: FilterScope) => + set({ filterDialog: { isOpen: true, scope } }), + closeFilterDialog: () => + set({ filterDialog: { isOpen: false, scope: null } }), + + // Internal helpers + getCatalog: (scope: FilterScope) => { + const catalogKey = getCatalogKey(scope); + if (!catalogKey) return {}; + + return get()[catalogKey]; + }, + + toggleItemFilter: (scope, category, itemId) => { + const workspaceId = get().getActiveWorkspaceId(); + if (!workspaceId) return; + + const currentWorkspaceFilters = get().workspaceFilters[workspaceId] || {}; + const currentTabFilter = + currentWorkspaceFilters[scope] || createEmptyFilter(); + + const currentCategoryIds = currentTabFilter[category] || []; + + const isSelected = currentCategoryIds.includes(itemId); + const newCategoryIds = isSelected + ? currentCategoryIds.filter((id) => id !== itemId) + : [...currentCategoryIds, itemId]; + + get().updateFilters(scope, { + ...currentTabFilter, + [category]: newCategoryIds, + }); + }, + + // Filter actions + selectAllFilters: (scope) => { + const workspaceId = get().getActiveWorkspaceId(); + if (!workspaceId) return; + + const items = get().getCatalog(scope); + + const fullFilter = createFullFilter(items); + get().updateFilters(scope, fullFilter); + }, + clearFilters: (scope) => { + const workspaceId = get().getActiveWorkspaceId(); + if (!workspaceId) return; + const emptyFilter = createEmptyFilter(); + get().updateFilters(scope, emptyFilter); + }, + toggleCategoryFilter: (scope, category, checked) => { + const workspaceId = get().getActiveWorkspaceId(); + if (!workspaceId) return; + + const catalog = get().getCatalog(scope); + + const currentFilters = + get().workspaceFilters[workspaceId]?.[scope] || createEmptyFilter(); + + const newItems = checked + ? catalog?.[category]?.map((item) => item.id) || [] + : []; + + get().updateFilters(scope, { + ...currentFilters, + [category]: newItems, + }); + }, + + workspaceFilters: {}, + initializeWorkspaceFilters: () => { + const commands = get().commandsCatalog; + const telemetry = get().telemetryCatalog; + + const currentFilters = get().workspaceFilters; + + // Only initialize if filters are empty (not persisted) + if (Object.keys(currentFilters).length === 0) { + set({ + workspaceFilters: generateInitialFilters({ + commands: createFullFilter(commands), + telemetry: createFullFilter(telemetry), + logs: createFullFilter(telemetry), + }), + }); + } + }, + updateFilters: (scope, filters) => { + const workspaceId = get().getActiveWorkspaceId(); + if (!workspaceId) return; + + set((state) => ({ + workspaceFilters: { + ...state.workspaceFilters, + [workspaceId]: { + ...(state.workspaceFilters[workspaceId] || {}), + [scope]: filters, + }, + }, + })); + }, + + getFilteredItemsByCategory: (scope, category) => { + const selected = get().getFilteredItemsIdsByCategory(scope, category); + const catalog = get().getCatalog(scope); + const items = catalog?.[category] || []; + return items.filter((i) => selected.includes(i.id)); + }, + + // Helper getters + getActiveFilters: (scope) => { + const id = get().getActiveWorkspaceId(); + return id ? get().workspaceFilters[id]?.[scope] : undefined; + }, + + // Expanded items + expandedItems: {}, + isItemExpanded: (scope, type, itemId) => { + const activeWorkspaceId = get().getActiveWorkspaceId(); + if (!activeWorkspaceId) return false; + + const expandedItems = get().expandedItems[activeWorkspaceId]?.[scope]; + if (!expandedItems) return false; + + return expandedItems.has(`${type}:${itemId}`); + }, + toggleExpandedItem: (scope, type, itemId) => { + const activeWorkspaceId = get().getActiveWorkspaceId(); + if (!activeWorkspaceId) return; + + set((state) => { + const expandedItems = + state.expandedItems[activeWorkspaceId]?.[scope] || new Set(); + const newExpandedItems = new Set(expandedItems); + + if (newExpandedItems.has(`${type}:${itemId}`)) { + newExpandedItems.delete(`${type}:${itemId}`); + } else { + newExpandedItems.add(`${type}:${itemId}`); + } + + return { + expandedItems: { + ...state.expandedItems, + [activeWorkspaceId]: { + ...state.expandedItems[activeWorkspaceId], + [scope]: newExpandedItems, + }, + }, + }; + }); + }, + getFlattenedRows: (scope, categories) => { + const rows: VirtualRow[] = []; + + categories.forEach((category) => { + const items = get().getFilteredItemsByCategory(scope, category); + if (items.length === 0) return; + + // Add the Header (Board) + rows.push({ + type: "board", + id: category, + label: category, + count: items.length, + }); + + // If the board is expanded, add its packets + if (get().isItemExpanded(scope, "board", category)) { + items.forEach((item) => { + rows.push({ + type: "packet", + id: item.id, + data: item, + }); + + // If the packet is expanded, add its variables/measurements + if (get().isItemExpanded(scope, "packet", item.id)) { + if ("measurements" in item) { + const variables = item.measurements as Variable[]; + variables.forEach((m) => { + rows.push({ + type: "variable", + id: `${item.id}-${m.id}`, + data: m, + packetId: item.id, + }); + }); + } + } + }); + } + }); + + return rows; + }, + // Filter dialog + filterDialog: { + isOpen: false, + scope: null, + }, + + getActiveExpanded: (scope) => { + const id = get().getActiveWorkspaceId(); + return id ? get().expandedItems[id]?.[scope] : undefined; + }, + + // Getters for filtered items + getFilteredItemsIds: (scope) => { + const filters = get().getActiveFilters(scope); + return filters ? Object.values(filters).flat() : []; + }, + + getFilteredItemsIdsByCategory: (scope, category) => { + return get().getActiveFilters(scope)?.[category] || []; + }, + getFilteredItems: (scope) => { + const filters = get().getActiveFilters(scope); + if (!filters) return []; + + const catalog = get().getCatalog(scope); + if (!catalog) return []; + + return Object.entries(catalog).flatMap(([cat, items]) => { + const selected = filters[cat as BoardName] || []; + return items.filter((i) => selected.includes(i.id)); + }); + }, + + // Stats getters + getFilteredCount: (scope) => get().getFilteredItemsIds(scope).length, + + getFilteredCountByCategory: (scope, category) => + get().getFilteredItemsIdsByCategory(scope, category).length, + + getTotalCount: (scope) => { + const catalog = get().getCatalog(scope); + return Object.values(catalog).reduce((acc, items) => acc + items.length, 0); + }, + + getSelectionState: (scope, category) => { + const selectedCount = get().getFilteredCountByCategory(scope, category); + const catalog = get().getCatalog(scope); + const totalItems = catalog?.[category]?.length || 0; + + if (totalItems === 0 || selectedCount === 0) return false; + if (selectedCount === totalItems) return true; + return "indeterminate"; + }, +}); diff --git a/frontend/testing-view/src/store/slices/workspacesSlice.ts b/frontend/testing-view/src/store/slices/workspacesSlice.ts index 10a42271a..f3fc4de4c 100644 --- a/frontend/testing-view/src/store/slices/workspacesSlice.ts +++ b/frontend/testing-view/src/store/slices/workspacesSlice.ts @@ -1,36 +1,13 @@ import type { StateCreator } from "zustand"; import { DEFAULT_WORKSPACES } from "../../constants/defaultWorkspaces"; -import { EMPTY_ARRAY } from "../../constants/emptyArray"; -import { - createEmptyFilter, - createFullFilter, - generateInitialFilters, - getCatalogKey, -} from "../../lib/utils"; -import type { CatalogItem } from "../../types/common/item"; -import type { BoardName } from "../../types/data/board"; -import type { Variable } from "../../types/data/telemetryCatalogItem"; -import type { VirtualRow } from "../../types/data/virtualization"; -import type { - CheckboxState, - WorkspaceChartConfig, - WorkspaceChartSeries, -} from "../../types/workspace/charts"; -import type { - FilterScope, - TabFilter, - WorkspaceFilters, -} from "../../types/workspace/filters"; +import { createFullFilter } from "../../lib/utils"; import type { KeyBinding } from "../../types/workspace/keyBinding"; -import type { - SidebarTab, - WorkspaceExpandedItems, -} from "../../types/workspace/sidebar"; +import type { SidebarTab } from "../../types/workspace/sidebar"; import type { Workspace } from "../../types/workspace/workspace"; import type { Store } from "../store"; export interface WorkspacesSlice { - // Workspaces + /** Workspaces */ activeWorkspace: Workspace | null; workspaces: Workspace[]; setActiveWorkspace: (workspace: Workspace) => void; @@ -39,113 +16,7 @@ export interface WorkspacesSlice { addWorkspace: (name: string, description: string) => void; getActiveWorkspaceId: () => string | null; - // Internal helpers - getCatalog: (scope: FilterScope) => Record; - - // Tabs (per workspace) - activeTab: Record; - getActiveTab: () => SidebarTab; - setActiveTab: (tab: SidebarTab) => void; - - // Filters (per workspace) - tabFilters: Record; - initializeTabFilters: () => void; - updateFilters: (scope: FilterScope, filters: TabFilter) => void; - - // Helper getters - getActiveFilters: (scope: FilterScope) => TabFilter | undefined; - getActiveExpanded: (scope: FilterScope) => Set | undefined; - - // Getters for filtered items - getFilteredItems: (scope: FilterScope) => CatalogItem[]; - getFilteredItemsIds: (scope: FilterScope) => number[]; - getFilteredItemsIdsByCategory: ( - scope: FilterScope, - category: BoardName, - ) => number[]; - getFilteredItemsByCategory: ( - scope: FilterScope, - category: BoardName, - ) => CatalogItem[]; - - // Stats getters - getFilteredCount: (scope: FilterScope) => number; - getFilteredCountByCategory: ( - scope: FilterScope, - category: BoardName, - ) => number; - getTotalCount: (scope: FilterScope) => number; - - // Selection state getters - getSelectionState: (scope: FilterScope, category: BoardName) => CheckboxState; - - // Filter actions - selectAllFilters: (scope: FilterScope) => void; - clearFilters: (scope: FilterScope) => void; - toggleCategoryFilter: ( - scope: FilterScope, - category: BoardName, - checked: boolean, - ) => void; - toggleItemFilter: ( - scope: FilterScope, - category: BoardName, - id: number, - ) => void; - - // Expanded items (per workspace) - expandedItems: Record; - isItemExpanded: ( - scope: SidebarTab, - type: string, - itemId: number | string, - ) => boolean; - toggleExpandedItem: ( - scope: SidebarTab, - type: string, - itemId: number | string, - ) => void; - getFlattenedRows: ( - scope: SidebarTab, - categories: readonly BoardName[], - ) => VirtualRow[]; - - // Filter dialog - filterDialog: { - isOpen: boolean; - scope: FilterScope | null; - }; - openFilterDialog: (scope: FilterScope) => void; - closeFilterDialog: () => void; - - // Telemetry Charts - charts: Record; - setCharts: (charts: Record) => void; - getActiveWorkspaceCharts: () => WorkspaceChartConfig[]; - addChart: (workspaceId: string) => string; - removeChart: (workspaceId: string, chartId: string) => void; - reorderCharts: ( - workspaceId: string, - oldIndex: number, - newIndex: number, - ) => void; - addSeriesToChart: ( - workspaceId: string, - chartId: string, - series: WorkspaceChartSeries, - ) => void; - removeSeriesFromChart: ( - workspaceId: string, - chartId: string, - variable: string, - ) => void; - setChartHistoryLimit: ( - workspaceId: string, - chartId: string, - newHistoryLimit: number, - ) => void; - - // Key Bindings + /** Key Bindings */ addKeyBinding: ( commandId: number, key: string, @@ -167,7 +38,7 @@ export const createWorkspacesSlice: StateCreator< [], WorkspacesSlice > = (set, get) => ({ - // Workspaces + /** Workspaces */ activeWorkspace: DEFAULT_WORKSPACES[0], workspaces: DEFAULT_WORKSPACES, setActiveWorkspace: (workspace) => set({ activeWorkspace: workspace }), @@ -189,8 +60,8 @@ export const createWorkspacesSlice: StateCreator< const commands = state.commandsCatalog; const telemetry = state.telemetryCatalog; - const newTabFilters = { - ...state.tabFilters, + const newWorkspaceFilters = { + ...state.workspaceFilters, [newWorkspaceId]: { commands: createFullFilter(commands), telemetry: createFullFilter(telemetry), @@ -223,7 +94,7 @@ export const createWorkspacesSlice: StateCreator< return { workspaces: newWorkspaces, activeWorkspace: newWorkspace, // Auto-switch to the new workspace - tabFilters: newTabFilters, + workspaceFilters: newWorkspaceFilters, expandedItems: newExpandedItems, activeTab: newActiveTabs, charts: newCharts, @@ -264,12 +135,12 @@ export const createWorkspacesSlice: StateCreator< } // Clean up workspace-specific data - const newTabFilters = { ...state.tabFilters }; + const newWorkspaceFilters = { ...state.workspaceFilters }; const newExpandedItems = { ...state.expandedItems }; const newActiveTabs = { ...state.activeTab }; const newCharts = { ...state.charts }; - delete newTabFilters[id]; + delete newWorkspaceFilters[id]; delete newExpandedItems[id]; delete newActiveTabs[id]; delete newCharts[id]; @@ -277,7 +148,7 @@ export const createWorkspacesSlice: StateCreator< return { workspaces: newWorkspaces, activeWorkspace: newActiveWorkspace, - tabFilters: newTabFilters, + workspaceFilters: newWorkspaceFilters, expandedItems: newExpandedItems, activeTab: newActiveTabs, charts: newCharts, @@ -290,382 +161,6 @@ export const createWorkspacesSlice: StateCreator< return activeWorkspace?.id ?? null; }, - // Internal helpers - getCatalog: (scope: FilterScope) => { - const catalogKey = getCatalogKey(scope); - if (!catalogKey) return {}; - - return get()[catalogKey]; - }, - - // Tabs (per workspace) - activeTab: {}, - getActiveTab: () => { - const activeWorkspaceId = get().getActiveWorkspaceId(); - if (!activeWorkspaceId) return "commands"; - return get().activeTab[activeWorkspaceId] || "commands"; - }, - setActiveTab: (tab) => { - const activeWorkspaceId = get().getActiveWorkspaceId(); - if (!activeWorkspaceId) return; - - set((state) => ({ - activeTab: { ...state.activeTab, [activeWorkspaceId]: tab }, - })); - }, - - // Filters (per workspace) - tabFilters: {}, - initializeTabFilters: () => { - const commands = get().commandsCatalog; - const telemetry = get().telemetryCatalog; - - const currentFilters = get().tabFilters; - - // Only initialize if filters are empty (not persisted) - if (Object.keys(currentFilters).length === 0) { - set({ - tabFilters: generateInitialFilters({ - commands: createFullFilter(commands), - telemetry: createFullFilter(telemetry), - logs: createFullFilter(telemetry), - }), - }); - } - }, - updateFilters: (scope, filters) => { - const workspaceId = get().getActiveWorkspaceId(); - if (!workspaceId) return; - - set((state) => ({ - tabFilters: { - ...state.tabFilters, - [workspaceId]: { - ...(state.tabFilters[workspaceId] || {}), - [scope]: filters, - }, - }, - })); - }, - - // Helper getters - getActiveFilters: (scope) => { - const id = get().getActiveWorkspaceId(); - return id ? get().tabFilters[id]?.[scope] : undefined; - }, - - getActiveExpanded: (scope) => { - const id = get().getActiveWorkspaceId(); - return id ? get().expandedItems[id]?.[scope] : undefined; - }, - - // Getters for filtered items - getFilteredItemsIds: (scope) => { - const filters = get().getActiveFilters(scope); - return filters ? Object.values(filters).flat() : []; - }, - - getFilteredItemsIdsByCategory: (scope, category) => { - return get().getActiveFilters(scope)?.[category] || []; - }, - getFilteredItems: (scope) => { - const filters = get().getActiveFilters(scope); - if (!filters) return []; - - const catalog = get().getCatalog(scope); - if (!catalog) return []; - - return Object.entries(catalog).flatMap(([cat, items]) => { - const selected = filters[cat as BoardName] || []; - return items.filter((i) => selected.includes(i.id)); - }); - }, - getFilteredItemsByCategory: (scope, category) => { - const selected = get().getFilteredItemsIdsByCategory(scope, category); - const catalog = get().getCatalog(scope); - const items = catalog?.[category] || []; - return items.filter((i) => selected.includes(i.id)); - }, - - // Stats getters - getFilteredCount: (scope) => get().getFilteredItemsIds(scope).length, - - getFilteredCountByCategory: (scope, category) => - get().getFilteredItemsIdsByCategory(scope, category).length, - - getTotalCount: (scope) => { - const catalog = get().getCatalog(scope); - return Object.values(catalog).reduce((acc, items) => acc + items.length, 0); - }, - - getSelectionState: (scope, category) => { - const selectedCount = get().getFilteredCountByCategory(scope, category); - const catalog = get().getCatalog(scope); - const totalItems = catalog?.[category]?.length || 0; - - if (totalItems === 0 || selectedCount === 0) return false; - if (selectedCount === totalItems) return true; - return "indeterminate"; - }, - - // getCategoryCheckedCount: (scope, category) => { - // const activeWorkspaceId = get().getActiveWorkspaceId(); - // if (!activeWorkspaceId) return 0; - - // const filter = get().tabFilters[activeWorkspaceId][scope]; - // if (!filter) return 0; - - // return filter[category].length; - // }, - - // Filter actions - selectAllFilters: (scope) => { - const workspaceId = get().getActiveWorkspaceId(); - if (!workspaceId) return; - - const items = get().getCatalog(scope); - - const fullFilter = createFullFilter(items); - get().updateFilters(scope, fullFilter); - }, - clearFilters: (scope) => { - const workspaceId = get().getActiveWorkspaceId(); - if (!workspaceId) return; - const emptyFilter = createEmptyFilter(); - get().updateFilters(scope, emptyFilter); - }, - toggleCategoryFilter: (scope, category, checked) => { - const workspaceId = get().getActiveWorkspaceId(); - if (!workspaceId) return; - - const catalog = get().getCatalog(scope); - - const currentFilters = - get().tabFilters[workspaceId]?.[scope] || createEmptyFilter(); - - const newItems = checked - ? catalog?.[category]?.map((item) => item.id) || [] - : []; - - get().updateFilters(scope, { - ...currentFilters, - [category]: newItems, - }); - }, - - toggleItemFilter: (scope, category, itemId) => { - const workspaceId = get().getActiveWorkspaceId(); - if (!workspaceId) return; - - const currentWorkspaceFilters = get().tabFilters[workspaceId] || {}; - const currentTabFilter = - currentWorkspaceFilters[scope] || createEmptyFilter(); - - const currentCategoryIds = currentTabFilter[category] || []; - - const isSelected = currentCategoryIds.includes(itemId); - const newCategoryIds = isSelected - ? currentCategoryIds.filter((id) => id !== itemId) - : [...currentCategoryIds, itemId]; - - get().updateFilters(scope, { - ...currentTabFilter, - [category]: newCategoryIds, - }); - }, - - // Expanded items - expandedItems: {}, - isItemExpanded: (scope, type, itemId) => { - const activeWorkspaceId = get().getActiveWorkspaceId(); - if (!activeWorkspaceId) return false; - - const expandedItems = get().expandedItems[activeWorkspaceId]?.[scope]; - if (!expandedItems) return false; - - return expandedItems.has(`${type}:${itemId}`); - }, - toggleExpandedItem: (scope, type, itemId) => { - const activeWorkspaceId = get().getActiveWorkspaceId(); - if (!activeWorkspaceId) return; - - set((state) => { - const expandedItems = - state.expandedItems[activeWorkspaceId]?.[scope] || new Set(); - const newExpandedItems = new Set(expandedItems); - - if (newExpandedItems.has(`${type}:${itemId}`)) { - newExpandedItems.delete(`${type}:${itemId}`); - } else { - newExpandedItems.add(`${type}:${itemId}`); - } - - return { - expandedItems: { - ...state.expandedItems, - [activeWorkspaceId]: { - ...state.expandedItems[activeWorkspaceId], - [scope]: newExpandedItems, - }, - }, - }; - }); - }, - getFlattenedRows: (scope, categories) => { - const rows: VirtualRow[] = []; - - categories.forEach((category) => { - const items = get().getFilteredItemsByCategory(scope, category); - if (items.length === 0) return; - - // Add the Header (Board) - rows.push({ - type: "board", - id: category, - label: category, - count: items.length, - }); - - // If the board is expanded, add its packets - if (get().isItemExpanded(scope, "board", category)) { - items.forEach((item) => { - rows.push({ - type: "packet", - id: item.id, - data: item, - }); - - // If the packet is expanded, add its variables/measurements - if (get().isItemExpanded(scope, "packet", item.id)) { - if ("measurements" in item) { - const variables = item.measurements as Variable[]; - variables.forEach((m) => { - rows.push({ - type: "variable", - id: `${item.id}-${m.id}`, - data: m, - packetId: item.id, - }); - }); - } - } - }); - } - }); - - return rows; - }, - // Filter dialog - filterDialog: { - isOpen: false, - scope: null, - }, - - openFilterDialog: (scope: FilterScope) => - set({ filterDialog: { isOpen: true, scope } }), - closeFilterDialog: () => - set({ filterDialog: { isOpen: false, scope: null } }), - - // Telemetry Charts - charts: { - "workspace-1": [], - "workspace-2": [], - "workspace-3": [], - }, - - setCharts: (charts) => set({ charts }), - - getActiveWorkspaceCharts: () => { - const id = get().getActiveWorkspaceId(); - if (!id) return EMPTY_ARRAY as WorkspaceChartConfig[]; - return get().charts[id] || EMPTY_ARRAY; - }, - - // Future-proofing Actions - addChart: (workspaceId) => { - const newChartId = crypto.randomUUID(); - set((state) => ({ - charts: { - ...state.charts, - [workspaceId]: [ - ...(state.charts[workspaceId] || []), - { id: newChartId, series: [], historyLimit: 200 }, - ], - }, - })); - return newChartId; - }, - - removeChart: (workspaceId, chartId) => - set((state) => ({ - charts: { - ...state.charts, - [workspaceId]: (state.charts[workspaceId] || []).filter( - (c) => c.id !== chartId, - ), - }, - })), - - clearCharts: () => { - const activeWorkspaceId = get().getActiveWorkspaceId(); - if (!activeWorkspaceId) return; - - set((state) => ({ - charts: { - ...state.charts, - [activeWorkspaceId]: [], - }, - })); - }, - - reorderCharts: (workspaceId, oldIndex, newIndex) => { - if (oldIndex < 0 || newIndex < 0) return; - - set((state) => { - const charts = [...(state.charts[workspaceId] || [])]; - const [removed] = charts.splice(oldIndex, 1); - charts.splice(newIndex, 0, removed); - return { - charts: { - ...state.charts, - [workspaceId]: charts, - }, - }; - }); - }, - - addSeriesToChart: (workspaceId, chartId, series) => - set((state) => ({ - charts: { - ...state.charts, - [workspaceId]: (state.charts[workspaceId] || []).map((c) => - c.id === chartId ? { ...c, series: [...c.series, series] } : c, - ), - }, - })), - - removeSeriesFromChart: (workspaceId, chartId, variable) => - set((state) => ({ - charts: { - ...state.charts, - [workspaceId]: (state.charts[workspaceId] || []).map((c) => - c.id === chartId - ? { ...c, series: c.series.filter((s) => s.variable !== variable) } - : c, - ), - }, - })), - - setChartHistoryLimit: (workspaceId, chartId, newHistoryLimit) => - set((state) => ({ - charts: { - ...state.charts, - [workspaceId]: (state.charts[workspaceId] || []).map((c) => - c.id === chartId ? { ...c, historyLimit: newHistoryLimit } : c, - ), - }, - })), - // Key Bindings addKeyBinding: (commandId, key, parameters) => { const activeWorkspaceId = get().getActiveWorkspaceId(); diff --git a/frontend/testing-view/src/store/store.ts b/frontend/testing-view/src/store/store.ts index 4c17cf660..7aaf692f3 100644 --- a/frontend/testing-view/src/store/store.ts +++ b/frontend/testing-view/src/store/store.ts @@ -2,10 +2,15 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; import { createAppSlice, type AppSlice } from "./slices/appSlice"; import { createCatalogSlice, type CatalogSlice } from "./slices/catalogSlice"; +import { createChartsSlice, type ChartsSlice } from "./slices/chartsSlice"; import { createConnectionsSlice, type ConnectionsSlice, } from "./slices/connectionsSlice"; +import { + createFilteringSlice, + type FilteringSlice, +} from "./slices/filteringSlice"; import { createMessagesSlice, type MessagesSlice, @@ -29,7 +34,9 @@ export type Store = AppSlice & TelemetrySlice & RightSidebarSlice & ConnectionsSlice & - MessagesSlice; + MessagesSlice & + ChartsSlice & + FilteringSlice; export const useStore = create()( // devtools( @@ -42,6 +49,8 @@ export const useStore = create()( ...createRightSidebarSlice(...a), ...createConnectionsSlice(...a), ...createMessagesSlice(...a), + ...createChartsSlice(...a), + ...createFilteringSlice(...a), }), { // Partial persist @@ -61,7 +70,7 @@ export const useStore = create()( // Workspace UI state activeTab: state.activeTab, - tabFilters: state.tabFilters, + tabFilters: state.workspaceFilters, }), }, ), From 2981c3d77a5b06bf362b217eee408e83518e6dbf Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:10:16 +0100 Subject: [PATCH 09/19] docs: document some of the important components --- .../src/components/Testing/Charts/TelemetryChart.tsx | 11 +++++++++++ .../Testing/RightSidebar/Tabs/StandardList.tsx | 6 ++++++ .../Testing/RightSidebar/Tabs/VirtualizedList.tsx | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx b/frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx index 0837ff857..27975a9f5 100644 --- a/frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx +++ b/frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx @@ -16,6 +16,17 @@ interface TelemetryChartProps { dragListeners?: any; } +/** + * A draggable, interactive chart container for visualizing telemetry data. + * + * Features: + * - Data Visualization: Renders high-frequency data using `uPlot` via `ChartSurface`. + * - Displays a `ChartLegend` to toggle or remove individual data series. + * - Supports drag-and-drop reordering within the grid. + * + * It manages local state for "disabled series" (toggled off in legend but still in config), + * preventing data loss when users just want to temporarily hide a line. + */ export const TelemetryChart = ({ id, series, diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx index 65b2614f2..a4f163446 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx +++ b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx @@ -10,6 +10,12 @@ interface StandardListProps { ItemComponent: ComponentType<{ item: CatalogItem }>; } +/** + * Standard tree renderer for smaller catalogs. Right now used for commands tab.\ + * Based on categories (board names) and item component recieved as prop. + * + * It's counterpart is VirtualizedList, which is used for Telemetry data and implements virtualization. + */ export const StandardList = ({ scope, categories, diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/VirtualizedList.tsx b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/VirtualizedList.tsx index 04f770c58..6a9bcd16f 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/VirtualizedList.tsx +++ b/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/VirtualizedList.tsx @@ -14,6 +14,15 @@ interface VirtualizedListProps { categories: readonly BoardName[]; } +/** + * A highly optimized list renderer for data. Only used for Telemetry data. + * + * Unlike StandardList, this component flattens the Board -> Packet -> Variable tree + * into a single virtualized array to handle thousands of items without lag by rerendering only the visible items. + * + * It relies on `usePacketRows` (which calls `getFlattenedRows` from the store) + * to maintain the visual illusion of a tree structure while actually rendering a flat list. + */ export const VirtualizedList = ({ scope, categories, From 79d34ca60006bc184bc316c562c69be51de61c32 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:04:47 +0100 Subject: [PATCH 10/19] docs: small change --- frontend/testing-view/src/types/data/board.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/testing-view/src/types/data/board.ts b/frontend/testing-view/src/types/data/board.ts index 5d63ff878..4c92463ad 100644 --- a/frontend/testing-view/src/types/data/board.ts +++ b/frontend/testing-view/src/types/data/board.ts @@ -3,7 +3,7 @@ import type { TelemetryCatalogItem } from "./telemetryCatalogItem"; /** * Name of a board, like LCU or HVBMS.\ - * I decided not to use array of string in case a new board is added in the future. + * I decided not to use array of predefined strings in case a new board is added in the future. */ export type BoardName = string; From f819374a51d835bc7758b5eb55aff3bcb174edbb Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:36:31 +0100 Subject: [PATCH 11/19] refact: add features folder --- frontend/testing-view/src/App.tsx | 2 +- .../src/components/Header/Header.tsx | 6 +++--- .../charts/components}/ChartLegend.tsx | 0 .../charts/components}/ChartSettings.tsx | 0 .../charts/components}/ChartSurface.tsx | 0 .../charts/components}/ChartsGrid.tsx | 0 .../charts/components}/SortableChart.tsx | 0 .../charts/components}/TelemetryChart.tsx | 0 .../charts/components}/tooltipPlugin.ts | 0 .../filtering/components}/FilterCategoryItem.tsx | 0 .../filtering/components}/FilterController.tsx | 0 .../filtering/components}/FilterDialog.tsx | 0 .../filtering/components}/FilterItem.tsx | 0 .../components}/AddKeyBindingDialog.tsx | 2 +- .../keyBindings/components}/KeyBindingCard.tsx | 0 .../components}/KeyBindingsButton.tsx | 0 .../components}/KeyBindingsDialog.tsx | 1 - .../components}/OrphanedKeyBindingCard.tsx | 0 .../workspace/components}/DndOverlay.tsx | 6 +++--- .../workspace/components}/EmptyWorkspace.tsx | 0 .../workspace/components}/LoggerControl.tsx | 12 ++++++------ .../workspace/components}/MainPanel.tsx | 8 ++++---- .../workspace/components}/Toolbar.tsx | 2 +- .../workspace/components}/WorkspaceDialog.tsx | 0 .../workspace/components}/WorkspaceSwitcher.tsx | 4 ++-- .../components/rightSidebar}/RightSidebar.tsx | 0 .../rightSidebar}/RightSidebarContent.tsx | 8 ++++---- .../rightSidebar}/RightSidebarHeader.tsx | 2 +- .../rightSidebar/sections}/MessageItem.tsx | 6 +++--- .../rightSidebar/sections}/MessagesList.tsx | 2 +- .../rightSidebar/sections}/MessagesSection.tsx | 2 +- .../sections}/NoneSelectedSection.tsx | 0 .../rightSidebar/sections}/TabsSection.tsx | 16 ++++++++-------- .../rightSidebar/tabs}/CategoryHeader.tsx | 0 .../rightSidebar/tabs}/CategoryItem.tsx | 8 ++++---- .../components/rightSidebar/tabs}/EmptyTab.tsx | 0 .../rightSidebar/tabs}/StandardList.tsx | 6 +++--- .../components/rightSidebar/tabs}/Tab.tsx | 8 ++++---- .../components/rightSidebar/tabs}/TabHeader.tsx | 4 ++-- .../rightSidebar/tabs}/VirtualizedList.tsx | 10 +++++----- .../rightSidebar/tabs/commands}/CommandItem.tsx | 6 +++--- .../tabs/commands}/CommandParameters.tsx | 2 +- .../rightSidebar/tabs/telemetry}/ChartPicker.tsx | 2 +- .../tabs/telemetry}/TelemetryHeader.tsx | 4 ++-- .../tabs/telemetry}/TelemetryItem.tsx | 4 ++-- .../tabs/telemetry}/TelemetryRow.tsx | 4 ++-- .../tabs/telemetry}/TelemetryValue.tsx | 2 +- .../tabs/telemetry}/VariableItem.tsx | 6 +++--- frontend/testing-view/src/layout/AppLayout.tsx | 6 +++--- frontend/testing-view/src/pages/Testing.tsx | 8 ++++---- 50 files changed, 79 insertions(+), 80 deletions(-) rename frontend/testing-view/src/{components/Testing/Charts => features/charts/components}/ChartLegend.tsx (100%) rename frontend/testing-view/src/{components/Testing/Charts => features/charts/components}/ChartSettings.tsx (100%) rename frontend/testing-view/src/{components/Testing/Charts => features/charts/components}/ChartSurface.tsx (100%) rename frontend/testing-view/src/{components/Testing/Charts => features/charts/components}/ChartsGrid.tsx (100%) rename frontend/testing-view/src/{components/Testing/Charts => features/charts/components}/SortableChart.tsx (100%) rename frontend/testing-view/src/{components/Testing/Charts => features/charts/components}/TelemetryChart.tsx (100%) rename frontend/testing-view/src/{components/Testing/Charts => features/charts/components}/tooltipPlugin.ts (100%) rename frontend/testing-view/src/{components/Testing/Filters => features/filtering/components}/FilterCategoryItem.tsx (100%) rename frontend/testing-view/src/{components/Testing/Filters => features/filtering/components}/FilterController.tsx (100%) rename frontend/testing-view/src/{components/Testing/Filters => features/filtering/components}/FilterDialog.tsx (100%) rename frontend/testing-view/src/{components/Testing/Filters => features/filtering/components}/FilterItem.tsx (100%) rename frontend/testing-view/src/{components/Testing/KeyBindings => features/keyBindings/components}/AddKeyBindingDialog.tsx (98%) rename frontend/testing-view/src/{components/Testing/KeyBindings => features/keyBindings/components}/KeyBindingCard.tsx (100%) rename frontend/testing-view/src/{components/Testing/KeyBindings => features/keyBindings/components}/KeyBindingsButton.tsx (100%) rename frontend/testing-view/src/{components/Testing/KeyBindings => features/keyBindings/components}/KeyBindingsDialog.tsx (98%) rename frontend/testing-view/src/{components/Testing/KeyBindings => features/keyBindings/components}/OrphanedKeyBindingCard.tsx (100%) rename frontend/testing-view/src/{components/Testing => features/workspace/components}/DndOverlay.tsx (84%) rename frontend/testing-view/src/{components/Testing => features/workspace/components}/EmptyWorkspace.tsx (100%) rename frontend/testing-view/src/{components/Testing => features/workspace/components}/LoggerControl.tsx (88%) rename frontend/testing-view/src/{components/Testing => features/workspace/components}/MainPanel.tsx (86%) rename frontend/testing-view/src/{components/Testing => features/workspace/components}/Toolbar.tsx (97%) rename frontend/testing-view/src/{components/Testing => features/workspace/components}/WorkspaceDialog.tsx (100%) rename frontend/testing-view/src/{components/Testing => features/workspace/components}/WorkspaceSwitcher.tsx (98%) rename frontend/testing-view/src/{components/Testing/RightSidebar => features/workspace/components/rightSidebar}/RightSidebar.tsx (100%) rename frontend/testing-view/src/{components/Testing/RightSidebar => features/workspace/components/rightSidebar}/RightSidebarContent.tsx (84%) rename frontend/testing-view/src/{components/Testing/RightSidebar => features/workspace/components/rightSidebar}/RightSidebarHeader.tsx (98%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Sections => features/workspace/components/rightSidebar/sections}/MessageItem.tsx (93%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Sections => features/workspace/components/rightSidebar/sections}/MessagesList.tsx (91%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Sections => features/workspace/components/rightSidebar/sections}/MessagesSection.tsx (95%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Sections => features/workspace/components/rightSidebar/sections}/NoneSelectedSection.tsx (100%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Sections => features/workspace/components/rightSidebar/sections}/TabsSection.tsx (85%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs => features/workspace/components/rightSidebar/tabs}/CategoryHeader.tsx (100%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs => features/workspace/components/rightSidebar/tabs}/CategoryItem.tsx (83%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs => features/workspace/components/rightSidebar/tabs}/EmptyTab.tsx (100%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs => features/workspace/components/rightSidebar/tabs}/StandardList.tsx (80%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs => features/workspace/components/rightSidebar/tabs}/Tab.tsx (82%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs => features/workspace/components/rightSidebar/tabs}/TabHeader.tsx (89%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs => features/workspace/components/rightSidebar/tabs}/VirtualizedList.tsx (86%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs/Commands => features/workspace/components/rightSidebar/tabs/commands}/CommandItem.tsx (96%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs/Commands => features/workspace/components/rightSidebar/tabs/commands}/CommandParameters.tsx (98%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs/Telemetry => features/workspace/components/rightSidebar/tabs/telemetry}/ChartPicker.tsx (95%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs/Telemetry => features/workspace/components/rightSidebar/tabs/telemetry}/TelemetryHeader.tsx (95%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs/Telemetry => features/workspace/components/rightSidebar/tabs/telemetry}/TelemetryItem.tsx (91%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs/Telemetry => features/workspace/components/rightSidebar/tabs/telemetry}/TelemetryRow.tsx (90%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs/Telemetry => features/workspace/components/rightSidebar/tabs/telemetry}/TelemetryValue.tsx (98%) rename frontend/testing-view/src/{components/Testing/RightSidebar/Tabs/Telemetry => features/workspace/components/rightSidebar/tabs/telemetry}/VariableItem.tsx (94%) diff --git a/frontend/testing-view/src/App.tsx b/frontend/testing-view/src/App.tsx index 2eb92f118..e7c3366a4 100644 --- a/frontend/testing-view/src/App.tsx +++ b/frontend/testing-view/src/App.tsx @@ -1,7 +1,7 @@ import { useTopic, useWebSocket } from "@workspace/ui/hooks"; import { Route, Routes } from "react-router"; import { AppModeRouter } from "./components/AppModeRouter"; -import { ModeSwitcher } from "./components/DevTools/ModeSwitcher"; +import { ModeSwitcher } from "./components/devTools/ModeSwitcher"; import { ErrorBoundary } from "./components/ErrorBoundary"; import useAppConfigs from "./hooks/useAppConfigs"; import { useAppMode } from "./hooks/useAppMode"; diff --git a/frontend/testing-view/src/components/Header/Header.tsx b/frontend/testing-view/src/components/Header/Header.tsx index 59b2949cf..1f6b977c2 100644 --- a/frontend/testing-view/src/components/Header/Header.tsx +++ b/frontend/testing-view/src/components/Header/Header.tsx @@ -1,10 +1,10 @@ import { Separator, SidebarTrigger } from "@workspace/ui"; import { useLocation } from "react-router"; import { PAGES } from "../../constants/pages"; +import { KeyBindingsButton } from "../../features/keyBindings/components/KeyBindingsButton"; +import { LoggerControl } from "../../features/workspace/components/LoggerControl"; +import WorkspaceSwitcher from "../../features/workspace/components/WorkspaceSwitcher"; import { useStore } from "../../store/store"; -import { KeyBindingsButton } from "../Testing/KeyBindings/KeyBindingsButton"; -import { LoggerControl } from "../Testing/LoggerControl"; -import WorkspaceSwitcher from "../Testing/WorkspaceSwitcher"; import { ModeBadge } from "./ModeBadge"; import { ReconnectButton } from "./ReconnectButton"; diff --git a/frontend/testing-view/src/components/Testing/Charts/ChartLegend.tsx b/frontend/testing-view/src/features/charts/components/ChartLegend.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Charts/ChartLegend.tsx rename to frontend/testing-view/src/features/charts/components/ChartLegend.tsx diff --git a/frontend/testing-view/src/components/Testing/Charts/ChartSettings.tsx b/frontend/testing-view/src/features/charts/components/ChartSettings.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Charts/ChartSettings.tsx rename to frontend/testing-view/src/features/charts/components/ChartSettings.tsx diff --git a/frontend/testing-view/src/components/Testing/Charts/ChartSurface.tsx b/frontend/testing-view/src/features/charts/components/ChartSurface.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Charts/ChartSurface.tsx rename to frontend/testing-view/src/features/charts/components/ChartSurface.tsx diff --git a/frontend/testing-view/src/components/Testing/Charts/ChartsGrid.tsx b/frontend/testing-view/src/features/charts/components/ChartsGrid.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Charts/ChartsGrid.tsx rename to frontend/testing-view/src/features/charts/components/ChartsGrid.tsx diff --git a/frontend/testing-view/src/components/Testing/Charts/SortableChart.tsx b/frontend/testing-view/src/features/charts/components/SortableChart.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Charts/SortableChart.tsx rename to frontend/testing-view/src/features/charts/components/SortableChart.tsx diff --git a/frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx b/frontend/testing-view/src/features/charts/components/TelemetryChart.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Charts/TelemetryChart.tsx rename to frontend/testing-view/src/features/charts/components/TelemetryChart.tsx diff --git a/frontend/testing-view/src/components/Testing/Charts/tooltipPlugin.ts b/frontend/testing-view/src/features/charts/components/tooltipPlugin.ts similarity index 100% rename from frontend/testing-view/src/components/Testing/Charts/tooltipPlugin.ts rename to frontend/testing-view/src/features/charts/components/tooltipPlugin.ts diff --git a/frontend/testing-view/src/components/Testing/Filters/FilterCategoryItem.tsx b/frontend/testing-view/src/features/filtering/components/FilterCategoryItem.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Filters/FilterCategoryItem.tsx rename to frontend/testing-view/src/features/filtering/components/FilterCategoryItem.tsx diff --git a/frontend/testing-view/src/components/Testing/Filters/FilterController.tsx b/frontend/testing-view/src/features/filtering/components/FilterController.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Filters/FilterController.tsx rename to frontend/testing-view/src/features/filtering/components/FilterController.tsx diff --git a/frontend/testing-view/src/components/Testing/Filters/FilterDialog.tsx b/frontend/testing-view/src/features/filtering/components/FilterDialog.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Filters/FilterDialog.tsx rename to frontend/testing-view/src/features/filtering/components/FilterDialog.tsx diff --git a/frontend/testing-view/src/components/Testing/Filters/FilterItem.tsx b/frontend/testing-view/src/features/filtering/components/FilterItem.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/Filters/FilterItem.tsx rename to frontend/testing-view/src/features/filtering/components/FilterItem.tsx diff --git a/frontend/testing-view/src/components/Testing/KeyBindings/AddKeyBindingDialog.tsx b/frontend/testing-view/src/features/keyBindings/components/AddKeyBindingDialog.tsx similarity index 98% rename from frontend/testing-view/src/components/Testing/KeyBindings/AddKeyBindingDialog.tsx rename to frontend/testing-view/src/features/keyBindings/components/AddKeyBindingDialog.tsx index efe35962b..da83ead66 100644 --- a/frontend/testing-view/src/components/Testing/KeyBindings/AddKeyBindingDialog.tsx +++ b/frontend/testing-view/src/features/keyBindings/components/AddKeyBindingDialog.tsx @@ -21,7 +21,7 @@ import { SPECIAL_KEY_BINDINGS } from "../../../constants/specialKeyBindings"; import { getDefaultParameterValues } from "../../../lib/commandUtils"; import { useStore } from "../../../store/store"; import type { CommandCatalogItem } from "../../../types/data/commandCatalogItem"; -import { CommandParameters } from "../RightSidebar/Tabs/Commands/CommandParameters"; +import { CommandParameters } from "../../workspace/components/rightSidebar/tabs/commands/CommandParameters"; interface AddKeyBindingDialogProps { open: boolean; diff --git a/frontend/testing-view/src/components/Testing/KeyBindings/KeyBindingCard.tsx b/frontend/testing-view/src/features/keyBindings/components/KeyBindingCard.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/KeyBindings/KeyBindingCard.tsx rename to frontend/testing-view/src/features/keyBindings/components/KeyBindingCard.tsx diff --git a/frontend/testing-view/src/components/Testing/KeyBindings/KeyBindingsButton.tsx b/frontend/testing-view/src/features/keyBindings/components/KeyBindingsButton.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/KeyBindings/KeyBindingsButton.tsx rename to frontend/testing-view/src/features/keyBindings/components/KeyBindingsButton.tsx diff --git a/frontend/testing-view/src/components/Testing/KeyBindings/KeyBindingsDialog.tsx b/frontend/testing-view/src/features/keyBindings/components/KeyBindingsDialog.tsx similarity index 98% rename from frontend/testing-view/src/components/Testing/KeyBindings/KeyBindingsDialog.tsx rename to frontend/testing-view/src/features/keyBindings/components/KeyBindingsDialog.tsx index 1565988ea..9accb26d9 100644 --- a/frontend/testing-view/src/components/Testing/KeyBindings/KeyBindingsDialog.tsx +++ b/frontend/testing-view/src/features/keyBindings/components/KeyBindingsDialog.tsx @@ -14,7 +14,6 @@ import type { KeyBinding } from "../../../types/workspace/keyBinding"; import { AddKeyBindingDialog } from "./AddKeyBindingDialog"; import { KeyBindingCard } from "./KeyBindingCard"; import { OrphanedKeyBindingCard } from "./OrphanedKeyBindingCard"; -// import { AddKeyBindingDialog } from "./AddKeyBindingDialog"; interface KeyBindingsDialogProps { open: boolean; diff --git a/frontend/testing-view/src/components/Testing/KeyBindings/OrphanedKeyBindingCard.tsx b/frontend/testing-view/src/features/keyBindings/components/OrphanedKeyBindingCard.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/KeyBindings/OrphanedKeyBindingCard.tsx rename to frontend/testing-view/src/features/keyBindings/components/OrphanedKeyBindingCard.tsx diff --git a/frontend/testing-view/src/components/Testing/DndOverlay.tsx b/frontend/testing-view/src/features/workspace/components/DndOverlay.tsx similarity index 84% rename from frontend/testing-view/src/components/Testing/DndOverlay.tsx rename to frontend/testing-view/src/features/workspace/components/DndOverlay.tsx index 4f3818560..751907680 100644 --- a/frontend/testing-view/src/components/Testing/DndOverlay.tsx +++ b/frontend/testing-view/src/features/workspace/components/DndOverlay.tsx @@ -1,8 +1,8 @@ import { DragOverlay } from "@dnd-kit/core"; import { Badge } from "@workspace/ui"; -import type { DndActiveData } from "../../types/app/dndData"; -import type { WorkspaceChartConfig } from "../../types/workspace/charts"; -import { TelemetryChart } from "./Charts/TelemetryChart"; +import type { DndActiveData } from "../../../types/app/dndData"; +import type { WorkspaceChartConfig } from "../../../types/workspace/charts"; +import { TelemetryChart } from "../../charts/components/TelemetryChart"; interface DndOverlayProps { activeDragData: DndActiveData | null; diff --git a/frontend/testing-view/src/components/Testing/EmptyWorkspace.tsx b/frontend/testing-view/src/features/workspace/components/EmptyWorkspace.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/EmptyWorkspace.tsx rename to frontend/testing-view/src/features/workspace/components/EmptyWorkspace.tsx diff --git a/frontend/testing-view/src/components/Testing/LoggerControl.tsx b/frontend/testing-view/src/features/workspace/components/LoggerControl.tsx similarity index 88% rename from frontend/testing-view/src/components/Testing/LoggerControl.tsx rename to frontend/testing-view/src/features/workspace/components/LoggerControl.tsx index 89862ed34..c4ac6d891 100644 --- a/frontend/testing-view/src/components/Testing/LoggerControl.tsx +++ b/frontend/testing-view/src/features/workspace/components/LoggerControl.tsx @@ -1,12 +1,12 @@ import { Button, Separator } from "@workspace/ui"; import { Settings2 } from "@workspace/ui/icons"; import { cn } from "@workspace/ui/lib"; -import { LOGGER_CONTROL_CONFIG } from "../../constants/loggerControlConfig"; -import useConfirmClose from "../../hooks/useConfirmClose"; -import { useLogger } from "../../hooks/useLogger"; -import { useStore } from "../../store/store"; -import type { BoardName } from "../../types/data/board"; -import type { TelemetryCatalogItem } from "../../types/data/telemetryCatalogItem"; +import { LOGGER_CONTROL_CONFIG } from "../../../constants/loggerControlConfig"; +import useConfirmClose from "../../../hooks/useConfirmClose"; +import { useLogger } from "../../../hooks/useLogger"; +import { useStore } from "../../../store/store"; +import type { BoardName } from "../../../types/data/board"; +import type { TelemetryCatalogItem } from "../../../types/data/telemetryCatalogItem"; interface LoggerControlProps { disabled: boolean; diff --git a/frontend/testing-view/src/components/Testing/MainPanel.tsx b/frontend/testing-view/src/features/workspace/components/MainPanel.tsx similarity index 86% rename from frontend/testing-view/src/components/Testing/MainPanel.tsx rename to frontend/testing-view/src/features/workspace/components/MainPanel.tsx index f0e0239ca..c57f43b97 100644 --- a/frontend/testing-view/src/components/Testing/MainPanel.tsx +++ b/frontend/testing-view/src/features/workspace/components/MainPanel.tsx @@ -1,9 +1,9 @@ import { useDroppable } from "@dnd-kit/core"; import { cn } from "@workspace/ui/lib"; -import { useStore } from "../../store/store"; -import type { DndActiveData } from "../../types/app/dndData"; -import { ChartsGrid } from "./Charts/ChartsGrid"; -import { EmptyWorkspace } from "./EmptyWorkspace"; +import { useStore } from "../../../store/store"; +import type { DndActiveData } from "../../../types/app/dndData"; +import { ChartsGrid } from "../../charts/components/ChartsGrid"; +import { EmptyWorkspace } from "../testing/EmptyWorkspace"; import { TestingToolbar } from "./Toolbar"; interface MainPanelProps { diff --git a/frontend/testing-view/src/components/Testing/Toolbar.tsx b/frontend/testing-view/src/features/workspace/components/Toolbar.tsx similarity index 97% rename from frontend/testing-view/src/components/Testing/Toolbar.tsx rename to frontend/testing-view/src/features/workspace/components/Toolbar.tsx index 4aaaa6fc2..872c4f692 100644 --- a/frontend/testing-view/src/components/Testing/Toolbar.tsx +++ b/frontend/testing-view/src/features/workspace/components/Toolbar.tsx @@ -1,6 +1,6 @@ import { Button } from "@workspace/ui"; import { ChevronLeft, Plus } from "@workspace/ui/icons"; -import { useStore } from "../../store/store"; +import { useStore } from "../../../store/store"; interface TestingToolbarProps { columns: number; diff --git a/frontend/testing-view/src/components/Testing/WorkspaceDialog.tsx b/frontend/testing-view/src/features/workspace/components/WorkspaceDialog.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/WorkspaceDialog.tsx rename to frontend/testing-view/src/features/workspace/components/WorkspaceDialog.tsx diff --git a/frontend/testing-view/src/components/Testing/WorkspaceSwitcher.tsx b/frontend/testing-view/src/features/workspace/components/WorkspaceSwitcher.tsx similarity index 98% rename from frontend/testing-view/src/components/Testing/WorkspaceSwitcher.tsx rename to frontend/testing-view/src/features/workspace/components/WorkspaceSwitcher.tsx index 1f097aad1..1c4aedd27 100644 --- a/frontend/testing-view/src/components/Testing/WorkspaceSwitcher.tsx +++ b/frontend/testing-view/src/features/workspace/components/WorkspaceSwitcher.tsx @@ -18,8 +18,8 @@ import { } from "@workspace/ui/icons"; import { cn } from "@workspace/ui/lib"; import { useState } from "react"; -import { useStore } from "../../store/store"; -import type { Workspace } from "../../types/workspace/workspace"; +import { useStore } from "../../../store/store"; +import type { Workspace } from "../../../types/workspace/workspace"; import { WorkspaceDialog } from "./WorkspaceDialog"; interface WorkspaceSwitcherProps { diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/RightSidebar.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebar.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/RightSidebar/RightSidebar.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebar.tsx diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/RightSidebarContent.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarContent.tsx similarity index 84% rename from frontend/testing-view/src/components/Testing/RightSidebar/RightSidebarContent.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarContent.tsx index e3d474f23..e567372c2 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/RightSidebarContent.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarContent.tsx @@ -4,10 +4,10 @@ import { ResizablePanelGroup, } from "@workspace/ui"; import { Activity } from "react"; -import { useStore } from "../../../store/store"; -import MessagesSection from "./Sections/MessagesSection"; -import { NoneSelectedSection } from "./Sections/NoneSelectedSection"; -import TabsSection from "./Sections/TabsSection"; +import { useStore } from "../../../../store/store"; +import MessagesSection from "../../testing/RightSidebar/Sections/MessagesSection"; +import { NoneSelectedSection } from "../../testing/RightSidebar/Sections/NoneSelectedSection"; +import TabsSection from "../../testing/RightSidebar/Sections/TabsSection"; interface RightSidebarContentProps {} diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/RightSidebarHeader.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarHeader.tsx similarity index 98% rename from frontend/testing-view/src/components/Testing/RightSidebar/RightSidebarHeader.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarHeader.tsx index 786b51198..a4bf54257 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/RightSidebarHeader.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarHeader.tsx @@ -13,7 +13,7 @@ import { PanelRight, X, } from "@workspace/ui/icons"; -import { useStore } from "../../../store/store"; +import { useStore } from "../../../../store/store"; interface RightSidebarHeaderProps { onClose: () => void; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessageItem.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/MessageItem.tsx similarity index 93% rename from frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessageItem.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/sections/MessageItem.tsx index 8a35647ac..4b5ccbcb9 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessageItem.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/MessageItem.tsx @@ -7,9 +7,9 @@ import { import { ChevronDown } from "@workspace/ui/icons"; import { cn } from "@workspace/ui/lib"; import { useState } from "react"; -import { MESSAGE_KIND_COLORS } from "../../../../constants/messageKindColors"; -import { formatTimestamp } from "../../../../lib/utils"; -import type { Message } from "../../../../types/data/message"; +import { MESSAGE_KIND_COLORS } from "../../../../../constants/messageKindColors"; +import { formatTimestamp } from "../../../../../lib/utils"; +import type { Message } from "../../../../../types/data/message"; interface MessageItemProps { message: Message; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessagesList.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/MessagesList.tsx similarity index 91% rename from frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessagesList.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/sections/MessagesList.tsx index 8b3790075..cbc53dce3 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessagesList.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/MessagesList.tsx @@ -1,4 +1,4 @@ -import { useStore } from "../../../../store/store"; +import { useStore } from "../../../../../store/store"; import { MessageItem } from "./MessageItem"; export const MessagesList = () => { diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessagesSection.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/MessagesSection.tsx similarity index 95% rename from frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessagesSection.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/sections/MessagesSection.tsx index 8eae514a2..5343bb876 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/MessagesSection.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/MessagesSection.tsx @@ -1,6 +1,6 @@ import { Button } from "@workspace/ui"; import { Trash2 } from "@workspace/ui/icons"; -import { useStore } from "../../../../store/store"; +import { useStore } from "../../../../../store/store"; import { MessagesList } from "./MessagesList"; const MessagesSection = () => { diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/NoneSelectedSection.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/NoneSelectedSection.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/RightSidebar/Sections/NoneSelectedSection.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/sections/NoneSelectedSection.tsx diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/TabsSection.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TabsSection.tsx similarity index 85% rename from frontend/testing-view/src/components/Testing/RightSidebar/Sections/TabsSection.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TabsSection.tsx index 016784fbb..bac8e37ca 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Sections/TabsSection.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TabsSection.tsx @@ -7,14 +7,14 @@ import { TabsList, TabsTrigger, } from "@workspace/ui"; -import { BOARD_NAMES } from "../../../../constants/boards"; -import { useStore } from "../../../../store/store"; -import type { CommandCatalogItem } from "../../../../types/data/commandCatalogItem"; -import type { TelemetryCatalogItem } from "../../../../types/data/telemetryCatalogItem"; -import type { SidebarTab } from "../../../../types/workspace/sidebar"; -import { CommandItem } from "../Tabs/Commands/CommandItem"; -import { Tab } from "../Tabs/Tab"; -import { TelemetryItem } from "../Tabs/Telemetry/TelemetryItem"; +import { BOARD_NAMES } from "../../../../../constants/boards"; +import { useStore } from "../../../../../store/store"; +import type { CommandCatalogItem } from "../../../../../types/data/commandCatalogItem"; +import type { TelemetryCatalogItem } from "../../../../../types/data/telemetryCatalogItem"; +import type { SidebarTab } from "../../../../../types/workspace/sidebar"; +import { CommandItem } from "../tabs/commands/CommandItem"; +import { Tab } from "../tabs/Tab"; +import { TelemetryItem } from "../tabs/telemetry/TelemetryItem"; interface TabsSectionProps { isSplit: boolean; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/CategoryHeader.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/CategoryHeader.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/CategoryHeader.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/CategoryHeader.tsx diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/CategoryItem.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/CategoryItem.tsx similarity index 83% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/CategoryItem.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/CategoryItem.tsx index 7f72f782e..eb4929224 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/CategoryItem.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/CategoryItem.tsx @@ -1,10 +1,10 @@ import { Collapsible, CollapsibleContent } from "@workspace/ui"; import { type ComponentType } from "react"; import { useShallow } from "zustand/shallow"; -import { useStore } from "../../../../store/store"; -import type { CatalogItem } from "../../../../types/common/item"; -import type { BoardName } from "../../../../types/data/board"; -import type { SidebarTab } from "../../../../types/workspace/sidebar"; +import { useStore } from "../../../../../store/store"; +import type { CatalogItem } from "../../../../../types/common/item"; +import type { BoardName } from "../../../../../types/data/board"; +import type { SidebarTab } from "../../../../../types/workspace/sidebar"; import { CategoryHeader } from "./CategoryHeader"; interface CategoryItemProps { diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/EmptyTab.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/EmptyTab.tsx similarity index 100% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/EmptyTab.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/EmptyTab.tsx diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/StandardList.tsx similarity index 80% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/StandardList.tsx index a4f163446..8b89fca26 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/StandardList.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/StandardList.tsx @@ -1,7 +1,7 @@ import { type ComponentType } from "react"; -import type { CatalogItem } from "../../../../types/common/item"; -import type { BoardName } from "../../../../types/data/board"; -import type { SidebarTab } from "../../../../types/workspace/sidebar"; +import type { CatalogItem } from "../../../../../types/common/item"; +import type { BoardName } from "../../../../../types/data/board"; +import type { SidebarTab } from "../../../../../types/workspace/sidebar"; import { CategoryItem } from "./CategoryItem"; interface StandardListProps { diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Tab.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/Tab.tsx similarity index 82% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Tab.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/Tab.tsx index f7660115e..2c5e58f24 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Tab.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/Tab.tsx @@ -1,8 +1,8 @@ import { type ComponentType } from "react"; -import { useStore } from "../../../../store/store"; -import type { CatalogItem } from "../../../../types/common/item"; -import type { BoardName } from "../../../../types/data/board"; -import type { SidebarTab } from "../../../../types/workspace/sidebar"; +import { useStore } from "../../../../../store/store"; +import type { CatalogItem } from "../../../../../types/common/item"; +import type { BoardName } from "../../../../../types/data/board"; +import type { SidebarTab } from "../../../../../types/workspace/sidebar"; import { EmptyTab } from "./EmptyTab"; import { StandardList } from "./StandardList"; import { TabHeader } from "./TabHeader"; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/TabHeader.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx similarity index 89% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/TabHeader.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx index aa92aefee..244692953 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/TabHeader.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx @@ -1,7 +1,7 @@ import { Button } from "@workspace/ui"; import { ListFilterPlus } from "@workspace/ui/icons"; -import { useStore } from "../../../../store/store"; -import type { SidebarTab } from "../../../../types/workspace/sidebar"; +import { useStore } from "../../../../../store/store"; +import type { SidebarTab } from "../../../../../types/workspace/sidebar"; interface TabHeaderProps { title: string; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/VirtualizedList.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/VirtualizedList.tsx similarity index 86% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/VirtualizedList.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/VirtualizedList.tsx index 6a9bcd16f..01e7d6d8d 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/VirtualizedList.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/VirtualizedList.tsx @@ -2,11 +2,11 @@ import { useVirtualizer } from "@tanstack/react-virtual"; import { cn } from "@workspace/ui/lib"; import { useCallback, useRef } from "react"; -import { usePacketRows } from "../../../../hooks/usePacketRows"; -import type { BoardName } from "../../../../types/data/board"; -import type { VirtualRow } from "../../../../types/data/virtualization"; -import type { SidebarTab } from "../../../../types/workspace/sidebar"; -import { TelemetryRow } from "./Telemetry/TelemetryRow"; +import { usePacketRows } from "../../../../../hooks/usePacketRows"; +import type { BoardName } from "../../../../../types/data/board"; +import type { VirtualRow } from "../../../../../types/data/virtualization"; +import type { SidebarTab } from "../../../../../types/workspace/sidebar"; +import { TelemetryRow } from "./telemetry/TelemetryRow"; interface VirtualizedListProps { className?: string; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Commands/CommandItem.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/commands/CommandItem.tsx similarity index 96% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Commands/CommandItem.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/commands/CommandItem.tsx index be415a8f7..6de562468 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Commands/CommandItem.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/commands/CommandItem.tsx @@ -8,9 +8,9 @@ import { import { ChevronDown, Play, Send } from "@workspace/ui/icons"; import { cn } from "@workspace/ui/lib"; import { useState } from "react"; -import { getDefaultParameterValues } from "../../../../../lib/commandUtils"; -import { useStore } from "../../../../../store/store"; -import type { CommandCatalogItem } from "../../../../../types/data/commandCatalogItem"; +import { getDefaultParameterValues } from "../../../../../../lib/commandUtils"; +import { useStore } from "../../../../../../store/store"; +import type { CommandCatalogItem } from "../../../../../../types/data/commandCatalogItem"; import { CommandParameters } from "./CommandParameters"; interface CommandItemProps { diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Commands/CommandParameters.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/commands/CommandParameters.tsx similarity index 98% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Commands/CommandParameters.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/commands/CommandParameters.tsx index ac92ce798..52dd8454b 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Commands/CommandParameters.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/commands/CommandParameters.tsx @@ -12,7 +12,7 @@ import type { CommandParameter, EnumCommandParameter, NumericCommandParameter, -} from "../../../../../types/data/commandCatalogItem"; +} from "../../../../../../types/data/commandCatalogItem"; interface CommandParametersProps { fields: Record; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/ChartPicker.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/ChartPicker.tsx similarity index 95% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/ChartPicker.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/ChartPicker.tsx index 39bae8636..9e719985d 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/ChartPicker.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/ChartPicker.tsx @@ -8,7 +8,7 @@ import { DropdownMenuTrigger, } from "@workspace/ui"; import { Plus } from "@workspace/ui/icons"; -import type { WorkspaceChartConfig } from "../../../../../types/workspace/charts"; +import type { WorkspaceChartConfig } from "../../../../../../types/workspace/charts"; interface ChartPickerProps { charts: WorkspaceChartConfig[]; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryHeader.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryHeader.tsx similarity index 95% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryHeader.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryHeader.tsx index 9e80cc936..c9011defb 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryHeader.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryHeader.tsx @@ -2,8 +2,8 @@ import { Badge, Separator } from "@workspace/ui"; import { Activity, ChevronDown } from "@workspace/ui/icons"; import { cn } from "@workspace/ui/lib"; import { memo, useEffect, useRef, useState } from "react"; -import { useStore } from "../../../../../store/store"; -import type { TelemetryCatalogItem } from "../../../../../types/data/telemetryCatalogItem"; +import { useStore } from "../../../../../../store/store"; +import type { TelemetryCatalogItem } from "../../../../../../types/data/telemetryCatalogItem"; interface TelemetryHeaderProps { telemetryCatalogItem: TelemetryCatalogItem; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryItem.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryItem.tsx similarity index 91% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryItem.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryItem.tsx index e88853223..d263e7cd6 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryItem.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryItem.tsx @@ -4,8 +4,8 @@ import { CollapsibleTrigger, } from "@workspace/ui"; import { memo } from "react"; -import { useStore } from "../../../../../store/store"; -import type { TelemetryCatalogItem } from "../../../../../types/data/telemetryCatalogItem"; +import { useStore } from "../../../../../../store/store"; +import type { TelemetryCatalogItem } from "../../../../../../types/data/telemetryCatalogItem"; import { TelemetryHeader } from "./TelemetryHeader"; import { VariableItem } from "./VariableItem"; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryRow.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryRow.tsx similarity index 90% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryRow.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryRow.tsx index 9648479cc..14f911ba2 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryRow.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryRow.tsx @@ -1,6 +1,6 @@ import { memo, useCallback } from "react"; -import { useStore } from "../../../../../store/store"; -import type { VirtualRow } from "../../../../../types/data/virtualization"; +import { useStore } from "../../../../../../store/store"; +import type { VirtualRow } from "../../../../../../types/data/virtualization"; import { CategoryHeader } from "../CategoryHeader"; import { TelemetryHeader } from "./TelemetryHeader"; import { VariableItem } from "./VariableItem"; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryValue.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryValue.tsx similarity index 98% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryValue.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryValue.tsx index f5604e372..ab9934224 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/TelemetryValue.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/TelemetryValue.tsx @@ -2,7 +2,7 @@ import { Badge } from "@workspace/ui"; import { Check, X } from "@workspace/ui/icons"; import { cn } from "@workspace/ui/lib"; import { useCallback } from "react"; -import { useStore } from "../../../../../store/store"; +import { useStore } from "../../../../../../store/store"; interface TelemetryValueProps { units?: string; diff --git a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/VariableItem.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/VariableItem.tsx similarity index 94% rename from frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/VariableItem.tsx rename to frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/VariableItem.tsx index 064d8675a..7d9217cf2 100644 --- a/frontend/testing-view/src/components/Testing/RightSidebar/Tabs/Telemetry/VariableItem.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/VariableItem.tsx @@ -2,9 +2,9 @@ import { useDraggable } from "@dnd-kit/core"; import { Badge } from "@workspace/ui"; import { cn } from "@workspace/ui/lib"; import { useShallow } from "zustand/shallow"; -import { getTypeBadgeClass } from "../../../../../lib/utils"; -import { useStore } from "../../../../../store/store"; -import type { Variable } from "../../../../../types/data/telemetryCatalogItem"; +import { getTypeBadgeClass } from "../../../../../../lib/utils"; +import { useStore } from "../../../../../../store/store"; +import type { Variable } from "../../../../../../types/data/telemetryCatalogItem"; import ChartPicker from "./ChartPicker"; import { TelemetryValue } from "./TelemetryValue"; diff --git a/frontend/testing-view/src/layout/AppLayout.tsx b/frontend/testing-view/src/layout/AppLayout.tsx index 4a701ed9f..faf746950 100644 --- a/frontend/testing-view/src/layout/AppLayout.tsx +++ b/frontend/testing-view/src/layout/AppLayout.tsx @@ -1,9 +1,9 @@ import { SidebarInset, SidebarProvider } from "@workspace/ui/components"; import React, { useEffect } from "react"; import Footer from "../components/Footer"; -import Header from "../components/Header/Header"; -import AppSidebar from "../components/LeftSidebar/AppSidebar"; -import { SettingsDialog } from "../components/Settings/SettingsDialog"; +import Header from "../components/header/Header"; +import AppSidebar from "../components/leftSidebar/AppSidebar"; +import { SettingsDialog } from "../components/settings/SettingsDialog"; import { useStore } from "../store/store"; interface AppLayoutProps { diff --git a/frontend/testing-view/src/pages/Testing.tsx b/frontend/testing-view/src/pages/Testing.tsx index a5ac7388f..189542d7b 100644 --- a/frontend/testing-view/src/pages/Testing.tsx +++ b/frontend/testing-view/src/pages/Testing.tsx @@ -5,10 +5,10 @@ import { ResizablePanelGroup, } from "@workspace/ui"; import { SquareLibrary } from "@workspace/ui/icons"; -import { DndOverlay } from "../components/Testing/DndOverlay"; -import { FilterController } from "../components/Testing/Filters/FilterController"; -import { MainPanel } from "../components/Testing/MainPanel"; -import { RightSidebar } from "../components/Testing/RightSidebar/RightSidebar"; +import { FilterController } from "../features/filtering/components/FilterController"; +import { DndOverlay } from "../features/workspace/components/DndOverlay"; +import { MainPanel } from "../features/workspace/components/MainPanel"; +import { RightSidebar } from "../features/workspace/components/rightSidebar/RightSidebar"; import { useDnd } from "../hooks/useDnd"; import { useGlobalKeyBindings } from "../hooks/useGlobalKeyBindings"; import { useStore } from "../store/store"; From 8cc7db2b690f27eed799587f85b463530fa55923 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:38:03 +0100 Subject: [PATCH 12/19] fix: fix imports --- .../src/features/workspace/components/MainPanel.tsx | 2 +- .../components/rightSidebar/RightSidebarContent.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/testing-view/src/features/workspace/components/MainPanel.tsx b/frontend/testing-view/src/features/workspace/components/MainPanel.tsx index c57f43b97..1b8f8d8cc 100644 --- a/frontend/testing-view/src/features/workspace/components/MainPanel.tsx +++ b/frontend/testing-view/src/features/workspace/components/MainPanel.tsx @@ -3,7 +3,7 @@ import { cn } from "@workspace/ui/lib"; import { useStore } from "../../../store/store"; import type { DndActiveData } from "../../../types/app/dndData"; import { ChartsGrid } from "../../charts/components/ChartsGrid"; -import { EmptyWorkspace } from "../testing/EmptyWorkspace"; +import { EmptyWorkspace } from "./EmptyWorkspace"; import { TestingToolbar } from "./Toolbar"; interface MainPanelProps { diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarContent.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarContent.tsx index e567372c2..647240281 100644 --- a/frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarContent.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/RightSidebarContent.tsx @@ -5,9 +5,9 @@ import { } from "@workspace/ui"; import { Activity } from "react"; import { useStore } from "../../../../store/store"; -import MessagesSection from "../../testing/RightSidebar/Sections/MessagesSection"; -import { NoneSelectedSection } from "../../testing/RightSidebar/Sections/NoneSelectedSection"; -import TabsSection from "../../testing/RightSidebar/Sections/TabsSection"; +import MessagesSection from "./sections/MessagesSection"; +import { NoneSelectedSection } from "./sections/NoneSelectedSection"; +import TabsSection from "./sections/TabsSection"; interface RightSidebarContentProps {} From a5d92c034178fddde915fec82e48dfce91cc69fd Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:47:32 +0100 Subject: [PATCH 13/19] refact: change project structure --- frontend/testing-view/src/App.tsx | 2 +- .../charts/components/ChartLegend.tsx | 4 +-- .../charts/components/ChartSurface.tsx | 4 +-- .../features/charts/components/ChartsGrid.tsx | 2 +- .../charts/components/TelemetryChart.tsx | 2 +- .../charts/components/tooltipPlugin.ts | 4 +-- .../charts}/constants/chartsColors.ts | 0 .../charts}/hooks/useChartsConfiguration.ts | 4 +-- .../charts/store}/chartsSlice.ts | 6 ++-- .../charts/types}/charts.ts | 0 .../filtering/store}/filteringSlice.ts | 24 +++++++-------- .../filtering/types}/filters.ts | 4 +-- .../components/AddKeyBindingDialog.tsx | 2 +- .../keyBindings/components/KeyBindingCard.tsx | 2 +- .../components/KeyBindingsDialog.tsx | 2 +- .../components/OrphanedKeyBindingCard.tsx | 2 +- .../constants/specialKeyBindings.ts | 0 .../hooks/useGlobalKeyBindings.ts | 6 ++-- .../keyBindings/types}/keyBinding.ts | 0 .../workspace/components/DndOverlay.tsx | 4 +-- .../workspace/components/MainPanel.tsx | 2 +- .../components/WorkspaceSwitcher.tsx | 2 +- .../rightSidebar/sections/TabsSection.tsx | 2 +- .../rightSidebar/tabs/VirtualizedList.tsx | 4 +-- .../tabs/telemetry/ChartPicker.tsx | 2 +- .../workspace}/constants/defaultWorkspaces.ts | 2 +- .../{ => features/workspace}/hooks/useDnd.ts | 4 +-- .../workspace}/hooks/usePacketRows.ts | 6 ++-- .../workspace/store}/rightSidebarSlice.ts | 2 +- .../workspace/store}/workspacesSlice.ts | 12 ++++---- .../workspace/types}/dndData.ts | 0 .../workspace/types}/sidebar.ts | 0 .../workspace/types}/workspace.ts | 2 +- frontend/testing-view/src/lib/utils.test.ts | 2 +- frontend/testing-view/src/lib/utils.ts | 10 +++---- .../src/mocks/chartsConfigurations.ts | 2 +- frontend/testing-view/src/pages/Testing.tsx | 4 +-- frontend/testing-view/src/store/store.ts | 29 ++++++++++--------- 38 files changed, 82 insertions(+), 79 deletions(-) rename frontend/testing-view/src/{ => features/charts}/constants/chartsColors.ts (100%) rename frontend/testing-view/src/{ => features/charts}/hooks/useChartsConfiguration.ts (76%) rename frontend/testing-view/src/{store/slices => features/charts/store}/chartsSlice.ts (96%) rename frontend/testing-view/src/{types/workspace => features/charts/types}/charts.ts (100%) rename frontend/testing-view/src/{store/slices => features/filtering/store}/filteringSlice.ts (95%) rename frontend/testing-view/src/{types/workspace => features/filtering/types}/filters.ts (74%) rename frontend/testing-view/src/{ => features/keyBindings}/constants/specialKeyBindings.ts (100%) rename frontend/testing-view/src/{ => features/keyBindings}/hooks/useGlobalKeyBindings.ts (94%) rename frontend/testing-view/src/{types/workspace => features/keyBindings/types}/keyBinding.ts (100%) rename frontend/testing-view/src/{ => features/workspace}/constants/defaultWorkspaces.ts (86%) rename frontend/testing-view/src/{ => features/workspace}/hooks/useDnd.ts (95%) rename frontend/testing-view/src/{ => features/workspace}/hooks/usePacketRows.ts (77%) rename frontend/testing-view/src/{store/slices => features/workspace/store}/rightSidebarSlice.ts (95%) rename frontend/testing-view/src/{store/slices => features/workspace/store}/workspacesSlice.ts (95%) rename frontend/testing-view/src/{types/app => features/workspace/types}/dndData.ts (100%) rename frontend/testing-view/src/{types/workspace => features/workspace/types}/sidebar.ts (100%) rename frontend/testing-view/src/{types/workspace => features/workspace/types}/workspace.ts (81%) diff --git a/frontend/testing-view/src/App.tsx b/frontend/testing-view/src/App.tsx index e7c3366a4..d5db347a5 100644 --- a/frontend/testing-view/src/App.tsx +++ b/frontend/testing-view/src/App.tsx @@ -3,9 +3,9 @@ import { Route, Routes } from "react-router"; import { AppModeRouter } from "./components/AppModeRouter"; import { ModeSwitcher } from "./components/devTools/ModeSwitcher"; import { ErrorBoundary } from "./components/ErrorBoundary"; +import { useChartsConfiguration } from "./features/charts/hooks/useChartsConfiguration"; import useAppConfigs from "./hooks/useAppConfigs"; import { useAppMode } from "./hooks/useAppMode"; -import { useChartsConfiguration } from "./hooks/useChartsConfiguration"; import { useErrorHandler } from "./hooks/useErrorHandler"; import { useTransformedBoards } from "./hooks/useTransformedBoards"; import { AppLayout } from "./layout/AppLayout"; diff --git a/frontend/testing-view/src/features/charts/components/ChartLegend.tsx b/frontend/testing-view/src/features/charts/components/ChartLegend.tsx index 83b22f66d..576c0d74a 100644 --- a/frontend/testing-view/src/features/charts/components/ChartLegend.tsx +++ b/frontend/testing-view/src/features/charts/components/ChartLegend.tsx @@ -1,6 +1,6 @@ import { X } from "@workspace/ui/icons"; -import { COLORS } from "../../../constants/chartsColors"; -import type { WorkspaceChartSeries } from "../../../types/workspace/charts"; +import { COLORS } from "../constants/chartsColors"; +import type { WorkspaceChartSeries } from "../types/charts"; import { ChartSettings } from "./ChartSettings"; interface ChartLegendProps { diff --git a/frontend/testing-view/src/features/charts/components/ChartSurface.tsx b/frontend/testing-view/src/features/charts/components/ChartSurface.tsx index 91fad0634..f251bbd73 100644 --- a/frontend/testing-view/src/features/charts/components/ChartSurface.tsx +++ b/frontend/testing-view/src/features/charts/components/ChartSurface.tsx @@ -4,9 +4,9 @@ import { cn } from "@workspace/ui/lib"; import { memo, useEffect, useRef, useState } from "react"; import uPlot from "uplot"; import { useShallow } from "zustand/shallow"; -import { COLORS } from "../../../constants/chartsColors"; import { useStore } from "../../../store/store"; -import type { WorkspaceChartSeries } from "../../../types/workspace/charts"; +import { COLORS } from "../constants/chartsColors"; +import type { WorkspaceChartSeries } from "../types/charts"; import { createTooltipPlugin } from "./tooltipPlugin"; interface ChartSurfaceProps { diff --git a/frontend/testing-view/src/features/charts/components/ChartsGrid.tsx b/frontend/testing-view/src/features/charts/components/ChartsGrid.tsx index d6813db40..17c2b8deb 100644 --- a/frontend/testing-view/src/features/charts/components/ChartsGrid.tsx +++ b/frontend/testing-view/src/features/charts/components/ChartsGrid.tsx @@ -1,6 +1,6 @@ import { SortableContext, rectSortingStrategy } from "@dnd-kit/sortable"; import { cn } from "@workspace/ui/lib"; -import type { WorkspaceChartConfig } from "../../../types/workspace/charts"; +import type { WorkspaceChartConfig } from "../types/charts"; import { SortableChart } from "./SortableChart"; interface ChartsGridProps { diff --git a/frontend/testing-view/src/features/charts/components/TelemetryChart.tsx b/frontend/testing-view/src/features/charts/components/TelemetryChart.tsx index 27975a9f5..3e84b721c 100644 --- a/frontend/testing-view/src/features/charts/components/TelemetryChart.tsx +++ b/frontend/testing-view/src/features/charts/components/TelemetryChart.tsx @@ -3,7 +3,7 @@ import { cn } from "@workspace/ui/lib/utils"; import { useState } from "react"; import "uplot/dist/uPlot.min.css"; import { useStore } from "../../../store/store"; -import type { WorkspaceChartSeries } from "../../../types/workspace/charts"; +import type { WorkspaceChartSeries } from "../types/charts"; import { ChartLegend } from "./ChartLegend"; import { ChartSurface } from "./ChartSurface"; diff --git a/frontend/testing-view/src/features/charts/components/tooltipPlugin.ts b/frontend/testing-view/src/features/charts/components/tooltipPlugin.ts index f82a7e207..2390f70c2 100644 --- a/frontend/testing-view/src/features/charts/components/tooltipPlugin.ts +++ b/frontend/testing-view/src/features/charts/components/tooltipPlugin.ts @@ -1,5 +1,5 @@ -import { COLORS } from "../../../constants/chartsColors"; -import type { WorkspaceChartSeries } from "../../../types/workspace/charts"; +import { COLORS } from "../constants/chartsColors"; +import type { WorkspaceChartSeries } from "../types/charts"; export const createTooltipPlugin = (series: WorkspaceChartSeries[]) => { let tooltip: HTMLDivElement, diff --git a/frontend/testing-view/src/constants/chartsColors.ts b/frontend/testing-view/src/features/charts/constants/chartsColors.ts similarity index 100% rename from frontend/testing-view/src/constants/chartsColors.ts rename to frontend/testing-view/src/features/charts/constants/chartsColors.ts diff --git a/frontend/testing-view/src/hooks/useChartsConfiguration.ts b/frontend/testing-view/src/features/charts/hooks/useChartsConfiguration.ts similarity index 76% rename from frontend/testing-view/src/hooks/useChartsConfiguration.ts rename to frontend/testing-view/src/features/charts/hooks/useChartsConfiguration.ts index 1d2ddae67..27c65c5d0 100644 --- a/frontend/testing-view/src/hooks/useChartsConfiguration.ts +++ b/frontend/testing-view/src/features/charts/hooks/useChartsConfiguration.ts @@ -1,6 +1,6 @@ import { useEffect } from "react"; -import { MOCK_CHARTS } from "../mocks/chartsConfigurations"; -import { useStore } from "../store/store"; +import { MOCK_CHARTS } from "../../../mocks/chartsConfigurations"; +import { useStore } from "../../../store/store"; export function useChartsConfiguration() { const appMode = useStore((s) => s.appMode); diff --git a/frontend/testing-view/src/store/slices/chartsSlice.ts b/frontend/testing-view/src/features/charts/store/chartsSlice.ts similarity index 96% rename from frontend/testing-view/src/store/slices/chartsSlice.ts rename to frontend/testing-view/src/features/charts/store/chartsSlice.ts index b0d21e02d..aa1314c61 100644 --- a/frontend/testing-view/src/store/slices/chartsSlice.ts +++ b/frontend/testing-view/src/features/charts/store/chartsSlice.ts @@ -1,10 +1,10 @@ import type { StateCreator } from "zustand"; -import { EMPTY_ARRAY } from "../../constants/emptyArray"; +import { EMPTY_ARRAY } from "../../../constants/emptyArray"; +import type { Store } from "../../../store/store"; import type { WorkspaceChartConfig, WorkspaceChartSeries, -} from "../../types/workspace/charts"; -import type { Store } from "../store"; +} from "../types/charts"; export interface ChartsSlice { /** Map of WorkspaceID -> List of Charts */ diff --git a/frontend/testing-view/src/types/workspace/charts.ts b/frontend/testing-view/src/features/charts/types/charts.ts similarity index 100% rename from frontend/testing-view/src/types/workspace/charts.ts rename to frontend/testing-view/src/features/charts/types/charts.ts diff --git a/frontend/testing-view/src/store/slices/filteringSlice.ts b/frontend/testing-view/src/features/filtering/store/filteringSlice.ts similarity index 95% rename from frontend/testing-view/src/store/slices/filteringSlice.ts rename to frontend/testing-view/src/features/filtering/store/filteringSlice.ts index e523e2517..1bcc4cc6d 100644 --- a/frontend/testing-view/src/store/slices/filteringSlice.ts +++ b/frontend/testing-view/src/features/filtering/store/filteringSlice.ts @@ -4,22 +4,22 @@ import { createFullFilter, generateInitialFilters, getCatalogKey, -} from "../../lib/utils"; -import type { CatalogItem } from "../../types/common/item"; -import type { BoardName } from "../../types/data/board"; -import type { Variable } from "../../types/data/telemetryCatalogItem"; -import type { VirtualRow } from "../../types/data/virtualization"; -import type { CheckboxState } from "../../types/workspace/charts"; +} from "../../../lib/utils"; +import type { Store } from "../../../store/store"; +import type { CatalogItem } from "../../../types/common/item"; +import type { BoardName } from "../../../types/data/board"; +import type { Variable } from "../../../types/data/telemetryCatalogItem"; +import type { VirtualRow } from "../../../types/data/virtualization"; +import type { CheckboxState } from "../../charts/types/charts"; +import type { + SidebarTab, + WorkspaceExpandedItems, +} from "../../workspace/types/sidebar"; import type { FilterScope, TabFilter, WorkspaceFilters, -} from "../../types/workspace/filters"; -import type { - SidebarTab, - WorkspaceExpandedItems, -} from "../../types/workspace/sidebar"; -import type { Store } from "../store"; +} from "../types/filters"; export interface FilteringSlice { /** Sidebar Navigation */ diff --git a/frontend/testing-view/src/types/workspace/filters.ts b/frontend/testing-view/src/features/filtering/types/filters.ts similarity index 74% rename from frontend/testing-view/src/types/workspace/filters.ts rename to frontend/testing-view/src/features/filtering/types/filters.ts index 303812832..3817a0ad7 100644 --- a/frontend/testing-view/src/types/workspace/filters.ts +++ b/frontend/testing-view/src/features/filtering/types/filters.ts @@ -1,5 +1,5 @@ -import type { BoardName } from "../data/board"; -import type { SidebarTab } from "./sidebar"; +import type { BoardName } from "../../../types/data/board"; +import type { SidebarTab } from "../../workspace/types/sidebar"; /** * Filter scope identifiers. diff --git a/frontend/testing-view/src/features/keyBindings/components/AddKeyBindingDialog.tsx b/frontend/testing-view/src/features/keyBindings/components/AddKeyBindingDialog.tsx index da83ead66..cd4d633ed 100644 --- a/frontend/testing-view/src/features/keyBindings/components/AddKeyBindingDialog.tsx +++ b/frontend/testing-view/src/features/keyBindings/components/AddKeyBindingDialog.tsx @@ -17,11 +17,11 @@ import { } from "@workspace/ui"; import { X } from "@workspace/ui/icons"; import { useEffect, useRef, useState } from "react"; -import { SPECIAL_KEY_BINDINGS } from "../../../constants/specialKeyBindings"; import { getDefaultParameterValues } from "../../../lib/commandUtils"; import { useStore } from "../../../store/store"; import type { CommandCatalogItem } from "../../../types/data/commandCatalogItem"; import { CommandParameters } from "../../workspace/components/rightSidebar/tabs/commands/CommandParameters"; +import { SPECIAL_KEY_BINDINGS } from "../constants/specialKeyBindings"; interface AddKeyBindingDialogProps { open: boolean; diff --git a/frontend/testing-view/src/features/keyBindings/components/KeyBindingCard.tsx b/frontend/testing-view/src/features/keyBindings/components/KeyBindingCard.tsx index 23c1f1070..68d1e5596 100644 --- a/frontend/testing-view/src/features/keyBindings/components/KeyBindingCard.tsx +++ b/frontend/testing-view/src/features/keyBindings/components/KeyBindingCard.tsx @@ -3,7 +3,7 @@ import { ChevronDown, X } from "@workspace/ui/icons"; import { cn } from "@workspace/ui/lib"; import { useState } from "react"; import { useStore } from "../../../store/store"; -import type { KeyBinding } from "../../../types/workspace/keyBinding"; +import type { KeyBinding } from "../types/keyBinding"; interface KeyBindingCardProps { binding: KeyBinding; diff --git a/frontend/testing-view/src/features/keyBindings/components/KeyBindingsDialog.tsx b/frontend/testing-view/src/features/keyBindings/components/KeyBindingsDialog.tsx index 9accb26d9..66f5a5e56 100644 --- a/frontend/testing-view/src/features/keyBindings/components/KeyBindingsDialog.tsx +++ b/frontend/testing-view/src/features/keyBindings/components/KeyBindingsDialog.tsx @@ -10,7 +10,7 @@ import { Plus } from "@workspace/ui/icons"; import { useState } from "react"; import { useShallow } from "zustand/shallow"; import { useStore } from "../../../store/store"; -import type { KeyBinding } from "../../../types/workspace/keyBinding"; +import type { KeyBinding } from "../types/keyBinding"; import { AddKeyBindingDialog } from "./AddKeyBindingDialog"; import { KeyBindingCard } from "./KeyBindingCard"; import { OrphanedKeyBindingCard } from "./OrphanedKeyBindingCard"; diff --git a/frontend/testing-view/src/features/keyBindings/components/OrphanedKeyBindingCard.tsx b/frontend/testing-view/src/features/keyBindings/components/OrphanedKeyBindingCard.tsx index ceb8a4040..7afcbff84 100644 --- a/frontend/testing-view/src/features/keyBindings/components/OrphanedKeyBindingCard.tsx +++ b/frontend/testing-view/src/features/keyBindings/components/OrphanedKeyBindingCard.tsx @@ -1,7 +1,7 @@ import { Badge, Button } from "@workspace/ui"; import { AlertTriangle, X } from "@workspace/ui/icons"; import { useStore } from "../../../store/store"; -import type { KeyBinding } from "../../../types/workspace/keyBinding"; +import type { KeyBinding } from "../types/keyBinding"; interface OrphanedKeyBindingCardProps { binding: KeyBinding; diff --git a/frontend/testing-view/src/constants/specialKeyBindings.ts b/frontend/testing-view/src/features/keyBindings/constants/specialKeyBindings.ts similarity index 100% rename from frontend/testing-view/src/constants/specialKeyBindings.ts rename to frontend/testing-view/src/features/keyBindings/constants/specialKeyBindings.ts diff --git a/frontend/testing-view/src/hooks/useGlobalKeyBindings.ts b/frontend/testing-view/src/features/keyBindings/hooks/useGlobalKeyBindings.ts similarity index 94% rename from frontend/testing-view/src/hooks/useGlobalKeyBindings.ts rename to frontend/testing-view/src/features/keyBindings/hooks/useGlobalKeyBindings.ts index dc534d8bd..e942868a6 100644 --- a/frontend/testing-view/src/hooks/useGlobalKeyBindings.ts +++ b/frontend/testing-view/src/features/keyBindings/hooks/useGlobalKeyBindings.ts @@ -1,9 +1,9 @@ import { logger, socketService } from "@workspace/core"; import { useEffect } from "react"; +import { getDefaultParameterValues } from "../../../lib/commandUtils"; +import { useStore } from "../../../store/store"; +import type { CommandCatalogItem } from "../../../types/data/commandCatalogItem"; import { SPECIAL_KEY_BINDINGS } from "../constants/specialKeyBindings"; -import { getDefaultParameterValues } from "../lib/commandUtils"; -import { useStore } from "../store/store"; -import type { CommandCatalogItem } from "../types/data/commandCatalogItem"; export const useGlobalKeyBindings = () => { const getKeyBindings = useStore((s) => s.getKeyBindings); diff --git a/frontend/testing-view/src/types/workspace/keyBinding.ts b/frontend/testing-view/src/features/keyBindings/types/keyBinding.ts similarity index 100% rename from frontend/testing-view/src/types/workspace/keyBinding.ts rename to frontend/testing-view/src/features/keyBindings/types/keyBinding.ts diff --git a/frontend/testing-view/src/features/workspace/components/DndOverlay.tsx b/frontend/testing-view/src/features/workspace/components/DndOverlay.tsx index 751907680..16ebaab93 100644 --- a/frontend/testing-view/src/features/workspace/components/DndOverlay.tsx +++ b/frontend/testing-view/src/features/workspace/components/DndOverlay.tsx @@ -1,8 +1,8 @@ import { DragOverlay } from "@dnd-kit/core"; import { Badge } from "@workspace/ui"; -import type { DndActiveData } from "../../../types/app/dndData"; -import type { WorkspaceChartConfig } from "../../../types/workspace/charts"; import { TelemetryChart } from "../../charts/components/TelemetryChart"; +import type { WorkspaceChartConfig } from "../../charts/types/charts"; +import type { DndActiveData } from "../types/dndData"; interface DndOverlayProps { activeDragData: DndActiveData | null; diff --git a/frontend/testing-view/src/features/workspace/components/MainPanel.tsx b/frontend/testing-view/src/features/workspace/components/MainPanel.tsx index 1b8f8d8cc..9c4f2caa5 100644 --- a/frontend/testing-view/src/features/workspace/components/MainPanel.tsx +++ b/frontend/testing-view/src/features/workspace/components/MainPanel.tsx @@ -1,8 +1,8 @@ import { useDroppable } from "@dnd-kit/core"; import { cn } from "@workspace/ui/lib"; import { useStore } from "../../../store/store"; -import type { DndActiveData } from "../../../types/app/dndData"; import { ChartsGrid } from "../../charts/components/ChartsGrid"; +import type { DndActiveData } from "../types/dndData"; import { EmptyWorkspace } from "./EmptyWorkspace"; import { TestingToolbar } from "./Toolbar"; diff --git a/frontend/testing-view/src/features/workspace/components/WorkspaceSwitcher.tsx b/frontend/testing-view/src/features/workspace/components/WorkspaceSwitcher.tsx index 1c4aedd27..6a1a90607 100644 --- a/frontend/testing-view/src/features/workspace/components/WorkspaceSwitcher.tsx +++ b/frontend/testing-view/src/features/workspace/components/WorkspaceSwitcher.tsx @@ -19,7 +19,7 @@ import { import { cn } from "@workspace/ui/lib"; import { useState } from "react"; import { useStore } from "../../../store/store"; -import type { Workspace } from "../../../types/workspace/workspace"; +import type { Workspace } from "../types/workspace"; import { WorkspaceDialog } from "./WorkspaceDialog"; interface WorkspaceSwitcherProps { diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TabsSection.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TabsSection.tsx index bac8e37ca..63e86e18e 100644 --- a/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TabsSection.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TabsSection.tsx @@ -11,7 +11,7 @@ import { BOARD_NAMES } from "../../../../../constants/boards"; import { useStore } from "../../../../../store/store"; import type { CommandCatalogItem } from "../../../../../types/data/commandCatalogItem"; import type { TelemetryCatalogItem } from "../../../../../types/data/telemetryCatalogItem"; -import type { SidebarTab } from "../../../../../types/workspace/sidebar"; +import type { SidebarTab } from "../../../types/sidebar"; import { CommandItem } from "../tabs/commands/CommandItem"; import { Tab } from "../tabs/Tab"; import { TelemetryItem } from "../tabs/telemetry/TelemetryItem"; diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/VirtualizedList.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/VirtualizedList.tsx index 01e7d6d8d..c646ffeb3 100644 --- a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/VirtualizedList.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/VirtualizedList.tsx @@ -2,10 +2,10 @@ import { useVirtualizer } from "@tanstack/react-virtual"; import { cn } from "@workspace/ui/lib"; import { useCallback, useRef } from "react"; -import { usePacketRows } from "../../../../../hooks/usePacketRows"; import type { BoardName } from "../../../../../types/data/board"; import type { VirtualRow } from "../../../../../types/data/virtualization"; -import type { SidebarTab } from "../../../../../types/workspace/sidebar"; +import { usePacketRows } from "../../../hooks/usePacketRows"; +import type { SidebarTab } from "../../../types/sidebar"; import { TelemetryRow } from "./telemetry/TelemetryRow"; interface VirtualizedListProps { diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/ChartPicker.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/ChartPicker.tsx index 9e719985d..8a06598d0 100644 --- a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/ChartPicker.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/ChartPicker.tsx @@ -8,7 +8,7 @@ import { DropdownMenuTrigger, } from "@workspace/ui"; import { Plus } from "@workspace/ui/icons"; -import type { WorkspaceChartConfig } from "../../../../../../types/workspace/charts"; +import type { WorkspaceChartConfig } from "../../../../../charts/types/charts"; interface ChartPickerProps { charts: WorkspaceChartConfig[]; diff --git a/frontend/testing-view/src/constants/defaultWorkspaces.ts b/frontend/testing-view/src/features/workspace/constants/defaultWorkspaces.ts similarity index 86% rename from frontend/testing-view/src/constants/defaultWorkspaces.ts rename to frontend/testing-view/src/features/workspace/constants/defaultWorkspaces.ts index acf4541ff..b2349283b 100644 --- a/frontend/testing-view/src/constants/defaultWorkspaces.ts +++ b/frontend/testing-view/src/features/workspace/constants/defaultWorkspaces.ts @@ -1,4 +1,4 @@ -import type { Workspace } from "../types/workspace/workspace"; +import type { Workspace } from "../types/workspace"; export const DEFAULT_WORKSPACES: Workspace[] = [ { diff --git a/frontend/testing-view/src/hooks/useDnd.ts b/frontend/testing-view/src/features/workspace/hooks/useDnd.ts similarity index 95% rename from frontend/testing-view/src/hooks/useDnd.ts rename to frontend/testing-view/src/features/workspace/hooks/useDnd.ts index da9d5d7e4..821d99e59 100644 --- a/frontend/testing-view/src/hooks/useDnd.ts +++ b/frontend/testing-view/src/features/workspace/hooks/useDnd.ts @@ -6,8 +6,8 @@ import { useSensors, } from "@dnd-kit/core"; import { useState } from "react"; -import { useStore } from "../store/store"; -import type { DndActiveData } from "../types/app/dndData"; +import { useStore } from "../../../store/store"; +import type { DndActiveData } from "../types/dndData"; export function useDnd() { const [activeData, setActiveData] = useState(null); diff --git a/frontend/testing-view/src/hooks/usePacketRows.ts b/frontend/testing-view/src/features/workspace/hooks/usePacketRows.ts similarity index 77% rename from frontend/testing-view/src/hooks/usePacketRows.ts rename to frontend/testing-view/src/features/workspace/hooks/usePacketRows.ts index d05293d76..d19f82c2e 100644 --- a/frontend/testing-view/src/hooks/usePacketRows.ts +++ b/frontend/testing-view/src/features/workspace/hooks/usePacketRows.ts @@ -1,8 +1,8 @@ import { useMemo } from "react"; import { useShallow } from "zustand/shallow"; -import { useStore } from "../store/store"; -import type { BoardName } from "../types/data/board"; -import type { SidebarTab } from "../types/workspace/sidebar"; +import { useStore } from "../../../store/store"; +import type { BoardName } from "../../../types/data/board"; +import type { SidebarTab } from "../../../types/workspace/sidebar"; export const usePacketRows = ( scope: SidebarTab, diff --git a/frontend/testing-view/src/store/slices/rightSidebarSlice.ts b/frontend/testing-view/src/features/workspace/store/rightSidebarSlice.ts similarity index 95% rename from frontend/testing-view/src/store/slices/rightSidebarSlice.ts rename to frontend/testing-view/src/features/workspace/store/rightSidebarSlice.ts index 6a7575f6f..29be2806e 100644 --- a/frontend/testing-view/src/store/slices/rightSidebarSlice.ts +++ b/frontend/testing-view/src/features/workspace/store/rightSidebarSlice.ts @@ -1,5 +1,5 @@ import type { StateCreator } from "zustand"; -import type { Store } from "../store"; +import type { Store } from "../../../store/store"; export interface RightSidebarSlice { // Section visibility diff --git a/frontend/testing-view/src/store/slices/workspacesSlice.ts b/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts similarity index 95% rename from frontend/testing-view/src/store/slices/workspacesSlice.ts rename to frontend/testing-view/src/features/workspace/store/workspacesSlice.ts index f3fc4de4c..ea09a6844 100644 --- a/frontend/testing-view/src/store/slices/workspacesSlice.ts +++ b/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts @@ -1,10 +1,10 @@ import type { StateCreator } from "zustand"; -import { DEFAULT_WORKSPACES } from "../../constants/defaultWorkspaces"; -import { createFullFilter } from "../../lib/utils"; -import type { KeyBinding } from "../../types/workspace/keyBinding"; -import type { SidebarTab } from "../../types/workspace/sidebar"; -import type { Workspace } from "../../types/workspace/workspace"; -import type { Store } from "../store"; +import { createFullFilter } from "../../../lib/utils"; +import type { Store } from "../../../store/store"; +import type { KeyBinding } from "../../keyBindings/types/keyBinding"; +import { DEFAULT_WORKSPACES } from "../constants/defaultWorkspaces"; +import type { SidebarTab } from "../types/sidebar"; +import type { Workspace } from "../types/workspace"; export interface WorkspacesSlice { /** Workspaces */ diff --git a/frontend/testing-view/src/types/app/dndData.ts b/frontend/testing-view/src/features/workspace/types/dndData.ts similarity index 100% rename from frontend/testing-view/src/types/app/dndData.ts rename to frontend/testing-view/src/features/workspace/types/dndData.ts diff --git a/frontend/testing-view/src/types/workspace/sidebar.ts b/frontend/testing-view/src/features/workspace/types/sidebar.ts similarity index 100% rename from frontend/testing-view/src/types/workspace/sidebar.ts rename to frontend/testing-view/src/features/workspace/types/sidebar.ts diff --git a/frontend/testing-view/src/types/workspace/workspace.ts b/frontend/testing-view/src/features/workspace/types/workspace.ts similarity index 81% rename from frontend/testing-view/src/types/workspace/workspace.ts rename to frontend/testing-view/src/features/workspace/types/workspace.ts index bf835f3a1..493e4376a 100644 --- a/frontend/testing-view/src/types/workspace/workspace.ts +++ b/frontend/testing-view/src/features/workspace/types/workspace.ts @@ -1,4 +1,4 @@ -import type { KeyBinding } from "./keyBinding"; +import type { KeyBinding } from "../../keyBindings/types/keyBinding"; /** * Workspace definition. diff --git a/frontend/testing-view/src/lib/utils.test.ts b/frontend/testing-view/src/lib/utils.test.ts index b01eba7ad..947b6c5c7 100644 --- a/frontend/testing-view/src/lib/utils.test.ts +++ b/frontend/testing-view/src/lib/utils.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { variablesBadgeClasses } from "../constants/variablesBadgeClasses"; +import type { FilterScope } from "../features/filtering/types/filters"; import type { MessageTimestamp } from "../types/data/message"; -import type { FilterScope } from "../types/workspace/filters"; import { createEmptyFilter, createFullFilter, diff --git a/frontend/testing-view/src/lib/utils.ts b/frontend/testing-view/src/lib/utils.ts index 798855a52..629263f43 100644 --- a/frontend/testing-view/src/lib/utils.ts +++ b/frontend/testing-view/src/lib/utils.ts @@ -1,15 +1,15 @@ import { acronyms } from "../constants/acronyms"; import { BOARD_NAMES } from "../constants/boards"; -import { DEFAULT_WORKSPACES } from "../constants/defaultWorkspaces"; import { variablesBadgeClasses } from "../constants/variablesBadgeClasses"; -import type { CatalogItem } from "../types/common/item"; -import type { BoardName } from "../types/data/board"; -import type { MessageTimestamp } from "../types/data/message"; import type { FilterScope, TabFilter, WorkspaceFilters, -} from "../types/workspace/filters"; +} from "../features/filtering/types/filters"; +import { DEFAULT_WORKSPACES } from "../features/workspace/constants/defaultWorkspaces"; +import type { CatalogItem } from "../types/common/item"; +import type { BoardName } from "../types/data/board"; +import type { MessageTimestamp } from "../types/data/message"; type InitialFilters = Record; diff --git a/frontend/testing-view/src/mocks/chartsConfigurations.ts b/frontend/testing-view/src/mocks/chartsConfigurations.ts index 7e0fed4b1..cb1a5eacc 100644 --- a/frontend/testing-view/src/mocks/chartsConfigurations.ts +++ b/frontend/testing-view/src/mocks/chartsConfigurations.ts @@ -1,4 +1,4 @@ -import type { WorkspaceChartConfig } from "../types/workspace/charts"; +import type { WorkspaceChartConfig } from "../features/charts/types/charts"; export const MOCK_CHARTS: Record = { "workspace-1": [ diff --git a/frontend/testing-view/src/pages/Testing.tsx b/frontend/testing-view/src/pages/Testing.tsx index 189542d7b..b02ccbb9b 100644 --- a/frontend/testing-view/src/pages/Testing.tsx +++ b/frontend/testing-view/src/pages/Testing.tsx @@ -6,11 +6,11 @@ import { } from "@workspace/ui"; import { SquareLibrary } from "@workspace/ui/icons"; import { FilterController } from "../features/filtering/components/FilterController"; +import { useGlobalKeyBindings } from "../features/keyBindings/hooks/useGlobalKeyBindings"; import { DndOverlay } from "../features/workspace/components/DndOverlay"; import { MainPanel } from "../features/workspace/components/MainPanel"; import { RightSidebar } from "../features/workspace/components/rightSidebar/RightSidebar"; -import { useDnd } from "../hooks/useDnd"; -import { useGlobalKeyBindings } from "../hooks/useGlobalKeyBindings"; +import { useDnd } from "../features/workspace/hooks/useDnd"; import { useStore } from "../store/store"; export const Testing = () => { diff --git a/frontend/testing-view/src/store/store.ts b/frontend/testing-view/src/store/store.ts index 7aaf692f3..9ed8695ee 100644 --- a/frontend/testing-view/src/store/store.ts +++ b/frontend/testing-view/src/store/store.ts @@ -1,32 +1,35 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; +import { + createChartsSlice, + type ChartsSlice, +} from "../features/charts/store/chartsSlice"; +import { + createFilteringSlice, + type FilteringSlice, +} from "../features/filtering/store/filteringSlice"; +import { + createRightSidebarSlice, + type RightSidebarSlice, +} from "../features/workspace/store/rightSidebarSlice"; +import { + createWorkspacesSlice, + type WorkspacesSlice, +} from "../features/workspace/store/workspacesSlice"; import { createAppSlice, type AppSlice } from "./slices/appSlice"; import { createCatalogSlice, type CatalogSlice } from "./slices/catalogSlice"; -import { createChartsSlice, type ChartsSlice } from "./slices/chartsSlice"; import { createConnectionsSlice, type ConnectionsSlice, } from "./slices/connectionsSlice"; -import { - createFilteringSlice, - type FilteringSlice, -} from "./slices/filteringSlice"; import { createMessagesSlice, type MessagesSlice, } from "./slices/messagesSlice"; -import { - createRightSidebarSlice, - type RightSidebarSlice, -} from "./slices/rightSidebarSlice"; import { createTelemetrySlice, type TelemetrySlice, } from "./slices/telemetrySlice"; -import { - createWorkspacesSlice, - type WorkspacesSlice, -} from "./slices/workspacesSlice"; export type Store = AppSlice & CatalogSlice & From 52c62b7a68970ccc56f4cdfbcb4de97f99d0a4c7 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:53:52 +0100 Subject: [PATCH 14/19] fix: fix variable type --- .../src/types/data/telemetryCatalogItem.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts index a526962eb..29220cfe6 100644 --- a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts +++ b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts @@ -7,9 +7,25 @@ export interface Variable { id: string; name: string; type: string; +} + +export interface NumericVariable extends Variable { + safeRange: (number | null)[]; + warningRange: (number | null)[]; units: string; } +export interface EnumVariable extends Variable { + options: string[]; +} + +type BooleanVariable = Variable; + +export type TelemetryVariable = + | NumericVariable + | EnumVariable + | BooleanVariable; + /** * Definition of a telemetry packet as it arrives from the backend. */ @@ -17,7 +33,7 @@ export interface RawPacket extends Item { count: number; cycleTime: number; type: string; - measurements: Variable[]; + measurements: TelemetryVariable[]; /** Currently unused (always equals to "000000" placeholder on the backend) */ hexValue: string; } From 2aedc20c0bb716a8128561448ae20d7e33315484 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:56:27 +0100 Subject: [PATCH 15/19] fix: imports --- frontend/frontend-kit/core/src/minMaxDownsample.ts | 2 +- frontend/testing-view/src/hooks/useBoardData.ts | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/frontend/frontend-kit/core/src/minMaxDownsample.ts b/frontend/frontend-kit/core/src/minMaxDownsample.ts index bfc7b41e3..a0324c22f 100644 --- a/frontend/frontend-kit/core/src/minMaxDownsample.ts +++ b/frontend/frontend-kit/core/src/minMaxDownsample.ts @@ -1,4 +1,4 @@ -import { TelemetryPacket, VariableValue } from "./types"; +import { type TelemetryPacket, type VariableValue } from "./types"; /** * Helper to extract a numeric value for comparison. diff --git a/frontend/testing-view/src/hooks/useBoardData.ts b/frontend/testing-view/src/hooks/useBoardData.ts index 724c0314b..22ff3d8f9 100644 --- a/frontend/testing-view/src/hooks/useBoardData.ts +++ b/frontend/testing-view/src/hooks/useBoardData.ts @@ -4,18 +4,14 @@ import { formatName } from "../lib/utils"; import { MOCK_COMMANDS_CATALOG } from "../mocks/commands"; import { MOCK_TELEMETRY_CATALOG } from "../mocks/telemetry"; import type { AppMode } from "../types/app/mode"; -import type { BoardName } from "../types/data/board"; +import type { BoardName, OrdersData, PacketsData } from "../types/data/board"; import type { CommandCatalogItem } from "../types/data/commandCatalogItem"; import type { TelemetryCatalogItem } from "../types/data/telemetryCatalogItem"; -import type { - OrdersData, - PacketsData, - TransformedBoards, -} from "../types/data/transformedBoards"; +import type { TransformedBoards } from "../types/data/transformedBoards"; export function useBoardData( - packets: PacketsData | null, - commands: OrdersData | null, + packets: PacketsData | null | undefined, + commands: OrdersData | null | undefined, appMode: AppMode, ) { const transformedBoards = useMemo(() => { From 2763809bd4bfe0a804509ab70f564f88094717c8 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:01:04 +0100 Subject: [PATCH 16/19] fix: some more imports --- .../rightSidebar/tabs/CategoryItem.tsx | 2 +- .../rightSidebar/tabs/StandardList.tsx | 2 +- .../components/rightSidebar/tabs/Tab.tsx | 2 +- .../rightSidebar/tabs/TabHeader.tsx | 2 +- frontend/testing-view/src/mocks/messages.ts | 81 ------------------- 5 files changed, 4 insertions(+), 85 deletions(-) delete mode 100644 frontend/testing-view/src/mocks/messages.ts diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/CategoryItem.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/CategoryItem.tsx index eb4929224..3622797d8 100644 --- a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/CategoryItem.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/CategoryItem.tsx @@ -4,7 +4,7 @@ import { useShallow } from "zustand/shallow"; import { useStore } from "../../../../../store/store"; import type { CatalogItem } from "../../../../../types/common/item"; import type { BoardName } from "../../../../../types/data/board"; -import type { SidebarTab } from "../../../../../types/workspace/sidebar"; +import type { SidebarTab } from "../../../types/sidebar"; import { CategoryHeader } from "./CategoryHeader"; interface CategoryItemProps { diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/StandardList.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/StandardList.tsx index 8b89fca26..92bf8839e 100644 --- a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/StandardList.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/StandardList.tsx @@ -1,8 +1,8 @@ import { type ComponentType } from "react"; import type { CatalogItem } from "../../../../../types/common/item"; import type { BoardName } from "../../../../../types/data/board"; -import type { SidebarTab } from "../../../../../types/workspace/sidebar"; import { CategoryItem } from "./CategoryItem"; +import type { SidebarTab } from "../../../types/sidebar"; interface StandardListProps { scope: SidebarTab; diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/Tab.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/Tab.tsx index 2c5e58f24..7e5dfa44e 100644 --- a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/Tab.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/Tab.tsx @@ -2,11 +2,11 @@ import { type ComponentType } from "react"; import { useStore } from "../../../../../store/store"; import type { CatalogItem } from "../../../../../types/common/item"; import type { BoardName } from "../../../../../types/data/board"; -import type { SidebarTab } from "../../../../../types/workspace/sidebar"; import { EmptyTab } from "./EmptyTab"; import { StandardList } from "./StandardList"; import { TabHeader } from "./TabHeader"; import { VirtualizedList } from "./VirtualizedList"; +import type { SidebarTab } from "../../../types/sidebar"; interface TabProps { title: string; diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx index 244692953..13ff18450 100644 --- a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx @@ -1,7 +1,7 @@ import { Button } from "@workspace/ui"; import { ListFilterPlus } from "@workspace/ui/icons"; import { useStore } from "../../../../../store/store"; -import type { SidebarTab } from "../../../../../types/workspace/sidebar"; +import type { SidebarTab } from "../../../types/sidebar"; interface TabHeaderProps { title: string; diff --git a/frontend/testing-view/src/mocks/messages.ts b/frontend/testing-view/src/mocks/messages.ts deleted file mode 100644 index 979051b05..000000000 --- a/frontend/testing-view/src/mocks/messages.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { Message } from "../types/data/message"; - -// Mock Messages -export const MOCK_MESSAGES: Message[] = [ - { - id: "1", - timestamp: { - counter: 1, - second: 56, - minute: 34, - hour: 12, - day: 1, - month: 1, - year: 2026, - }, - kind: "info", - payload: { - connection: "established", - device: "0x01", - }, - board: "board1", - name: "connection", - }, - { - id: "2", - timestamp: { - counter: 2, - second: 12, - minute: 35, - hour: 12, - day: 1, - month: 1, - year: 2026, - }, - kind: "warning", - payload: { - latency: 250, - threshold: 200, - }, - board: "board1", - name: "latency", - }, - { - id: "3", - timestamp: { - counter: 4, - second: 1, - minute: 36, - hour: 12, - day: 1, - month: 1, - year: 2026, - }, - kind: "fault", - payload: { - error: "Failed to send packet", - details: "Timeout after 3 retries", - }, - board: "board1", - name: "packet", - }, - { - id: "4", - timestamp: { - counter: 5, - second: 15, - minute: 36, - hour: 12, - day: 1, - month: 1, - year: 2026, - }, - kind: "ok", - payload: { - message: "Calibration complete", - details: "Accuracy: 99.8%", - }, - board: "board1", - name: "calibration", - }, -]; From 6761ea7e51faaa6e92c15d3dda6c3ed65134abe2 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:06:05 +0100 Subject: [PATCH 17/19] chore: enforce lowercase folder names --- .../src/components/{DevTools => devTools}/ModeSwitcher.tsx | 0 .../testing-view/src/components/{Header => header}/Header.tsx | 0 .../testing-view/src/components/{Header => header}/ModeBadge.tsx | 0 .../src/components/{Header => header}/ReconnectButton.tsx | 0 .../src/components/{LeftSidebar => leftSidebar}/AppSidebar.tsx | 0 .../components/{LeftSidebar => leftSidebar}/ColorSchemeToggle.tsx | 0 .../{LeftSidebar => leftSidebar}/ConnectionStatusGroup.tsx | 0 .../src/components/{LeftSidebar => leftSidebar}/Logo.tsx | 0 .../components/{LeftSidebar => leftSidebar}/NavigationGroup.tsx | 0 .../src/components/{LeftSidebar => leftSidebar}/SettingsItem.tsx | 0 .../components/{LeftSidebar => leftSidebar}/ThemeToggleItem.tsx | 0 .../src/components/{Settings => settings}/BooleanField.tsx | 0 .../src/components/{Settings => settings}/MultiCheckboxField.tsx | 0 .../src/components/{Settings => settings}/PathField.tsx | 0 .../src/components/{Settings => settings}/SelectField.tsx | 0 .../src/components/{Settings => settings}/SettingsDialog.tsx | 0 .../src/components/{Settings => settings}/SettingsForm.tsx | 0 .../src/components/{Settings => settings}/TextField.tsx | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename frontend/testing-view/src/components/{DevTools => devTools}/ModeSwitcher.tsx (100%) rename frontend/testing-view/src/components/{Header => header}/Header.tsx (100%) rename frontend/testing-view/src/components/{Header => header}/ModeBadge.tsx (100%) rename frontend/testing-view/src/components/{Header => header}/ReconnectButton.tsx (100%) rename frontend/testing-view/src/components/{LeftSidebar => leftSidebar}/AppSidebar.tsx (100%) rename frontend/testing-view/src/components/{LeftSidebar => leftSidebar}/ColorSchemeToggle.tsx (100%) rename frontend/testing-view/src/components/{LeftSidebar => leftSidebar}/ConnectionStatusGroup.tsx (100%) rename frontend/testing-view/src/components/{LeftSidebar => leftSidebar}/Logo.tsx (100%) rename frontend/testing-view/src/components/{LeftSidebar => leftSidebar}/NavigationGroup.tsx (100%) rename frontend/testing-view/src/components/{LeftSidebar => leftSidebar}/SettingsItem.tsx (100%) rename frontend/testing-view/src/components/{LeftSidebar => leftSidebar}/ThemeToggleItem.tsx (100%) rename frontend/testing-view/src/components/{Settings => settings}/BooleanField.tsx (100%) rename frontend/testing-view/src/components/{Settings => settings}/MultiCheckboxField.tsx (100%) rename frontend/testing-view/src/components/{Settings => settings}/PathField.tsx (100%) rename frontend/testing-view/src/components/{Settings => settings}/SelectField.tsx (100%) rename frontend/testing-view/src/components/{Settings => settings}/SettingsDialog.tsx (100%) rename frontend/testing-view/src/components/{Settings => settings}/SettingsForm.tsx (100%) rename frontend/testing-view/src/components/{Settings => settings}/TextField.tsx (100%) diff --git a/frontend/testing-view/src/components/DevTools/ModeSwitcher.tsx b/frontend/testing-view/src/components/devTools/ModeSwitcher.tsx similarity index 100% rename from frontend/testing-view/src/components/DevTools/ModeSwitcher.tsx rename to frontend/testing-view/src/components/devTools/ModeSwitcher.tsx diff --git a/frontend/testing-view/src/components/Header/Header.tsx b/frontend/testing-view/src/components/header/Header.tsx similarity index 100% rename from frontend/testing-view/src/components/Header/Header.tsx rename to frontend/testing-view/src/components/header/Header.tsx diff --git a/frontend/testing-view/src/components/Header/ModeBadge.tsx b/frontend/testing-view/src/components/header/ModeBadge.tsx similarity index 100% rename from frontend/testing-view/src/components/Header/ModeBadge.tsx rename to frontend/testing-view/src/components/header/ModeBadge.tsx diff --git a/frontend/testing-view/src/components/Header/ReconnectButton.tsx b/frontend/testing-view/src/components/header/ReconnectButton.tsx similarity index 100% rename from frontend/testing-view/src/components/Header/ReconnectButton.tsx rename to frontend/testing-view/src/components/header/ReconnectButton.tsx diff --git a/frontend/testing-view/src/components/LeftSidebar/AppSidebar.tsx b/frontend/testing-view/src/components/leftSidebar/AppSidebar.tsx similarity index 100% rename from frontend/testing-view/src/components/LeftSidebar/AppSidebar.tsx rename to frontend/testing-view/src/components/leftSidebar/AppSidebar.tsx diff --git a/frontend/testing-view/src/components/LeftSidebar/ColorSchemeToggle.tsx b/frontend/testing-view/src/components/leftSidebar/ColorSchemeToggle.tsx similarity index 100% rename from frontend/testing-view/src/components/LeftSidebar/ColorSchemeToggle.tsx rename to frontend/testing-view/src/components/leftSidebar/ColorSchemeToggle.tsx diff --git a/frontend/testing-view/src/components/LeftSidebar/ConnectionStatusGroup.tsx b/frontend/testing-view/src/components/leftSidebar/ConnectionStatusGroup.tsx similarity index 100% rename from frontend/testing-view/src/components/LeftSidebar/ConnectionStatusGroup.tsx rename to frontend/testing-view/src/components/leftSidebar/ConnectionStatusGroup.tsx diff --git a/frontend/testing-view/src/components/LeftSidebar/Logo.tsx b/frontend/testing-view/src/components/leftSidebar/Logo.tsx similarity index 100% rename from frontend/testing-view/src/components/LeftSidebar/Logo.tsx rename to frontend/testing-view/src/components/leftSidebar/Logo.tsx diff --git a/frontend/testing-view/src/components/LeftSidebar/NavigationGroup.tsx b/frontend/testing-view/src/components/leftSidebar/NavigationGroup.tsx similarity index 100% rename from frontend/testing-view/src/components/LeftSidebar/NavigationGroup.tsx rename to frontend/testing-view/src/components/leftSidebar/NavigationGroup.tsx diff --git a/frontend/testing-view/src/components/LeftSidebar/SettingsItem.tsx b/frontend/testing-view/src/components/leftSidebar/SettingsItem.tsx similarity index 100% rename from frontend/testing-view/src/components/LeftSidebar/SettingsItem.tsx rename to frontend/testing-view/src/components/leftSidebar/SettingsItem.tsx diff --git a/frontend/testing-view/src/components/LeftSidebar/ThemeToggleItem.tsx b/frontend/testing-view/src/components/leftSidebar/ThemeToggleItem.tsx similarity index 100% rename from frontend/testing-view/src/components/LeftSidebar/ThemeToggleItem.tsx rename to frontend/testing-view/src/components/leftSidebar/ThemeToggleItem.tsx diff --git a/frontend/testing-view/src/components/Settings/BooleanField.tsx b/frontend/testing-view/src/components/settings/BooleanField.tsx similarity index 100% rename from frontend/testing-view/src/components/Settings/BooleanField.tsx rename to frontend/testing-view/src/components/settings/BooleanField.tsx diff --git a/frontend/testing-view/src/components/Settings/MultiCheckboxField.tsx b/frontend/testing-view/src/components/settings/MultiCheckboxField.tsx similarity index 100% rename from frontend/testing-view/src/components/Settings/MultiCheckboxField.tsx rename to frontend/testing-view/src/components/settings/MultiCheckboxField.tsx diff --git a/frontend/testing-view/src/components/Settings/PathField.tsx b/frontend/testing-view/src/components/settings/PathField.tsx similarity index 100% rename from frontend/testing-view/src/components/Settings/PathField.tsx rename to frontend/testing-view/src/components/settings/PathField.tsx diff --git a/frontend/testing-view/src/components/Settings/SelectField.tsx b/frontend/testing-view/src/components/settings/SelectField.tsx similarity index 100% rename from frontend/testing-view/src/components/Settings/SelectField.tsx rename to frontend/testing-view/src/components/settings/SelectField.tsx diff --git a/frontend/testing-view/src/components/Settings/SettingsDialog.tsx b/frontend/testing-view/src/components/settings/SettingsDialog.tsx similarity index 100% rename from frontend/testing-view/src/components/Settings/SettingsDialog.tsx rename to frontend/testing-view/src/components/settings/SettingsDialog.tsx diff --git a/frontend/testing-view/src/components/Settings/SettingsForm.tsx b/frontend/testing-view/src/components/settings/SettingsForm.tsx similarity index 100% rename from frontend/testing-view/src/components/Settings/SettingsForm.tsx rename to frontend/testing-view/src/components/settings/SettingsForm.tsx diff --git a/frontend/testing-view/src/components/Settings/TextField.tsx b/frontend/testing-view/src/components/settings/TextField.tsx similarity index 100% rename from frontend/testing-view/src/components/Settings/TextField.tsx rename to frontend/testing-view/src/components/settings/TextField.tsx From 828d9b30ab8d1fadc8405d8c06dfb794fd0c12f2 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:12:13 +0100 Subject: [PATCH 18/19] fix: errors --- .../components/rightSidebar/tabs/telemetry/VariableItem.tsx | 2 +- .../testing-view/src/features/workspace/hooks/usePacketRows.ts | 2 +- frontend/testing-view/src/features/workspace/types/sidebar.ts | 2 +- frontend/testing-view/src/types/data/telemetryCatalogItem.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/VariableItem.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/VariableItem.tsx index 7d9217cf2..8c0351b29 100644 --- a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/VariableItem.tsx +++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/telemetry/VariableItem.tsx @@ -125,7 +125,7 @@ export const VariableItem = ({ packetId, variable }: VariableItemProps) => { {/* Live Value */} diff --git a/frontend/testing-view/src/features/workspace/hooks/usePacketRows.ts b/frontend/testing-view/src/features/workspace/hooks/usePacketRows.ts index d19f82c2e..bcc6ac39f 100644 --- a/frontend/testing-view/src/features/workspace/hooks/usePacketRows.ts +++ b/frontend/testing-view/src/features/workspace/hooks/usePacketRows.ts @@ -2,7 +2,7 @@ import { useMemo } from "react"; import { useShallow } from "zustand/shallow"; import { useStore } from "../../../store/store"; import type { BoardName } from "../../../types/data/board"; -import type { SidebarTab } from "../../../types/workspace/sidebar"; +import type { SidebarTab } from "../types/sidebar"; export const usePacketRows = ( scope: SidebarTab, diff --git a/frontend/testing-view/src/features/workspace/types/sidebar.ts b/frontend/testing-view/src/features/workspace/types/sidebar.ts index aaad45f35..701e6971f 100644 --- a/frontend/testing-view/src/features/workspace/types/sidebar.ts +++ b/frontend/testing-view/src/features/workspace/types/sidebar.ts @@ -1,4 +1,4 @@ -import type { FilterScope } from "./filters"; +import type { FilterScope } from "../../filtering/types/filters"; /** * Sidebar tab identifiers. diff --git a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts index 29220cfe6..a51593d90 100644 --- a/frontend/testing-view/src/types/data/telemetryCatalogItem.ts +++ b/frontend/testing-view/src/types/data/telemetryCatalogItem.ts @@ -7,12 +7,12 @@ export interface Variable { id: string; name: string; type: string; + units?: string; } export interface NumericVariable extends Variable { safeRange: (number | null)[]; warningRange: (number | null)[]; - units: string; } export interface EnumVariable extends Variable { From 8b4f6a1f8606dbe881e8dcdb684daf23e752ac00 Mon Sep 17 00:00:00 2001 From: Maxim <74974283+maximka76667@users.noreply.github.com> Date: Wed, 4 Feb 2026 09:45:25 +0100 Subject: [PATCH 19/19] chore: update README.md --- frontend/README.md | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/frontend/README.md b/frontend/README.md index f867ea4ac..de434e676 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -31,14 +31,25 @@ The frontend is organized as 6 workspaces out of 9 in the whole monorepo, divide ## State Management -The application uses **Zustand** with a slice-based architecture: +The application uses **Zustand** with a slice-based architecture, organized by feature domain: -- `workspacesSlice` - Manages workspaces, filters, charts, and tabs -- `catalogSlice` - Stores telemetry and command catalogs -- `telemetrySlice` - Real-time telemetry data +### Global Slices + +- `appSlice` - Application mode, settings, and configuration +- `connectionsSlice` - WebSockets connection statuses +- `telemetrySlice` - Real-time telemetry data buffer - `messagesSlice` - System messages and logs -- `appSlice` - Application mode and settings -- `rightSidebarSlice` - UI state for sidebar panels +- `catalogSlice` - Static definitions for telemetry packets and commands + +### Feature Slices + +- **Workspace Feature** (`features/workspace`) + - `workspacesSlice` - Manages workspace layout + - `rightSidebarSlice` - UI state for the collapsible sidebar and its tabs +- **Charts Feature** (`features/charts`) + - `chartsSlice` - Manages chart instances, series configuration, and visualization settings +- **Filtering Feature** (`features/filtering`) + - `filteringSlice` - Manages active filters, search queries, and category selection ### Workspace System @@ -98,7 +109,7 @@ import { Plus, Settings } from "@workspace/ui/icons"; - **CSS Variables** for theming (defined in `globals.css`) - **Tailwind CSS** for utility classes - **Dark mode** support via CSS class toggling -- Multiple color schemes (default, pink, etc.) +- Multiple color schemes (default and pink) ### Adding Icons @@ -133,13 +144,14 @@ frontend/ ├── testing-view/ │ ├── src/ │ │ ├── assets/ # Assets (images, gifs, etc.) -│ │ ├── components/ # UI components +│ │ ├── components/ # Global UI components +│ │ ├── features/ # Components, hooks, types and store slices related to features │ │ ├── layout/ # App layout │ │ ├── pages/ # Route pages -│ │ ├── store/ # Zustand store slices -│ │ ├── hooks/ # Custom hooks +│ │ ├── store/ # Global Zustand store slices +│ │ ├── hooks/ # Global custom hooks │ │ ├── constants/ # Config and constants -│ │ ├── types/ # TypeScript types +│ │ ├── types/ # Global TypeScript types │ │ ├── mocks/ # Mocks │ │ └── lib/ # Utilities │ └── public/ # Static assets