WIP config update

This commit is contained in:
Sacha Weatherstone
2023-01-23 13:14:02 +10:00
parent 703eacc269
commit e3f2a4585f
27 changed files with 116 additions and 270 deletions

View File

@@ -13,8 +13,8 @@ lint:
- oxipng@8.0.0
- svgo@3.0.2
- git-diff-check
- actionlint@1.6.22
- gitleaks@8.15.2
- actionlint@1.6.23
- gitleaks@8.15.3
runtimes:
enabled:
- go@1.18.3

View File

@@ -11,7 +11,7 @@ import { PeersPage } from "@pages/Peers.js";
export const PageRouter = (): JSX.Element => {
const { activePage } = useDevice();
return (
<div className="flex-grow overflow-y-auto border-l-2 border-backgroundPrimary">
<div className="flex-grow overflow-y-auto bg-backgroundPrimary">
{activePage === "messages" && <MessagesPage />}
{activePage === "map" && <MapPage />}
{activePage === "extensions" && <ExtensionsPage />}

View File

@@ -7,15 +7,15 @@ import { PageNav } from "@app/Nav/PageNav.js";
import { Mono } from "@components/generic/Mono.js";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { PlusIcon } from "@heroicons/react/24/outline";
import { MoonIcon, SunIcon } from "@primer/octicons-react";
export const DeviceSelector = (): JSX.Element => {
const { getDevices } = useDeviceStore();
const { selectedDevice, setSelectedDevice, darkMode } = useAppStore();
const { selectedDevice, setSelectedDevice, darkMode, setDarkMode } = useAppStore();
return (
<div className="flex h-full w-14 items-center gap-3 bg-backgroundPrimary pt-3 [writing-mode:vertical-rl]">
<div className="flex items-center gap-3">
<Mono className="select-none">Connected Devices</Mono>
<span className="flex font-bold text-textPrimary">
{getDevices().map((device) => (
<div
@@ -55,7 +55,12 @@ export const DeviceSelector = (): JSX.Element => {
<NavSpacer />
<div>//actions</div>
<div onClick={() => setDarkMode(!darkMode)} className="bg-backgroundPrimary py-5 px-4 hover:brightness-hover active:brightness-press text-textSecondary hover:text-textPrimary">{
darkMode ? (
<SunIcon className="w-4" />
) : (
<MoonIcon className="w-4" />
)}</div>
<img
src={darkMode ? "Logo_White.svg" : "Logo_Black.svg"}

View File

@@ -39,16 +39,6 @@ export const NewDevice = () => {
<div className="m-auto h-96 w-96">
<TabbedContent
tabs={tabs}
actions={[
{
icon: darkMode ? (
<SunIcon className="w-4" />
) : (
<MoonIcon className="w-4" />
),
action: () => setDarkMode(!darkMode)
}
]}
/>
</div>
);

View File

@@ -49,17 +49,7 @@ export const Map = (): JSX.Element => {
// }, [reset, rasterSources]);
return (
<Form
title="Map Config"
breadcrumbs={["App Config", "Map"]}
reset={() =>
reset({
rasterSources
})
}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<InfoWrapper label="WMS Sources">
<div className="flex flex-col gap-2">
{fields.map((field, index) => (

View File

@@ -105,31 +105,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
});
return (
<Form
title="Channel Editor"
breadcrumbs={[
"Channels",
channel.settings?.name.length
? channel.settings.name
: channel.role === Protobuf.Channel_Role.PRIMARY
? "Primary"
: `Channel: ${channel.index}`
]}
reset={() =>
reset({
enabled: [
Protobuf.Channel_Role.SECONDARY,
Protobuf.Channel_Role.PRIMARY
].find((role) => role === channel?.role)
? true
: false,
...channel?.settings,
psk: fromByteArray(channel?.settings?.psk ?? new Uint8Array(0))
})
}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
{channel?.index !== 0 && (
<>
<Controller

View File

@@ -71,10 +71,6 @@ export const Bluetooth = (): JSX.Element => {
return (
<Form
title="Bluetooth Config"
breadcrumbs={["Config", "Bluetooth"]}
reset={() => reset(config.bluetooth)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Controller

View File

@@ -64,10 +64,6 @@ export const Device = (): JSX.Element => {
return (
<Form
title="Device Config"
breadcrumbs={["Config", "Device"]}
reset={() => reset(config.device)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Select

View File

@@ -7,7 +7,7 @@ import { toast } from "react-hot-toast";
import { Input } from "@app/components/form/Input.js";
import { Select } from "@app/components/form/Select.js";
import { Toggle } from "@app/components/form/Toggle.js";
import { DisplayValidation } from "@app/validation/config/display.js";
import type { DisplayValidation } from "@app/validation/config/display.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
@@ -22,54 +22,48 @@ export const Display = (): JSX.Element => {
formState: { errors, isDirty },
reset,
control
} = useForm<Protobuf.Config_DisplayConfig>({
defaultValues: config.display,
resolver: classValidatorResolver(DisplayValidation)
} = useForm<DisplayValidation>({
defaultValues: config.display
// resolver: classValidatorResolver(DisplayValidation)
});
useEffect(() => {
reset(config.display);
}, [reset, config.display]);
// useEffect(() => {
// reset(config.display);
// }, [reset, config.display]);
const onSubmit = handleSubmit((data) => {
if (connection) {
void toast.promise(
connection
.setConfig(
new Protobuf.Config({
payloadVariant: {
case: "display",
value: data
}
})
)
.then(() =>
setConfig(
new Protobuf.Config({
payloadVariant: {
case: "display",
value: data
}
})
)
),
{
loading: "Saving...",
success: "Saved Display Config, Restarting Node",
error: "No response received"
}
);
}
// if (connection) {
// void toast.promise(
// connection
// .setConfig(
// new Protobuf.Config({
// payloadVariant: {
// case: "display",
// value: data
// }
// })
// )
// .then(() =>
// setConfig(
// new Protobuf.Config({
// payloadVariant: {
// case: "display",
// value: data
// }
// })
// )
// ),
// {
// loading: "Saving...",
// success: "Saved Display Config, Restarting Node",
// error: "No response received"
// }
// );
// }
});
return (
<Form
title="Display Config"
breadcrumbs={["Config", "Display"]}
reset={() => reset(config.display)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Input
label="Screen Timeout"
description="Turn off the display after this long"

View File

@@ -72,10 +72,6 @@ export const LoRa = (): JSX.Element => {
return (
<Form
title="LoRa Config"
breadcrumbs={["Config", "LoRa"]}
reset={() => reset(config.lora)}
dirty={isDirty}
onSubmit={onSubmit}
>
<FormSection title="Modem Settings">

View File

@@ -85,10 +85,6 @@ export const Network = (): JSX.Element => {
return (
<Form
title="Network Config"
breadcrumbs={["Config", "Network"]}
reset={() => reset(config.network)}
dirty={isDirty}
onSubmit={onSubmit}
>
<ErrorMessage errors={errors} name="wifiEnabled" />

View File

@@ -108,10 +108,6 @@ export const Position = (): JSX.Element => {
return (
<Form
title="Position Config"
breadcrumbs={["Config", "Position"]}
reset={() => reset(config.position)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Controller

View File

@@ -63,10 +63,6 @@ export const Power = (): JSX.Element => {
return (
<Form
title="Power Config"
breadcrumbs={["Config", "Power"]}
reset={() => reset(config.power)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Input

View File

@@ -62,16 +62,6 @@ export const User = (): JSX.Element => {
return (
<Form
title="User Config"
breadcrumbs={["Config", "User"]}
reset={() => {
reset({
longName: myNode?.data.user?.longName,
shortName: myNode?.data.user?.shortName,
isLicensed: myNode?.data.user?.isLicensed
});
}}
dirty={isDirty}
onSubmit={onSubmit}
>
<ErrorMessage errors={errors} name="longName" />

View File

@@ -63,13 +63,7 @@ export const Audio = (): JSX.Element => {
});
return (
<Form
title="Audio Config"
breadcrumbs={["Module Config", "Audio"]}
reset={() => reset(moduleConfig.audio)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Controller
name="codec2Enabled"
control={control}

View File

@@ -69,13 +69,7 @@ export const CannedMessage = (): JSX.Element => {
});
return (
<Form
title="Canned Message Config"
breadcrumbs={["Module Config", "Canned Message"]}
reset={() => reset(moduleConfig.cannedMessage)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Controller
name="enabled"
control={control}

View File

@@ -66,13 +66,7 @@ export const ExternalNotification = (): JSX.Element => {
});
return (
<Form
title="External Notification Config"
breadcrumbs={["Module Config", "External Notification"]}
reset={() => reset(moduleConfig.externalNotification)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Controller
name="enabled"
control={control}

View File

@@ -67,13 +67,7 @@ export const MQTT = (): JSX.Element => {
});
return (
<Form
title="MQTT Config"
breadcrumbs={["Module Config", "MQTT"]}
reset={() => reset(moduleConfig.mqtt)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Controller
name="enabled"
control={control}

View File

@@ -68,10 +68,6 @@ export const RangeTest = (): JSX.Element => {
return (
<Form
title="Range Test Config"
breadcrumbs={["Module Config", "Range Test"]}
reset={() => reset(moduleConfig.rangeTest)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Controller

View File

@@ -69,13 +69,7 @@ export const Serial = (): JSX.Element => {
});
return (
<Form
title="Serial Config"
breadcrumbs={["Module Config", "Serial"]}
reset={() => reset(moduleConfig.serial)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Controller
name="enabled"
control={control}

View File

@@ -67,13 +67,7 @@ export const StoreForward = (): JSX.Element => {
});
return (
<Form
title="Store & Forward Config"
breadcrumbs={["Module Config", "Store & Forward"]}
reset={() => reset(moduleConfig.storeForward)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Controller
name="enabled"
control={control}

View File

@@ -61,13 +61,7 @@ export const Telemetry = (): JSX.Element => {
});
return (
<Form
title="Telemetry Config"
breadcrumbs={["Module Config", "Telemetry"]}
reset={() => reset(moduleConfig.telemetry)}
dirty={isDirty}
onSubmit={onSubmit}
>
<Form onSubmit={onSubmit}>
<Controller
name="environmentMeasurementEnabled"
control={control}

View File

@@ -21,10 +21,7 @@ export const DeviceWidget = ({
}: DeviceWidgetProps): JSX.Element => {
return (
<div className="relative flex shrink-0 flex-col overflow-hidden rounded-md text-sm text-textPrimary">
<div className="absolute bottom-20 h-full w-full">
<Hashicon size={350} value={nodeNum} />
</div>
<div className="backdrop-brightness-50 flex p-3 backdrop-blur-md backdrop-hue-rotate-30">
<div className="bg-backgroundPrimary flex p-3">
<div>
<Hashicon size={96} value={nodeNum} />
</div>

View File

@@ -16,7 +16,7 @@ export const Button = ({
}: ButtonProps): JSX.Element => {
return (
<button
className={`flex w-full rounded-md bg-accentMuted px-3 text-textPrimary hover:brightness-hover focus:outline-none active:brightness-press ${
className={`flex w-full select-none rounded-md bg-accentMuted px-3 text-textPrimary hover:brightness-hover focus:outline-none active:brightness-press ${
size === "sm"
? "h-8 text-sm"
: size === "md"

View File

@@ -1,73 +1,22 @@
import type React from "react";
import type { HTMLProps } from "react";
import { Button } from "@components/form/Button.js";
import {
ArrowRightCircleIcon,
ArrowUturnLeftIcon,
CheckIcon,
ChevronRightIcon,
HomeIcon
} from "@heroicons/react/24/outline";
export interface FormProps extends HTMLProps<HTMLFormElement> {
title: string;
breadcrumbs: string[];
reset: () => void;
onSubmit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
dirty: boolean;
}
export const Form = ({
title,
breadcrumbs,
reset,
dirty,
children,
onSubmit,
...props
}: FormProps): JSX.Element => {
return (
// eslint-disable-next-line @typescript-eslint/no-misused-promises
<form className="w-full px-2" onSubmit={onSubmit} {...props}>
<div className="select-none rounded-md bg-backgroundPrimary p-4">
<ol className="flex gap-4 text-textSecondary">
<li className="cursor-pointer hover:brightness-disabled">
<HomeIcon className="h-5 w-5 flex-shrink-0" />
</li>
{breadcrumbs.map((breadcrumb, index) => (
<li key={index} className="flex gap-4">
<ChevronRightIcon className="h-5 w-5 flex-shrink-0 brightness-disabled" />
<span className="cursor-pointer text-sm font-medium hover:brightness-disabled">
{breadcrumb}
</span>
</li>
))}
</ol>
<div className="mt-2 flex items-center">
<h2 className="text-3xl font-bold tracking-tight text-textPrimary">
{title}
</h2>
<div className="ml-auto flex gap-2">
<Button
type="button"
onClick={() => {
reset();
}}
iconBefore={<ArrowUturnLeftIcon className="w-4" />}
>
Reset
</Button>
<Button
disabled={!dirty}
iconBefore={<CheckIcon className="w-4" />}
>
Save
</Button>
</div>
</div>
</div>
<div className="flex flex-col gap-3 p-2">{children}</div>
<form
className="mr-2 w-full rounded-md bg-backgroundSecondary px-2"
onSubmit={onSubmit}
{...props}
>
<div className="flex flex-col gap-3 p-4">{children}</div>
</form>
);
};

View File

@@ -28,7 +28,7 @@ export const TabbedContent = ({
actions
}: TabbedContentProps): JSX.Element => {
return (
<Tab.Group as="div" className="flex flex-grow flex-col gap-2">
<Tab.Group as="div" className="flex flex-grow flex-col">
<Tab.List className="flex bg-backgroundPrimary">
{tabs.map((entry, index) => (
<Tab key={index} disabled={entry.disabled}>

View File

@@ -11,6 +11,9 @@ import { Power } from "@components/PageComponents/Config/Power.js";
import { User } from "@components/PageComponents/Config/User.js";
import { useDevice } from "@core/providers/useDevice.js";
import { Tab } from "@headlessui/react";
import { ChevronRightIcon, HomeIcon } from "@heroicons/react/24/outline";
import { Button } from "@app/components/form/Button.js";
import { CheckIcon } from "@primer/octicons-react";
export const DeviceConfig = (): JSX.Element => {
const { hardware } = useDevice();
@@ -52,31 +55,53 @@ export const DeviceConfig = (): JSX.Element => {
];
return (
<Tab.Group as="div" className="flex w-full gap-3">
<Tab.List className="flex w-44 flex-col gap-1">
{configSections.map((Config, index) => (
<Tab key={index} as={Fragment}>
{({ selected }) => (
<div
className={`flex cursor-pointer items-center rounded-md px-3 py-2 text-sm font-medium ${
selected
? "bg-gray-100 text-gray-900"
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900"
}`}
>
{Config.label}
</div>
)}
</Tab>
))}
</Tab.List>
<Tab.Panels as={Fragment}>
{configSections.map((Config, index) => (
<Tab.Panel key={index} as={Fragment}>
<Config.element />
</Tab.Panel>
))}
</Tab.Panels>
</Tab.Group>
<div className="w-full">
<div className="m-2 flex rounded-md bg-backgroundSecondary p-2">
<ol className="my-auto ml-2 flex gap-4 text-textSecondary">
<li className="cursor-pointer hover:brightness-disabled">
<HomeIcon className="h-5 w-5 flex-shrink-0" />
</li>
{["Config", "User"].map((breadcrumb, index) => (
<li key={index} className="flex gap-4">
<ChevronRightIcon className="h-5 w-5 flex-shrink-0 brightness-disabled" />
<span className="cursor-pointer text-sm font-medium hover:brightness-disabled">
{breadcrumb}
</span>
</li>
))}
</ol>
<div className="ml-auto">
<Button iconBefore={<CheckIcon className="w-4" />}>Save</Button>
</div>
</div>
<Tab.Group as="div" className="flex w-full gap-3">
<Tab.List className="flex w-44 flex-col">
{configSections.map((Config, index) => (
<Tab key={index} as={Fragment}>
{({ selected }) => (
<div
className={`flex cursor-pointer items-center border-l-4 p-4 text-sm font-medium ${
selected
? "border-accent bg-accentMuted bg-opacity-10 text-textPrimary"
: "border-backgroundPrimary text-textSecondary"
}`}
>
{Config.label}
<span className="ml-auto bg-accent rounded-full px-3 text-textPrimary">3</span>
</div>
)}
</Tab>
))}
</Tab.List>
<Tab.Panels as={Fragment}>
{configSections.map((Config, index) => (
<Tab.Panel key={index} as={Fragment}>
<Config.element />
</Tab.Panel>
))}
</Tab.Panels>
</Tab.Group>
</div>
);
};