Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4e672e3
Added stateBinding variable to update when a slice state updates so c…
NexInfinite Mar 14, 2026
6cad1b2
Added a simple UI, removed old HTML encoder/decoder
NexInfinite Mar 15, 2026
9e8355f
Updated UI and fixed bugs with changing a auto completion value
NexInfinite Mar 15, 2026
ad76fe2
Simplified applying auto bidnings to reduce duplicate code and added …
NexInfinite Mar 15, 2026
50b686e
Added gps statelite, gps hdop, fix type, ekf healthy, compass healthy…
NexInfinite Mar 22, 2026
6a327dc
Merge branch 'main' into 1034-initial-design-for-autocomplete-checklist
NexInfinite Mar 22, 2026
c73dc79
stash
NexInfinite Mar 22, 2026
e64fdd5
formatted
NexInfinite Mar 22, 2026
802e877
Merge remote-tracking branch 'refs/remotes/origin/1034-initial-design…
NexInfinite Mar 22, 2026
b8a4a44
Fixed copilot suggestions
NexInfinite Mar 22, 2026
0563d77
Formatted
NexInfinite Mar 22, 2026
1aa361c
Merge branch 'main' into 1034-initial-design-for-autocomplete-checklist
NexInfinite Mar 22, 2026
730ecc4
Update gcs/src/redux/slices/checklistSlice.js
NexInfinite Mar 22, 2026
2af4e50
Fixed hardcoded value
NexInfinite Mar 22, 2026
69f9c2c
Update gcs/src/components/dashboard/preFlightChecklist/checkListEdit.jsx
NexInfinite Mar 22, 2026
b43f373
Fixed linter and removed vibe checks
NexInfinite Mar 22, 2026
0e5cdc0
Merge remote-tracking branch 'refs/remotes/origin/1034-initial-design…
NexInfinite Mar 22, 2026
4fa55a1
Merge branch 'main' into 1034-initial-design-for-autocomplete-checklist
NexInfinite Mar 22, 2026
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
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"falconred",
"frametype",
"frametypename",
"hdop",
"maplibre",
"maptilers",
"mavutil",
Expand Down
102 changes: 32 additions & 70 deletions gcs/src/components/dashboard/preFlightChecklist/checkListArea.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

// Native imports
import { useEffect, useState } from "react"
import { useState } from "react"

