24 changed files with 5835 additions and 5184 deletions
File diff suppressed because it is too large
@ -0,0 +1,158 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import ApexChart from 'react-apexcharts'; |
||||
|
|
||||
|
import { Button } from './Button'; |
||||
|
|
||||
|
type DefaultDivProps = JSX.IntrinsicElements['div']; |
||||
|
|
||||
|
interface ISeries { |
||||
|
name: string; |
||||
|
data: { |
||||
|
x: string | Date; |
||||
|
y: number; |
||||
|
}[]; |
||||
|
} |
||||
|
|
||||
|
interface ChartProps extends DefaultDivProps { |
||||
|
title: string; |
||||
|
description: string; |
||||
|
hasMultipleSeries: boolean; |
||||
|
series: ISeries[]; |
||||
|
} |
||||
|
|
||||
|
export const Chart = ({ |
||||
|
title, |
||||
|
description, |
||||
|
hasMultipleSeries, |
||||
|
series, |
||||
|
...props |
||||
|
}: ChartProps): JSX.Element => { |
||||
|
const [activeSeries, setActiveSeries] = React.useState<ISeries>(series[0]); |
||||
|
return ( |
||||
|
<div |
||||
|
className="flex flex-col flex-auto text-white shadow-md dark bg-primaryDark rounded-3xl" |
||||
|
{...props} |
||||
|
> |
||||
|
<div className="flex items-center justify-between mx-10 mt-10"> |
||||
|
<div className="flex flex-col"> |
||||
|
<div className="mr-4 text-2xl font-semibold leading-7 tracking-tight md:text-3xl"> |
||||
|
{title} |
||||
|
</div> |
||||
|
<div className="font-medium text-gray-400">{description}</div> |
||||
|
</div> |
||||
|
{hasMultipleSeries && ( |
||||
|
<div className="flex space-x-2"> |
||||
|
{series.map((data, index) => ( |
||||
|
<Button |
||||
|
active={data.name === activeSeries.name} |
||||
|
key={index} |
||||
|
className="font-medium" |
||||
|
onClick={(): void => { |
||||
|
setActiveSeries(series[index]); |
||||
|
}} |
||||
|
> |
||||
|
{data.name} |
||||
|
</Button> |
||||
|
))} |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
<div className="h-80"> |
||||
|
<ApexChart |
||||
|
height="96%" |
||||
|
type="area" |
||||
|
options={{ |
||||
|
chart: { |
||||
|
animations: { |
||||
|
speed: 400, |
||||
|
animateGradually: { |
||||
|
enabled: false, |
||||
|
}, |
||||
|
}, |
||||
|
toolbar: { |
||||
|
show: false, |
||||
|
}, |
||||
|
zoom: { |
||||
|
enabled: false, |
||||
|
}, |
||||
|
}, |
||||
|
colors: ['#818CF8'], |
||||
|
dataLabels: { |
||||
|
enabled: false, |
||||
|
}, |
||||
|
fill: { |
||||
|
colors: ['#312E81'], |
||||
|
}, |
||||
|
grid: { |
||||
|
padding: { |
||||
|
top: 10, |
||||
|
left: 0, |
||||
|
right: 0, |
||||
|
}, |
||||
|
|
||||
|
xaxis: { |
||||
|
lines: { |
||||
|
show: false, |
||||
|
}, |
||||
|
}, |
||||
|
yaxis: { |
||||
|
lines: { |
||||
|
show: false, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
stroke: { |
||||
|
width: 2, |
||||
|
}, |
||||
|
tooltip: { |
||||
|
followCursor: true, |
||||
|
theme: 'dark', |
||||
|
x: { |
||||
|
format: 'MMM dd, yyyy', |
||||
|
}, |
||||
|
y: { |
||||
|
formatter: (value: number): string => `${value}`, |
||||
|
}, |
||||
|
}, |
||||
|
xaxis: { |
||||
|
axisBorder: { |
||||
|
show: false, |
||||
|
}, |
||||
|
axisTicks: { |
||||
|
show: false, |
||||
|
}, |
||||
|
crosshairs: { |
||||
|
stroke: { |
||||
|
color: '#475569', |
||||
|
dashArray: 0, |
||||
|
width: 2, |
||||
|
}, |
||||
|
}, |
||||
|
labels: { |
||||
|
style: { |
||||
|
colors: '#CBD5E1', |
||||
|
}, |
||||
|
}, |
||||
|
tooltip: { |
||||
|
enabled: false, |
||||
|
}, |
||||
|
type: 'datetime', |
||||
|
}, |
||||
|
yaxis: { |
||||
|
axisTicks: { |
||||
|
show: false, |
||||
|
}, |
||||
|
axisBorder: { |
||||
|
show: false, |
||||
|
}, |
||||
|
show: false, |
||||
|
}, |
||||
|
}} |
||||
|
series={[activeSeries]} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,92 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Tab } from '@headlessui/react'; |
||||
|
|
||||
|
type DefaultDivProps = JSX.IntrinsicElements['div']; |
||||
|
|
||||
|
interface TabProps extends DefaultDivProps { |
||||
|
tabs: { |
||||
|
name: string; |
||||
|
body: JSX.Element; |
||||
|
}[]; |
||||
|
} |
||||
|
|
||||
|
export const Tabs = ({ tabs }: TabProps) => { |
||||
|
// let [categories] = useState({
|
||||
|
// Recent: [
|
||||
|
// {
|
||||
|
// id: 1,
|
||||
|
// title: 'Does drinking coffee make you smarter?',
|
||||
|
// date: '5h ago',
|
||||
|
// commentCount: 5,
|
||||
|
// shareCount: 2,
|
||||
|
// },
|
||||
|
// {
|
||||
|
// id: 2,
|
||||
|
// title: "So you've bought coffee... now what?",
|
||||
|
// date: '2h ago',
|
||||
|
// commentCount: 3,
|
||||
|
// shareCount: 2,
|
||||
|
// },
|
||||
|
// ],
|
||||
|
// Popular: [
|
||||
|
// {
|
||||
|
// id: 1,
|
||||
|
// title: 'Is tech making coffee better or worse?',
|
||||
|
// date: 'Jan 7',
|
||||
|
// commentCount: 29,
|
||||
|
// shareCount: 16,
|
||||
|
// },
|
||||
|
// {
|
||||
|
// id: 2,
|
||||
|
// title: 'The most innovative things happening in coffee',
|
||||
|
// date: 'Mar 19',
|
||||
|
// commentCount: 24,
|
||||
|
// shareCount: 12,
|
||||
|
// },
|
||||
|
// ],
|
||||
|
// Trending: [
|
||||
|
// {
|
||||
|
// id: 1,
|
||||
|
// title: 'Ask Me Anything: 10 answers to your questions about coffee',
|
||||
|
// date: '2d ago',
|
||||
|
// commentCount: 9,
|
||||
|
// shareCount: 5,
|
||||
|
// },
|
||||
|
// {
|
||||
|
// id: 2,
|
||||
|
// title: "The worst advice we've ever heard about coffee",
|
||||
|
// date: '4d ago',
|
||||
|
// commentCount: 1,
|
||||
|
// shareCount: 2,
|
||||
|
// },
|
||||
|
// ],
|
||||
|
// })
|
||||
|
|
||||
|
return ( |
||||
|
<Tab.Group as="div"> |
||||
|
<Tab.List className="flex p-2 space-x-2 border shadow-md rounded-t-3xl dark:border-gray-600"> |
||||
|
{tabs.map((tab) => ( |
||||
|
<Tab |
||||
|
key={tab.name} |
||||
|
className={({ selected }) => `w-full text-lg font-medium`} |
||||
|
> |
||||
|
{tab.name} |
||||
|
</Tab> |
||||
|
))} |
||||
|
</Tab.List> |
||||
|
<Tab.Panels> |
||||
|
{tabs.map((tab, index) => ( |
||||
|
<Tab.Panel |
||||
|
key={index} |
||||
|
className={ |
||||
|
'border dark:border-gray-600 rounded-b-3xl p-2 h-80 shadow-md' |
||||
|
} |
||||
|
> |
||||
|
{tab.body} |
||||
|
</Tab.Panel> |
||||
|
))} |
||||
|
</Tab.Panels> |
||||
|
</Tab.Group> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,51 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Switch } from '@headlessui/react'; |
||||
|
|
||||
|
type DefaultButtonProps = JSX.IntrinsicElements['button']; |
||||
|
|
||||
|
interface ToggleProps extends DefaultButtonProps { |
||||
|
label?: string; |
||||
|
valid?: boolean; |
||||
|
validationMessage?: string; |
||||
|
} |
||||
|
|
||||
|
export const Toggle = ({ |
||||
|
label, |
||||
|
valid, |
||||
|
validationMessage, |
||||
|
id, |
||||
|
...props |
||||
|
}: ToggleProps): JSX.Element => { |
||||
|
const [enabled, setEnabled] = React.useState(false); |
||||
|
|
||||
|
return ( |
||||
|
<div className="w-full"> |
||||
|
<label htmlFor={id} className="block text-sm font-medium dark:text-white"> |
||||
|
{label} |
||||
|
</label> |
||||
|
<div className="float-right"> |
||||
|
<Switch |
||||
|
id={id} |
||||
|
{...props} |
||||
|
checked={enabled} |
||||
|
onChange={setEnabled} |
||||
|
className={`${ |
||||
|
enabled ? 'bg-primary' : 'bg-gray-200 dark:bg-primaryDark' |
||||
|
} |
||||
|
relative inline-flex flex-shrink-0 h-[38px] w-[74px] border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`}
|
||||
|
> |
||||
|
<span className="sr-only">Use setting</span> |
||||
|
<span |
||||
|
aria-hidden="true" |
||||
|
className={`${enabled ? 'translate-x-9' : 'translate-x-0'} |
||||
|
pointer-events-none inline-block h-[34px] w-[34px] rounded-full bg-white shadow-lg transform ring-0 transition ease-in-out duration-200`}
|
||||
|
/> |
||||
|
</Switch> |
||||
|
</div> |
||||
|
{!valid && ( |
||||
|
<div className="text-sm text-gray-600">{validationMessage}</div> |
||||
|
)} |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,72 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { useForm } from 'react-hook-form'; |
||||
|
import { useTranslation } from 'react-i18next'; |
||||
|
|
||||
|
import { Tabs } from '@app/components/generic/Tabs'; |
||||
|
import { connection } from '@app/core/connection'; |
||||
|
import { useAppSelector } from '@app/hooks/redux'; |
||||
|
import { Button } from '@components/generic/Button'; |
||||
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate'; |
||||
|
import { MenuIcon, SaveIcon } from '@heroicons/react/outline'; |
||||
|
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export interface ConnectionProps { |
||||
|
navOpen: boolean; |
||||
|
setNavOpen: React.Dispatch<React.SetStateAction<boolean>>; |
||||
|
} |
||||
|
|
||||
|
export const Connection = ({ |
||||
|
navOpen, |
||||
|
setNavOpen, |
||||
|
}: ConnectionProps): JSX.Element => { |
||||
|
const { t } = useTranslation(); |
||||
|
const user = useAppSelector((state) => state.meshtastic.user); |
||||
|
|
||||
|
const { register, handleSubmit, formState } = useForm<Protobuf.User>({ |
||||
|
defaultValues: user, |
||||
|
}); |
||||
|
|
||||
|
const onSubmit = handleSubmit((data) => { |
||||
|
void connection.setOwner(data); |
||||
|
}); |
||||
|
|
||||
|
return ( |
||||
|
<PrimaryTemplate |
||||
|
title="Connection" |
||||
|
tagline="Settings" |
||||
|
button={ |
||||
|
<Button |
||||
|
icon={<MenuIcon className="w-5 h-5" />} |
||||
|
onClick={(): void => { |
||||
|
setNavOpen(!navOpen); |
||||
|
}} |
||||
|
circle |
||||
|
/> |
||||
|
} |
||||
|
footer={ |
||||
|
<Button |
||||
|
className="px-10 ml-auto" |
||||
|
icon={<SaveIcon className="w-5 h-5" />} |
||||
|
disabled={!formState.isDirty} |
||||
|
active |
||||
|
border |
||||
|
> |
||||
|
{t('strings.save_changes')} |
||||
|
</Button> |
||||
|
} |
||||
|
> |
||||
|
<div className="w-full max-w-3xl md:max-w-xl"> |
||||
|
<form className="space-y-2" onSubmit={onSubmit}> |
||||
|
<Tabs |
||||
|
tabs={[ |
||||
|
{ name: 'HTTP', body: <div>HTTP</div> }, |
||||
|
{ name: 'Bluetooth', body: <div>BLE</div> }, |
||||
|
{ name: 'Serial', body: <div>SERIAL</div> }, |
||||
|
]} |
||||
|
/> |
||||
|
</form> |
||||
|
</div> |
||||
|
</PrimaryTemplate> |
||||
|
); |
||||
|
}; |
||||
File diff suppressed because it is too large
Loading…
Reference in new issue