diff --git a/resources/scripts/api/admin/egg.ts b/resources/scripts/api/admin/egg.ts index 874fd7ad7..8f9f0284b 100644 --- a/resources/scripts/api/admin/egg.ts +++ b/resources/scripts/api/admin/egg.ts @@ -85,7 +85,7 @@ export const searchEggs = async ( return data.data.map(Transformers.toEgg); }; -export const exportEgg = async (eggId: number): Promise> => { +export const exportEgg = async (eggId: number): Promise => { const { data } = await http.get(`/api/application/eggs/${eggId}/export`); return data; }; diff --git a/resources/scripts/api/admin/users.ts b/resources/scripts/api/admin/users.ts index 817721cb3..db0f62ed9 100644 --- a/resources/scripts/api/admin/users.ts +++ b/resources/scripts/api/admin/users.ts @@ -1,13 +1,11 @@ -import http, { - FractalPaginatedResponse, - PaginatedResult, - QueryBuilderParams, - getPaginationSet, - withQueryBuilderParams, -} from '@/api/http'; -import { Transformers, User } from '@definitions/admin'; -import useSWR, { SWRConfiguration, SWRResponse } from 'swr'; -import { AxiosError } from 'axios'; +import type { AxiosError } from 'axios'; +import type { SWRConfiguration, SWRResponse } from 'swr'; +import useSWR from 'swr'; + +import type { FractalPaginatedResponse, PaginatedResult, QueryBuilderParams } from '@/api/http'; +import http, { getPaginationSet, withQueryBuilderParams } from '@/api/http'; +import type { User } from '@definitions/admin'; +import { Transformers } from '@definitions/admin'; export interface UpdateUserValues { externalId: string; @@ -32,7 +30,10 @@ const useGetUsers = ( params: withQueryBuilderParams(params), }); - return getPaginationSet(data, Transformers.toUser); + return { + items: (data.data || []).map(Transformers.toUser), + pagination: getPaginationSet(data.meta.pagination), + }; }, config || { revalidateOnMount: true, revalidateOnFocus: false }, ); diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index ff53fb80e..e66ecb553 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -28,6 +28,8 @@ interface ExtendedWindow extends Window { root_admin: boolean; use_totp: boolean; language: string; + avatar_url: string; + admin_role_name: string; updated_at: string; created_at: string; /* eslint-enable camelcase */ @@ -45,6 +47,8 @@ function App() { email: PterodactylUser.email, language: PterodactylUser.language, rootAdmin: PterodactylUser.root_admin, + avatarURL: PterodactylUser.avatar_url, + roleName: PterodactylUser.admin_role_name, useTotp: PterodactylUser.use_totp, createdAt: new Date(PterodactylUser.created_at), updatedAt: new Date(PterodactylUser.updated_at), diff --git a/resources/scripts/components/NavigationBar.tsx b/resources/scripts/components/NavigationBar.tsx index 954d6d4cd..28e9f8861 100644 --- a/resources/scripts/components/NavigationBar.tsx +++ b/resources/scripts/components/NavigationBar.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { Link, NavLink } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCogs, faLayerGroup, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'; +import { faLayerGroup, faScrewdriverWrench, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'; import { useStoreState } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import SearchContainer from '@/components/dashboard/search/SearchContainer'; @@ -57,6 +57,7 @@ export default () => { {name} + @@ -66,14 +67,6 @@ export default () => { - {rootAdmin && ( - - - - - - )} - @@ -82,6 +75,14 @@ export default () => { + {rootAdmin && ( + + + + + + )} + + ); }; diff --git a/resources/scripts/components/admin/databases/DatabaseEditContainer.tsx b/resources/scripts/components/admin/databases/DatabaseEditContainer.tsx index 89d8df46f..dc4f74a32 100644 --- a/resources/scripts/components/admin/databases/DatabaseEditContainer.tsx +++ b/resources/scripts/components/admin/databases/DatabaseEditContainer.tsx @@ -14,7 +14,7 @@ import AdminContentBlock from '@/components/admin/AdminContentBlock'; import Spinner from '@/components/elements/Spinner'; import FlashMessageRender from '@/components/FlashMessageRender'; import AdminBox from '@/components/admin/AdminBox'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; import Field from '@/components/elements/Field'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import DatabaseDeleteButton from '@/components/admin/databases/DatabaseDeleteButton'; @@ -115,7 +115,7 @@ export const InformationContainer = ({ title, initialValues, children, onSubmit
{children}
-
diff --git a/resources/scripts/components/admin/databases/DatabasesContainer.tsx b/resources/scripts/components/admin/databases/DatabasesContainer.tsx index 73ff3027c..e36555071 100644 --- a/resources/scripts/components/admin/databases/DatabasesContainer.tsx +++ b/resources/scripts/components/admin/databases/DatabasesContainer.tsx @@ -10,17 +10,18 @@ import FlashMessageRender from '@/components/FlashMessageRender'; import AdminCheckbox from '@/components/admin/AdminCheckbox'; import AdminContentBlock from '@/components/admin/AdminContentBlock'; import AdminTable, { + ContentWrapper, + Loading, + NoItems, + Pagination, TableBody, TableHead, TableHeader, TableRow, - Pagination, - Loading, - NoItems, - ContentWrapper, useTableHooks, } from '@/components/admin/AdminTable'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; +import { Size } from '@/components/elements/button/types'; import CopyOnClick from '@/components/elements/CopyOnClick'; const RowCheckbox = ({ id }: { id: number }) => { @@ -93,7 +94,7 @@ const DatabasesContainer = () => {
- diff --git a/resources/scripts/components/admin/locations/LocationDeleteButton.tsx b/resources/scripts/components/admin/locations/LocationDeleteButton.tsx index 9fb80954e..99aa7d1e2 100644 --- a/resources/scripts/components/admin/locations/LocationDeleteButton.tsx +++ b/resources/scripts/components/admin/locations/LocationDeleteButton.tsx @@ -4,7 +4,8 @@ import { useState } from 'react'; import tw from 'twin.macro'; import deleteLocation from '@/api/admin/locations/deleteLocation'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; +import { Shape } from '@/components/elements/button/types'; import ConfirmationModal from '@/components/elements/ConfirmationModal'; import type { ApplicationStore } from '@/state'; @@ -53,7 +54,7 @@ export default ({ locationId, onDeleted }: Props) => { to it. - + ); }; diff --git a/resources/scripts/components/admin/locations/LocationEditContainer.tsx b/resources/scripts/components/admin/locations/LocationEditContainer.tsx index 6c0e74c6f..261dec534 100644 --- a/resources/scripts/components/admin/locations/LocationEditContainer.tsx +++ b/resources/scripts/components/admin/locations/LocationEditContainer.tsx @@ -13,7 +13,7 @@ import updateLocation from '@/api/admin/locations/updateLocation'; import AdminBox from '@/components/admin/AdminBox'; import AdminContentBlock from '@/components/admin/AdminContentBlock'; import LocationDeleteButton from '@/components/admin/locations/LocationDeleteButton'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; import Field from '@/components/elements/Field'; import Spinner from '@/components/elements/Spinner'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; @@ -99,7 +99,7 @@ const EditInformationContainer = () => {
-
diff --git a/resources/scripts/components/admin/locations/LocationsContainer.tsx b/resources/scripts/components/admin/locations/LocationsContainer.tsx index 81ddd710a..603def7a2 100644 --- a/resources/scripts/components/admin/locations/LocationsContainer.tsx +++ b/resources/scripts/components/admin/locations/LocationsContainer.tsx @@ -24,7 +24,7 @@ import FlashMessageRender from '@/components/FlashMessageRender'; import useFlash from '@/plugins/useFlash'; import { AdminContext } from '@/state/admin'; -const RowCheckbox = ({ id }: { id: number }) => { +function RowCheckbox({ id }: { id: number }) { const isChecked = AdminContext.useStoreState(state => state.locations.selectedLocations.indexOf(id) >= 0); const appendSelectedLocation = AdminContext.useStoreActions(actions => actions.locations.appendSelectedLocation); const removeSelectedLocation = AdminContext.useStoreActions(actions => actions.locations.removeSelectedLocation); @@ -42,9 +42,9 @@ const RowCheckbox = ({ id }: { id: number }) => { }} /> ); -}; +} -const LocationsContainer = () => { +function LocationsContainer() { const { page, setPage, setFilters, sort, setSort, sortDirection } = useContext(LocationsContext); const { clearFlashes, clearAndAddHttpError } = useFlash(); const { data: locations, error, isValidating } = getLocations(); @@ -173,7 +173,7 @@ const LocationsContainer = () => { ); -}; +} export default () => { const hooks = useTableHooks(); diff --git a/resources/scripts/components/admin/locations/NewLocationButton.tsx b/resources/scripts/components/admin/locations/NewLocationButton.tsx index f7f688e9b..0b5d0ff88 100644 --- a/resources/scripts/components/admin/locations/NewLocationButton.tsx +++ b/resources/scripts/components/admin/locations/NewLocationButton.tsx @@ -6,7 +6,8 @@ import { object, string } from 'yup'; import createLocation from '@/api/admin/locations/createLocation'; import getLocations from '@/api/admin/locations/getLocations'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; +import { Size, Variant } from '@/components/elements/button/types'; import Field from '@/components/elements/Field'; import Modal from '@/components/elements/Modal'; import FlashMessageRender from '@/components/FlashMessageRender'; @@ -82,14 +83,14 @@ export default () => {
- + @@ -100,8 +101,8 @@ export default () => { + ); }; diff --git a/resources/scripts/components/admin/mounts/MountForm.tsx b/resources/scripts/components/admin/mounts/MountForm.tsx index a86b947a8..8a9d2bd99 100644 --- a/resources/scripts/components/admin/mounts/MountForm.tsx +++ b/resources/scripts/components/admin/mounts/MountForm.tsx @@ -1,10 +1,11 @@ import type { FormikHelpers } from 'formik'; import { Field as FormikField, Form, Formik } from 'formik'; +import type { ReactNode } from 'react'; import tw from 'twin.macro'; import { boolean, object, string } from 'yup'; import AdminBox from '@/components/admin/AdminBox'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; import Field from '@/components/elements/Field'; import Label from '@/components/elements/Label'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; @@ -25,7 +26,7 @@ interface Props { onSubmit: (values: Values, helpers: FormikHelpers) => void; - children?: React.ReactNode; + children?: ReactNode; } function MountForm({ action, title, initialValues, children, onSubmit }: Props) { @@ -118,7 +119,7 @@ function MountForm({ action, title, initialValues, children, onSubmit }: Props) {children}
-
diff --git a/resources/scripts/components/admin/mounts/MountsContainer.tsx b/resources/scripts/components/admin/mounts/MountsContainer.tsx index bc3e0054d..f9aa5fb05 100644 --- a/resources/scripts/components/admin/mounts/MountsContainer.tsx +++ b/resources/scripts/components/admin/mounts/MountsContainer.tsx @@ -18,7 +18,8 @@ import AdminTable, { ContentWrapper, useTableHooks, } from '@/components/admin/AdminTable'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; +import { Size } from '@/components/elements/button/types'; import CopyOnClick from '@/components/elements/CopyOnClick'; import FlashMessageRender from '@/components/FlashMessageRender'; import useFlash from '@/plugins/useFlash'; @@ -94,7 +95,7 @@ const MountsContainer = () => {
- diff --git a/resources/scripts/components/admin/nests/ImportEggButton.tsx b/resources/scripts/components/admin/nests/ImportEggButton.tsx index 1dd6d2dd2..66aedf496 100644 --- a/resources/scripts/components/admin/nests/ImportEggButton.tsx +++ b/resources/scripts/components/admin/nests/ImportEggButton.tsx @@ -1,13 +1,17 @@ +import { LanguageDescription } from '@codemirror/language'; +import { json } from '@codemirror/lang-json'; +import { useState } from 'react'; +import { useParams } from 'react-router-dom'; +import tw from 'twin.macro'; + import getEggs from '@/api/admin/nests/getEggs'; import importEgg from '@/api/admin/nests/importEgg'; import useFlash from '@/plugins/useFlash'; -// import { Editor } from '@/components/elements/editor'; -import { useState } from 'react'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; +import { Size, Variant } from '@/components/elements/button/types'; +import { Editor } from '@/components/elements/editor'; import Modal from '@/components/elements/Modal'; import FlashMessageRender from '@/components/FlashMessageRender'; -import { useParams } from 'react-router-dom'; -import tw from 'twin.macro'; export default ({ className }: { className?: string }) => { const [visible, setVisible] = useState(false); @@ -43,24 +47,24 @@ export default ({ className }: { className?: string }) => {

Import Egg

- {/* {*/} - {/* fetchFileContent = value;*/} - {/* }}*/} - {/*/>*/} + { + fetchFileContent = value; + }} + language={LanguageDescription.of({ name: 'json', support: json() })} + />
- + @@ -68,12 +72,12 @@ export default ({ className }: { className?: string }) => { diff --git a/resources/scripts/components/admin/nests/NestDeleteButton.tsx b/resources/scripts/components/admin/nests/NestDeleteButton.tsx index 09d573377..48528a010 100644 --- a/resources/scripts/components/admin/nests/NestDeleteButton.tsx +++ b/resources/scripts/components/admin/nests/NestDeleteButton.tsx @@ -4,7 +4,8 @@ import { useState } from 'react'; import tw from 'twin.macro'; import deleteNest from '@/api/admin/nests/deleteNest'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; +import { Shape } from '@/components/elements/button/types'; import ConfirmationModal from '@/components/elements/ConfirmationModal'; import type { ApplicationStore } from '@/state'; @@ -52,7 +53,7 @@ export default ({ nestId, onDeleted }: Props) => { Are you sure you want to delete this nest? Deleting a nest will delete all eggs assigned to it. - + ); }; diff --git a/resources/scripts/components/admin/nests/NestEditContainer.tsx b/resources/scripts/components/admin/nests/NestEditContainer.tsx index dabbead60..924eabba9 100644 --- a/resources/scripts/components/admin/nests/NestEditContainer.tsx +++ b/resources/scripts/components/admin/nests/NestEditContainer.tsx @@ -14,7 +14,8 @@ import FlashMessageRender from '@/components/FlashMessageRender'; import type { Nest } from '@/api/admin/nests/getNests'; import getNest from '@/api/admin/nests/getNest'; import updateNest from '@/api/admin/nests/updateNest'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; +import { Size } from '@/components/elements/button/types'; import Field from '@/components/elements/Field'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import AdminBox from '@/components/admin/AdminBox'; @@ -117,7 +118,7 @@ const EditInformationContainer = () => {
-
@@ -222,7 +223,7 @@ const NestEditContainer = () => { - diff --git a/resources/scripts/components/admin/nests/NewEggContainer.tsx b/resources/scripts/components/admin/nests/NewEggContainer.tsx index 1e79eb4ee..9a96df9fd 100644 --- a/resources/scripts/components/admin/nests/NewEggContainer.tsx +++ b/resources/scripts/components/admin/nests/NewEggContainer.tsx @@ -15,7 +15,7 @@ import { EggProcessContainerRef, EggStartupContainer, } from '@/components/admin/nests/eggs/EggSettingsContainer'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; import FlashMessageRender from '@/components/FlashMessageRender'; import useFlash from '@/plugins/useFlash'; @@ -45,7 +45,16 @@ export default () => { values.configStartup = (await ref.current?.getStartupConfiguration()) || ''; values.configFiles = (await ref.current?.getFilesConfiguration()) || ''; - createEgg({ ...values, dockerImages: values.dockerImages.split('\n'), nestId }) + const dockerImages: Record = {}; + values.dockerImages.split('\n').forEach(v => { + dockerImages[v] = v; + }); + + createEgg({ + ...values, + dockerImages, + nestId, + }) .then(egg => navigate(`/admin/nests/${nestId}/eggs/${egg.id}`)) .catch(error => { console.error(error); @@ -97,12 +106,7 @@ export default () => {
-
diff --git a/resources/scripts/components/admin/nests/NewNestButton.tsx b/resources/scripts/components/admin/nests/NewNestButton.tsx index 60a613840..620255970 100644 --- a/resources/scripts/components/admin/nests/NewNestButton.tsx +++ b/resources/scripts/components/admin/nests/NewNestButton.tsx @@ -1,4 +1,9 @@ -import React, { useState } from 'react'; +import type { FormikHelpers } from 'formik'; +import { Form, Formik } from 'formik'; +import { useState } from 'react'; +import tw from 'twin.macro'; +import { object, string } from 'yup'; + import createNest from '@/api/admin/nests/createNest'; import getNests from '@/api/admin/nests/getNests'; import Button from '@/components/elements/Button'; @@ -6,25 +11,19 @@ import Field from '@/components/elements/Field'; import Modal from '@/components/elements/Modal'; import FlashMessageRender from '@/components/FlashMessageRender'; import useFlash from '@/plugins/useFlash'; -import { Form, Formik, FormikHelpers } from 'formik'; -import { object, string } from 'yup'; -import tw from 'twin.macro'; interface Values { - name: string, - description: string, + name: string; + description: string; } const schema = object().shape({ - name: string() - .required('A nest name must be provided.') - .max(32, 'Nest name must not exceed 32 characters.'), - description: string() - .max(255, 'Nest description must not exceed 255 characters.'), + name: string().required('A nest name must be provided.').max(32, 'Nest name must not exceed 32 characters.'), + description: string().max(255, 'Nest description must not exceed 255 characters.'), }); export default () => { - const [ visible, setVisible ] = useState(false); + const [visible, setVisible] = useState(false); const { clearFlashes, clearAndAddHttpError } = useFlash(); const { mutate } = getNests(); @@ -33,7 +32,7 @@ export default () => { setSubmitting(true); createNest(name, description) - .then(async (nest) => { + .then(async nest => { await mutate(data => ({ ...data!, items: data!.items.concat(nest) }), false); setVisible(false); }) @@ -45,66 +44,65 @@ export default () => { return ( <> - - { - ({ isSubmitting, resetForm }) => ( - { - resetForm(); - setVisible(false); - }} - > - + + {({ isSubmitting, resetForm }) => ( + { + resetForm(); + setVisible(false); + }} + > + -

New Nest

+

New Nest

-
+ + + +
+
-
- -
- -
- - -
- -
- ) - } +
+ + +
+ +
+ )}
- diff --git a/resources/scripts/components/admin/nests/eggs/EggDeleteButton.tsx b/resources/scripts/components/admin/nests/eggs/EggDeleteButton.tsx index 11a141693..c14286f55 100644 --- a/resources/scripts/components/admin/nests/eggs/EggDeleteButton.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggDeleteButton.tsx @@ -4,7 +4,8 @@ import { useState } from 'react'; import tw from 'twin.macro'; import deleteEgg from '@/api/admin/eggs/deleteEgg'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; +import { Shape } from '@/components/elements/button/types'; import ConfirmationModal from '@/components/elements/ConfirmationModal'; import type { ApplicationStore } from '@/state'; @@ -52,7 +53,7 @@ export default ({ eggId, onDeleted }: Props) => { Are you sure you want to delete this egg? You may only delete an egg with no servers using it. - + ); }; diff --git a/resources/scripts/components/admin/nests/eggs/EggExportButton.tsx b/resources/scripts/components/admin/nests/eggs/EggExportButton.tsx index 513bc972d..bcb6c1785 100644 --- a/resources/scripts/components/admin/nests/eggs/EggExportButton.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggExportButton.tsx @@ -1,22 +1,25 @@ -import { exportEgg } from '@/api/admin/egg'; -import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; -import useFlash from '@/plugins/useFlash'; -// import { jsonLanguage } from '@codemirror/lang-json'; -// import Editor from '@/components/elements/Editor'; +import { LanguageDescription } from '@codemirror/language'; +import { json } from '@codemirror/lang-json'; import { useEffect, useState } from 'react'; -import Button from '@/components/elements/Button'; -import Modal from '@/components/elements/Modal'; -import FlashMessageRender from '@/components/FlashMessageRender'; import { useParams } from 'react-router-dom'; import tw from 'twin.macro'; +import { exportEgg } from '@/api/admin/egg'; +import FlashMessageRender from '@/components/FlashMessageRender'; +import { Button } from '@/components/elements/button'; +import { Variant } from '@/components/elements/button/types'; +import { Editor } from '@/components/elements/editor'; +import Modal from '@/components/elements/Modal'; +import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; +import useFlash from '@/plugins/useFlash'; + export default ({ className }: { className?: string }) => { const params = useParams<'id'>(); const { clearAndAddHttpError, clearFlashes } = useFlash(); const [visible, setVisible] = useState(false); const [loading, setLoading] = useState(true); - const [_content, setContent] = useState | null>(null); + const [content, setContent] = useState(undefined); useEffect(() => { if (!visible) { @@ -45,21 +48,22 @@ export default ({ className }: { className?: string }) => {

Export Egg

- {/**/} +
- + +
- + ); }; diff --git a/resources/scripts/components/admin/nests/eggs/EggInstallContainer.tsx b/resources/scripts/components/admin/nests/eggs/EggInstallContainer.tsx index 3a9626c38..a6f97610c 100644 --- a/resources/scripts/components/admin/nests/eggs/EggInstallContainer.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggInstallContainer.tsx @@ -1,15 +1,18 @@ +import { LanguageDescription, LanguageSupport, StreamLanguage } from '@codemirror/language'; +import { shell } from '@codemirror/legacy-modes/mode/shell'; +import { faScroll } from '@fortawesome/free-solid-svg-icons'; +import type { FormikHelpers } from 'formik'; +import { Form, Formik } from 'formik'; +import tw from 'twin.macro'; + import { useEggFromRoute } from '@/api/admin/egg'; import updateEgg from '@/api/admin/eggs/updateEgg'; -import Field from '@/components/elements/Field'; -import useFlash from '@/plugins/useFlash'; -// import { shell } from '@codemirror/legacy-modes/mode/shell'; -import { faScroll } from '@fortawesome/free-solid-svg-icons'; -import { Form, Formik, FormikHelpers } from 'formik'; -import tw from 'twin.macro'; import AdminBox from '@/components/admin/AdminBox'; -import Button from '@/components/elements/Button'; -// import Editor from '@/components/elements/Editor'; +import { Button } from '@/components/elements/button'; +import { Editor } from '@/components/elements/editor'; +import Field from '@/components/elements/Field'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; +import useFlash from '@/plugins/useFlash'; interface Values { scriptContainer: string; @@ -60,14 +63,17 @@ export default function EggInstallContainer() {
- {/* {*/} - {/* fetchFileContent = value;*/} - {/* }}*/} - {/*/>*/} + { + fetchFileContent = value; + }} + language={LanguageDescription.of({ + name: 'shell', + support: new LanguageSupport(StreamLanguage.define(shell)), + })} + />
@@ -92,12 +98,7 @@ export default function EggInstallContainer() {
-
diff --git a/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx b/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx index 314b02cba..20532efe6 100644 --- a/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx @@ -1,23 +1,26 @@ +import { LanguageDescription } from '@codemirror/language'; +import { json } from '@codemirror/lang-json'; +import { faDocker } from '@fortawesome/free-brands-svg-icons'; +import { faEgg, faFireAlt, faMicrochip, faTerminal } from '@fortawesome/free-solid-svg-icons'; +import type { FormikHelpers } from 'formik'; +import { Form, Formik, useFormikContext } from 'formik'; +import { forwardRef, useImperativeHandle, useRef } from 'react'; +import { useNavigate } from 'react-router-dom'; +import tw from 'twin.macro'; +import { object } from 'yup'; + import { useEggFromRoute } from '@/api/admin/egg'; import updateEgg from '@/api/admin/eggs/updateEgg'; +import AdminBox from '@/components/admin/AdminBox'; import EggDeleteButton from '@/components/admin/nests/eggs/EggDeleteButton'; import EggExportButton from '@/components/admin/nests/eggs/EggExportButton'; -import Button from '@/components/elements/Button'; -// import Editor from '@/components/elements/Editor'; +import { Button } from '@/components/elements/button'; +import { Editor } from '@/components/elements/editor'; import Field, { TextareaField } from '@/components/elements/Field'; import Input from '@/components/elements/Input'; import Label from '@/components/elements/Label'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import useFlash from '@/plugins/useFlash'; -// import { jsonLanguage } from '@codemirror/lang-json'; -import { faDocker } from '@fortawesome/free-brands-svg-icons'; -import { faEgg, faFireAlt, faMicrochip, faTerminal } from '@fortawesome/free-solid-svg-icons'; -import { forwardRef, useImperativeHandle, useRef } from 'react'; -import AdminBox from '@/components/admin/AdminBox'; -import { useNavigate } from 'react-router-dom'; -import tw from 'twin.macro'; -import { object } from 'yup'; -import { Form, Formik, FormikHelpers, useFormikContext } from 'formik'; export function EggInformationContainer() { const { isSubmitting } = useFormikContext(); @@ -104,8 +107,7 @@ export const EggProcessContainer = forwardRef(fun { className }, ref, ) { - // const { isSubmitting, values } = useFormikContext(); - const { isSubmitting } = useFormikContext(); + const { isSubmitting, values } = useFormikContext(); let fetchStartupConfiguration: (() => Promise) | null = null; let fetchFilesConfiguration: (() => Promise) | null = null; @@ -132,26 +134,26 @@ export const EggProcessContainer = forwardRef(fun
- {/* {*/} - {/* fetchStartupConfiguration = value;*/} - {/* }}*/} - {/*/>*/} + { + fetchStartupConfiguration = value; + }} + language={LanguageDescription.of({ name: 'json', support: json() })} + />
- {/* {*/} - {/* fetchFilesConfiguration = value;*/} - {/* }}*/} - {/*/>*/} + { + fetchFilesConfiguration = value; + }} + language={LanguageDescription.of({ name: 'json', support: json() })} + />
); @@ -233,7 +235,7 @@ export default function EggSettingsContainer() {
navigate('/admin/nests')} /> -
diff --git a/resources/scripts/components/admin/nests/eggs/EggVariablesContainer.tsx b/resources/scripts/components/admin/nests/eggs/EggVariablesContainer.tsx index c6cbfe925..5b3b4e438 100644 --- a/resources/scripts/components/admin/nests/eggs/EggVariablesContainer.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggVariablesContainer.tsx @@ -13,11 +13,12 @@ import type { EggVariable } from '@/api/admin/egg'; import { useEggFromRoute } from '@/api/admin/egg'; import NewVariableButton from '@/components/admin/nests/eggs/NewVariableButton'; import AdminBox from '@/components/admin/AdminBox'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; import Checkbox from '@/components/elements/Checkbox'; import Field, { FieldRow, TextareaField } from '@/components/elements/Field'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import useFlash from '@/plugins/useFlash'; +import Label from '@/components/elements/Label'; export const validationSchema = object().shape({ name: string().required().min(1).max(191), @@ -59,14 +60,23 @@ export function EggVariableForm({ prefix }: { prefix: string }) {
- +
+ {/* TODO: fix Checkbox component, current one is designed for subuser permissions and not for individual values */} + - +
+ +
+
+ +
+ {/* TODO: fix Checkbox component, current one is designed for subuser permissions and not for individual values */} + + +
+ +
+
void) = css={tw`ml-auto text-neutral-500 hover:text-neutral-300`} onClick={() => setVisible(true)} > - + ); @@ -200,12 +210,7 @@ export default function EggVariablesContainer() {
-
diff --git a/resources/scripts/components/admin/nests/eggs/NewVariableButton.tsx b/resources/scripts/components/admin/nests/eggs/NewVariableButton.tsx index fd9057f3c..753c221ae 100644 --- a/resources/scripts/components/admin/nests/eggs/NewVariableButton.tsx +++ b/resources/scripts/components/admin/nests/eggs/NewVariableButton.tsx @@ -9,8 +9,9 @@ import { useEggFromRoute } from '@/api/admin/egg'; import { EggVariableForm, validationSchema } from '@/components/admin/nests/eggs/EggVariablesContainer'; import Modal from '@/components/elements/Modal'; import FlashMessageRender from '@/components/FlashMessageRender'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button'; import useFlash from '@/plugins/useFlash'; +import { Variant } from '@/components/elements/button/types'; export default function NewVariableButton() { const { setValues } = useFormikContext(); @@ -75,16 +76,16 @@ export default function NewVariableButton() {
diff --git a/resources/scripts/components/admin/users/UserTableRow.tsx b/resources/scripts/components/admin/users/UserTableRow.tsx index ee8baf729..0ce0bb97d 100644 --- a/resources/scripts/components/admin/users/UserTableRow.tsx +++ b/resources/scripts/components/admin/users/UserTableRow.tsx @@ -4,7 +4,6 @@ import { useState } from 'react'; import Checkbox from '@/components/elements/inputs/Checkbox'; import { Dropdown } from '@/components/elements/dropdown'; import { Dialog } from '@/components/elements/dialog'; -import { Button } from '@/components/elements/button'; import { User } from '@definitions/admin'; interface Props { @@ -18,14 +17,17 @@ const UserTableRow = ({ user, selected, onRowChange }: Props) => { return ( <> - setVisible(false)}> - + setVisible(false)} + onConfirmed={() => { + console.log('yeet'); + }} + > This account will be permanently deleted. - - setVisible(false)}>Cancel - Delete - - + +
diff --git a/resources/scripts/components/admin/users/UsersContainer.tsx b/resources/scripts/components/admin/users/UsersContainer.tsx index b361722dd..c7dbc80d5 100644 --- a/resources/scripts/components/admin/users/UsersContainer.tsx +++ b/resources/scripts/components/admin/users/UsersContainer.tsx @@ -12,6 +12,7 @@ import TFootPaginated from '@/components/elements/table/TFootPaginated'; import type { User } from '@definitions/admin'; import extractSearchFilters from '@/helpers/extractSearchFilters'; import useDebouncedState from '@/plugins/useDebouncedState'; +import { Shape } from '@/components/elements/button/types'; const filters = ['id', 'uuid', 'external_id', 'username', 'email'] as const; @@ -77,13 +78,13 @@ const UsersContainer = () => { onChange={onSelectAll} />
- + - + - +
diff --git a/resources/scripts/components/elements/Checkbox.tsx b/resources/scripts/components/elements/Checkbox.tsx index 1432fe1e3..02b1d0dec 100644 --- a/resources/scripts/components/elements/Checkbox.tsx +++ b/resources/scripts/components/elements/Checkbox.tsx @@ -1,9 +1,13 @@ -import { Field, FieldProps } from 'formik'; +import type { FieldProps } from 'formik'; +import { Field } from 'formik'; + import Input from '@/components/elements/Input'; interface Props { + id: string; name: string; - value: string; + value?: string; + label?: string; className?: string; } diff --git a/resources/scripts/components/elements/Editor.tsx b/resources/scripts/components/elements/Editor.tsx deleted file mode 100644 index c3a77b9c6..000000000 --- a/resources/scripts/components/elements/Editor.tsx +++ /dev/null @@ -1,318 +0,0 @@ -import { autocompletion, completionKeymap } from '@codemirror/autocomplete'; -import { closeBrackets, closeBracketsKeymap } from '@codemirror/closebrackets'; -import { defaultKeymap, indentWithTab } from '@codemirror/commands'; -import { commentKeymap } from '@codemirror/comment'; -import { foldGutter, foldKeymap } from '@codemirror/fold'; -import { lineNumbers, highlightActiveLineGutter } from '@codemirror/gutter'; -import { defaultHighlightStyle } from '@codemirror/highlight'; -import { history, historyKeymap } from '@codemirror/history'; -import { indentOnInput, LanguageSupport, LRLanguage, indentUnit } from '@codemirror/language'; -import { lintKeymap } from '@codemirror/lint'; -import { bracketMatching } from '@codemirror/matchbrackets'; -import { rectangularSelection } from '@codemirror/rectangular-selection'; -import { searchKeymap, highlightSelectionMatches } from '@codemirror/search'; -import { Compartment, Extension, EditorState } from '@codemirror/state'; -import { StreamLanguage, StreamParser } from '@codemirror/stream-parser'; -import { keymap, highlightSpecialChars, drawSelection, highlightActiveLine, EditorView } from '@codemirror/view'; -import { clike } from '@codemirror/legacy-modes/mode/clike'; -import { cpp } from '@codemirror/lang-cpp'; -import { css } from '@codemirror/lang-css'; -import { Cassandra, MariaSQL, MSSQL, MySQL, PostgreSQL, sql, SQLite, StandardSQL } from '@codemirror/lang-sql'; -import { diff } from '@codemirror/legacy-modes/mode/diff'; -import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile'; -import { markdown, markdownLanguage } from '@codemirror/lang-markdown'; -import { go } from '@codemirror/legacy-modes/mode/go'; -import { html } from '@codemirror/lang-html'; -import { http } from '@codemirror/legacy-modes/mode/http'; -import { javascript, typescriptLanguage } from '@codemirror/lang-javascript'; -import { json } from '@codemirror/lang-json'; -import { lua } from '@codemirror/legacy-modes/mode/lua'; -import { properties } from '@codemirror/legacy-modes/mode/properties'; -import { python } from '@codemirror/legacy-modes/mode/python'; -import { ruby } from '@codemirror/legacy-modes/mode/ruby'; -import { rust } from '@codemirror/lang-rust'; -import { shell } from '@codemirror/legacy-modes/mode/shell'; -import { toml } from '@codemirror/legacy-modes/mode/toml'; -import { xml } from '@codemirror/lang-xml'; -import { yaml } from '@codemirror/legacy-modes/mode/yaml'; -import React, { useCallback, useEffect, useState } from 'react'; -import tw, { styled, TwStyle } from 'twin.macro'; -import { ayuMirage } from '@/components/elements/EditorTheme'; - -type EditorMode = LanguageSupport | LRLanguage | StreamParser; - -export interface Mode { - name: string; - mime: string; - mimes?: string[]; - mode?: EditorMode; - ext?: string[]; - alias?: string[]; - file?: RegExp; -} - -export const modes: Mode[] = [ - { name: 'C', mime: 'text/x-csrc', mode: clike({}), ext: [ 'c', 'h', 'ino' ] }, - { name: 'C++', mime: 'text/x-c++src', mode: cpp(), ext: [ 'cpp', 'c++', 'cc', 'cxx', 'hpp', 'h++', 'hh', 'hxx' ], alias: [ 'cpp' ] }, - { name: 'C#', mime: 'text/x-csharp', mode: clike({}), ext: [ 'cs' ], alias: [ 'csharp', 'cs' ] }, - { name: 'CSS', mime: 'text/css', mode: css(), ext: [ 'css' ] }, - { name: 'CQL', mime: 'text/x-cassandra', mode: sql({ dialect: Cassandra }), ext: [ 'cql' ] }, - { name: 'Diff', mime: 'text/x-diff', mode: diff, ext: [ 'diff', 'patch' ] }, - { name: 'Dockerfile', mime: 'text/x-dockerfile', mode: dockerFile, file: /^Dockerfile$/ }, - { name: 'Git Markdown', mime: 'text/x-gfm', mode: markdown({ defaultCodeLanguage: markdownLanguage }), file: /^(readme|contributing|history|license).md$/i }, - { name: 'Golang', mime: 'text/x-go', mode: go, ext: [ 'go' ] }, - { name: 'HTML', mime: 'text/html', mode: html(), ext: [ 'html', 'htm', 'handlebars', 'hbs' ], alias: [ 'xhtml' ] }, - { name: 'HTTP', mime: 'message/http', mode: http }, - { name: 'JavaScript', mime: 'text/javascript', mimes: [ 'text/javascript', 'text/ecmascript', 'application/javascript', 'application/x-javascript', 'application/ecmascript' ], mode: javascript(), ext: [ 'js' ], alias: [ 'ecmascript', 'js', 'node' ] }, - { name: 'JSON', mime: 'application/json', mimes: [ 'application/json', 'application/x-json' ], mode: json(), ext: [ 'json', 'json5', 'map' ], alias: [ 'json5' ] }, - { name: 'Lua', mime: 'text/x-lua', mode: lua, ext: [ 'lua' ] }, - { name: 'Markdown', mime: 'text/x-markdown', mode: markdown({ defaultCodeLanguage: markdownLanguage }), ext: [ 'markdown', 'md', 'mkd' ] }, - { name: 'MariaDB', mime: 'text/x-mariadb', mode: sql({ dialect: MariaSQL }) }, - { name: 'MS SQL', mime: 'text/x-mssql', mode: sql({ dialect: MSSQL }) }, - { name: 'MySQL', mime: 'text/x-mysql', mode: sql({ dialect: MySQL }) }, - { name: 'Plain Text', mime: 'text/plain', mode: undefined, ext: [ 'txt', 'text', 'conf', 'def', 'list', 'log' ] }, - { name: 'PostgreSQL', mime: 'text/x-pgsql', mode: sql({ dialect: PostgreSQL }) }, - { name: 'Properties', mime: 'text/x-properties', mode: properties, ext: [ 'properties', 'ini', 'in' ], alias: [ 'ini', 'properties' ] }, - { name: 'Python', mime: 'text/x-python', mode: python, ext: [ 'BUILD', 'bzl', 'py', 'pyw' ], file: /^(BUCK|BUILD)$/ }, - { name: 'Ruby', mime: 'text/x-ruby', mode: ruby, ext: [ 'rb' ], alias: [ 'jruby', 'macruby', 'rake', 'rb', 'rbx' ] }, - { name: 'Rust', mime: 'text/x-rustsrc', mode: rust(), ext: [ 'rs' ] }, - { name: 'Sass', mime: 'text/x-sass', mode: css(), ext: [ 'sass' ] }, - { name: 'SCSS', mime: 'text/x-scss', mode: css(), ext: [ 'scss' ] }, - { name: 'Shell', mime: 'text/x-sh', mimes: [ 'text/x-sh', 'application/x-sh' ], mode: shell, ext: [ 'sh', 'ksh', 'bash' ], alias: [ 'bash', 'sh', 'zsh' ], file: /^PKGBUILD$/ }, - { name: 'SQL', mime: 'text/x-sql', mode: sql({ dialect: StandardSQL }), ext: [ 'sql' ] }, - { name: 'SQLite', mime: 'text/x-sqlite', mode: sql({ dialect: SQLite }) }, - { name: 'TOML', mime: 'text/x-toml', mode: toml, ext: [ 'toml' ] }, - { name: 'TypeScript', mime: 'application/typescript', mode: typescriptLanguage, ext: [ 'ts' ], alias: [ 'ts' ] }, - { name: 'XML', mime: 'application/xml', mimes: [ 'application/xml', 'text/xml' ], mode: xml(), ext: [ 'xml', 'xsl', 'xsd', 'svg' ], alias: [ 'rss', 'wsdl', 'xsd' ] }, - { name: 'YAML', mime: 'text/x-yaml', mimes: [ 'text/x-yaml', 'text/yaml' ], mode: yaml, ext: [ 'yaml', 'yml' ], alias: [ 'yml' ] }, -]; - -export const modeToExtension = (m: EditorMode): Extension => { - if (m instanceof LanguageSupport) { - return m; - } - - if (m instanceof LRLanguage) { - return m; - } - - return StreamLanguage.define(m); -}; - -const findModeByFilename = (filename: string): Mode => { - for (let i = 0; i < modes.length; i++) { - const info = modes[i]; - - if (info.file && info.file.test(filename)) { - return info; - } - } - - const dot = filename.lastIndexOf('.'); - const ext = dot > -1 && filename.substring(dot + 1, filename.length); - - if (ext) { - for (let i = 0; i < modes.length; i++) { - const info = modes[i]; - if (info.ext) { - for (let j = 0; j < info.ext.length; j++) { - if (info.ext[j] === ext) { - return info; - } - } - } - } - } - - const plainText = modes.find(m => m.mime === 'text/plain'); - if (plainText === undefined) { - throw new Error('failed to find \'text/plain\' mode'); - } - return plainText; -}; - -const findLanguageExtensionByMode = (mode: Mode): Extension => { - if (mode.mode === undefined) { - return []; - } - return modeToExtension(mode.mode); -}; - -const defaultExtensions: Extension = [ - ayuMirage, - - lineNumbers(), - highlightActiveLineGutter(), - highlightSpecialChars(), - history(), - foldGutter(), - drawSelection(), - EditorState.allowMultipleSelections.of(true), - indentOnInput(), - defaultHighlightStyle.fallback, - bracketMatching(), - closeBrackets(), - autocompletion(), - rectangularSelection(), - highlightActiveLine(), - highlightSelectionMatches(), - keymap.of([ - ...closeBracketsKeymap, - ...defaultKeymap, - ...searchKeymap, - ...historyKeymap, - ...foldKeymap, - ...commentKeymap, - ...completionKeymap, - ...lintKeymap, - indentWithTab, - ]), - EditorState.tabSize.of(4), - // This is gonna piss people off, but that isn't my problem. - indentUnit.of('\t'), -]; - -const EditorContainer = styled.div<{ overrides?: TwStyle }>` - //min-height: 12rem; - ${tw`relative`}; - - & > div { - ${props => props.overrides}; - - &.cm-focused { - outline: none; - } - } -`; - -export interface Props { - className?: string; - style?: React.CSSProperties; - overrides?: TwStyle; - - initialContent?: string; - extensions?: Extension[]; - mode?: EditorMode; - - filename?: string; - onModeChanged?: (mode: Mode) => void; - fetchContent?: (callback: () => Promise) => void; - onContentSaved?: () => void; -} - -export default ({ className, style, overrides, initialContent, extensions, mode, filename, onModeChanged, fetchContent, onContentSaved }: Props) => { - const [ languageConfig ] = useState(new Compartment()); - const [ keybinds ] = useState(new Compartment()); - const [ view, setView ] = useState(); - - const createEditorState = () => { - return EditorState.create({ - doc: initialContent, - extensions: [ - ...defaultExtensions, - ...(extensions !== undefined ? extensions : []), - - languageConfig.of(mode !== undefined ? modeToExtension(mode) : findLanguageExtensionByMode(findModeByFilename(filename || ''))), - keybinds.of([]), - ], - }); - }; - - const ref = useCallback((node) => { - if (!node) { - return; - } - - const view = new EditorView({ - state: createEditorState(), - parent: node, - }); - setView(view); - }, []); - - // This useEffect is required to send the proper mode back to the parent element - // due to the initial language being set with EditorState#create, rather than in - // an useEffect like this one, or one watching `filename`. - useEffect(() => { - if (onModeChanged === undefined) { - return; - } - - onModeChanged(findModeByFilename(filename || '')); - }, []); - - useEffect(() => { - if (view === undefined) { - return; - } - - if (mode === undefined) { - return; - } - - view.dispatch({ - effects: languageConfig.reconfigure(modeToExtension(mode)), - }); - }, [ mode ]); - - useEffect(() => { - if (view === undefined) { - return; - } - - if (filename === undefined) { - return; - } - - const mode = findModeByFilename(filename || ''); - - view.dispatch({ - effects: languageConfig.reconfigure(findLanguageExtensionByMode(mode)), - }); - - if (onModeChanged !== undefined) { - onModeChanged(mode); - } - }, [ filename ]); - - useEffect(() => { - if (view === undefined) { - return; - } - - // We could dispatch a view update to replace the content, but this would keep the edit history, - // and previously would duplicate the content of the editor. - view.setState(createEditorState()); - }, [ initialContent ]); - - useEffect(() => { - if (fetchContent === undefined) { - return; - } - - if (!view) { - fetchContent(() => Promise.reject(new Error('no editor session has been configured'))); - return; - } - - if (onContentSaved !== undefined) { - view.dispatch({ - effects: keybinds.reconfigure(keymap.of([ - { - key: 'Mod-s', - run: () => { - onContentSaved(); - return true; - }, - }, - ])), - }); - } - - fetchContent(() => Promise.resolve(view.state.doc.toString())); - }, [ view, fetchContent, onContentSaved ]); - - return ( - - ); -}; diff --git a/resources/scripts/components/elements/button/Button.tsx b/resources/scripts/components/elements/button/Button.tsx index 6b140ddf1..e4bd4ddfb 100644 --- a/resources/scripts/components/elements/button/Button.tsx +++ b/resources/scripts/components/elements/button/Button.tsx @@ -1,6 +1,8 @@ import { forwardRef } from 'react'; import classNames from 'classnames'; -import { ButtonProps, Options } from '@/components/elements/button/types'; + +import type { ButtonProps } from '@/components/elements/button/types'; +import { Options } from '@/components/elements/button/types'; import styles from './style.module.css'; const Button = forwardRef( diff --git a/resources/scripts/components/elements/inputs/Checkbox.tsx b/resources/scripts/components/elements/inputs/Checkbox.tsx index e9c043b61..31f4720c8 100644 --- a/resources/scripts/components/elements/inputs/Checkbox.tsx +++ b/resources/scripts/components/elements/inputs/Checkbox.tsx @@ -1,15 +1,25 @@ -import { forwardRef } from 'react'; -import * as React from 'react'; import classNames from 'classnames'; +import type { ComponentProps } from 'react'; +import { forwardRef } from 'react'; + import styles from './styles.module.css'; -type Props = Omit, 'type'>; +type Props = Omit, 'type'> & { + indeterminate?: boolean; +}; -export default forwardRef(({ className, ...props }, ref) => ( +export default forwardRef(({ className, indeterminate, ...props }, ref) => ( )); diff --git a/resources/scripts/routers/AdminRouter.tsx b/resources/scripts/routers/AdminRouter.tsx index ddf9a4970..4f918a06e 100644 --- a/resources/scripts/routers/AdminRouter.tsx +++ b/resources/scripts/routers/AdminRouter.tsx @@ -48,9 +48,9 @@ import Sidebar from '@/components/admin/Sidebar'; import UsersContainer from '@/components/admin/users/UsersContainer'; function AdminRouter() { - // const email = useStoreState((state: State) => state.user.data!.email); - // const roleName = useStoreState((state: State) => state.user.data!.roleName); - // const avatarURL = useStoreState((state: State) => state.user.data!.avatarURL); + const email = useStoreState((state: ApplicationStore) => state.user.data!.email); + const roleName = useStoreState((state: ApplicationStore) => state.user.data!.roleName); + const avatarURL = useStoreState((state: ApplicationStore) => state.user.data!.avatarURL); const applicationName = useStoreState((state: ApplicationStore) => state.settings.data!.name); // const [collapsed, setCollapsed] = useUserPersistedState('admin_sidebar_collapsed', false); @@ -71,7 +71,7 @@ function AdminRouter() {
Administration - + Overview @@ -118,27 +118,27 @@ function AdminRouter() { Return - {/**/} - {/* {avatarURL && (*/} - {/* */} - {/* )}*/} - {/*
*/} - {/* */} - {/* {email}*/} - {/* */} - {/* */} - {/* {roleName}*/} - {/* */} - {/*
*/} - {/*
*/} + + {avatarURL && ( + Profile Picture + )} +
+ + {email} + + + {roleName} + +
+
diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index fa97dba9c..e3c91b64d 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -64,7 +64,7 @@ function ServerRouter() { error ? ( ) : ( - + ) ) : ( <> @@ -75,21 +75,24 @@ function ServerRouter() { .map(route => route.permission ? ( - + {route.name} ) : ( - + {route.name} ), )} {rootAdmin && ( - // eslint-disable-next-line react/jsx-no-target-blank - + - + )}
diff --git a/resources/scripts/state/user.ts b/resources/scripts/state/user.ts index 0ec785137..bf2c9b610 100644 --- a/resources/scripts/state/user.ts +++ b/resources/scripts/state/user.ts @@ -8,6 +8,8 @@ export interface UserData { language: string; rootAdmin: boolean; useTotp: boolean; + avatarURL: string; + roleName: string; createdAt: Date; updatedAt: Date; }