diff --git a/.eslintrc.json b/.eslintrc.json index 99f8d09..263e4f6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,7 +5,7 @@ "es2021": true }, "extends": [ - "eslint:recommended", + "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react/recommended" ], @@ -20,284 +20,14 @@ "react" ], "rules": { - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-var-requires":"off", - "react/no-unescaped-entities": "warn", - "react/prop-types": "off", - "accessor-pairs": "error", - "array-bracket-newline": "error", - "array-bracket-spacing": [ - "error", - "never" - ], - "array-callback-return": "error", - "array-element-newline": "off", - "arrow-body-style": "off", - "arrow-parens": "off", - "arrow-spacing": [ - "error", - { - "after": true, - "before": true - } - ], - "block-scoped-var": "error", - "block-spacing": [ - "error", - "always" + "nonblock-statement-body-position": [ + "warn", + "below" ], "brace-style": [ - "error", - "1tbs", - { - "allowSingleLine": true - } - ], - "camelcase": "error", - "capitalized-comments": "off", - "class-methods-use-this": "error", - "comma-dangle": "error", - "comma-spacing": "off", - "comma-style": [ - "error", - "last" - ], - "complexity": "off", - "computed-property-spacing": [ - "error", - "never" - ], - "consistent-return": "off", - "consistent-this": "error", - "curly": "off", - "default-case": "error", - "default-case-last": "error", - "default-param-last": "error", - "dot-location": [ "warn", - "property" + "allman" ], - "dot-notation": [ - "error", - { - "allowKeywords": true - } - ], - "eol-last": "off", - "eqeqeq": "error", - "func-call-spacing": "error", - "func-name-matching": "error", - "func-names": "error", - "func-style": [ - "error", - "declaration", - { - "allowArrowFunctions": true - } - ], - "function-call-argument-newline": [ - "error", - "consistent" - ], - "function-paren-newline": "error", - "generator-star-spacing": "error", - "grouped-accessor-pairs": "error", - "guard-for-in": "error", - "id-denylist": "error", - "id-length": "off", - "id-match": "error", - "implicit-arrow-linebreak": [ - "error", - "beside" - ], - "indent": "off", - "init-declarations": "error", - "jsx-quotes": "off", - "key-spacing": "off", - "keyword-spacing": "off", - "line-comment-position": "off", - "linebreak-style": "off", - "lines-around-comment": "error", - "max-classes-per-file": "error", - "max-depth": "error", - "max-len": "off", - // "max-lines": "error", - "max-lines-per-function": "off", - "max-nested-callbacks": "error", - "max-params": "off", - "max-statements": "off", - "max-statements-per-line": "error", - "multiline-comment-style": [ - "error", - "separate-lines" - ], - "multiline-ternary": [ - "error", - "always-multiline" - ], - "new-cap": "error", - "new-parens": "error", - "newline-per-chained-call": "error", - "no-alert": "error", - "no-array-constructor": "error", - "no-await-in-loop": "error", - "no-bitwise": "error", - "no-caller": "error", - "no-confusing-arrow": "error", - "no-console": "off", - "no-constructor-return": "error", - "no-continue": "error", - "no-div-regex": "error", - "no-duplicate-imports": "error", - "no-else-return": [ - "error", - { - "allowElseIf": true - } - ], - "no-empty-function": "off", - "no-eq-null": "error", - "no-eval": "error", - "no-extend-native": "error", - "no-extra-bind": "error", - "no-extra-label": "error", - // "no-extra-parens": "error", - "no-floating-decimal": "error", - "no-implicit-coercion": "error", - "no-implicit-globals": "error", - "no-implied-eval": "error", - "no-inline-comments": "off", - "no-invalid-this": "error", - "no-iterator": "error", - "no-label-var": "error", - "no-labels": "error", - "no-lone-blocks": "error", - "no-lonely-if": "error", - "no-loop-func": "error", - "no-loss-of-precision": "error", - "no-magic-numbers": "off", - "no-mixed-operators": "error", - "no-multi-assign": "error", - "no-multi-spaces": "error", - "no-multi-str": "error", - "no-multiple-empty-lines": "error", - "no-negated-condition": "off", - "no-nested-ternary": "error", - "no-new": "error", - "no-new-func": "error", - "no-new-object": "error", - "no-new-wrappers": "error", - "no-nonoctal-decimal-escape": "error", - "no-octal-escape": "error", - "no-param-reassign": "off", - "no-plusplus": "off", - "no-promise-executor-return": "error", - "no-proto": "error", - "no-restricted-exports": "error", - "no-restricted-globals": "error", - "no-restricted-imports": "error", - "no-restricted-properties": "error", - "no-restricted-syntax": "error", - "no-return-assign": "error", - "no-return-await": "error", - "no-script-url": "error", - "no-self-compare": "error", - "no-sequences": "error", - "no-shadow": "off", - "no-tabs": "error", - "no-template-curly-in-string": "error", - "no-ternary": "off", - "no-throw-literal": "error", - "no-trailing-spaces": "off", - "no-undef-init": "error", - "no-undefined": "error", - "no-underscore-dangle": "off", - "no-unmodified-loop-condition": "error", - "no-unneeded-ternary": "error", - "no-unreachable-loop": "error", - "no-unsafe-optional-chaining": "error", - "no-unused-expressions": "error", - "no-use-before-define": "off", - "no-useless-backreference": "error", - "no-useless-call": "error", - "no-useless-computed-key": "error", - "no-useless-concat": "error", - "no-useless-constructor": "error", - "no-useless-rename": "error", - "no-useless-return": "error", - "no-var": "error", - "no-void": "error", - "no-warning-comments": "warn", - "no-whitespace-before-property": "error", - // "nonblock-statement-body-position": ["warn", "below"], - "object-curly-newline": "error", - "object-curly-spacing": "off", - // "object-property-newline": "error", - "object-shorthand": "error", - "one-var": "off", - "one-var-declaration-per-line": "error", - "operator-assignment": [ - "error", - "always" - ], - "operator-linebreak": "off", - "padded-blocks": "off", - "padding-line-between-statements": "error", - "prefer-arrow-callback": "error", - "prefer-const": "error", - "prefer-destructuring": "error", - "prefer-exponentiation-operator": "error", - "prefer-named-capture-group": "error", - "prefer-numeric-literals": "error", - "prefer-object-spread": "error", - "prefer-promise-reject-errors": "error", - "prefer-regex-literals": "error", - "prefer-rest-params": "error", - "prefer-spread": "error", - "prefer-template": "off", - "quote-props": "off", - "quotes": "off", - "radix": [ - "error", - "as-needed" - ], - "require-atomic-updates": "error", - "require-await": "error", - "require-unicode-regexp": "off", - "rest-spread-spacing": "error", - "semi": "warn", - "semi-spacing": "warn", - "semi-style": [ - "error", - "last" - ], - "sort-keys": "off", - "sort-vars": "off", - "space-before-blocks": "error", - "space-before-function-paren": "off", - "space-in-parens": [ - "error", - "never" - ], - "space-infix-ops": "off", - "space-unary-ops": "error", - "spaced-comment": "off", - "strict": "error", - "switch-colon-spacing": "error", - "symbol-description": "error", - "template-curly-spacing": "off", - "template-tag-spacing": "error", - "unicode-bom": [ - "error", - "never" - ], - "vars-on-top": "error", - "wrap-iife": "error", - "wrap-regex": "error", - "yield-star-spacing": "error", - "yoda": [ - "error", - "never" - ] + "indent": "warn" } -} +} \ No newline at end of file diff --git a/src/css/pages/Admin.css b/src/css/pages/Admin.css index 739d5ee..bb25a48 100644 --- a/src/css/pages/Admin.css +++ b/src/css/pages/Admin.css @@ -1,102 +1,102 @@ -.input-group { - display: flex; - flex-direction: row; -} - -.input-group > button { - margin-bottom: 1rem; -} - -.flag-list-tiles { - display: flex; - flex-wrap: wrap; - gap: 10px -} - -.flag { - /* background-color: var(--bg-color); */ -} - -details .flag { - margin: 1.7rem; - padding-top: 5px; -} - -.tile { - width: calc(25% - 8px); - background-color: var(--bg-color); -} - -.flag.list-element { - margin-left: 20px; - user-select: text; - padding: 1rem 1rem; -} -.flag span.clickable:hover{ - font-style: italic; -} -.list.category { - user-select: none; - background-color: var(--color-darkGrey); - padding: 1rem; - margin: 0.5rem 0rem; - border-radius: 1rem; - border: 2px solid var(--bg-color); -} - -.list.category > * { - margin-left: 10px; - margin-right: 10px; -} - -.popup .flag { - height: 500px; - width: 500px; -} - -.list .listBody{ - display: none; -} -.list.open > .listBody{ - display: inherit; -} - -.list .listHeader{ - display: flex; - align-items: center; -} -.list-carrot{ - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Layer_1' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cg%3E%3Cg%3E%3Cpath fill='%23f5f5f5' d='M441.751,475.584L222.166,256L441.75,36.416c6.101-6.101,7.936-15.275,4.629-23.253C443.094,5.184,435.286,0,426.667,0 H320.001c-5.675,0-11.093,2.24-15.083,6.251L70.251,240.917c-8.341,8.341-8.341,21.824,0,30.165l234.667,234.667 c3.989,4.011,9.408,6.251,15.083,6.251h106.667c8.619,0,16.427-5.184,19.712-13.163 C449.687,490.858,447.852,481.685,441.751,475.584z'/%3E%3C/g%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E"); - width: 10px; - height: 10px; - transition: transform ease-in-out 0.1s; - background-size: 10px; - background-repeat: no-repeat; - background-position: center; - transform: rotate(-180deg); - display: inline-block; - margin-right: 8px; - margin-left: 3px; -} -.list-element:has(div.unsaved) { - outline: solid 1px #ffb300a6; -} -.list.open > .listHeader > .list-carrot, .list .listBody details[open] summary > .list-carrot{ - transform: rotate(-90deg); - transition: transform ease-in-out 0.1s; -} -.list .listBody details summary::marker{ - content: ""; -} -.list .listBody details summary{ - display: flex; - align-items: center; -} - -.unsaved-changes { - outline: solid 1px #ffb300a6; -} - -.list input{ - box-sizing: border-box; +.input-group { + display: flex; + flex-direction: row; +} + +.input-group > button { + margin-bottom: 1rem; +} + +.flag-list-tiles { + display: flex; + flex-wrap: wrap; + gap: 10px +} + +.flag { + /* background-color: var(--bg-color); */ +} + +details .flag { + margin: 1.7rem; + padding-top: 5px; +} + +.tile { + width: calc(25% - 8px); + background-color: var(--bg-color); +} + +.flag.list-element { + margin-left: 20px; + user-select: text; + padding: 1rem 1rem; +} +.flag span.clickable:hover{ + font-style: italic; +} +.list.category { + user-select: none; + background-color: var(--color-darkGrey); + padding: 1rem; + margin: 0.5rem 0rem; + border-radius: 1rem; + border: 2px solid var(--bg-color); +} + +.list.category > * { + margin-left: 10px; + margin-right: 10px; +} + +.popup .flag { + height: 500px; + width: 500px; +} + +.list .listBody{ + display: none; +} +.list.open > .listBody{ + display: inherit; +} + +.list .listHeader{ + display: flex; + align-items: center; +} +.list-carrot{ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' id='Layer_1' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cg%3E%3Cg%3E%3Cpath fill='%23f5f5f5' d='M441.751,475.584L222.166,256L441.75,36.416c6.101-6.101,7.936-15.275,4.629-23.253C443.094,5.184,435.286,0,426.667,0 H320.001c-5.675,0-11.093,2.24-15.083,6.251L70.251,240.917c-8.341,8.341-8.341,21.824,0,30.165l234.667,234.667 c3.989,4.011,9.408,6.251,15.083,6.251h106.667c8.619,0,16.427-5.184,19.712-13.163 C449.687,490.858,447.852,481.685,441.751,475.584z'/%3E%3C/g%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E"); + width: 10px; + height: 10px; + transition: transform ease-in-out 0.1s; + background-size: 10px; + background-repeat: no-repeat; + background-position: center; + transform: rotate(-180deg); + display: inline-block; + margin-right: 8px; + margin-left: 3px; +} +.list-element:has(div.unsaved) { + outline: solid 1px #ffb300a6; +} +.list.open > .listHeader > .list-carrot, .list .listBody details[open] summary > .list-carrot{ + transform: rotate(-90deg); + transition: transform ease-in-out 0.1s; +} +.list .listBody details summary::marker{ + content: ""; +} +.list .listBody details summary{ + display: flex; + align-items: center; +} + +.unsaved-changes { + outline: solid 1px #ffb300a6; +} + +.list input{ + box-sizing: border-box; } \ No newline at end of file diff --git a/src/pages/admin/Flags.tsx b/src/pages/admin/Flags.tsx index 2f8da1b..725accd 100644 --- a/src/pages/admin/Flags.tsx +++ b/src/pages/admin/Flags.tsx @@ -1,108 +1,132 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Flag as APIFlag } from "../../@types/ApiStructures"; -import { OrganisedFlags, capitalise, get, organiseFlags } from "../../util/Util"; -import { ClickToEdit, Dropdown, InputElementType, NumberInput, StringInput, ToggleSwitch } from "../../components/InputElements"; -import { PageButtons } from "../../components/PageControls"; +import React, { useEffect, useRef, useState } from 'react'; +import { Flag as APIFlag } from '../../@types/ApiStructures'; +import { OrganisedFlags, capitalise, get, organiseFlags, patch } from '../../util/Util'; +import { ClickToEdit, Dropdown, NumberInput, StringInput, ToggleSwitch } from '../../components/InputElements'; +import { PageButtons } from '../../components/PageControls'; -const Flag = ({ flag: incoming }: { flag: APIFlag }) => { +const Flag = ({ flag: incoming }: { flag: APIFlag }) => +{ - const [flag, setFlag] = useState(incoming); + const [ flag, setFlag ] = useState(incoming); const [unsaved, setUnsaved] = useState(false); + const [error, setError] = useState(); const valueRef = useRef(null); - const updateFlag = (f: APIFlag) => { - console.log(f); + const updateFlag = (f: APIFlag) => + { setFlag(f); setUnsaved(true); }; - const save = () => { - console.log('save'); - console.log(flag); - setUnsaved(false); + const save = async () => + { + const response = await patch(`/api/flags/${flag.id}`, flag); + if (response.success) + setUnsaved(false); + else + setError(response.message) }; let Input =

Loading...

; - if (flag.type === 'string') Input = setUnsaved(true)} inputRef={valueRef} value={flag.value as string} />; - else if (flag.type === 'number') Input = setUnsaved(true)} inputRef={valueRef} value={flag.value as number} type='float' />; - else if (flag.type === 'boolean') Input = setUnsaved(true)} inputRef={valueRef} value={flag.value as boolean} />; + if (flag.type === 'string') + Input = setUnsaved(true)} inputRef={valueRef} value={flag.value as string} />; + else if (flag.type === 'number') + Input = setUnsaved(true)} inputRef={valueRef} value={flag.value as number} type='float' />; + else if (flag.type === 'boolean') + Input = setUnsaved(true)} inputRef={valueRef} value={flag.value as boolean} />; return
+ {/* TODO: Improve these*/} {unsaved && Unsaved changes} -

