Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement mesh wide config stepper #459

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
2e997fe
chore(meshconfig): implement WizardWrapper
selankon Sep 25, 2024
bbcdfe9
chore(meshconfig): implement useMeshWideConfigState
selankon Sep 25, 2024
eb57f55
chore(meshconfig): implement NodesListWrapper
selankon Oct 2, 2024
dedfb2f
chore(meshconfig): implement NodeInfoListItem
selankon Oct 3, 2024
3c69b2c
chore(meshconfig): fix mesh types
selankon Oct 3, 2024
5d2e014
chore(meshconfig): mock stepper types
selankon Oct 9, 2024
618671d
chore(meshconfig): implement lime config json parser
selankon Oct 10, 2024
3d91ec0
chore(meshconfig): implement entry edition
selankon Oct 10, 2024
e56ec34
chore(meshconfig): implement delete entry
selankon Oct 10, 2024
d63b6f3
chore(meshconfig): support list forms
selankon Oct 15, 2024
8b5b101
chore(meshconfig): implement add new section
selankon Oct 15, 2024
c7bcb4f
chore(meshconfig): implement add new config section
selankon Oct 15, 2024
9f20538
chore(meshconfig): implement add new list item
selankon Oct 15, 2024
c697949
chore(meshconfig): fix rebase
selankon Oct 25, 2024
c77a333
chore(meshconfig): implement add new configuration section
selankon Oct 25, 2024
112dec5
chore(meshconfig): bulk bugfix
selankon Oct 28, 2024
53e24dc
chore(meshconfig): fix form
selankon Oct 29, 2024
cf1a170
chore(meshconfig): fix form layout
selankon Oct 29, 2024
134a6b6
chore(meshconfig): fix form
selankon Oct 29, 2024
acb85e6
chore(meshconfig): implement is dirty
selankon Oct 30, 2024
b0c3e0a
chore(meshconfig): implement submit
selankon Oct 30, 2024
597a92f
chore(mesh-config): fix query keys
selankon Nov 4, 2024
9a17064
chore(mesh-config): implement api calls
selankon Nov 4, 2024
6941e61
chore(mesh-config): create standarized api call
selankon Nov 5, 2024
be47aa9
chore(mesh-config): reimplement parse community file
selankon Nov 5, 2024
c355afe
chore(mesh-config): mock base states
selankon Nov 8, 2024
ccfa68d
chore(mesh-config): refactor query keys
selankon Nov 8, 2024
15c3a54
chore(mesh-config): implement abort
selankon Nov 8, 2024
2465c17
chore(mesh-config): implement mutate new config
selankon Nov 8, 2024
75c569a
chore(mesh-config): improve mesh info messages
selankon Nov 8, 2024
cde4ed0
chore(mesh-config): implemented schedule safe rebot
selankon Nov 8, 2024
e83f702
chore(mesh-config): implemented confirm
selankon Nov 8, 2024
70903a1
chore(mesh-config): some bulkfix
selankon Nov 8, 2024
91c691c
chore(mesh-config): move common files
selankon Nov 12, 2024
18901be
chore(mesh-config): implement stepper states
selankon Nov 12, 2024
f1c8757
chore(mesh-config): implement applying
selankon Nov 12, 2024
f0e2bbc
chore(mesh-config): fixes key name
javierbrk Nov 21, 2024
a9b3ae7
chore(mesh-config): implement spanish translation
javierbrk Nov 21, 2024
ccd6cce
f_mc: fixes some text related to upgrade
javierbrk Nov 27, 2024
629ec2a
chore(mesh-config): fix parser to ignore \\t
selankon Dec 3, 2024
a3ded95
chore(mesh-config): fix full screen modal loading
selankon Dec 3, 2024
c035956
chore(mesh-config): fix message
selankon Dec 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
633 changes: 420 additions & 213 deletions i18n/es/messages.po

Large diffs are not rendered by default.

24 changes: 14 additions & 10 deletions plugins/lime-plugin-mesh-wide-config/src/components/Components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,27 @@ export const EditOrDelete = ({
onEdit,
onDelete,
}: {
onEdit: (e) => void;
onDelete: (e) => void;
onEdit?: (e) => void;
onDelete?: (e) => void;
}) => {
const runCb = (e, cb) => {
e.stopPropagation();
cb();
};
return (
<div className={"flex flex-row gap-3"}>
<EditIcon
className={"cursor-pointer"}
onClick={(e) => runCb(e, onEdit)}
/>
<BinIcon
className={"cursor-pointer"}
onClick={(e) => runCb(e, onDelete)}
/>
{!!onEdit && (
<EditIcon
className={"cursor-pointer"}
onClick={(e) => runCb(e, onEdit)}
/>
)}
{!!onDelete && (
<BinIcon
className={"cursor-pointer"}
onClick={(e) => runCb(e, onDelete)}
/>
)}
</div>
);
};
121 changes: 0 additions & 121 deletions plugins/lime-plugin-mesh-wide-config/src/components/ConfigSection.tsx