// 3rd Party Imports
import { ActionIcon, Button, Checkbox, Modal, Tooltip } from "@mantine/core"
Expand All @@ -16,7 +16,6 @@ import {

// Local Imports
import EditCheckList from "./checkListEdit.jsx"
import { generateCheckListObjectFromHTMLString } from "../../../helpers/checkList.js"

// Redux
import { useDispatch, useSelector } from "react-redux"
Expand All @@ -32,59 +31,32 @@ export default function CheckListArea({ id }) {

const [showDeleteModal, setDeleteModal] = useState(false)
const [editCheckListModal, setEditCheckListModal] = useState(false)
const [checkBoxListString, setCheckboxListString] = useState(
generateCheckboxListString(),
)
const [mappedItems, setMappedItems] = useState(generateMappedItems())
const [lastToggleCheck, setLastToggleCheck] = useState(false) // false = uncheck, true = check

function generateCheckboxListString(set = false) {
// Go from list to string, returns0
let final = "<ul>"
checklist.value.map((element) => {
final += "<li><p>" + element.name + "</p></li>"
})
function toggleCheck() {
const updated = checklist.value.map((item) =>
item.stateBinding ? item : { ...item, checked: lastToggleCheck },
)

final += "</ul>"
if (set) {
setCheckboxListString(final)
if (JSON.stringify(checklist.value) !== JSON.stringify(updated)) {
dispatch(setChecklistValueById({ id: checklist.id, value: updated }))
}

return final
}

function generateCheckboxList(defaultCheck = false) {
let final = generateCheckListObjectFromHTMLString(
checkBoxListString,
defaultCheck,
)
dispatch(setChecklistValueById({ id: checklist.id, value: final }))
}

function toggleCheck() {
generateCheckboxList(lastToggleCheck)
setLastToggleCheck(!lastToggleCheck)
}
Comment thread
NexInfinite marked this conversation as resolved.

function setChecked(name, value) {
let final = []
checkBoxListString
.split("<li><p>")
.splice(1)
.forEach((element) => {
let elementName = element.split("</p>")[0].trim()
final.push({
checked:
elementName === name
? value
: checklist.value.find((e) => e.name === elementName).checked,
name: elementName,
})
})
const updated = checklist.value.map((item) => {
if (item.name !== name || item.stateBinding) {
return item
}

Comment thread
NexInfinite marked this conversation as resolved.
return { ...item, checked: value }
})

// Check our checklist value is not the same as the updated one to stop unnecessary redux calls
if (JSON.stringify(checklist.value) !== JSON.stringify(final)) {
dispatch(setChecklistValueById({ id: checklist.id, value: final }))
if (JSON.stringify(checklist.value) !== JSON.stringify(updated)) {
dispatch(setChecklistValueById({ id: checklist.id, value: updated }))
}
}

Expand All @@ -106,26 +78,6 @@ export default function CheckListArea({ id }) {
URL.revokeObjectURL(objectUrl)
}

function generateMappedItems() {
return checklist.value.map((element) => {
return (
<Checkbox
checked={element.checked}
key={element.name}
label={element.name}
onChange={() => setChecked(element.name, !element.checked)}
/>
)
})
}

useEffect(() => {
setMappedItems(generateMappedItems())
dispatch(
setChecklistValueById({ id: checklist.id, value: checklist.value }),
)
}, [checklist.value])

return (
<>
{/* Checkbox area */}
Expand Down Expand Up @@ -165,18 +117,28 @@ export default function CheckListArea({ id }) {
</div>
</div>

{mappedItems}
{checklist.value.map((item, index) => (
<Tooltip
label="Auto completing field, this can't be manually checked"
disabled={item.stateBinding == null}
openDelay={500}
key={`${item.name}-${index}`}
>
<Checkbox
checked={item.checked}
key={`${item.name}-${index}`}
label={item.name}
onChange={() => setChecked(item.name, !item.checked)}
Comment thread
NexInfinite marked this conversation as resolved.
/>
</Tooltip>
))}
</div>

{/* Edit mode */}
<EditCheckList
passedName={checklist.name}
checklist={checklist}
opened={editCheckListModal}
close={() => setEditCheckListModal(false)}
checklistId={checklist.id}
checkListSet={[checkBoxListString, setCheckboxListString]}
generateCheckboxListString={generateCheckboxListString}
generateCheckboxList={generateCheckboxList}
/>

{/* Generic "are you sure" modal */}
Expand Down
206 changes: 120 additions & 86 deletions gcs/src/components/dashboard/preFlightChecklist/checkListEdit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,82 @@
*/

// Native Imports
import { useState } from "react"
import { useEffect, useState } from "react"

// 3rd Party Imports
import { Button, Modal, TextInput } from "@mantine/core"
import { useEditor } from "@tiptap/react"
import BulletList from "@tiptap/extension-bullet-list"
import ListItem from "@tiptap/extension-list-item"
import { RichTextEditor } from "@mantine/tiptap"
import { Node } from "@tiptap/core"
import { ActionIcon, Button, Modal, TextInput, Select } from "@mantine/core"
import { IconPlus, IconTrashX } from "@tabler/icons-react"

// Helpers
import { CHECKLIST_AUTO_BINDING_OPTIONS } from "../../../helpers/checklistAutoBindings.js"

// Redux
import { useDispatch } from "react-redux"
import { setNewChecklistName } from "../../../redux/slices/checklistSlice.js"

export default function EditCheckList({
opened,
close,
passedName,
checklistId,
checkListSet,
generateCheckboxListString,
generateCheckboxList,
}) {
import {
setChecklistValueById,
setNewChecklistName,
} from "../../../redux/slices/checklistSlice.js"

export default function EditCheckList({ checklist, opened, close }) {
const dispatch = useDispatch()
const [name, setName] = useState(passedName)
const [checkboxList, setCheckboxList] = checkListSet

const Document = Node.create({
name: "doc",
topNode: true,
content: "list+",
})

const Paragraph = Node.create({
name: "paragraph",
group: "block",
content: "inline*",
parseHTML() {
return [{ tag: "p" }]
},
renderHTML({ HTMLAttributes }) {
return ["p", HTMLAttributes, 0]
},
})

const Text = Node.create({
name: "text",
group: "inline",
})

const editor = useEditor({
extensions: [Document, Text, Paragraph, BulletList, ListItem],
content: checkboxList,
onUpdate: ({ editor }) => {
setCheckboxList(editor.getHTML())
},
autofocus: "end",
})
const [name, setName] = useState(checklist.name)
const [items, setItems] = useState(checklist.value ?? [])

const autoChecklistBindingsOptions = CHECKLIST_AUTO_BINDING_OPTIONS.map(
(item) => ({
value: item.key,
label: item.label,
}),
)

useEffect(() => {
if (!opened) {
return
}

setName(checklist.name)
setItems(checklist.value ?? [])
}, [opened, checklist.id])

function updateItem(index, key, value) {
setItems((currentItems) =>
currentItems.map((item, itemIndex) => {
if (itemIndex !== index) {
return item
}

if (key === "stateBinding") {
const trimmedValue = typeof value === "string" ? value.trim() : ""
return {
...item,
stateBinding: trimmedValue === "" ? null : trimmedValue,
}
}

return { ...item, [key]: value }
}),
)
}

function removeItem(index) {
setItems((currentItems) =>
currentItems.filter((_, itemIndex) => itemIndex !== index),
)
}

function addItem() {
setItems((currentItems) => [
...currentItems,
{ name: "", checked: false, stateBinding: null },
])
}

function handleSave(event) {
event.preventDefault()
dispatch(setNewChecklistName({ id: checklist.id, newName: name }))
dispatch(setChecklistValueById({ id: checklist.id, value: items }))
close()
}

return (
<Modal
Expand All @@ -75,51 +93,67 @@ export default function EditCheckList({
size={"xl"}
centered
>
<form
onSubmit={(e) => {
e.preventDefault()
dispatch(setNewChecklistName({ id: checklistId, newName: name }))
generateCheckboxList()
close()
}}
>
<div className="flex flex-col gap-2">
<form onSubmit={handleSave}>
<div className="flex flex-col gap-4">
{/* Inputs */}
<h1>Name</h1>
<h1>Checklist Name</h1>
<TextInput
value={name}
onChange={(event) => setName(event.currentTarget.value)}
/>

<div>
<h1>Items</h1>
<h2 className="text-falcongrey-300 text-sm">
Bullet point list of items
</h2>
<div className="flex flex-col gap-3">
<div className="flex w-full gap-2">
<h1 className="w-1/2">List Item</h1>
<h1>Auto complete?</h1>
</div>
{items.map((item, index) => (
<div key={item.id} className="flex w-full gap-2">
<TextInput
value={item.name}
onChange={(event) =>
updateItem(index, "name", event.currentTarget.value)
}
className="w-1/2"
/>
<Select
placeholder="No auto completion"
data={autoChecklistBindingsOptions}
clearable
onChange={(value) => {
updateItem(index, "stateBinding", value)
updateItem(index, "checked", false)
}}
value={item.stateBinding ?? null}
className="grow"
/>
<ActionIcon
color="red"
variant="light"
type="button"
onClick={() => removeItem(index)}
className="self-stretch !h-auto"
>
<IconTrashX size={20} stroke={1.5} />
</ActionIcon>
</div>
))}

<Button
leftSection={<IconPlus size={16} />}
onClick={addItem}
type="button"
variant="light"
>
Add Item
</Button>
</div>
<RichTextEditor
editor={editor}
classNames={{ content: "!list-disc" }}
>
{/*
Going to keep this for future use with code blocks, no need to delete.
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.BulletList />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
*/}

<RichTextEditor.Content />
</RichTextEditor>

{/* Controls */}
<div className="w-full flex justify-between pt-2">
<Button
onClick={() => {
close()
generateCheckboxListString(true)
}}
onClick={close}
type="button"
variant="filled"
color={"red"}
>
Expand Down
Loading
Loading