Skip to content

Commit

Permalink
Added state for instances (add, edit and delete)
Browse files Browse the repository at this point in the history
  • Loading branch information
RicYaben committed Sep 3, 2022
1 parent 0a224aa commit 19a9b70
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 89 deletions.
11 changes: 6 additions & 5 deletions ui/src/components/table/Table.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "./Table.scss";

const Cell = ({ content }: { content: any }) => {
export const Cell = ({ content }: { content: any }) => {
return <td className="tableCell">{content}</td>;
};

Expand All @@ -20,7 +20,7 @@ const Headers = ({ headers }: { headers: any }) => {
);
};

const Row = ({ cells }: { cells: any }) => {
export const Row = ({ cells }: { cells: any }) => {
return (
<tr className="tableRow">
{cells.map((content: any, ind: Number) => {
Expand All @@ -30,22 +30,23 @@ const Row = ({ cells }: { cells: any }) => {
);
};

const Body = ({ rows }: { rows: any[] }) => {
const Body = ({ rows, children }: { rows: any[]; children: any[] }) => {
return (
<tbody>
{rows.map((cells: any[], ind: Number) => {
return <Row cells={cells} />;
})}
{children}
</tbody>
);
};

const Table = ({ data }: { data: any }) => {
export const Table = ({ data, rows }: { data: any; rows?: any }) => {
return (
<div className="table-container">
<table>
<Headers headers={data.headers} />
<Body rows={data.rows} />
<Body rows={data.rows}>{rows}</Body>
</table>
</div>
);
Expand Down
51 changes: 48 additions & 3 deletions ui/src/recoil/atoms/instances.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,51 @@
import { atom } from "recoil";
import { atom, atomFamily, selectorFamily } from "recoil";
import { Profile } from "./profiles";
import { recoilPersist } from "recoil-persist";

export const instances = atom({
key: "instancesList",
const { persistAtom } = recoilPersist();

export type Instance = {
id: number | undefined;
name: string;
host: string;
description: string;
profile: Profile | undefined;
};

export const instanceIds = atom<number[]>({
key: "instanceIds",
default: [],
effects_UNSTABLE: [persistAtom],
});

export const DefaultInstance = {
id: undefined,
name: "",
host: "",
description: "",
profile: undefined,
};

export const instances = atomFamily<Instance, number>({
key: "instance",
default: DefaultInstance,
effects_UNSTABLE: [persistAtom],
});

export const intanceFormErrors = atom({
key: "intanceFormErrors",
default: {} as { [key: string]: string },
});

export const intanceFormFieldErrors = selectorFamily({
key: "profileFormFieldErrors",
get:
(field: string) =>
({ get }) =>
get(intanceFormErrors)[field],
});

export const instanceFormFields = atom({
key: "instanceFormFields",
default: DefaultInstance as { [key: string]: any },
});
32 changes: 32 additions & 0 deletions ui/src/routes/instances/InstanceForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,38 @@ type InstanceFormProps = {
onSubmit: any;
};

export const InstanceFormFields = [
{
name: "name",
help: "Name of the instance",
fieldattrs: {
type: "text",
required: true,
placeholder: "Type a name for the instance",
},
},
{
name: "description",
help: "Instance description",
fieldattrs: {
type: "text",
required: false,
placeholder: "Add a short description for the instance",
as: "textarea",
},
},
{
name: "host",
help: "Host in where the instance can be reached. E.g.: localhost, 0.0.0.0, etc.",
readOnly: true,
fieldattrs: {
type: "text",
required: true,
placeholder: "localhost",
},
},
];

const InstanceForm = (props: InstanceFormProps) => {
// TODO: Perhaps repalce this in the future with an API call to get the fields and their structure
let fields = [
Expand Down
4 changes: 1 addition & 3 deletions ui/src/routes/instances/Instances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { InstancesTable } from "./InstancesTable";
import { InstancesUtils } from "./InstancesUtils";
import InstancesHeader from "./InstancesHeader";

const Page = "Instances";

/*
* This component is to create line charts.
* Enable and modify this component to add visual information regarding
Expand Down Expand Up @@ -38,7 +36,7 @@ const Instances = () => {
return (
<main>
<InstancesHeader view="Instances" />
<InstancesUtils view={Page} />
<InstancesUtils />
<SearchBar filter="instances" />
<InstancesTable />
</main>
Expand Down
130 changes: 91 additions & 39 deletions ui/src/routes/instances/InstancesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,24 @@ import {
} from "react-icons/ai";
import { BsArrowRepeat, BsGlobe } from "react-icons/bs";
import { FaNetworkWired } from "react-icons/fa";
import { useRecoilCallback, useRecoilState, useRecoilValue } from "recoil";
import { SimpleForm } from "../../components/forms/Form";
import { Pop } from "../../components/pop/Pop";
import Table from "../../components/table/Table";
import { Table, Row } from "../../components/table/Table";
import {
DeleteDropdownItem,
EditDropdownItem,
InteractionBadge,
OptionsDropdown,
} from "../../components/utils/Common";
import { getPage, InteractionOption } from "../../constants/globals";
import {
getPage,
InteractionOption,
InteractionOptions,
} from "../../constants/globals";
Instance,
instanceIds,
instances,
intanceFormFieldErrors,
} from "../../recoil/atoms/instances";
import { InstanceFormFields } from "./InstanceForm";

interface InstanceService {
name: string;
Expand All @@ -27,11 +33,34 @@ interface InstanceService {
running: boolean;
}

interface Instance {
name: string;
services: InstanceService[];
address?: string;
}
const EditInstance = ({ instance }: { instance: Instance }) => {
const pageName = "Instances";
// Get the page to set the icon
const page = getPage(pageName);

// Create a setter for the submit
const id = instance.id !== undefined ? instance.id : -1;
const onSubmit = useRecoilCallback(({ set }) => (instance: Instance) => {
set(instances(id), (prev) => ({ ...prev, ...instance }));
});

// Create the form with the default values as they currently are
const content = (
<SimpleForm
create={false}
defaultValues={instance}
errors={intanceFormFieldErrors}
onSubmit={onSubmit}
page={pageName}
fields={InstanceFormFields}
/>
);

// Get the form with the update tag
return (
<EditDropdownItem form={content} icon={page?.icon} title={"profile"} />
);
};

const ProxyInfoPop = ({ proxy }: { proxy: Number }) => {
return (
Expand Down Expand Up @@ -74,7 +103,13 @@ const InstanceRowInfoPop = ({ services }: { services: InstanceService[] }) => {
);
};

const InstanceRowInfo = ({ name, services }: Instance) => {
const InstanceRowInfo = ({
name,
services,
}: {
name: string;
services: InstanceService[];
}) => {
return (
<>
<div>{name}</div>
Expand All @@ -83,7 +118,13 @@ const InstanceRowInfo = ({ name, services }: Instance) => {
);
};

const InstanceRowAddress = ({ value }: { value?: string }) => {
const InstanceRowAddress = ({ id }: { id: number }) => {
const [instance, setter] = useRecoilState(instances(id));
const [value, setValue] = useState(instance.host);
const onClick = () => {
setter({ ...instance, host: value });
};

return (
<Col xs="8">
<InputGroup size="sm" className="address">
Expand All @@ -95,63 +136,74 @@ const InstanceRowAddress = ({ value }: { value?: string }) => {
aria-label="Instance's address"
aria-describedby="basic-addon2"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<Button variant="outline-secondary" id="button-addon2">
<Button
variant="outline-secondary"
id="button-addon2"
onClick={onClick}
>
<BsArrowRepeat />
</Button>
</InputGroup>
</Col>
);
};

const InstanceRowOptions = ({ name }: { name: string }) => {
const InstanceRowOptions = ({ instance }: { instance: Instance }) => {
const ids = useRecoilValue(instanceIds);

const deleteCallback = useRecoilCallback(
({ set }) =>
(id: number | undefined) => {
set(
instanceIds,
ids.filter((curr) => curr !== id)
);
}
);

const page = getPage("Instances");
const note =
"Instance services will be stopped and removed from the instance register.";
return (
<OptionsDropdown>
<EditInstance instance={instance} />
{page && (
<DeleteDropdownItem
onClick={() => {
return;
}}
page={page}
note={note}
name={name}
name={instance.name}
onClick={() => deleteCallback(instance.id)}
/>
)}
</OptionsDropdown>
);
};

const InstanceRow = ({ name, address, services }: Instance) => {
return [
<InstanceRowInfo name={name} services={services} />,
<InstanceRowAddress value={address} />,
<InstanceRowOptions name={name} />,
const InstanceRow = ({ id }: { id: number }) => {
const instance = useRecoilValue(instances(id));
const services: InstanceService[] = [];

const cells = [
<InstanceRowInfo name={instance.name} services={services} />,
<InstanceRowAddress id={id} />,
<InstanceRowOptions instance={instance} />,
];

return <Row cells={cells} />;
};

export const InstancesTable = () => {
const props: Instance = {
name: "Lab1",
address: "127.0.0.11",
services: [
{
name: "CoAP",
proxy: 5683,
interaction: InteractionOptions[0],
running: true,
},
],
};

const rows = [InstanceRow(props), InstanceRow(props)];
const insts = useRecoilValue(instanceIds);
const rows = insts.map((instance, index) => (
<InstanceRow key={index} id={instance} />
));

const data = {
headers: [`${rows.length} Instances`, "", ""],
rows: rows,
rows: [],
};

return <Table data={data} />;
return <Table data={data} rows={rows} />;
};
Loading

0 comments on commit 19a9b70

Please sign in to comment.