From 09a9a22bcff4c9d9ba00e6169f977d46671d626a Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 17 Jan 2024 18:54:16 +0100 Subject: [PATCH 1/2] Added Notes Feature --- package.json | 2 +- src-tauri/tauri.conf.json | 2 +- src/Icons/pack_1.tsx | 18 +++ src/Interfaces/IConfigFile.ts | 34 +++++ .../JobCardComponent/JobCardComponent.tsx | 4 +- src/helpers/StorageManager.ts | 141 +++++++++++++++--- .../Jarvis/Views/BuildView/BuildView.tsx | 34 ++--- .../BooleanParameterValue.tsx | 5 +- .../CredentialsParameterValue.tsx | 6 +- .../FileParameterValue.tsx | 6 +- .../ParameterComponents/OtherParameter.tsx | 6 +- .../PasswordParameterValue.tsx | 6 +- .../StringParameterValue.tsx | 6 +- .../TextParameterValue.tsx | 6 +- .../Jarvis/Views/StatusView/NotesModal.tsx | 125 ++++++++++++++++ .../Jarvis/Views/StatusView/StatusView.tsx | 12 +- .../Jarvis/Views/SwitchProjectView/Modal.tsx | 4 +- 17 files changed, 352 insertions(+), 65 deletions(-) create mode 100644 src/Interfaces/IConfigFile.ts create mode 100644 src/screens/Jarvis/Views/StatusView/NotesModal.tsx diff --git a/package.json b/package.json index f121826..d5c59cd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jarvis", "private": true, - "version": "0.9.7", + "version": "0.9.75", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ba20d7e..e0b1255 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -7,7 +7,7 @@ }, "package": { "productName": "Jarvis Client", - "version": "0.9.7" + "version": "0.9.75" }, "tauri": { "allowlist": { diff --git a/src/Icons/pack_1.tsx b/src/Icons/pack_1.tsx index 877ed06..6b4124a 100644 --- a/src/Icons/pack_1.tsx +++ b/src/Icons/pack_1.tsx @@ -321,3 +321,21 @@ export const IcoClose = ({ size = 24, color = "currentColor", className }: IconP ); + +// Icon Pack: Phosphor Icons +// License: MIT - Copyright (c) 2023 Phosphor Icons +// Used in: Params View +export const IcoNotes = ({ size = 24, color = "currentColor", className }: IconProps) => ( + + + + +); + +// Icon Pack: Phosphor Icons +// License: MIT - Copyright (c) 2023 Phosphor Icons +// Used in: Params View +export const IcoSave = ({ size = 24, color = "currentColor", className }: IconProps) => ( + + +); diff --git a/src/Interfaces/IConfigFile.ts b/src/Interfaces/IConfigFile.ts new file mode 100644 index 0000000..dc76269 --- /dev/null +++ b/src/Interfaces/IConfigFile.ts @@ -0,0 +1,34 @@ +export interface ConfigFile { + "baseurl" : string | undefined + "username" : string | undefined + "apiToken" : string | undefined + "onboardState" : string | undefined + "openInBrowser" : boolean | undefined + "pinnedJobs" : string[] | undefined + "projectName" : string | undefined + "notificationSetJobs" : string[] | undefined + "projects" : string[] | undefined + "titlebarStyle" : "winStyle" | "macStyle" | "eggStyle" | undefined + "notificationPermission" : string | undefined + "notes": Notes[] | undefined +} + +interface Notes { + [key: string]: { + [key: string]: string + } +} + +export type allowedKeys = + "baseurl" | + "username" | + "apiToken" | + "onboardState" | + "openInBrowser" | + "pinnedJobs" | + "projectName" | + "notificationSetJobs" | + "projects" | + "titlebarStyle" | + "notificationPermission" | + "notes"; \ No newline at end of file diff --git a/src/components/JobCardComponent/JobCardComponent.tsx b/src/components/JobCardComponent/JobCardComponent.tsx index a85df50..0a16010 100644 --- a/src/components/JobCardComponent/JobCardComponent.tsx +++ b/src/components/JobCardComponent/JobCardComponent.tsx @@ -44,9 +44,9 @@ const JobCardComponent: React.FC = ({ {pinned && } {notification_set && } -
+

{buildData.description} -

