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