From fa41baa8b6bb204f1182155aa266a2bedadf93aa Mon Sep 17 00:00:00 2001 From: seunghyunOh Date: Sun, 4 Dec 2022 09:58:51 +0900 Subject: [PATCH 1/3] Update dashboard component --- frontend/components/basic/buttons/Button.tsx | 48 ++++--- ...dInputField.js => DashboardInputField.tsx} | 128 ++++++++++-------- 2 files changed, 103 insertions(+), 73 deletions(-) rename frontend/components/dashboard/{DashboardInputField.js => DashboardInputField.tsx} (57%) diff --git a/frontend/components/basic/buttons/Button.tsx b/frontend/components/basic/buttons/Button.tsx index 40251be932..863dc4b48a 100644 --- a/frontend/components/basic/buttons/Button.tsx +++ b/frontend/components/basic/buttons/Button.tsx @@ -1,25 +1,28 @@ import React from "react"; import Image from "next/image"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome"; +import { + FontAwesomeIcon, + FontAwesomeIconProps, +} from "@fortawesome/react-fontawesome"; var classNames = require("classnames"); type ButtonProps = { text: string; onButtonPressed: () => void; - loading: boolean; + loading?: boolean; color: string; size: string; - icon: IconProp; - active: boolean; - iconDisabled: string; - textDisabled: string; -} + icon?: IconProp; + active?: boolean; + iconDisabled?: string; + textDisabled?: string; +}; /** * This is the main butto component in the app. - * @param {object} props + * @param {object} props * @param {string} props.text - text inside the button * @param {function} props.onButtonPressed - the action that happens when the button is clicked * @param {boolean} props.loading - if a button is currently in the laoding state @@ -29,21 +32,28 @@ type ButtonProps = { * @param {boolean} props.active - if the button is active or disabled * @param {FontAwesomeIconProps} props.text - the icon inside the button when it is disabled * @param {string} props.textDisable - text inside the button when it is disabled - * @returns + * @returns */ -export default function Button (props: ButtonProps): JSX.Element { +export default function Button(props: ButtonProps): JSX.Element { // Check if the button show always be 'active' - then true; // or if it should switch between 'active' and 'disabled' - then give the status - const activityStatus = props.active || (props.text != "" && props.textDisabled == undefined) - + const activityStatus = + props.active || (props.text != "" && props.textDisabled == undefined); + let styleButton = classNames( "group m-auto md:m-0 inline-block rounded-md duration-200", // Setting background colors and hover modes - props.color == "mineshaft" && activityStatus && "bg-mineshaft-700 hover:bg-primary", + props.color == "mineshaft" && + activityStatus && + "bg-mineshaft-700 hover:bg-primary", props.color == "mineshaft" && !activityStatus && "bg-mineshaft", - (props.color == "primary" || !props.color) && activityStatus && "bg-primary hover:opacity-80", - (props.color == "primary" || !props.color) && !activityStatus && "bg-primary", + (props.color == "primary" || !props.color) && + activityStatus && + "bg-primary hover:opacity-80", + (props.color == "primary" || !props.color) && + !activityStatus && + "bg-primary", props.color == "red" && "bg-red", // Changing the opacity when active vs when not @@ -73,7 +83,7 @@ export default function Button (props: ButtonProps): JSX.Element { "relative duration-200", // Show the loading sign if the loading indicator is on - props.loading == true ? "opacity-0" : "opacity-100", + Boolean(props.loading) ? "opacity-0" : "opacity-100", props.size == "md" && "text-sm", props.size == "lg" && "text-lg" ); @@ -100,7 +110,7 @@ export default function Button (props: ButtonProps): JSX.Element { {props.icon && ( )} {(props.text || props.textDisabled) && ( -

{activityStatus ? props.text : props.textDisabled}

+