This file was deleted.

185 changes: 185 additions & 0 deletions plugins/lime-plugin-mesh-wide-config/src/components/FormEdit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { Trans, t } from "@lingui/macro";
import { useEffect, useState } from "preact/hooks";
import { Controller, useFormContext } from "react-hook-form";
import { v4 as uuidv4 } from "uuid";

import { useDisclosure } from "components/Modal/useDisclosure";
import { Button, ButtonProps } from "components/buttons/button";
import InputField from "components/inputs/InputField";
import { useToast } from "components/toast/toastProvider";

import { EditOrDelete } from "plugins/lime-plugin-mesh-wide-config/src/components/Components";
import {
AddNewSectionFormProps,
AddNewSectionModal,
} from "plugins/lime-plugin-mesh-wide-config/src/components/modals";
import { IMeshWideConfig } from "plugins/lime-plugin-mesh-wide-config/src/meshConfigTypes";

export const EditableField = ({
isList,
name,
}: {
isList: boolean;
name: string;
}) => {
const { control, setValue, watch, getValues } = useFormContext();

const value = watch(name);
// Hack to force re-render when the list changes
const [uniqueKeys, setUniqueKeys] = useState(
isList && value?.length ? value.map(() => uuidv4()) : []
);

const syncKeysWithValues = () => {
// Ensure uniqueKeys matches the length of value array
setUniqueKeys((keys) => [
...keys,
...Array(value.length - keys.length)
.fill(null)
.map(() => uuidv4()),
]);
};

const removeListItem = (index) => {
const updatedValues = value.filter((_, i) => i !== index);
setValue(name, updatedValues);
setUniqueKeys((keys) => keys.filter((_, i) => i !== index));
};

const addListItem = () => {
setValue(name, [...value, ""]);
setUniqueKeys((keys) => [...keys, uuidv4()]);
};

// Ensure the list has at least one item at the start
useEffect(() => {
if (isList && value.length === 0) {
setValue(name, [""]);
setUniqueKeys([uuidv4()]); // Reset keys for new list
} else if (isList) {
// Sync keys with values length on every render
syncKeysWithValues();
}
}, [isList, value, name, setValue]);

if (isList) {
return (
<div key={name} className={"flex flex-col gap-6"}>
{uniqueKeys.map((item, index) => (
<Controller
key={uniqueKeys[index]} // Use the unique key
control={control}
name={`${name}[${index}]`}
rules={{
minLength: {
value: 1,
message: t`Minimum length is 1`,
},
required: t`This field cannot be empty`,
}}
render={({ field, fieldState: { error } }) => {
return (
<div
className={
"flex flex-row justify-center align-items-center gap-4"
}
>
<InputField
id={`${name}[${index}]`}
className="w-100"
error={error?.message}
{...field}
/>
<EditOrDelete
onDelete={() => removeListItem(index)}
/>
</div>
);
}}
/>
))}
<AddElementButton onClick={addListItem} />
</div>
);
}

return (
<Controller
name={name}
control={control}
rules={{
minLength: {
value: 1,
message: t`Minimum length is 1`,
},
required: t`This field cannot be empty`,
}}
render={({ field, fieldState: { error } }) => (
<InputField
id={name}
label={<Trans>Value</Trans>}
className="w-100"
error={error?.message}
{...field}
/>
)}
/>
);
};

export const AddNewConfigSection = ({
sectionName,
}: {
sectionName?: string;
}) => {
const { watch, setValue } = useFormContext<IMeshWideConfig>();

const { open, onOpen, onClose } = useDisclosure();
const { showToast } = useToast();

const section = watch(sectionName);

const onSuccess = (data: AddNewSectionFormProps) => {
if (!sectionName) {
setValue(data.name, {});
} else {
let value: string | string[] = data.value;
if (data.isList) {
value = data.values;
}
setValue(sectionName, {
...section,
[data.name]: value,
});
}
onClose();
showToast({
text: <Trans>Added section {data.name}</Trans>,
});
};

return (
<>
<AddElementButton onClick={onOpen} />
<AddNewSectionModal
sectionName={sectionName}
isOpen={open}
onSuccess={onSuccess}
onClose={onClose}
/>
</>
);
};

export const AddElementButton = (props: ButtonProps) => {
return (
<div className="flex justify-center">
<Button
{...props}
className="flex items-center justify-center w-12 h-12 rounded-full border-2 border-gray-400 text-gray-400 hover:bg-gray-100 font-bold cursor-pointer"
>
+
</Button>
</div>
);
};
Loading
Loading