finish config edit page
This commit is contained in:
parent
529885498d
commit
04962ba56a
@ -1,6 +1,6 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
# Build commands:
|
# Build commands:
|
||||||
# - $VERSION=0.9
|
# - $VERSION=1.0
|
||||||
# - rm build -r -Force ; rm ./node_modules/.cache/babel-loader -r -Force ; yarn build
|
# - rm build -r -Force ; rm ./node_modules/.cache/babel-loader -r -Force ; yarn build
|
||||||
# - docker build --pull --rm -t oncharterliz/multi-frigate:latest -t oncharterliz/multi-frigate:$VERSION "."
|
# - docker build --pull --rm -t oncharterliz/multi-frigate:latest -t oncharterliz/multi-frigate:$VERSION "."
|
||||||
# - docker image push --all-tags oncharterliz/multi-frigate
|
# - docker image push --all-tags oncharterliz/multi-frigate
|
||||||
|
|||||||
@ -1,28 +1,61 @@
|
|||||||
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
import { Button, Flex, Text, useMantineTheme } from '@mantine/core';
|
||||||
|
import { useClipboard } from '@mantine/hooks';
|
||||||
|
import Editor, { Monaco } from '@monaco-editor/react';
|
||||||
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import * as monaco from "monaco-editor";
|
||||||
|
import { SchemasSettings, configureMonacoYaml } from 'monaco-yaml';
|
||||||
|
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { Context } from '..';
|
import { Context } from '..';
|
||||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
|
||||||
import { frigateApi, frigateQueryKeys, mapHostToHostname, proxyApi } from '../services/frigate.proxy/frigate.api';
|
|
||||||
import { Button, Flex, useMantineTheme, Text } from '@mantine/core';
|
|
||||||
import { useClipboard } from '@mantine/hooks';
|
|
||||||
import Editor, { Monaco } from '@monaco-editor/react'
|
|
||||||
import * as monaco from "monaco-editor";
|
|
||||||
import CenterLoader from '../shared/components/loaders/CenterLoader';
|
|
||||||
import RetryErrorPage from './RetryErrorPage';
|
|
||||||
import { useAdminRole } from '../hooks/useAdminRole';
|
import { useAdminRole } from '../hooks/useAdminRole';
|
||||||
import Forbidden from './403';
|
import { frigateApi, frigateQueryKeys, mapHostToHostname, proxyApi } from '../services/frigate.proxy/frigate.api';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { GetFrigateHost } from '../services/frigate.proxy/frigate.schema';
|
||||||
|
import CenterLoader from '../shared/components/loaders/CenterLoader';
|
||||||
import { isProduction } from '../shared/env.const';
|
import { isProduction } from '../shared/env.const';
|
||||||
import { SaveOption } from '../types/saveConfig';
|
import { SaveOption } from '../types/saveConfig';
|
||||||
import { GetFrigateHost } from '../services/frigate.proxy/frigate.schema';
|
import Forbidden from './403';
|
||||||
import { error } from 'console';
|
import RetryErrorPage from './RetryErrorPage';
|
||||||
|
import { notifications } from '@mantine/notifications';
|
||||||
|
import { IconAlertCircle, IconCircleCheck } from '@tabler/icons-react';
|
||||||
|
|
||||||
|
|
||||||
|
window.MonacoEnvironment = {
|
||||||
|
getWorker(moduleId, label) {
|
||||||
|
switch (label) {
|
||||||
|
case 'editorWorkerService':
|
||||||
|
return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url))
|
||||||
|
case 'css':
|
||||||
|
case 'less':
|
||||||
|
case 'scss':
|
||||||
|
return new Worker(new URL('monaco-editor/esm/vs/language/css/css.worker', import.meta.url))
|
||||||
|
case 'handlebars':
|
||||||
|
case 'html':
|
||||||
|
case 'razor':
|
||||||
|
return new Worker(
|
||||||
|
new URL('monaco-editor/esm/vs/language/html/html.worker', import.meta.url)
|
||||||
|
)
|
||||||
|
case 'json':
|
||||||
|
return new Worker(
|
||||||
|
new URL('monaco-editor/esm/vs/language/json/json.worker', import.meta.url)
|
||||||
|
)
|
||||||
|
case 'javascript':
|
||||||
|
case 'typescript':
|
||||||
|
return new Worker(
|
||||||
|
new URL('monaco-editor/esm/vs/language/typescript/ts.worker', import.meta.url)
|
||||||
|
)
|
||||||
|
case 'yaml':
|
||||||
|
return new Worker(new URL('monaco-yaml/yaml.worker', import.meta.url))
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown label ${label}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const HostConfigPage = () => {
|
const HostConfigPage = () => {
|
||||||
const executed = useRef(false)
|
const executed = useRef(false)
|
||||||
const host = useRef<GetFrigateHost | undefined>()
|
const host = useRef<GetFrigateHost | undefined>()
|
||||||
const { sideBarsStore } = useContext(Context)
|
const { sideBarsStore } = useContext(Context)
|
||||||
const [saveMessage, setSaveMessage] = useState<string>()
|
|
||||||
|
|
||||||
let { id } = useParams<'id'>()
|
let { id } = useParams<'id'>()
|
||||||
const { isAdmin, isLoading: adminLoading } = useAdminRole()
|
const { isAdmin, isLoading: adminLoading } = useAdminRole()
|
||||||
@ -51,10 +84,26 @@ const HostConfigPage = () => {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setSaveMessage(data?.message)
|
notifications.show({
|
||||||
|
id: data?.message,
|
||||||
|
withCloseButton: true,
|
||||||
|
autoClose: 5000,
|
||||||
|
title: `Sucess: ${data?.success}`,
|
||||||
|
message: data?.message,
|
||||||
|
color: 'green',
|
||||||
|
icon: <IconCircleCheck />
|
||||||
|
})
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (e) => {
|
||||||
setSaveMessage(error.message)
|
notifications.show({
|
||||||
|
id: e.message,
|
||||||
|
withCloseButton: true,
|
||||||
|
autoClose: false,
|
||||||
|
title: "Error",
|
||||||
|
message: e.message,
|
||||||
|
color: 'red',
|
||||||
|
icon: <IconAlertCircle />,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -72,28 +121,24 @@ const HostConfigPage = () => {
|
|||||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null)
|
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null)
|
||||||
|
|
||||||
function handleEditorWillMount(monaco: Monaco) {
|
function handleEditorWillMount(monaco: Monaco) {
|
||||||
monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true);
|
const hostName = mapHostToHostname(host.current)
|
||||||
|
if (!hostName) return
|
||||||
// TODO add yaml schema
|
const schemaURL = proxyApi.configSchemaURL(hostName)
|
||||||
// const modelUri = monaco.Uri.parse("http://localhost:4000/proxy/api/config/schema.json?hostName=localhost:5001")
|
const defaultSchema: SchemasSettings = {
|
||||||
// configureMonacoYaml(monaco, {
|
uri: schemaURL,
|
||||||
// enableSchemaRequest: true,
|
fileMatch: ['monaco-yaml.yaml']
|
||||||
// hover: true,
|
}
|
||||||
// completion: true,
|
configureMonacoYaml(monaco, {
|
||||||
// validate: true,
|
enableSchemaRequest: true,
|
||||||
// format: true,
|
schemas: [defaultSchema]
|
||||||
// schemas: [
|
})
|
||||||
// {
|
|
||||||
// uri: `http://localhost:4000/proxy/api/config/schema.json?hostName=localhost:5001`,
|
|
||||||
// fileMatch: ['**/.schema.*'],
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEditorDidMount(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) {
|
function handleEditorDidMount(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) {
|
||||||
// here is another way to get monaco instance
|
// here is another way to get monaco instance
|
||||||
// you can also store it in `useRef` for further usage
|
// you can also store it in `useRef` for further usage
|
||||||
|
const model = monaco.editor.createModel(config, 'yaml', monaco.Uri.parse('monaco-yaml.yaml'))
|
||||||
|
editor.setModel(model)
|
||||||
editorRef.current = editor;
|
editorRef.current = editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,11 +178,6 @@ const HostConfigPage = () => {
|
|||||||
Save Only
|
Save Only
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
{!saveMessage ? null :
|
|
||||||
<Flex w='100%' justify='center' wrap='nowrap' mt='1rem'>
|
|
||||||
<Text>{saveMessage}</Text>
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
<Flex h='100%' mt='1rem'>
|
<Flex h='100%' mt='1rem'>
|
||||||
<Editor
|
<Editor
|
||||||
defaultLanguage='yaml'
|
defaultLanguage='yaml'
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
import { Flex, Grid, Group } from '@mantine/core';
|
import { Flex, Group, useMantineTheme } from '@mantine/core';
|
||||||
|
import { Editor, Monaco } from '@monaco-editor/react';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
import { SchemasSettings, configureMonacoYaml } from 'monaco-yaml';
|
||||||
import { useContext, useEffect, useRef } from 'react';
|
import { useContext, useEffect, useRef } from 'react';
|
||||||
import { Context } from '..';
|
import { Context } from '..';
|
||||||
import HeadSearch from '../shared/components/inputs/HeadSearch';
|
import HeadSearch from '../shared/components/inputs/HeadSearch';
|
||||||
import TestItem from './TestItem';
|
|
||||||
|
|
||||||
const Test = () => {
|
const Test = () => {
|
||||||
const executed = useRef(false)
|
const executed = useRef(false)
|
||||||
|
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null)
|
||||||
|
const theme = useMantineTheme();
|
||||||
|
|
||||||
const { sideBarsStore } = useContext(Context)
|
const { sideBarsStore } = useContext(Context)
|
||||||
sideBarsStore.rightVisible = true
|
sideBarsStore.rightVisible = true
|
||||||
@ -21,20 +25,98 @@ const Test = () => {
|
|||||||
}, [sideBarsStore])
|
}, [sideBarsStore])
|
||||||
|
|
||||||
|
|
||||||
// const handleClick = () => {
|
|
||||||
// const startOfDay = new Date();
|
|
||||||
// startOfDay.setHours(0, 0, 0, 0);
|
|
||||||
// setValue([startOfDay, startOfDay])
|
|
||||||
// }
|
|
||||||
|
|
||||||
const cards = (qty: number) => {
|
|
||||||
let items = []
|
const value = `
|
||||||
for (let i = 0; i < qty; i++) {
|
# Property descriptions are displayed when hovering over properties using your cursor
|
||||||
items.push(<TestItem key={i} />)
|
property: This property has a JSON schema description
|
||||||
|
|
||||||
|
|
||||||
|
# Titles work too!
|
||||||
|
titledProperty: Titles work too!
|
||||||
|
|
||||||
|
|
||||||
|
# Even markdown descriptions work
|
||||||
|
markdown: hover me to get a markdown based description 😮
|
||||||
|
|
||||||
|
|
||||||
|
# Enums can be autocompleted by placing the cursor after the colon and pressing Ctrl+Space
|
||||||
|
enum:
|
||||||
|
|
||||||
|
|
||||||
|
# Unused anchors will be reported
|
||||||
|
unused anchor: &unused anchor
|
||||||
|
|
||||||
|
|
||||||
|
# Of course numbers are supported!
|
||||||
|
number: 12
|
||||||
|
|
||||||
|
|
||||||
|
# As well as booleans!
|
||||||
|
boolean: true
|
||||||
|
|
||||||
|
|
||||||
|
# And strings
|
||||||
|
string: I am a string
|
||||||
|
|
||||||
|
|
||||||
|
# This property is using the JSON schema recursively
|
||||||
|
reference:
|
||||||
|
boolean: Not a boolean
|
||||||
|
|
||||||
|
|
||||||
|
# Also works in arrays
|
||||||
|
array:
|
||||||
|
- string: 12
|
||||||
|
enum: Mewtwo
|
||||||
|
reference:
|
||||||
|
reference:
|
||||||
|
boolean: true
|
||||||
|
|
||||||
|
|
||||||
|
# JSON referenses can be clicked for navigation
|
||||||
|
pointer:
|
||||||
|
$ref: '#/array'
|
||||||
|
|
||||||
|
|
||||||
|
# This anchor can be referenced
|
||||||
|
anchorRef: &anchor can be clicked as well
|
||||||
|
|
||||||
|
|
||||||
|
# Press control while hovering over the anchor
|
||||||
|
anchorPointer: *anchor
|
||||||
|
|
||||||
|
|
||||||
|
formatting: Formatting is supported too! Under the hood this is powered by Prettier. Just press Ctrl+Shift+I or right click and press Format to format this document.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`.replace(/:$/m, ': ')
|
||||||
|
|
||||||
|
function handleEditorWillMount(monaco: Monaco) {
|
||||||
|
const defaultSchema: SchemasSettings = {
|
||||||
|
uri: 'https://github.com/remcohaszing/monaco-yaml/blob/HEAD/examples/demo/src/schema.json',
|
||||||
|
fileMatch: ['monaco-yaml.yaml']
|
||||||
}
|
}
|
||||||
return items
|
|
||||||
|
configureMonacoYaml(monaco, {
|
||||||
|
enableSchemaRequest: true,
|
||||||
|
schemas: [defaultSchema]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleEditorDidMount(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) {
|
||||||
|
// here is another way to get monaco instance
|
||||||
|
// you can also store it in `useRef` for further usage
|
||||||
|
const model = monaco.editor.createModel(value, 'yaml', monaco.Uri.parse('monaco-yaml.yaml'))
|
||||||
|
editor.setModel(model)
|
||||||
|
editorRef.current = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction='column' h='100%' >
|
<Flex direction='column' h='100%' >
|
||||||
<Flex justify='space-between' align='center' w='100%'>
|
<Flex justify='space-between' align='center' w='100%'>
|
||||||
@ -54,9 +136,14 @@ const Test = () => {
|
|||||||
</Group>
|
</Group>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justify='center' h='100%' direction='column' >
|
<Flex justify='center' h='100%' direction='column' >
|
||||||
<Grid mt='sm' justify="center" mb='sm' align='stretch'>
|
<Editor
|
||||||
{cards(60)}
|
defaultLanguage='yaml'
|
||||||
</Grid>
|
value={value}
|
||||||
|
defaultValue="// Data empty"
|
||||||
|
theme={theme.colorScheme === "dark" ? "vs-dark" : "vs-light"}
|
||||||
|
beforeMount={handleEditorWillMount}
|
||||||
|
onMount={handleEditorDidMount}
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -143,6 +143,7 @@ export const proxyApi = {
|
|||||||
|
|
||||||
getEventsSummary: (hostName: string, cameraName: string) =>
|
getEventsSummary: (hostName: string, cameraName: string) =>
|
||||||
instanceApi.get(`proxy/${hostName}/api/${cameraName}/events/summary`).then(res => res.data),
|
instanceApi.get(`proxy/${hostName}/api/${cameraName}/events/summary`).then(res => res.data),
|
||||||
|
configSchemaURL: (hostName: string) => `${proxyPrefix}${hostName}/api/config/schema.json`,
|
||||||
cameraWsURL: (hostName: string, cameraName: string) => {
|
cameraWsURL: (hostName: string, cameraName: string) => {
|
||||||
const protocol = proxyURL.protocol === 'https:' ? 'wss' : 'ws';
|
const protocol = proxyURL.protocol === 'https:' ? 'wss' : 'ws';
|
||||||
return `${protocol}://${proxyURL.host}/proxy-ws/${hostName}/live/jsmpeg/${cameraName}`
|
return `${protocol}://${proxyURL.host}/proxy-ws/${hostName}/live/jsmpeg/${cameraName}`
|
||||||
|
|||||||
@ -10857,7 +10857,12 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2:
|
|||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||||
|
|
||||||
yaml@^2.0.0, yaml@^2.3.4:
|
yaml@^2.0.0:
|
||||||
|
version "2.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed"
|
||||||
|
integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==
|
||||||
|
|
||||||
|
yaml@^2.3.4:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.0.tgz#2376db1083d157f4b3a452995803dbcf43b08140"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.0.tgz#2376db1083d157f4b3a452995803dbcf43b08140"
|
||||||
integrity sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==
|
integrity sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user