+ {activityStatus ? props.text : props.textDisabled} +

)} diff --git a/frontend/components/dashboard/DashboardInputField.js b/frontend/components/dashboard/DashboardInputField.tsx similarity index 57% rename from frontend/components/dashboard/DashboardInputField.js rename to frontend/components/dashboard/DashboardInputField.tsx index 634591c073..2867927842 100644 --- a/frontend/components/dashboard/DashboardInputField.js +++ b/frontend/components/dashboard/DashboardInputField.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from "react"; +import React, { SyntheticEvent, UIEvent, useRef, WheelEvent } from "react"; import { faCircle } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -6,6 +6,15 @@ import guidGenerator from "../utilities/randomId"; const REGEX = /([$]{.*?})/g; +interface DashboardInputFieldProps { + index: number; + onChangeHandler: (value: string, index: number) => void; + value: string; + type: "varName" | "value"; + blurred: boolean; + duplicates: string[]; +} + /** * This component renders the input fields on the dashboard * @param {object} obj - the order number of a keyPair @@ -14,51 +23,60 @@ const REGEX = /([$]{.*?})/g; * @param {string} obj.type - whether the input field is for a Key Name or for a Key Value * @param {string} obj.value - value of the InputField * @param {boolean} obj.blurred - whether the input field should be blurred (behind the gray dots) or not; this can be turned on/off in the dashboard - * @param {string[]} obj.duplicates - list of all the suplicated key names on the dashboard + * @param {string[]} obj.duplicates - list of all the duplicated key names on the dashboard * @returns */ + const DashboardInputField = ({ index, onChangeHandler, type, value, blurred, - duplicates -}) => { - const ref = useRef(null); - const syncScroll = (e) => { - ref.current.scrollTop = e.target.scrollTop; - ref.current.scrollLeft = e.target.scrollLeft; + duplicates, +}: DashboardInputFieldProps) => { + const ref = useRef(null); + const syncScroll = (e: SyntheticEvent) => { + if (ref.current == null) return; + + ref.current.scrollTop = e.currentTarget.scrollTop; + ref.current.scrollLeft = e.currentTarget.scrollLeft; }; - + if (type === "varName") { - const startsWithNumber = !isNaN(value.charAt(0)) && value != ""; + const startsWithNumber = !isNaN(Number(value.charAt(0))) && value != ""; const hasDuplicates = duplicates?.includes(value); const error = startsWithNumber || hasDuplicates; return (
onChangeHandler(e.target.value.toUpperCase(), index)} + onChange={(e) => + onChangeHandler(e.target.value.toUpperCase(), index) + } type={type} value={value} - className={`asolute z-10 peer font-mono ph-no-capture bg-bunker-800 rounded-md caret-white text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 ${error ? "focus:ring-red/50" : "focus:ring-primary/50"} duration-200`} + className={`absolute z-10 peer font-mono ph-no-capture bg-bunker-800 rounded-md caret-white text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 ${ + error ? "focus:ring-red/50" : "focus:ring-primary/50" + } duration-200`} spellCheck="false" />
- {startsWithNumber && + {startsWithNumber && (

Should not start with a number

- } - {hasDuplicates && !startsWithNumber && + )} + {hasDuplicates && !startsWithNumber && (

Secret names should be unique

- } + )}
); } else if (type === "value") { @@ -78,61 +96,61 @@ const DashboardInputField = ({ } z-10 peer font-mono ph-no-capture bg-transparent rounded-md caret-white text-transparent text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-200 no-scrollbar no-scrollbar::-webkit-scrollbar`} spellCheck="false" /> -
- { - value - .split(REGEX) - .map((word, id) => { - if (word.match(REGEX) !== null) { - return ( - - {word.slice(0, 2)} - - {word.slice(2, word.length - 1)} - - {word.slice(word.length - 1, word.length) == "}" ? ( - - {word.slice(word.length - 1, word.length)} - - ) : ( - - {word.slice(word.length - 1, word.length)} - - )} + {value.split(REGEX).map((word, id) => { + if (word.match(REGEX) !== null) { + return ( + + {word.slice(0, 2)} + + {word.slice(2, word.length - 1)} - ); - } else { - return {word}; - } - }) - } + {word.slice(word.length - 1, word.length) == "}" ? ( + + {word.slice(word.length - 1, word.length)} + + ) : ( + + {word.slice(word.length - 1, word.length)} + + )} + + ); + } else { + return ( + + {word} + + ); + } + })}
{blurred && (
- {value - .split("") - .map(() => ( - - ))} + {value.split("").map(() => ( + + ))}
- )} + )} ); } + + return <>Something Wrong; }; export default React.memo(DashboardInputField); From 249635f0cc28df63d43b4f4fcdb9f3062b784fe3 Mon Sep 17 00:00:00 2001 From: seunghyunOh Date: Sun, 4 Dec 2022 20:45:56 +0900 Subject: [PATCH 2/3] Remove useless type --- frontend/components/dashboard/DashboardInputField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/dashboard/DashboardInputField.tsx b/frontend/components/dashboard/DashboardInputField.tsx index 2867927842..cc70daa0ac 100644 --- a/frontend/components/dashboard/DashboardInputField.tsx +++ b/frontend/components/dashboard/DashboardInputField.tsx @@ -1,4 +1,4 @@ -import React, { SyntheticEvent, UIEvent, useRef, WheelEvent } from "react"; +import React, { SyntheticEvent, useRef } from "react"; import { faCircle } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; From a5d509c541890baa10dc8c297c0865ca8e755504 Mon Sep 17 00:00:00 2001 From: seunghyunOh Date: Sun, 4 Dec 2022 20:46:23 +0900 Subject: [PATCH 3/3] Change DropZone component to tsx --- .../dashboard/{DropZone.js => DropZone.tsx} | 92 +++++++++++-------- 1 file changed, 55 insertions(+), 37 deletions(-) rename frontend/components/dashboard/{DropZone.js => DropZone.tsx} (69%) diff --git a/frontend/components/dashboard/DropZone.js b/frontend/components/dashboard/DropZone.tsx similarity index 69% rename from frontend/components/dashboard/DropZone.js rename to frontend/components/dashboard/DropZone.tsx index 6171e682f3..7519bca10b 100644 --- a/frontend/components/dashboard/DropZone.js +++ b/frontend/components/dashboard/DropZone.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { type ChangeEvent, type DragEvent, useState } from "react"; import Image from "next/image"; import { faUpload } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -8,27 +8,37 @@ import Error from "../basic/Error"; import parse from "../utilities/file"; import guidGenerator from "../utilities/randomId"; +interface DropZoneProps { + // TODO: change Data type from any + setData: (data: any) => void; + setErrorDragAndDrop: (hasError: boolean) => void; + createNewFile: () => void; + errorDragAndDrop: boolean; + setButtonReady: (isReady: boolean) => void; + keysExist: boolean; + numCurrentRows: number; +} + const DropZone = ({ setData, setErrorDragAndDrop, createNewFile, errorDragAndDrop, - addPresetRow, setButtonReady, keysExist, numCurrentRows, -}) => { - const handleDragEnter = (e) => { +}: DropZoneProps) => { + const handleDragEnter = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); }; - const handleDragLeave = (e) => { + const handleDragLeave = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); }; - const handleDragOver = (e) => { + const handleDragOver = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); @@ -39,22 +49,25 @@ const DropZone = ({ const [loading, setLoading] = useState(false); // This function function immediately parses the file after it is dropped - const handleDrop = async (e) => { + const handleDrop = async (e: DragEvent) => { setLoading(true); setTimeout(() => setLoading(false), 5000); e.preventDefault(); e.stopPropagation(); e.dataTransfer.dropEffect = "copy"; - var file = e.dataTransfer.files[0], - reader = new FileReader(); - reader.onload = function (event) { - const keyPairs = parse(event.target.result); + const file = e.dataTransfer.files[0]; + const reader = new FileReader(); + + reader.onload = (event) => { + if (event.target === null || event.target.result === null) return; + // parse function's argument looks like to be ArrayBuffer + const keyPairs = parse(event.target.result as Buffer); const newData = Object.keys(keyPairs).map((key, index) => [ guidGenerator(), numCurrentRows + index, key, - keyPairs[key], + keyPairs[key as keyof typeof keyPairs], "shared", ]); setData(newData); @@ -72,23 +85,28 @@ const DropZone = ({ }; // This function is used when the user manually selects a file from the in-browser dircetory (not drag and drop) - const handleFileSelect = (e) => { + const handleFileSelect = (e: ChangeEvent) => { setLoading(true); setTimeout(() => setLoading(false), 5000); - var file = e.target.files[0], - reader = new FileReader(); - reader.onload = function (event) { - const newData = event.target.result - .split("\n") - .map((line, index) => [ - guidGenerator(), - numCurrentRows + index, - line.split("=")[0], - line.split("=").slice(1, line.split("=").length).join("="), - "shared", - ]); - setData(newData); - setButtonReady(true); + if (e.currentTarget.files === null) return; + const file = e.currentTarget.files[0]; + const reader = new FileReader(); + reader.onload = (event) => { + if (event.target === null || event.target.result === null) return; + const { result } = event.target; + if (typeof result === "string") { + const newData = result + .split("\n") + .map((line: string, index: number) => [ + guidGenerator(), + numCurrentRows + index, + line.split("=")[0], + line.split("=").slice(1, line.split("=").length).join("="), + "shared", + ]); + setData(newData); + setButtonReady(true); + } }; reader.readAsText(file); }; @@ -105,17 +123,17 @@ const DropZone = ({ ) : keysExist ? (
handleDragEnter(e)} - onDragOver={(e) => handleDragOver(e)} - onDragLeave={(e) => handleDragLeave(e)} - onDrop={(e) => handleDrop(e)} + onDragEnter={handleDragEnter} + onDragOver={handleDragOver} + onDragLeave={handleDragLeave} + onDrop={handleDrop} > handleFileSelect(e)} + onChange={handleFileSelect} /> {errorDragAndDrop ? (
@@ -142,10 +160,10 @@ const DropZone = ({ ) : (
handleDragEnter(e)} - onDragOver={(e) => handleDragOver(e)} - onDragLeave={(e) => handleDragLeave(e)} - onDrop={(e) => handleDrop(e)} + onDragEnter={handleDragEnter} + onDragOver={handleDragOver} + onDragLeave={handleDragLeave} + onDrop={handleDrop} >

Drag and drop your .env file here.

@@ -154,7 +172,7 @@ const DropZone = ({ type="file" className="opacity-0 absolute w-full h-full" accept=".txt,.env" - onChange={(e) => handleFileSelect(e)} + onChange={handleFileSelect} />