+

); diff --git a/src/helpers/StorageManager.ts b/src/helpers/StorageManager.ts index 7b86b7b..ff6d394 100644 --- a/src/helpers/StorageManager.ts +++ b/src/helpers/StorageManager.ts @@ -1,26 +1,15 @@ +import { BaseDirectory, createDir, exists, readTextFile, writeTextFile } from "@tauri-apps/api/fs"; import Logger from "./Logger"; +import { ConfigFile, allowedKeys } from "@/Interfaces/IConfigFile"; +import { CONFIG_FILE } from "@/config/constants"; -type allowedKeys = - "baseurl" | - "username" | - "apiToken" | - "onboardState" | - "openInBrowser" | - "pinnedJobs" | - "projectName" | - "notificationSetJobs" | - "projects" | - "titlebarStyle" | - "notificationPermission"; - -/* -const saveToConfigFile = async (content: ConfigFile, CONFIG_FILE: string) => { +const saveToConfigFile = async (content: ConfigFile) => { await writeTextFile(CONFIG_FILE, JSON.stringify(content), { dir: BaseDirectory.AppData }); }; -const getConfigFile = async (CONFIG_FILE: string): Promise => { +const getConfigFile = async (): Promise => { const appDataDirExists = await exists("", { dir: BaseDirectory.AppData }); if (!appDataDirExists) { @@ -28,14 +17,130 @@ const getConfigFile = async (CONFIG_FILE: string): Promise => { } if (await exists(CONFIG_FILE, { dir: BaseDirectory.AppData })) { + let content = await readTextFile(CONFIG_FILE, { dir: BaseDirectory.AppData }); + + // If the file is empty, return an empty object + if (content === "") return {} as ConfigFile; + // File exists, return it - return await JSON.parse(await readTextFile(CONFIG_FILE, { dir: BaseDirectory.AppData })); + return await JSON.parse(content); } else { // File doesnt exist, return empty object return {} as ConfigFile; } }; -*/ + +/** + * @classdesc A class to manage the storage of data in the browser. + * @note This is still a work in progress and will be adapated gradually. + */ +export const new_StorageManager = { + get: async (key: allowedKeys): Promise => { + try { + let configFile = await getConfigFile(); + + if (configFile[key] === undefined) return false; + + return configFile[key]; + } catch (error) { + Logger.error("helpers/StorageManager.ts", error); + return false; + } + }, + save: async (key: K, value: ConfigFile[K]): Promise => { + try { + let configFile = await getConfigFile(); + configFile[key] = value; + await saveToConfigFile(configFile); + + return true; + } catch (error) { + Logger.error("helpers/StorageManager.ts", error); + return false; + } + }, + removeItem: async (key: allowedKeys): Promise => { + try { + let configFile = await getConfigFile(); + delete configFile[key]; + await saveToConfigFile(configFile); + + return true; + } catch (error) { + Logger.error("helpers/StorageManager.ts", error); + return false; + } + }, + clearAll: async (): Promise => { + try { + await saveToConfigFile({} as ConfigFile); + return true; + } catch (error) { + Logger.error("helpers/StorageManager.ts", error); + return false; + } + }, + notes: { + get: async (project: string, job: string): Promise => { + try { + const configFile = await getConfigFile(); + + if (configFile.notes) { + const projectNotes = configFile.notes.find((note) => note[project]); + + if (projectNotes) { + return projectNotes[project][job]; + } + } + + return undefined; + } catch (error) { + Logger.error("helpers/StorageManager.ts", error); + return undefined; + } + }, + save: async (project: string, job: string, note: string): Promise => { + try { + const configFile = await getConfigFile(); + + // Ensure that the notes object and its nested properties are defined + if (!configFile.notes) { + configFile.notes = []; + Logger.debug("Empty notes array created") + } + + const projectIndex = configFile.notes.findIndex((note) => Object.keys(note)[0] === project); + if (!configFile.notes[projectIndex] || configFile.notes[projectIndex] === undefined || projectIndex === -1) { + configFile.notes[projectIndex] = {}; + Logger.debug("Empty project index created") + } + + if (projectIndex < 0) { + configFile.notes.push({ + [project]: { + [job]: note, + }, + }); + } else { + configFile.notes[projectIndex][project][job] = note; + } + + Logger.debug("Saving note", configFile.notes); + + await saveToConfigFile(configFile); + + return true; + } catch (error) { + Logger.error("helpers/StorageManager.ts", error); + return false; + } + }, + + + }, +}; + + /** * @classdesc A class to manage the storage of data in the browser. diff --git a/src/screens/Jarvis/Views/BuildView/BuildView.tsx b/src/screens/Jarvis/Views/BuildView/BuildView.tsx index a6ea470..2f46dcf 100644 --- a/src/screens/Jarvis/Views/BuildView/BuildView.tsx +++ b/src/screens/Jarvis/Views/BuildView/BuildView.tsx @@ -139,23 +139,23 @@ const BuildView: React.FC = ({ parameterDefinition, buildData }) => {
{getComponentForParameter(parameter)}
))} -
- - Start Build - - - BUILD STARTED - -
+
+ + Start Build + + + BUILD STARTED + +
); diff --git a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/BooleanParameterValue.tsx b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/BooleanParameterValue.tsx index c27c098..62d6d15 100644 --- a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/BooleanParameterValue.tsx +++ b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/BooleanParameterValue.tsx @@ -8,8 +8,8 @@ interface Props { const BooleanParameterValue: React.FC = ({ parameter }): React.JSX.Element => (
-
- +
+

{parameter.name}

{parameter.description}

@@ -23,7 +23,6 @@ const BooleanParameterValue: React.FC = ({ parameter }): React.JSX.Elemen value={parameter.value ? "✔" : ""} className="w-[30px] h-[30px] bg-property-background font-medium rounded-md text-comment-color px-2 mt-2 active:bg-property-background-selected hover:brightness-[1.3]" /> -
); diff --git a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/CredentialsParameterValue.tsx b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/CredentialsParameterValue.tsx index 9acfa96..daa2fb6 100644 --- a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/CredentialsParameterValue.tsx +++ b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/CredentialsParameterValue.tsx @@ -7,9 +7,9 @@ interface Props { } const CredentialsParameterValue: React.FC = ({ parameter }): React.JSX.Element => ( -
-
- +
+
+

{parameter.name}

{parameter.description}

diff --git a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/FileParameterValue.tsx b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/FileParameterValue.tsx index ff7a2f0..f0a57b8 100644 --- a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/FileParameterValue.tsx +++ b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/FileParameterValue.tsx @@ -25,9 +25,9 @@ const FileParameterValue: React.FC = ({ parameter, buildNumber }): React. }; return ( -
-
- +
+
+

{parameter.name}

{parameter.description}

diff --git a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/OtherParameter.tsx b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/OtherParameter.tsx index c18299c..f1ccef2 100644 --- a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/OtherParameter.tsx +++ b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/OtherParameter.tsx @@ -7,9 +7,9 @@ interface Props { } const OtherParameterValue: React.FC = ({ parameter }): React.JSX.Element => ( -
-
- +
+
+

{parameter.name}

{parameter.description}

diff --git a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/PasswordParameterValue.tsx b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/PasswordParameterValue.tsx index 36aeb35..083508b 100644 --- a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/PasswordParameterValue.tsx +++ b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/PasswordParameterValue.tsx @@ -7,9 +7,9 @@ interface Props { } const PasswordParameterValue: React.FC = ({ parameter }): JSX.Element => ( -
-
- +
+
+

{parameter.name}

{parameter.description}

diff --git a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/StringParameterValue.tsx b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/StringParameterValue.tsx index 9f53df9..1bd1b85 100644 --- a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/StringParameterValue.tsx +++ b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/StringParameterValue.tsx @@ -7,9 +7,9 @@ interface Props { } const StringParameterValue: React.FC = ({ parameter }): JSX.Element => ( -
-
- +
+
+

{parameter.name}

{parameter.description}

diff --git a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/TextParameterValue.tsx b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/TextParameterValue.tsx index 48b3d88..b69c2b5 100644 --- a/src/screens/Jarvis/Views/ParametersView/ParameterComponents/TextParameterValue.tsx +++ b/src/screens/Jarvis/Views/ParametersView/ParameterComponents/TextParameterValue.tsx @@ -7,9 +7,9 @@ interface Props { } const TextParameterValue: React.FC = ({ parameter }): JSX.Element => ( -
-
- +
+
+

{parameter.name}

{parameter.description}

diff --git a/src/screens/Jarvis/Views/StatusView/NotesModal.tsx b/src/screens/Jarvis/Views/StatusView/NotesModal.tsx new file mode 100644 index 0000000..7a7330c --- /dev/null +++ b/src/screens/Jarvis/Views/StatusView/NotesModal.tsx @@ -0,0 +1,125 @@ +import React, { useEffect } from "react"; +import { motion } from "framer-motion"; +import { IcoCross, IcoNotes, IcoSave } from "@/Icons/pack_1"; +import StorageManager, { new_StorageManager } from "@/helpers/StorageManager"; +import showdown from "showdown"; +import { IJenkinsBuild } from "@/Interfaces/IBuildInterface"; +import Logger from "@/helpers/Logger"; + +interface Props { + isOpen: boolean; + closeModal: () => void; + buildData: IJenkinsBuild; +} + +const NotesModal: React.FC = ({ isOpen, closeModal, buildData }) => { + const [notes, setNotes] = React.useState(""); + const [projectName, setProjectName] = React.useState(""); + const [inEditMode, setInEditMode] = React.useState(false); + const [notesData, setNotesData] = React.useState(""); + + const saveNotes = () => { + new_StorageManager.notes.save(projectName, buildData.id, notes); + return true; + } + + const renderMarkdown = (str: string): { __html: string } => { + const converter = new showdown.Converter(); + const html: string = converter.makeHtml(str); + return { __html: html }; + }; + + useEffect(() => { + const init = async () => { + const project = await StorageManager.get("projectName"); + if (!project) return; + setProjectName(project); + + const data = await new_StorageManager.notes.get(project, buildData.id); + + if (data) { + setNotesData(data); + setNotes(data); + } + + if (data === "" || data === undefined) { + setInEditMode(true); + } + } + + init(); + + Logger.info("NotesModal.tsx", "NotesModal mounted"); + + }, [buildData]); + + return (isOpen ? ( + closeModal()} > +
e.stopPropagation()} > +
closeModal()} + className="absolute top-4 right-4 cursor-pointer"> + +
+
+ {inEditMode ? ( + /* Edit Section */ +
+ +

Edit Notes for Build #{buildData.id}

+