+ {error &&

{error}

} +

updateFlag({ ...flag, name: val })} value={flag.name} />

-

ID: {flag.id}

-

+

ID: {flag.id}

+

Environment: updateFlag({ ...flag, env: val })} value={flag.env} />

-

+

Consumer: updateFlag({ ...flag, consumer: val })} value={flag.consumer} />

-

+

Hierarchy: updateFlag({ ...flag, hierarchy: val })} value={flag.hierarchy} />

-

+

Value: {Input}

- - {unsaved && } + + {unsaved && }
; }; -const FlagTile = ({ flag }: { flag: APIFlag }) => { - return
+const FlagTile = ({ flag }: { flag: APIFlag }) => +{ + return
; }; -const FlagListElement = ({ flag }: { flag: APIFlag, onClick?: React.ReactEventHandler }) => { +const FlagListElement = ({ flag }: { flag: APIFlag, onClick?: React.ReactEventHandler }) => +{ return
- - + + {flag.name} [{flag.type}] ({flag.id})
; }; -const ListCategory = ({ flags, name, setFlag }: { flags: OrganisedFlags, name: string, setFlag?: (flag: APIFlag) => void }) => { +const ListCategory = ({ flags, name, setFlag }: { flags: OrganisedFlags, name: string, setFlag?: (flag: APIFlag) => void }) => +{ const categories = Object.keys(flags); const elements: JSX.Element[] = []; - for (const category of categories) { + for (const category of categories) + { const element = flags[category]; if (element.id) - elements.push( setFlag && setFlag(element as APIFlag)} key={element.id as string} flag={element as APIFlag} />); + elements.push( setFlag && setFlag(element as APIFlag)} + key={element.id as string} + flag={element as APIFlag} + />); else elements.push(); } - const [hidden, setHidden] = useState(true); + const [ hidden, setHidden ] = useState(true); - if (name === 'root') return
- {elements} -
; + if (name === 'root') + return
+ {elements} +
; - return