diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7229cda57..519424e1f 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -13,6 +13,7 @@
"RSSI",
"serialutil",
"SITL",
- "statustext"
+ "statustext",
+ "SUAS"
]
}
diff --git a/gcs/package.json b/gcs/package.json
index 252c734c6..823f1e1ed 100644
--- a/gcs/package.json
+++ b/gcs/package.json
@@ -27,14 +27,19 @@
"dependencies": {
"@headlessui/react": "2.1.4",
"@mantine/code-highlight": "^7.17.1",
- "@mantine/core": "^7.3.2",
- "@mantine/hooks": "^7.3.2",
+ "@mantine/core": "^7.17.3",
+ "@mantine/hooks": "^7.17.3",
"@mantine/notifications": "^7.4.0",
"@mantine/spotlight": "^7.15.3",
+ "@mantine/tiptap": "^7.17.3",
"@reduxjs/toolkit": "^2.2.7",
"@robloche/chartjs-plugin-streaming": "^3.1.0",
"@tabler/icons-react": "^2.44.0",
"@tailwindcss/container-queries": "^0.1.1",
+ "@tiptap/extension-link": "^2.11.6",
+ "@tiptap/pm": "^2.11.6",
+ "@tiptap/react": "^2.11.6",
+ "@tiptap/starter-kit": "^2.11.6",
"@tremor/react": "^3.12.1",
"@turf/turf": "^7.2.0",
"chart.js": "^4.4.2",
diff --git a/gcs/src/components/customMantineTheme.jsx b/gcs/src/components/customMantineTheme.jsx
index d44efe320..ee32fa6c1 100644
--- a/gcs/src/components/customMantineTheme.jsx
+++ b/gcs/src/components/customMantineTheme.jsx
@@ -27,4 +27,5 @@ export const CustomMantineTheme = createTheme({
tailwindColors.falcongrey[950],
],
},
+ cursorType: "pointer",
})
diff --git a/gcs/src/components/dashboard/preFlightChecklist/checkListArea.jsx b/gcs/src/components/dashboard/preFlightChecklist/checkListArea.jsx
new file mode 100644
index 000000000..51b33c977
--- /dev/null
+++ b/gcs/src/components/dashboard/preFlightChecklist/checkListArea.jsx
@@ -0,0 +1,203 @@
+/*
+ The Checklist area, this can be edited.
+*/
+
+// Native imports
+import { useEffect, useState } from "react"
+
+// 3rd Party Imports
+import { ActionIcon, Button, Checkbox, Modal, Tooltip } from "@mantine/core"
+
+// Local Imports
+import EditCheckList from "./checkListEdit.jsx"
+
+// Styling imports
+import resolveConfig from "tailwindcss/resolveConfig"
+import tailwindConfig from "../../../../tailwind.config.js"
+import { IconCheckbox, IconEdit, IconTrashX } from "@tabler/icons-react"
+const tailwindColors = resolveConfig(tailwindConfig).theme.colors
+
+export default function CheckListArea({
+ name,
+ items,
+ saveItems,
+ deleteChecklist,
+ setName,
+}) {
+ const [showDeleteModal, setDeleteModal] = useState(false)
+ const [editCheckListModal, setEditCheckListModal] = useState(false)
+ const [checkListName, setChecklistName] = useState(name)
+ const [checkBoxList, setCheckboxList] = useState(items)
+ 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
+ var final = "
"
+ checkBoxList.map((element) => {
+ final += "" + element.name + "
"
+ })
+
+ final += " "
+ if (set) {
+ setCheckboxListString(final)
+ }
+
+ return final
+ }
+
+ function generateCheckboxList(defaultCheck = false) {
+ // Go from string to list, does not return
+ console.log(checkBoxListString)
+ var final = []
+ checkBoxListString
+ .split("")
+ .splice(1)
+ .map((element) => {
+ var text = element.split("
")[0].trim()
+ if (text !== "") {
+ final.push({
+ checked: defaultCheck,
+ name: element.split("")[0].trim(),
+ })
+ }
+ })
+ setCheckboxList(final)
+ }
+
+ function toggleCheck() {
+ generateCheckboxList(lastToggleCheck)
+ setLastToggleCheck(!lastToggleCheck)
+ }
+
+ function setChecked(name, value) {
+ var final = []
+ checkBoxListString
+ .split("")
+ .splice(1)
+ .map((element) => {
+ var elementName = element.split("
")[0].trim()
+ final.push({
+ checked:
+ elementName == name
+ ? value
+ : checkBoxList.find((e) => e.name == elementName).checked,
+ name: elementName,
+ })
+ })
+ setCheckboxList(final)
+ }
+
+ function generateMappedItems() {
+ return checkBoxList.map((element) => {
+ return (
+ setChecked(element.name, !element.checked)}
+ />
+ )
+ })
+ }
+
+ useEffect(() => {
+ setMappedItems(generateMappedItems())
+ saveItems(checkBoxList)
+ }, [checkBoxList])
+
+ return (
+ <>
+ {/* Checkbox area */}
+
+
+
+
+ toggleCheck()}
+ >
+
+
+
+
+ setEditCheckListModal(true)}
+ >
+
+
+
+
+
+
+ setDeleteModal(true)}
+ >
+
+
+
+
+
+ {mappedItems}
+
+
+ {/* Edit mode */}
+ setEditCheckListModal(false)}
+ nameSet={[checkListName, setChecklistName, (e) => setName(e)]}
+ checkListSet={[checkBoxListString, setCheckboxListString]}
+ generateCheckboxListString={generateCheckboxListString}
+ generateCheckboxList={generateCheckboxList}
+ />
+
+ {/* Generic "are you sure" modal */}
+ setDeleteModal(false)}
+ title="Are you sure you want to delete this checklist?"
+ centered
+ styles={{
+ content: {
+ borderRadius: "0.5rem",
+ },
+ }}
+ withCloseButton={false}
+ >
+
+ setDeleteModal(false)}
+ >
+ No, cancel
+
+ deleteChecklist()}
+ data-autofocus
+ >
+ Yes, Continue
+
+
+
+ >
+ )
+}
diff --git a/gcs/src/components/dashboard/preFlightChecklist/checkListEdit.jsx b/gcs/src/components/dashboard/preFlightChecklist/checkListEdit.jsx
new file mode 100644
index 000000000..ec1f97d71
--- /dev/null
+++ b/gcs/src/components/dashboard/preFlightChecklist/checkListEdit.jsx
@@ -0,0 +1,136 @@
+/*
+ The modal to edit a checklist
+*/
+
+// 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"
+
+// Styling imports
+import resolveConfig from "tailwindcss/resolveConfig"
+import tailwindConfig from "../../../../tailwind.config.js"
+const tailwindColors = resolveConfig(tailwindConfig).theme.colors
+
+export default function EditCheckList({
+ opened,
+ close,
+ nameSet,
+ checkListSet,
+ generateCheckboxListString,
+ generateCheckboxList,
+}) {
+ const [name, setName, finaliseName] = nameSet // Finalise changes it in the selected accordion (ik annoying...)
+ 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",
+ })
+
+ return (
+ close()}
+ styles={{
+ content: {
+ borderRadius: "0.5rem",
+ },
+ }}
+ size={"xl"}
+ centered
+ >
+
+
+ )
+}
diff --git a/gcs/src/components/dashboard/resizableInfoBox.jsx b/gcs/src/components/dashboard/resizableInfoBox.jsx
index 24e2c7b77..a22d34c5c 100644
--- a/gcs/src/components/dashboard/resizableInfoBox.jsx
+++ b/gcs/src/components/dashboard/resizableInfoBox.jsx
@@ -34,7 +34,7 @@ export default function ResizableInfoBox(props) {
}}
className="h-full"
>
-
+
{props.children}
diff --git a/gcs/src/components/dashboard/tabsSection.jsx b/gcs/src/components/dashboard/tabsSection.jsx
index 002a971b3..6940e071d 100644
--- a/gcs/src/components/dashboard/tabsSection.jsx
+++ b/gcs/src/components/dashboard/tabsSection.jsx
@@ -11,6 +11,7 @@ import CameraTabsSection from "./tabsSectionTabs/cameraTabsSection"
import ActionTabsSection from "./tabsSectionTabs/actionTabsSection"
import MissionTabsSection from "./tabsSectionTabs/missionTabsSection"
import DataTabsSection from "./tabsSectionTabs/dataTabsSection"
+import PreFlightChecklistTab from "./tabsSectionTabs/preFlightChecklistSection"
export default function TabsSection({
connected,
@@ -22,16 +23,18 @@ export default function TabsSection({
displayedData,
setDisplayedData,
}) {
- const tabPadding = "pt-6"
+ const tabPadding = "pt-6 pb-4"
return (
-
+
Data
Actions
Mission
Camera
+ Pre-Flight Checklist
+
{/* Data */}
+
+ {/* Pre Flight Checklist */}
+
)
}
diff --git a/gcs/src/components/dashboard/tabsSectionTabs/preFlightChecklistSection.jsx b/gcs/src/components/dashboard/tabsSectionTabs/preFlightChecklistSection.jsx
new file mode 100644
index 000000000..288790f53
--- /dev/null
+++ b/gcs/src/components/dashboard/tabsSectionTabs/preFlightChecklistSection.jsx
@@ -0,0 +1,160 @@
+/**
+ * PreFlightCheckListSection
+ * Contains the preflight checklist
+ */
+
+// Native imports
+import { useState } from "react"
+
+// 3rd Party Imports
+import { Tabs, Accordion, Button, Modal, TextInput } from "@mantine/core"
+import { useLocalStorage } from "@mantine/hooks"
+
+// Local imports
+import CheckListArea from "../preFlightChecklist/checkListArea.jsx"
+import { showErrorNotification } from "../../../helpers/notification.js"
+
+// Styling imports
+import resolveConfig from "tailwindcss/resolveConfig"
+import tailwindConfig from "../../../../tailwind.config.js"
+import { AddCommand } from "../../spotlight/commandHandler.js"
+const tailwindColors = resolveConfig(tailwindConfig).theme.colors
+
+export default function PreFlightChecklistTab({ tabPadding }) {
+ const [preFlightChecklistItems, setPreFlightChecklistItems] = useLocalStorage(
+ { key: "preFlightCheckList", defaultValue: [] },
+ )
+ const [openChecklist, setOpenChecklist] = useLocalStorage({
+ key: "lastOpenedPreFlightCheckList",
+ defaultValue: "",
+ })
+
+ // New checklist
+ const [showNewChecklistModal, setNewChecklistModal] = useState(false)
+ const [newChecklistName, setNewChecklistName] = useState("")
+
+ function deleteChecklist(toDelete) {
+ var final = []
+ preFlightChecklistItems.map((element) => {
+ if (element != toDelete) {
+ final.push(element)
+ }
+ })
+ setPreFlightChecklistItems(final)
+ }
+
+ function createNewChecklist() {
+ if (newChecklistName !== "") {
+ preFlightChecklistItems.push({
+ name: newChecklistName,
+ value: [
+ {
+ checked: false,
+ name: "Your first item, press edit to add more!",
+ },
+ ],
+ })
+ setPreFlightChecklistItems(preFlightChecklistItems)
+ setOpenChecklist(newChecklistName)
+ setNewChecklistModal(false)
+ setNewChecklistName("")
+ return
+ }
+
+ // Show error message
+ showErrorNotification("Name cannot be empty")
+ }
+
+ // Add create new checklist as a spotlight command
+ AddCommand("new_preflight_checklist", () => setNewChecklistModal(true))
+
+ const items = preFlightChecklistItems.map((item) => (
+
setOpenChecklist(item.name)}
+ >
+ {item.name}
+
+ {
+ item.value = e
+ setPreFlightChecklistItems(preFlightChecklistItems)
+ }}
+ deleteChecklist={() => deleteChecklist(item)}
+ name={item.name}
+ setName={(e) => {
+ item.name = e
+ setOpenChecklist(e)
+ }}
+ />
+
+
+ ))
+
+ return (
+
+
+ {/* List, known issue of not opening the same list if name was changed but it's not worth it */}
+
+ {items}
+
+ {/* Controls */}
+
setNewChecklistModal(true)}
+ >
+ Add a new Checklist
+
+
+ {/* New checklist modal */}
+
setNewChecklistModal(false)}
+ title="New Checklist"
+ centered
+ styles={{
+ content: {
+ borderRadius: "0.5rem",
+ },
+ }}
+ >
+
+
+
+
+ )
+}
diff --git a/gcs/src/components/dashboard/telemetry.jsx b/gcs/src/components/dashboard/telemetry.jsx
index af7a30f65..3fc86de24 100644
--- a/gcs/src/components/dashboard/telemetry.jsx
+++ b/gcs/src/components/dashboard/telemetry.jsx
@@ -164,14 +164,13 @@ export default function TelemetrySection({
- {batteryData.map(battery => (
+ {batteryData.map((battery) => (
BATTERY{battery.id}
- {(battery.voltages
- ? battery.voltages[0] / 1000
- : 0
- ).toFixed(2)}
+ {(battery.voltages ? battery.voltages[0] / 1000 : 0).toFixed(
+ 2,
+ )}
V
diff --git a/gcs/src/components/error/errorBoundary.jsx b/gcs/src/components/error/errorBoundary.jsx
index ffffa8c33..fdaa9c33d 100644
--- a/gcs/src/components/error/errorBoundary.jsx
+++ b/gcs/src/components/error/errorBoundary.jsx
@@ -7,7 +7,7 @@ import React from "react"
// 3rd Party Imports
import { Button } from "@mantine/core"
-import { CodeHighlight } from '@mantine/code-highlight'
+import { CodeHighlight } from "@mantine/code-highlight"
export default function ErrorBoundaryFallback({ error }) {
return (
diff --git a/gcs/src/components/navbar.jsx b/gcs/src/components/navbar.jsx
index ab6d7bab7..b4b9a7012 100644
--- a/gcs/src/components/navbar.jsx
+++ b/gcs/src/components/navbar.jsx
@@ -259,12 +259,12 @@ export default function Navbar({ currentPage }) {
backgroundOpacity: 0.55,
blur: 3,
}}
- withCloseButton={false}
styles={{
content: {
borderRadius: "0.5rem",
},
}}
+ withCloseButton={false}
>