mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-06 07:53:54 +00:00
Feat/navigate (#81)
* feat/navigate upgrade react router * feat/navigate migrate to react router 6 * feat/navigate style modal * feat/navigate create endpoints for app settings * feat/navigate protect editor with pin * feat/navigate apply dynamic routing draft * feat/navigate upgrade relevant packages * feat/navigate restructure directory and add tests * feat/navigate test aliases validation * feat/navigate validate aliases before sending * feat/navigate create data endpoint * feat/navigate config: prettier * feat/navigate invalidate empty strings * feat/navigate create endpoints * feat/navigate parse on import * feat/navigate navigate to alias * feat/navigate user help and sample data * feat/navigate refact aliases modal * feat/navigate refact settings style * feat/navigate update sample db * feat/navigate link is relative to hostname * feat/navigate navigate to first match * feat/navigate update readme and version bump * feat/navigate config: create shared module * feat/navigate config: cheat module install * feat/navigate fix tests * feat/navigate run tests in pull request * Update ontime_cy.yml
This commit is contained in:
+8
-7
@@ -3,26 +3,26 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^1.7.1",
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@chakra-ui/react": "^1.7.3",
|
||||
"@emotion/react": "^11.7.1",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"axios": "^0.24.0",
|
||||
"framer-motion": "^4.1.6",
|
||||
"jotai": "^0.16.5",
|
||||
"ontime-utils": "link: ../server/utils/",
|
||||
"react": "17.0.2",
|
||||
"react-beautiful-dnd": "^13.1.0",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-fast-compare": "^3.2.0",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-qr-code": "^2.0.2",
|
||||
"react-qr-code": "2.0.3",
|
||||
"react-query": "^3.34.5",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-router-dom": "^6.2.1",
|
||||
"react-scripts": "5.0.0",
|
||||
"socket.io-client": "^4.3.2",
|
||||
"socket.io-client": "4.4.0",
|
||||
"typeface-open-sans": "^1.1.13",
|
||||
"use-fit-text": "^2.4.0",
|
||||
"web-vitals": "^1.0.1"
|
||||
@@ -31,6 +31,7 @@
|
||||
"start": "set BROWSER=none&&react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"test:pipeline": "react-scripts test --watchAll=false --runInBand --detectOpenHandles --forceExit ",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"browserslist": {
|
||||
|
||||
+49
-41
@@ -1,18 +1,17 @@
|
||||
import { lazy, Suspense, useCallback, useEffect } from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
||||
import './App.scss';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import SocketProvider from 'app/context/socketContext';
|
||||
import withSocket from 'features/viewers/ViewWrapper';
|
||||
import ErrorBoundary from 'common/components/errorBoundary/ErrorBoundary';
|
||||
import ProtectRoute from './common/components/protectRoute/ProtectRoute';
|
||||
import { useFetch } from './app/hooks/useFetch';
|
||||
import { ALIASES } from './app/api/apiConstants';
|
||||
import { getAliases } from './app/api/ontimeApi';
|
||||
|
||||
const Editor = lazy(() => import('features/editors/Editor'));
|
||||
const PresenterView = lazy(() =>
|
||||
import('features/viewers/presenter/PresenterView')
|
||||
);
|
||||
const PresenterSimple = lazy(() =>
|
||||
import('features/viewers/presenter/PresenterSimple')
|
||||
);
|
||||
const StageManager = lazy(() =>
|
||||
import('features/viewers/backstage/StageManager')
|
||||
);
|
||||
@@ -21,18 +20,9 @@ const Lower = lazy(() =>
|
||||
import('features/viewers/production/lower/LowerWrapper')
|
||||
);
|
||||
const Pip = lazy(() => import('features/viewers/production/Pip'));
|
||||
|
||||
const StudioClock = lazy(() => import('features/viewers/studio/StudioClock'));
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
// Seemed to cause issues
|
||||
// broadcastQueryClient({
|
||||
// queryClient,
|
||||
// broadcastChannel: 'ontime',
|
||||
// });
|
||||
|
||||
const SPresenter = withSocket(PresenterView);
|
||||
const SPresenterSimple = withSocket(PresenterSimple);
|
||||
const SStageManager = withSocket(StageManager);
|
||||
const SPublic = withSocket(Public);
|
||||
const SLowerThird = withSocket(Lower);
|
||||
@@ -40,6 +30,10 @@ const SPip = withSocket(Pip);
|
||||
const SStudio = withSocket(StudioClock);
|
||||
|
||||
function App() {
|
||||
const { data } = useFetch(ALIASES, getAliases);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Handle keyboard shortcuts
|
||||
const handleKeyPress = useCallback((e) => {
|
||||
// check if the alt key is pressed
|
||||
@@ -65,33 +59,47 @@ function App() {
|
||||
};
|
||||
}, [handleKeyPress]);
|
||||
|
||||
// navigate if is alias route
|
||||
useEffect(() => {
|
||||
if (data == null) return;
|
||||
for (const d of data) {
|
||||
if (`/${d.alias}` === location.pathname && d.enabled) {
|
||||
navigate(`/${d.pathAndParams}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [data, location, navigate]);
|
||||
|
||||
return (
|
||||
<SocketProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<div className='App'>
|
||||
<ErrorBoundary>
|
||||
<Suspense fallback={null}>
|
||||
<Switch>
|
||||
<Route exact path='/' component={SPresenter} />
|
||||
<Route exact path='/sm' component={SStageManager} />
|
||||
<Route exact path='/speaker' component={SPresenter} />
|
||||
<Route exact path='/presenter' component={SPresenter} />
|
||||
<Route exact path='/stage' component={SPresenter} />
|
||||
<Route exact path='/presentersimple' component={SPresenterSimple} />
|
||||
<Route exact path='/editor' component={Editor} />
|
||||
<Route exact path='/public' component={SPublic} />
|
||||
<Route exact path='/pip' component={SPip} />
|
||||
<Route exact path='/studio' component={SStudio} />
|
||||
{/* Lower cannot have fallback */}
|
||||
<Route exact path='/lower' component={SLowerThird} />
|
||||
{/* Send to default if nothing found */}
|
||||
<Route component={SPresenter} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</QueryClientProvider>
|
||||
</SocketProvider>
|
||||
<div className='App'>
|
||||
<ErrorBoundary>
|
||||
<Suspense fallback={null}>
|
||||
<Routes>
|
||||
<Route path='/' element={<SPresenter />} />
|
||||
<Route path='/sm' element={<SStageManager />} />
|
||||
<Route path='/speaker' element={<SPresenter />} />
|
||||
<Route path='/presenter' element={<SPresenter />} />
|
||||
<Route path='/stage' element={<SPresenter />} />
|
||||
<Route path='/public' element={<SPublic />} />
|
||||
<Route path='/pip' element={<SPip />} />
|
||||
<Route path='/studio' element={<SStudio />} />
|
||||
{/*/!* Lower cannot have fallback *!/*/}
|
||||
<Route path='/lower' element={<SLowerThird />} />
|
||||
{/*/!* Protected Routes *!/*/}
|
||||
<Route
|
||||
path='/editor'
|
||||
element={
|
||||
<ProtectRoute>
|
||||
<Editor />
|
||||
</ProtectRoute>
|
||||
}
|
||||
/>
|
||||
{/* Send to default if nothing found */}
|
||||
<Route path='*' element={<SPresenter />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
$ontime-accent: #4bffabcc;
|
||||
$ontime-pink: #ff7597;
|
||||
$ontime-roll: #2b6cb0;
|
||||
|
||||
$notes-color: #d69e2e;
|
||||
|
||||
$header-gray: #ccc;
|
||||
$label-gray: #aaa;
|
||||
|
||||
@mixin container-bg {
|
||||
background-color: rgba(0, 0, 0, 0.13);
|
||||
border-radius: 2px;
|
||||
padding: 0 0.5em;
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
export const NODE_PORT = 4001;
|
||||
export const EVENT_TABLE = 'event';
|
||||
export const ALIASES = 'aliases';
|
||||
export const EVENTS_TABLE = 'events';
|
||||
export const APP_TABLE = 'appinfo';
|
||||
export const OSC_SETTINGS = 'oscSettings';
|
||||
export const APP_SETTINGS = 'appSettings';
|
||||
|
||||
const calculateServer = () => {
|
||||
return window.location.origin.replace(window.location.port, `${NODE_PORT}/`);
|
||||
|
||||
@@ -9,6 +9,18 @@ export const ontimePlaceholderInfo = {
|
||||
},
|
||||
};
|
||||
|
||||
export const ontimePlaceholderSettings = {
|
||||
pinCode: null,
|
||||
};
|
||||
|
||||
export const eventPlaceholderSettings = {
|
||||
title: '',
|
||||
url: '',
|
||||
publicInfo: '',
|
||||
backstageInfo: '',
|
||||
endMessage: '',
|
||||
};
|
||||
|
||||
export const oscPlaceholderSettings = {
|
||||
port: '',
|
||||
portOut: '',
|
||||
@@ -74,6 +86,15 @@ export const ontimeVars = [
|
||||
},
|
||||
];
|
||||
|
||||
export const getSettings = async () => {
|
||||
const res = await axios.get(`${ontimeURL}/settings`);
|
||||
return res.data;
|
||||
};
|
||||
|
||||
export const postSettings = async (data) => {
|
||||
return await axios.post(`${ontimeURL}/settings`, data);
|
||||
};
|
||||
|
||||
export const getInfo = async () => {
|
||||
const res = await axios.get(`${ontimeURL}/info`);
|
||||
return res.data;
|
||||
@@ -83,6 +104,15 @@ export const postInfo = async (data) => {
|
||||
return await axios.post(`${ontimeURL}/info`, data);
|
||||
};
|
||||
|
||||
export const getAliases = async () => {
|
||||
const res = await axios.get(`${ontimeURL}/aliases`);
|
||||
return res.data;
|
||||
};
|
||||
|
||||
export const postAliases = async (data) => {
|
||||
return await axios.post(`${ontimeURL}/aliases`, data);
|
||||
};
|
||||
|
||||
export const getOSC = async () => {
|
||||
const res = await axios.get(`${ontimeURL}/osc`);
|
||||
return res.data;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Exported viewer links
|
||||
const speakerLink = 'http://localhost:4001/speaker';
|
||||
const smLink = 'http://localhost:4001/sm';
|
||||
const publicLink = 'http://localhost:4001/public';
|
||||
const pipLink = 'http://localhost:4001/pip';
|
||||
const studioLink = 'http://localhost:4001/studio';
|
||||
|
||||
export const viewerLinks = [
|
||||
{ link: speakerLink, label: 'Speaker Screen' },
|
||||
{ link: smLink, label: 'Backstage Screen' },
|
||||
{ link: publicLink, label: 'Public Screen' },
|
||||
{ link: pipLink, label: 'Picture in Picture' },
|
||||
{ link: studioLink, label: 'Studio Clock' }
|
||||
];
|
||||
@@ -0,0 +1,37 @@
|
||||
import { createContext, useCallback, useEffect, useState } from 'react';
|
||||
import { useFetch } from '../hooks/useFetch';
|
||||
import { APP_SETTINGS } from '../api/apiConstants';
|
||||
import { getSettings } from '../api/ontimeApi';
|
||||
|
||||
export const AppContext = createContext({
|
||||
auth: false,
|
||||
data: {
|
||||
pinCode: null
|
||||
}
|
||||
});
|
||||
|
||||
export const AppContextProvider = (props) => {
|
||||
const [auth, setAuth] = useState(true);
|
||||
const { data } = useFetch(APP_SETTINGS, getSettings);
|
||||
|
||||
useEffect(() => {
|
||||
if (data == null) return;
|
||||
if (data?.pinCode === null || data?.pinCode === '') {
|
||||
setAuth(true);
|
||||
} else {
|
||||
setAuth(false);
|
||||
}
|
||||
},[data])
|
||||
|
||||
const validate = useCallback((pin) => {
|
||||
const correct = pin === data.pinCode;
|
||||
setAuth(correct);
|
||||
return correct;
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={{ auth, validate }}>
|
||||
{props.children}
|
||||
</AppContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useSocket } from './socketContext';
|
||||
import { createContext, useCallback, useEffect, useState } from 'react';
|
||||
import { generateId } from 'ontime-server/utils/generate_id';
|
||||
import { nowInMillis, stringFromMillis } from 'ontime-server/utils/time';
|
||||
import { generateId } from 'ontime-utils/generate_id';
|
||||
import { nowInMillis, stringFromMillis } from 'ontime-utils/time';
|
||||
|
||||
export const LoggingContext = createContext({
|
||||
logData: [],
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export const userConfig = {
|
||||
timerColorOnPause: '#555',
|
||||
timerColorOnRunning: '#FFF',
|
||||
timerColorOnMessage: '#CCC',
|
||||
timerColorOnTimeOver: '#F00',
|
||||
overTimeText: '',
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export const clamp = (num, a, b) =>
|
||||
Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
|
||||
@@ -0,0 +1,27 @@
|
||||
import { validateAlias } from '../aliases';
|
||||
|
||||
describe('An alias fails if incorrect', () => {
|
||||
|
||||
const testsToFail = [
|
||||
// no empty
|
||||
'',
|
||||
// no https, http or www
|
||||
'https://www.test.com',
|
||||
'http://www.test.com',
|
||||
'www.test.com',
|
||||
// no hostname
|
||||
'localhost/test',
|
||||
'127.0.0.1/test',
|
||||
'0.0.0.0/test',
|
||||
// no editor
|
||||
'editor',
|
||||
'editor?test'
|
||||
];
|
||||
|
||||
testsToFail.forEach((t) => (
|
||||
test(`${t}`, () => {
|
||||
expect(validateAlias(t).status).toBeFalsy();
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { clamp } from '../math';
|
||||
|
||||
test('Clamps a set of numbers correctly', () => {
|
||||
const testCases = [
|
||||
{ num: 10, min: 0, max: 20, result: 10 },
|
||||
{ num: 0, min: 0, max: 20, result: 0 },
|
||||
{ num: 20, min: 0, max: 20, result: 20 },
|
||||
{ num: 20, min: 0, max: 20, result: 20 },
|
||||
{ num: -20, min: 0, max: 20, result: 0 },
|
||||
{ num: -0, min: 0, max: 20, result: 0 },
|
||||
{ num: -50, min: -30, max: -20, result: -30 },
|
||||
{ num: -50, min: 0, max: 0, result: 0 },
|
||||
{ num: 50.5, min: 0, max: 100, result: 50.5 },
|
||||
{ num: 50, min: 0, max: 20.32, result: 20.32 },
|
||||
{ num: 10, min: 20.32, max: 40, result: 20.32 }
|
||||
];
|
||||
|
||||
testCases.forEach((t) => (
|
||||
expect(clamp(t.num, t.min, t.max)).toBe(t.result)
|
||||
));
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Validates an alias against defined parameters
|
||||
* @param {string} alias
|
||||
* @returns {{message: string, status: boolean}}
|
||||
*/
|
||||
export const validateAlias = (alias) => {
|
||||
|
||||
const valid = { status: true, message: 'ok' };
|
||||
|
||||
if (alias === '' || alias == null) {
|
||||
// cannot be empty
|
||||
valid.status = false;
|
||||
valid.message = 'should not be empty';
|
||||
} else if (alias.includes('http') || alias.includes('https') || alias.includes('www')) {
|
||||
// cannot contain http, https or www
|
||||
valid.status = false;
|
||||
valid.message = 'should not include http, https, www';
|
||||
} else if (alias.includes('127.0.0.1') || alias.includes('localhost') || alias.includes('0.0.0.0')) {
|
||||
// aliases cannot contain hostname
|
||||
valid.status = false;
|
||||
valid.message = 'should not include hostname';
|
||||
} else if (alias.includes('editor')) {
|
||||
// no editor
|
||||
valid.status = false;
|
||||
valid.message = 'No aliases to editor page allowed';
|
||||
}
|
||||
|
||||
return valid;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Clamps a value between a min and a max
|
||||
* @param {number} num - Value to clamp
|
||||
* @param {number} min - min value
|
||||
* @param {number} max - max value
|
||||
* @returns {number}
|
||||
*/
|
||||
export const clamp = (num, min, max) =>
|
||||
Math.max(Math.min(num, Math.max(min, max)), Math.min(min, max));
|
||||
@@ -1,5 +1,5 @@
|
||||
import EditableTimer from 'common/input/EditableTimer';
|
||||
import { stringFromMillis } from 'ontime-server/utils/time';
|
||||
import { stringFromMillis } from 'ontime-utils/time';
|
||||
import { useContext } from 'react';
|
||||
import { LoggingContext } from '../../../app/context/LoggingContext';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { clamp } from 'app/utils';
|
||||
import { clamp } from 'app/utils/math';
|
||||
import styles from './MyProgressBar.module.css';
|
||||
|
||||
export default function MyProgressBar(props) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { Link, Redirect } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Image } from '@chakra-ui/react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
@@ -57,7 +57,6 @@ export default function NavLogo(props) {
|
||||
to='/presenter'
|
||||
className={style.navItem}
|
||||
tabIndex={1}
|
||||
onKeyDownCapture={() => <Redirect push to='/presenter' />}
|
||||
>
|
||||
Presenter
|
||||
</Link>
|
||||
@@ -65,7 +64,6 @@ export default function NavLogo(props) {
|
||||
to='/sm'
|
||||
className={style.navItem}
|
||||
tabIndex={2}
|
||||
onKeyDownCapture={() => <Redirect push to='/sm' />}
|
||||
>
|
||||
Backstage
|
||||
</Link>
|
||||
@@ -73,7 +71,6 @@ export default function NavLogo(props) {
|
||||
to='/public'
|
||||
className={style.navItem}
|
||||
tabIndex={3}
|
||||
onKeyDownCapture={() => <Redirect push to='/public' />}
|
||||
>
|
||||
Public
|
||||
</Link>
|
||||
@@ -81,7 +78,6 @@ export default function NavLogo(props) {
|
||||
to='/lower'
|
||||
className={style.navItem}
|
||||
tabIndex={4}
|
||||
onKeyDownCapture={() => <Redirect push to='/lower' />}
|
||||
>
|
||||
Lower Thirds
|
||||
</Link>
|
||||
@@ -89,7 +85,6 @@ export default function NavLogo(props) {
|
||||
to='/pip'
|
||||
className={style.navItem}
|
||||
tabIndex={4}
|
||||
onKeyDownCapture={() => <Redirect push to='/pip' />}
|
||||
>
|
||||
PIP
|
||||
</Link>
|
||||
@@ -97,7 +92,6 @@ export default function NavLogo(props) {
|
||||
to='/studio'
|
||||
className={style.navItem}
|
||||
tabIndex={5}
|
||||
onKeyDownCapture={() => <Redirect push to='/studio' />}
|
||||
>
|
||||
Studio Clock
|
||||
</Link>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import style from './ProtectRoute.module.scss';
|
||||
import { PinInput, PinInputField } from '@chakra-ui/react';
|
||||
import { IconButton } from '@chakra-ui/button';
|
||||
import { FiCheck } from 'react-icons/fi';
|
||||
import { AppContext } from '../../../app/context/AppContext';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
|
||||
|
||||
export default function ProtectRoute(props) {
|
||||
const isLocal = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
|
||||
const [pin, setPin] = useState('');
|
||||
const [failed, setFailed] = useState(false);
|
||||
const { auth, validate } = useContext(AppContext);
|
||||
|
||||
// Set window title
|
||||
useEffect(() => {
|
||||
document.title = 'ontime';
|
||||
}, []);
|
||||
|
||||
const handleValidation = () => {
|
||||
const r = validate(pin);
|
||||
if (!r) {
|
||||
setFailed(true);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isLocal && !auth ? (
|
||||
<div className={style.container}>
|
||||
ontime
|
||||
<div className={failed ? style.pin__failed : style.pin}>
|
||||
<PinInput
|
||||
type='alphanumeric'
|
||||
size='lg'
|
||||
mask
|
||||
onChange={(value) => {
|
||||
setFailed(false);
|
||||
setPin(value);
|
||||
}}
|
||||
>
|
||||
<PinInputField />
|
||||
<PinInputField />
|
||||
<PinInputField />
|
||||
<PinInputField />
|
||||
</PinInput>
|
||||
<IconButton
|
||||
aria-label='Enter'
|
||||
size='lg'
|
||||
isRound
|
||||
icon={<FiCheck />}
|
||||
style={{ fontSize: '1.5em' }}
|
||||
onClick={() => handleValidation()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
props.children
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ProtectRoute.propTypes = {
|
||||
children: PropTypes.node.isRequired
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
@use '../../../styles/main' as *;
|
||||
@use '../../../styles/mixins' as *;
|
||||
|
||||
.container {
|
||||
background: #222;
|
||||
|
||||
display: grid;
|
||||
place-content: center;
|
||||
height: 100vh;
|
||||
padding-bottom: 30vh;
|
||||
|
||||
color: $ontime-pink;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
font-size: 3vw;
|
||||
}
|
||||
|
||||
.pin,
|
||||
.pin__failed {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 20px;
|
||||
|
||||
input {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.pin__failed {
|
||||
input {
|
||||
animation: colourFade 1.5s ease;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes colourFade {
|
||||
from {
|
||||
background: $ontime-pink;
|
||||
}
|
||||
to {
|
||||
background: rgba($ontime-pink, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { stringFromMillis } from 'ontime-server/utils/time';
|
||||
import { stringFromMillis } from 'ontime-utils/time';
|
||||
import style from './Paginator.module.css';
|
||||
export default function TodayItem(props) {
|
||||
const { selected, timeStart, timeEnd, title, backstageEvent } = props;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
isTimeString,
|
||||
timeStringToMillis,
|
||||
} from '../utils/dateConfig';
|
||||
import { stringFromMillis } from 'ontime-server/utils/time';
|
||||
import { stringFromMillis } from 'ontime-utils/time';
|
||||
import style from './EditableTimer.module.css';
|
||||
import { LoggingContext } from '../../app/context/LoggingContext';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { stringFromMillis } from 'ontime-server/utils/time';
|
||||
import { stringFromMillis } from 'ontime-utils/time';
|
||||
|
||||
/**
|
||||
* @description From a list of events, returns only events of type event with calculated delays
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Handles link to external URLs: specifically for a electron / browser case
|
||||
* If electron: ask main process to call a new browser window
|
||||
* If browser: open in new tab
|
||||
* @param url
|
||||
*/
|
||||
export default function handleLink(url) {
|
||||
if (window.process?.type === 'renderer') {
|
||||
window.ipcRenderer.send('send-to-link', url);
|
||||
} else {
|
||||
window.open(url);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import style from './PlaybackControl.module.scss';
|
||||
import Countdown from 'common/components/countdown/Countdown';
|
||||
import { stringFromMillis } from 'ontime-server/utils/time';
|
||||
import { stringFromMillis } from 'ontime-utils/time';
|
||||
import {Tooltip} from '@chakra-ui/react';
|
||||
import {Button} from '@chakra-ui/button';
|
||||
import {memo} from 'react';
|
||||
|
||||
@@ -24,12 +24,14 @@ export default function Editor() {
|
||||
|
||||
return (
|
||||
<LoggingProvider>
|
||||
<ModalManager isOpen={isOpen} onClose={onClose} />
|
||||
<ErrorBoundary>
|
||||
<ModalManager isOpen={isOpen} onClose={onClose} />
|
||||
</ErrorBoundary>
|
||||
|
||||
<div className={styles.mainContainer}>
|
||||
<Box id='settings' className={styles.settings}>
|
||||
<ErrorBoundary>
|
||||
<MenuBar onOpen={onOpen} />
|
||||
<MenuBar onOpen={onOpen} isOpen={isOpen} />
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@use '../../main' as *;
|
||||
@use '../../styles/main' as *;
|
||||
@use '../../styles/mixins' as *;
|
||||
|
||||
@mixin container {
|
||||
margin-top: 1em;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@use 'Info.module' as *;
|
||||
@use '../../main' as *;
|
||||
@use '../../styles/main' as *;
|
||||
@use '../../styles/mixins' as *;
|
||||
|
||||
.container,
|
||||
.container__expanded{
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getInfo, ontimePlaceholderInfo } from 'app/api/ontimeApi';
|
||||
import { useFetch } from 'app/hooks/useFetch';
|
||||
import style from './Info.module.scss';
|
||||
import CollapseBar from '../../common/components/collapseBar/CollapseBar';
|
||||
import handleLink from '../../common/utils/handleLink';
|
||||
|
||||
export default function InfoNif() {
|
||||
const { data, status } = useFetch(APP_TABLE, getInfo, {
|
||||
@@ -12,14 +13,6 @@ export default function InfoNif() {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const baseURL = 'http://__IP__:4001';
|
||||
|
||||
const handleLink = (url) => {
|
||||
if (window.process?.type === 'renderer') {
|
||||
window.ipcRenderer.send('send-to-link', url);
|
||||
} else {
|
||||
window.open(url);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={style.container}>
|
||||
<CollapseBar title={'Network Info'} isCollapsed={collapsed} onClick={() => setCollapsed((c) => !c)}/>
|
||||
|
||||
@@ -6,14 +6,15 @@ import SettingsIconBtn from './buttons/SettingsIconBtn';
|
||||
import MaxIconBtn from './buttons/MaxIconBtn';
|
||||
import MinIconBtn from './buttons/MinIconBtn';
|
||||
import QuitIconBtn from './buttons/QuitIconBtn';
|
||||
import style from './MenuBar.module.css';
|
||||
import style from './MenuBar.module.scss';
|
||||
import HelpIconBtn from './buttons/HelpIconBtn';
|
||||
import UploadIconBtn from './buttons/UploadIconBtn';
|
||||
import { useContext, useRef } from 'react';
|
||||
import { LoggingContext } from '../../app/context/LoggingContext';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function MenuBar(props) {
|
||||
const { onOpen } = props;
|
||||
const { isOpen, onOpen } = props;
|
||||
const { emitError } = useContext(LoggingContext);
|
||||
const hiddenFileInput = useRef(null);
|
||||
const queryClient = useQueryClient();
|
||||
@@ -33,6 +34,10 @@ export default function MenuBar(props) {
|
||||
}
|
||||
};
|
||||
|
||||
const buttonStyle = {
|
||||
fontSize: '1.5em'
|
||||
};
|
||||
|
||||
const handleUpload = (event) => {
|
||||
const fileUploaded = event.target.files[0];
|
||||
if (fileUploaded == null) return;
|
||||
@@ -93,25 +98,27 @@ export default function MenuBar(props) {
|
||||
<>
|
||||
<QuitIconBtn size='lg' clickhandler={() => handleIPC('shutdown')} />
|
||||
<MaxIconBtn
|
||||
style={{ fontSize: '1.5em' }}
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={() => handleIPC('max')}
|
||||
/>
|
||||
<MinIconBtn
|
||||
style={{ fontSize: '1.5em' }}
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={() => handleIPC('min')}
|
||||
/>
|
||||
<div className={style.gap} />
|
||||
<HelpIconBtn
|
||||
style={{ fontSize: '1.5em' }}
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={() => handleIPC('help')}
|
||||
/>
|
||||
<SettingsIconBtn
|
||||
style={{ fontSize: '1.5em' }}
|
||||
style={{...buttonStyle}}
|
||||
size='lg'
|
||||
className={isOpen ? style.open : ''}
|
||||
clickhandler={onOpen}
|
||||
isRound
|
||||
/>
|
||||
<div className={style.gap} />
|
||||
<input
|
||||
@@ -122,15 +129,21 @@ export default function MenuBar(props) {
|
||||
accept='.json, .xlsx'
|
||||
/>
|
||||
<UploadIconBtn
|
||||
style={{ fontSize: '1.5em' }}
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={handleClick}
|
||||
/>
|
||||
<DownloadIconBtn
|
||||
style={{ fontSize: '1.5em' }}
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={handleDownload}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
MenuBar.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.gap {
|
||||
height: 1em;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
@use '../../styles/main' as *;
|
||||
|
||||
.gap {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.open {
|
||||
background: $light-bg;
|
||||
}
|
||||
@@ -1,174 +1,321 @@
|
||||
import { Button, IconButton } from '@chakra-ui/button';
|
||||
import { FiPlus, FiMinus } from 'react-icons/fi';
|
||||
import { FiInfo, FiMinus, FiSun } from 'react-icons/fi';
|
||||
import { ModalBody } from '@chakra-ui/modal';
|
||||
import { Input } from '@chakra-ui/react';
|
||||
import { fetchEvent } from 'app/api/eventApi';
|
||||
import { useState } from 'react';
|
||||
import { getAliases, postAliases } from '../../app/api/ontimeApi';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { useFetch } from 'app/hooks/useFetch';
|
||||
import { EVENT_TABLE } from 'app/api/apiConstants';
|
||||
import { ALIASES } from 'app/api/apiConstants';
|
||||
import style from './Modals.module.scss';
|
||||
import { viewerLinks } from '../../app/appConstants';
|
||||
import { LoggingContext } from '../../app/context/LoggingContext';
|
||||
import { validateAlias } from '../../app/utils/aliases';
|
||||
import { Tooltip } from '@chakra-ui/tooltip';
|
||||
import SubmitContainer from './SubmitContainer';
|
||||
import handleLink from '../../common/utils/handleLink';
|
||||
|
||||
export default function AliasesModal() {
|
||||
const { data, status, isError } = useFetch(EVENT_TABLE, fetchEvent);
|
||||
const { data, status, refetch } = useFetch(ALIASES, getAliases);
|
||||
const { emitError } = useContext(LoggingContext);
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [aliases, setAliases] = useState([]);
|
||||
const host = window.location.host;
|
||||
|
||||
/**
|
||||
* Set formdata from server state
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (data == null) return;
|
||||
if (changed) return;
|
||||
setAliases([...data]);
|
||||
}, [changed, data]);
|
||||
|
||||
/**
|
||||
* Validate and submit data
|
||||
*/
|
||||
const submitHandler = async (event) => {
|
||||
event.preventDefault();
|
||||
// NOTHING HERE YET
|
||||
setSubmitting(true);
|
||||
|
||||
const validatedAliases = [...aliases];
|
||||
let errors = false;
|
||||
for (const alias of validatedAliases) {
|
||||
// validate url
|
||||
const isURLValid = validateAlias(alias.pathAndParams);
|
||||
if (!isURLValid.status) {
|
||||
alias.urlError = isURLValid.message;
|
||||
errors = true;
|
||||
} else {
|
||||
alias.urlError = undefined;
|
||||
}
|
||||
// validate alias
|
||||
const isAliasValid = validateAlias(alias.alias);
|
||||
if (!isAliasValid.status) {
|
||||
alias.aliasError = isAliasValid.message;
|
||||
errors = true;
|
||||
} else {
|
||||
alias.aliasError = undefined;
|
||||
}
|
||||
}
|
||||
setAliases(validatedAliases);
|
||||
|
||||
if (!errors) {
|
||||
await postAliases(aliases);
|
||||
await refetch();
|
||||
setChanged(false);
|
||||
}
|
||||
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
// Hardcoded links for now
|
||||
// it will need dynamic PORT assignment
|
||||
const speakerLink = 'http://localhost:4001/speaker';
|
||||
const smLink = 'http://localhost:4001/sm';
|
||||
const publicLink = 'http://localhost:4001/public';
|
||||
const pipLink = 'http://localhost:4001/pip';
|
||||
const studioLink = 'http://localhost:4001/studio';
|
||||
/**
|
||||
* Creates a new alias in state with a temporary id
|
||||
*/
|
||||
const addNew = () => {
|
||||
if (aliases.length > 20) {
|
||||
emitError('Maximum amount of aliases reacted (20)');
|
||||
return;
|
||||
}
|
||||
|
||||
const emptyAlias = {
|
||||
id: Math.floor(Math.random() * 1000),
|
||||
enabled: false,
|
||||
alias: '',
|
||||
pathAndParams: '',
|
||||
};
|
||||
setAliases((prevState) => [...prevState, emptyAlias]);
|
||||
setChanged(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes an alias by a given id
|
||||
* @param {string} id - id of alias to delete
|
||||
*/
|
||||
const deleteAlias = (id) => {
|
||||
setAliases((prevState) => [...prevState.filter((a) => a.id !== id)]);
|
||||
setChanged(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets enabled flag to true / false
|
||||
* @param {string} id - object id
|
||||
* @param {boolean} isEnabled - whether to enable / disable flag
|
||||
*/
|
||||
const setEnabled = (id, isEnabled) => {
|
||||
const aliasesState = [...aliases];
|
||||
for (const a of aliasesState) {
|
||||
if (a.id === id) {
|
||||
if (isEnabled) {
|
||||
if (a.alias === '' || a.pathAndParams === '') {
|
||||
emitError('Alias incomplete');
|
||||
break;
|
||||
}
|
||||
|
||||
const isRepeated = aliases.some(
|
||||
(r) => a.alias === r.alias && r.enabled
|
||||
);
|
||||
if (isRepeated) {
|
||||
emitError('There is already an alias with this name');
|
||||
break;
|
||||
}
|
||||
}
|
||||
a.enabled = isEnabled;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setChanged(true);
|
||||
setAliases(aliasesState);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts local state equals to server state
|
||||
*/
|
||||
const revert = async () => {
|
||||
setChanged(false);
|
||||
await refetch();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles change of input field in local state
|
||||
* @param {number} index - index of item in array
|
||||
* @param {string} field - object parameter to update
|
||||
* @param {string} value - new object parameter value
|
||||
*/
|
||||
const handleChange = (index, field, value) => {
|
||||
const temp = [...aliases];
|
||||
temp[index][field] = value;
|
||||
setAliases(temp);
|
||||
setChanged(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalBody className={style.modalBody}>
|
||||
<p className={style.notes}>
|
||||
Configure easy to use URL Aliases
|
||||
<br />
|
||||
🔥 Changes take effect on save 🔥
|
||||
</p>
|
||||
<form onSubmit={submitHandler}>
|
||||
<ModalBody className={style.modalBody}>
|
||||
<p className={style.notes}>
|
||||
Configure easy to use URL Aliases
|
||||
<div className={style.modalFields}>
|
||||
<div className={style.hSeparator}>Default URLs</div>
|
||||
<div className={style.blockNotes}>
|
||||
{viewerLinks.map((l) => (
|
||||
<a
|
||||
href={l.link}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className={style.flexNote}
|
||||
key={l.link}
|
||||
onClick={() => handleLink(`${host}/${l.link}`)}
|
||||
>
|
||||
{`${l.label} - ${l.link}`}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<div className={style.hSeparator}>Custom Aliases</div>
|
||||
<div className={style.blockNotes}>
|
||||
<span className={style.inlineFlex}>
|
||||
<FiInfo color='#2b6cb0' fontSize={'2em'} />
|
||||
URL aliases are useful in two main scenarios
|
||||
</span>
|
||||
<span className={style.labelNote}>Complicated URLs</span>
|
||||
<br />
|
||||
❄️ Feature is not yet implemented ❄️
|
||||
</p>
|
||||
|
||||
<span>Default URLs</span>
|
||||
|
||||
<div className={style.highNotes}>
|
||||
<p className={style.flexNote}>
|
||||
Presenter Screen <br />
|
||||
<a
|
||||
href={speakerLink}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className={style.label}
|
||||
>
|
||||
{speakerLink}
|
||||
</a>
|
||||
</p>
|
||||
<p className={style.flexNote}>
|
||||
Backstage / Stage Manager Screen <br />
|
||||
<a
|
||||
href={smLink}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className={style.label}
|
||||
>
|
||||
{smLink}
|
||||
</a>
|
||||
</p>
|
||||
<p className={style.flexNote}>
|
||||
Public / Foyer Screen <br />
|
||||
<a
|
||||
href={publicLink}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className={style.label}
|
||||
>
|
||||
{publicLink}
|
||||
</a>
|
||||
</p>
|
||||
<p className={style.flexNote}>
|
||||
Picture in Picture Screen <br />
|
||||
<a
|
||||
href={pipLink}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className={style.label}
|
||||
>
|
||||
{pipLink}
|
||||
</a>
|
||||
</p>
|
||||
<p className={style.flexNote}>
|
||||
Studio Clock<br />
|
||||
<a
|
||||
href={studioLink}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className={style.label}
|
||||
>
|
||||
{studioLink}
|
||||
</a>
|
||||
</p>
|
||||
eg. a lower third url with some custom parameters
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className={style.labelNote} style={{ width: '30%' }}>
|
||||
Alias
|
||||
</td>
|
||||
<td className={style.labelNote}>Page URL</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mylower</td>
|
||||
<td>lower?bg=ff2&text=f00&size=0.6&transition=5</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<span className={style.labelNote}>
|
||||
URLs to be changed dynamically
|
||||
</span>
|
||||
<br />
|
||||
eg. an unattended screen that you would need to change route from
|
||||
the app
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className={style.labelNote} style={{ width: '30%' }}>
|
||||
Alias
|
||||
</td>
|
||||
<td className={style.labelNote}>Page URL</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>thirdfloor</td>
|
||||
<td>public</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div
|
||||
className={style.inlineAliasPlaceholder}
|
||||
style={{ padding: '0.5em 0' }}
|
||||
>
|
||||
<span className={style.labelNote}>Alias</span>
|
||||
<span className={style.labelNote}>Page URL</span>
|
||||
</div>
|
||||
{aliases.map((alias, index) => (
|
||||
<div key={alias.id}>
|
||||
<div className={style.inlineAlias}>
|
||||
<Input
|
||||
size='sm'
|
||||
variant='flushed'
|
||||
name='Alias'
|
||||
placeholder='URL Alias'
|
||||
autoComplete='off'
|
||||
value={alias.alias}
|
||||
isInvalid={alias.aliasError}
|
||||
onChange={(event) =>
|
||||
handleChange(index, 'alias', event.target.value)
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
size='sm'
|
||||
fontSize={'0.75em'}
|
||||
variant='flushed'
|
||||
name='URL'
|
||||
placeholder='URL (portion after ontime Port)'
|
||||
autoComplete='off'
|
||||
value={alias.pathAndParams}
|
||||
isInvalid={alias.urlError}
|
||||
onChange={(event) =>
|
||||
handleChange(index, 'pathAndParams', event.target.value)
|
||||
}
|
||||
/>
|
||||
<Tooltip label={`Test /${alias.pathAndParams}`} openDelay={500}>
|
||||
<a
|
||||
href='#!'
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleLink(`http://${host}/${alias.pathAndParams}`);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label='Enable alias' openDelay={500}>
|
||||
<IconButton
|
||||
size='xs'
|
||||
icon={<FiSun />}
|
||||
colorScheme='blue'
|
||||
variant={alias.enabled ? null : 'outline'}
|
||||
onClick={() => setEnabled(alias.id, !alias.enabled)}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label='Delete alias' openDelay={500}>
|
||||
<IconButton
|
||||
size='xs'
|
||||
icon={<FiMinus />}
|
||||
colorScheme='red'
|
||||
onClick={() => deleteAlias(alias.id)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{alias.aliasError ? (
|
||||
<div
|
||||
className={style.error}
|
||||
>{`Alias error: ${alias.aliasError}`}</div>
|
||||
) : null}
|
||||
{alias.urlError ? (
|
||||
<div
|
||||
className={style.error}
|
||||
>{`URL error: ${alias.urlError}`}</div>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<span>Manage custom aliases</span>
|
||||
<div className={style.modalInline}>
|
||||
<Input
|
||||
size='sm'
|
||||
name='URL'
|
||||
placeholder='A long URL'
|
||||
autoComplete='off'
|
||||
value={'A long URL'}
|
||||
onChange={(event) => {
|
||||
// Nothing here yet
|
||||
}}
|
||||
isDisabled={true}
|
||||
/>
|
||||
<Input
|
||||
size='sm'
|
||||
name='Alias'
|
||||
placeholder='A nice alias'
|
||||
autoComplete='off'
|
||||
value={'A nice alias'}
|
||||
onChange={(event) => {
|
||||
// Nothing here yet
|
||||
}}
|
||||
isDisabled={true}
|
||||
/>
|
||||
<IconButton
|
||||
size='sm'
|
||||
icon={<FiMinus />}
|
||||
colorScheme='red'
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className={style.separator} />
|
||||
<div className={style.modalInline}>
|
||||
<Input
|
||||
size='sm'
|
||||
name='URL'
|
||||
placeholder='URL'
|
||||
autoComplete='off'
|
||||
value={'URL'}
|
||||
onChange={(event) => {
|
||||
// Nothing here yet
|
||||
}}
|
||||
isDisabled={true}
|
||||
/>
|
||||
<Input
|
||||
size='sm'
|
||||
name='Alias'
|
||||
placeholder='Alias'
|
||||
autoComplete='off'
|
||||
value={'Alias'}
|
||||
onChange={(event) => {
|
||||
// Nothing here yet
|
||||
}}
|
||||
isDisabled={true}
|
||||
/>
|
||||
<IconButton
|
||||
size='sm'
|
||||
icon={<FiPlus />}
|
||||
colorScheme='blue'
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className={style.submitContainer}>
|
||||
<div
|
||||
className={style.inlineAliasPlaceholder}
|
||||
style={{ padding: '0.5em 0' }}
|
||||
>
|
||||
<Button
|
||||
size='xs'
|
||||
colorScheme='blue'
|
||||
type='submit'
|
||||
isLoading={submitting}
|
||||
disabled={true}
|
||||
variant='outline'
|
||||
onClick={() => addNew()}
|
||||
>
|
||||
Save
|
||||
Add new
|
||||
</Button>
|
||||
</div>
|
||||
</ModalBody>
|
||||
</div>
|
||||
<SubmitContainer
|
||||
revert={revert}
|
||||
submitting={submitting}
|
||||
changed={changed}
|
||||
status={status}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
</ModalBody>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,183 +1,170 @@
|
||||
import { ModalBody } from '@chakra-ui/modal';
|
||||
import { FormLabel, FormControl, Input, Button } from '@chakra-ui/react';
|
||||
import { getOSC, oscPlaceholderSettings, postOSC } from 'app/api/ontimeApi';
|
||||
import {
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
PinInput,
|
||||
PinInputField,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
getSettings,
|
||||
ontimePlaceholderSettings,
|
||||
postSettings,
|
||||
} from 'app/api/ontimeApi';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { useFetch } from 'app/hooks/useFetch';
|
||||
import { OSC_SETTINGS } from 'app/api/apiConstants';
|
||||
import { APP_SETTINGS } from 'app/api/apiConstants';
|
||||
import style from './Modals.module.scss';
|
||||
import { LoggingContext } from '../../app/context/LoggingContext';
|
||||
import { IconButton } from '@chakra-ui/button';
|
||||
import { FiEye } from 'react-icons/fi';
|
||||
import SubmitContainer from './SubmitContainer';
|
||||
import { inputProps } from './modalHelper';
|
||||
|
||||
export default function AppSettingsModal() {
|
||||
const { data, status } = useFetch(OSC_SETTINGS, getOSC);
|
||||
const { emitError } = useContext(LoggingContext);
|
||||
const [formData, setFormData] = useState(oscPlaceholderSettings);
|
||||
const { data, status, refetch } = useFetch(APP_SETTINGS, getSettings);
|
||||
const { emitError, emitWarning } = useContext(LoggingContext);
|
||||
const [formData, setFormData] = useState(ontimePlaceholderSettings);
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [hidePin, setHidePin] = useState(true);
|
||||
|
||||
/**
|
||||
* Set formdata from server state
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (data == null) return;
|
||||
setFormData({ ...data });
|
||||
}, [data]);
|
||||
if (changed) return;
|
||||
setFormData({
|
||||
pinCode: data.pinCode,
|
||||
});
|
||||
}, [changed, data]);
|
||||
|
||||
/**
|
||||
* Validate and submit data
|
||||
*/
|
||||
const submitHandler = async (event) => {
|
||||
event.preventDefault();
|
||||
setSubmitting(true);
|
||||
|
||||
const f = formData;
|
||||
let e = { status: false, message: '' };
|
||||
|
||||
// Validate fields
|
||||
if (f.port < 1024 || f.port > 65535) {
|
||||
// Port in incorrect range
|
||||
if (f.pinCode === '' || f.pinCode == null) {
|
||||
e.status = true;
|
||||
e.message += 'OSC IN Port in incorrect range (1024 - 65535)';
|
||||
} else if (f.portOut < 1024 || f.portOut > 65535) {
|
||||
// Port in incorrect range
|
||||
e.message += 'App pin code removed';
|
||||
} else {
|
||||
e.status = true;
|
||||
e.message += 'OSC OUT Port in incorrect range (1024 - 65535)';
|
||||
} else if (f.port === f.portOut) {
|
||||
// Cant use the same port
|
||||
e.status = true;
|
||||
e.message += 'OSC IN and OUT Ports cant be the same';
|
||||
e.message += 'App pin code added';
|
||||
}
|
||||
|
||||
// set fields with error
|
||||
if (e.status) {
|
||||
if (!e.status) {
|
||||
emitError(`Invalid Input: ${e.message}`);
|
||||
return;
|
||||
} else {
|
||||
await postSettings(formData);
|
||||
await refetch();
|
||||
emitWarning(e.message);
|
||||
setChanged(false);
|
||||
}
|
||||
|
||||
// Post here
|
||||
postOSC(formData);
|
||||
|
||||
setChanged(false);
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<form onSubmit={submitHandler}>
|
||||
<ModalBody className={style.modalBody}>
|
||||
{status === 'success' && (
|
||||
<>
|
||||
<p className={style.notes}>
|
||||
Options related to the application
|
||||
<br />
|
||||
🔥 Changes take effect after app restart 🔥
|
||||
</p>
|
||||
/**
|
||||
* Reverts local state equals to server state
|
||||
*/
|
||||
const revert = async () => {
|
||||
setChanged(false);
|
||||
await refetch();
|
||||
};
|
||||
|
||||
<FormControl id='serverPort'>
|
||||
<FormLabel htmlFor='serverPort'>
|
||||
Viewer Port
|
||||
<span className={style.notes}>Port to access viewers</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
/**
|
||||
* Handles change of input field in local state
|
||||
* @param {string} field - object parameter to update
|
||||
* @param {string} value - new object parameter value
|
||||
*/
|
||||
const handleChange = (field, value) => {
|
||||
const temp = { ...formData };
|
||||
temp[field] = value;
|
||||
setFormData(temp);
|
||||
setChanged(true);
|
||||
};
|
||||
|
||||
const disableModal = status !== 'success';
|
||||
|
||||
return (
|
||||
<ModalBody className={style.modalBody}>
|
||||
<p className={style.notes}>
|
||||
Options related to the application
|
||||
<br />
|
||||
🔥 Changes take effect after app restart 🔥
|
||||
</p>
|
||||
<form onSubmit={submitHandler}>
|
||||
<div className={style.modalFields}>
|
||||
<div className={style.hSeparator}>General App Settings</div>
|
||||
<div className={style.modalInline}>
|
||||
<FormControl id='serverPort'>
|
||||
<FormLabel htmlFor='serverPort'>
|
||||
Viewer Port
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
Ontime is available at port
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...inputProps}
|
||||
name='title'
|
||||
value={4001}
|
||||
disabled
|
||||
style={{ width: '6em', textAlign: 'center' }}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl id='editorPin'>
|
||||
<FormLabel htmlFor='editorPin'>
|
||||
Editor Pincode
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
Protect the editor with a Pincode
|
||||
</span>
|
||||
</FormLabel>
|
||||
<div className={style.pin}>
|
||||
<PinInput
|
||||
{...inputProps}
|
||||
type='alphanumeric'
|
||||
defaultValue=''
|
||||
value={formData.pinCode}
|
||||
mask={hidePin}
|
||||
isDisabled={disableModal}
|
||||
onChange={(value) => handleChange('pinCode', value)}
|
||||
>
|
||||
<PinInputField />
|
||||
<PinInputField />
|
||||
<PinInputField />
|
||||
<PinInputField />
|
||||
</PinInput>
|
||||
<IconButton
|
||||
size='sm'
|
||||
name='title'
|
||||
placeholder='4001'
|
||||
autoComplete='off'
|
||||
value={4001}
|
||||
readOnly
|
||||
style={{ width: '6em', textAlign: 'center' }}
|
||||
colorScheme='blue'
|
||||
variant='ghost'
|
||||
icon={<FiEye />}
|
||||
aria-label='Editor pin code'
|
||||
onMouseDown={() => setHidePin(false)}
|
||||
onMouseUp={() => setHidePin(true)}
|
||||
isDisabled={disableModal}
|
||||
/>
|
||||
<span className={style.notes}>(Read Only Value)</span>
|
||||
</FormControl>
|
||||
<FormControl id='port'>
|
||||
<FormLabel htmlFor='port'>
|
||||
OSC In Port
|
||||
<span className={style.notes}>
|
||||
<br />
|
||||
App Control - Default 8888
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
size='sm'
|
||||
name='port'
|
||||
placeholder='8888'
|
||||
autoComplete='off'
|
||||
type='number'
|
||||
value={formData.port}
|
||||
min='1024'
|
||||
max='65535'
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
port: parseInt(event.target.value),
|
||||
});
|
||||
}}
|
||||
isDisabled={submitting}
|
||||
style={{ width: '6em', textAlign: 'center' }}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className={style.modalInline}>
|
||||
<FormControl id='targetIP' width='auto'>
|
||||
<FormLabel htmlFor='targetIP'>
|
||||
OSC Out Target IP
|
||||
<span className={style.notes}>
|
||||
<br />
|
||||
App Feedback - Default 127.0.0.1
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
size='sm'
|
||||
name='targetIP'
|
||||
placeholder='127.0.0.1'
|
||||
autoComplete='off'
|
||||
value={formData.targetIP}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
targetIP: event.target.value,
|
||||
});
|
||||
}}
|
||||
isDisabled={submitting}
|
||||
style={{ width: '12em', textAlign: 'right' }}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl id='portOut' width='auto'>
|
||||
<FormLabel htmlFor='portOut'>
|
||||
OSC Out Port
|
||||
<span className={style.notes}>
|
||||
<br />
|
||||
Default 9999
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
size='sm'
|
||||
name='portOut'
|
||||
placeholder='9999'
|
||||
autoComplete='off'
|
||||
type='number'
|
||||
value={formData.portOut}
|
||||
min='1024'
|
||||
max='65535'
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
portOut: parseInt(event.target.value),
|
||||
});
|
||||
}}
|
||||
isDisabled={submitting}
|
||||
style={{ width: '6em', textAlign: 'left' }}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className={style.submitContainer}>
|
||||
<Button
|
||||
colorScheme='blue'
|
||||
type='submit'
|
||||
isLoading={submitting}
|
||||
disabled={!changed}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</FormControl>
|
||||
</div>
|
||||
</ModalBody>
|
||||
</div>
|
||||
<SubmitContainer
|
||||
revert={revert}
|
||||
submitting={submitting}
|
||||
changed={changed}
|
||||
status={status}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
</ModalBody>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,26 @@
|
||||
import { ModalBody } from '@chakra-ui/modal';
|
||||
import {
|
||||
FormLabel,
|
||||
FormControl,
|
||||
Input,
|
||||
Button,
|
||||
Textarea,
|
||||
} from '@chakra-ui/react';
|
||||
import { FormLabel, Input, Textarea } from '@chakra-ui/react';
|
||||
import { fetchEvent, postEvent } from 'app/api/eventApi';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useFetch } from 'app/hooks/useFetch';
|
||||
import { EVENT_TABLE } from 'app/api/apiConstants';
|
||||
import style from './Modals.module.scss';
|
||||
import { eventPlaceholderSettings } from '../../app/api/ontimeApi';
|
||||
import SubmitContainer from './SubmitContainer';
|
||||
import { inputProps } from './modalHelper';
|
||||
|
||||
export default function SettingsModal() {
|
||||
const { data, status } = useFetch(EVENT_TABLE, fetchEvent);
|
||||
const [formData, setFormData] = useState({
|
||||
title: '',
|
||||
url: '',
|
||||
publicInfo: '',
|
||||
backstageInfo: '',
|
||||
endMessage: '',
|
||||
});
|
||||
const { data, status, refetch } = useFetch(EVENT_TABLE, fetchEvent);
|
||||
const [formData, setFormData] = useState(eventPlaceholderSettings);
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
/**
|
||||
* Set formdata from server state
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (data == null) return;
|
||||
if (changed) return;
|
||||
|
||||
setFormData({
|
||||
title: data.title,
|
||||
@@ -34,8 +29,11 @@ export default function SettingsModal() {
|
||||
backstageInfo: data.backstageInfo,
|
||||
endMessage: data.endMessage,
|
||||
});
|
||||
}, [data]);
|
||||
}, [changed, data]);
|
||||
|
||||
/**
|
||||
* Validate and submit data
|
||||
*/
|
||||
const submitHandler = async (event) => {
|
||||
event.preventDefault();
|
||||
setSubmitting(true);
|
||||
@@ -46,133 +44,128 @@ export default function SettingsModal() {
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts local state equals to server state
|
||||
*/
|
||||
const revert = async () => {
|
||||
setChanged(false);
|
||||
await refetch();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles change of input field in local state
|
||||
* @param {string} field - object parameter to update
|
||||
* @param {string} value - new object parameter value
|
||||
*/
|
||||
const handleChange = (field, value) => {
|
||||
const temp = { ...formData };
|
||||
temp[field] = value;
|
||||
setFormData(temp);
|
||||
setChanged(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalBody className={style.modalBody}>
|
||||
<p className={style.notes}>
|
||||
Options related to the running event
|
||||
<br />
|
||||
Affects rendered views
|
||||
</p>
|
||||
<form onSubmit={submitHandler}>
|
||||
<ModalBody className={style.modalBody}>
|
||||
{status === 'success' && (
|
||||
<>
|
||||
<p className={style.notes}>
|
||||
Options related to the running event
|
||||
<br />
|
||||
Affect rendered views
|
||||
</p>
|
||||
|
||||
<FormControl id='title'>
|
||||
<FormLabel htmlFor='title'>Event Title</FormLabel>
|
||||
<Input
|
||||
size='sm'
|
||||
maxLength={35}
|
||||
name='title'
|
||||
placeholder='Event Title'
|
||||
autoComplete='off'
|
||||
value={formData.title}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({ ...formData, title: event.target.value });
|
||||
}}
|
||||
isDisabled={submitting}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl id='url'>
|
||||
<FormLabel htmlFor='url'>
|
||||
Event URL
|
||||
<span className={style.notes}>
|
||||
(shown as a QR code in some views)
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
size='sm'
|
||||
name='url'
|
||||
placeholder='www.onsite.no'
|
||||
autoComplete='off'
|
||||
value={formData.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({ ...formData, url: event.target.value });
|
||||
}}
|
||||
isDisabled={submitting}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl id='pubInfo'>
|
||||
<FormLabel htmlFor='pubInfo'>Public Info</FormLabel>
|
||||
<Textarea
|
||||
size='sm'
|
||||
name='pubInfo'
|
||||
placeholder='Information to be shown on public screens'
|
||||
autoComplete='off'
|
||||
value={formData.publicInfo}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
publicInfo: event.target.value,
|
||||
});
|
||||
}}
|
||||
isDisabled={submitting}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl id='backstageInfo'>
|
||||
<FormLabel htmlFor='backstageInfo'>Backstage Info</FormLabel>
|
||||
<Textarea
|
||||
size='sm'
|
||||
name='backstageInfo'
|
||||
placeholder='Information to be shown on backstage screens'
|
||||
autoComplete='off'
|
||||
resize={false}
|
||||
value={formData.backstageInfo}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
backstageInfo: event.target.value,
|
||||
});
|
||||
}}
|
||||
isDisabled={submitting}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl id='endMessage'>
|
||||
<FormLabel htmlFor='endMessage'>
|
||||
End Message
|
||||
<span className={style.notes}>
|
||||
Shown on presenter view when time is finished
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
size='sm'
|
||||
maxLength={30}
|
||||
name='endMessage'
|
||||
placeholder='Empty message shows elapsed time'
|
||||
autoComplete='off'
|
||||
value={formData.endMessage}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
endMessage: event.target.value,
|
||||
});
|
||||
}}
|
||||
isDisabled={submitting}
|
||||
/>
|
||||
</FormControl>
|
||||
</>
|
||||
)}
|
||||
<div className={style.submitContainer}>
|
||||
<Button
|
||||
colorScheme='blue'
|
||||
type='submit'
|
||||
isLoading={submitting}
|
||||
disabled={!changed}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<div className={style.modalFields}>
|
||||
<div className={style.hSeparator}>Event Data</div>
|
||||
<div className={style.spacedEntry}>
|
||||
<FormLabel htmlFor='title'>Event Title</FormLabel>
|
||||
<Input
|
||||
{...inputProps}
|
||||
maxLength={35}
|
||||
name='title'
|
||||
placeholder='Event Title'
|
||||
value={formData.title}
|
||||
onChange={(event) => handleChange('title', event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<div className={style.hSeparator}>Additional Screen Info</div>
|
||||
<div className={style.spacedEntry}>
|
||||
<FormLabel htmlFor='url'>
|
||||
Event URL
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
Shown as a QR code in some views
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...inputProps}
|
||||
name='url'
|
||||
placeholder='www.onsite.no'
|
||||
value={formData.url}
|
||||
onChange={(event) => handleChange('url', event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className={style.spacedEntry}>
|
||||
<FormLabel htmlFor='pubInfo'>
|
||||
Public Info
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
Information to be shown on public screens
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Textarea
|
||||
{...inputProps}
|
||||
name='pubInfo'
|
||||
placeholder='Information to be shown on public screens'
|
||||
value={formData.publicInfo}
|
||||
onChange={(event) =>
|
||||
handleChange('publicInfo', event.target.value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={style.spacedEntry}>
|
||||
<FormLabel htmlFor='backstageInfo'>
|
||||
Backstage Info
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
Information to be shown on backstage screens
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Textarea
|
||||
{...inputProps}
|
||||
name='backstageInfo'
|
||||
placeholder='Information to be shown on backstage screens'
|
||||
resize={false}
|
||||
value={formData.backstageInfo}
|
||||
onChange={(event) =>
|
||||
handleChange('backstageInfo', event.target.value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={style.spacedEntry}>
|
||||
<FormLabel htmlFor='endMessage'>
|
||||
End Message
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
Shown on presenter view when time is finished
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...inputProps}
|
||||
maxLength={30}
|
||||
name='endMessage'
|
||||
placeholder='Empty message shows elapsed time'
|
||||
value={formData.endMessage}
|
||||
onChange={(event) =>
|
||||
handleChange('endMessage', event.target.value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SubmitContainer
|
||||
revert={revert}
|
||||
submitting={submitting}
|
||||
changed={changed}
|
||||
status={status}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
</ModalBody>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import { ModalBody } from '@chakra-ui/modal';
|
||||
import {
|
||||
FormLabel,
|
||||
FormControl,
|
||||
Input,
|
||||
Button,
|
||||
Switch,
|
||||
} from '@chakra-ui/react';
|
||||
import { FormControl, FormLabel, Input, Switch } from '@chakra-ui/react';
|
||||
import {
|
||||
getInfo,
|
||||
httpPlaceholder,
|
||||
@@ -17,23 +11,23 @@ import { useFetch } from 'app/hooks/useFetch';
|
||||
import { APP_TABLE } from 'app/api/apiConstants';
|
||||
import style from './Modals.module.scss';
|
||||
import { LoggingContext } from '../../app/context/LoggingContext';
|
||||
import { FiInfo } from 'react-icons/fi';
|
||||
import SubmitContainer from './SubmitContainer';
|
||||
import { inputProps } from './modalHelper';
|
||||
|
||||
export default function IntegrationSettingsModal() {
|
||||
const { data, status } = useFetch(APP_TABLE, getInfo);
|
||||
const { data, status, refetch } = useFetch(APP_TABLE, getInfo);
|
||||
const { emitError } = useContext(LoggingContext);
|
||||
const [formData, setFormData] = useState(httpPlaceholder);
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
const ready = status === 'success';
|
||||
const integrationInputProps = {
|
||||
size: 'sm',
|
||||
autoComplete: 'off',
|
||||
isDisabled: submitting || !ready,
|
||||
};
|
||||
|
||||
/**
|
||||
* Set formdata from server state
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (data == null) return;
|
||||
if (changed) return;
|
||||
|
||||
setFormData({
|
||||
onLoad: data?.onLoad,
|
||||
@@ -42,8 +36,11 @@ export default function IntegrationSettingsModal() {
|
||||
onPause: data?.onPause,
|
||||
onStop: data?.onStop,
|
||||
});
|
||||
}, [data]);
|
||||
}, [changed, data]);
|
||||
|
||||
/**
|
||||
* Validate and submit data
|
||||
*/
|
||||
const submitHandler = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -54,283 +51,312 @@ export default function IntegrationSettingsModal() {
|
||||
if (e.status) {
|
||||
emitError(`Invalid Input: ${e.message}`);
|
||||
return;
|
||||
} else {
|
||||
await postInfo(f);
|
||||
setChanged(false);
|
||||
}
|
||||
|
||||
// Post here
|
||||
postInfo(f);
|
||||
|
||||
setChanged(false);
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<form onSubmit={submitHandler}>
|
||||
<ModalBody
|
||||
className={ready ? style.modalBody : style.modalBodyDisabled}
|
||||
>
|
||||
<>
|
||||
<p className={style.notes}>
|
||||
Integrate with third party over an HTTP API
|
||||
<br />
|
||||
🔥 Changes take effect after app restart 🔥
|
||||
</p>
|
||||
<div className={style.highNotes}>
|
||||
<p>
|
||||
Add HTTP messages that ontime will send during the app lifecycle
|
||||
</p>
|
||||
<p>
|
||||
You can use the variables below to pass data directly from
|
||||
ontime eg:
|
||||
<span className={style.emNote}>
|
||||
http://127.0.0.1:8088/API/?setHeadline=<b>$title</b>
|
||||
&setSub=<b>$presenter</b>
|
||||
</span>
|
||||
</p>
|
||||
/**
|
||||
* Reverts local state equals to server state
|
||||
*/
|
||||
const revert = async () => {
|
||||
setChanged(false);
|
||||
await refetch();
|
||||
};
|
||||
|
||||
<table>
|
||||
// Todo: make change handler
|
||||
// Todo: toggle between GET / POST
|
||||
// Todo: add test button
|
||||
// Todo: enabled should be button
|
||||
// Todo: add friendly placeholder to input
|
||||
|
||||
return (
|
||||
<ModalBody className={style.modalBody}>
|
||||
<p className={style.notes}>
|
||||
Integrate with third party over an HTTP API
|
||||
<br />
|
||||
🔥 Changes take effect after app restart 🔥
|
||||
</p>
|
||||
<form onSubmit={submitHandler}>
|
||||
<div className={style.modalFields}>
|
||||
<div className={style.hSeparator}>Ontime event cycle</div>
|
||||
<div className={style.blockNotes}>
|
||||
<span className={style.inlineFlex}>
|
||||
<FiInfo color='#2b6cb0' fontSize={'2em'} />
|
||||
Add HTTP messages that ontime will send during the event cycle
|
||||
</span>
|
||||
<span className={style.labelNote}>
|
||||
You can use variables in the HTTP request URL to send data from
|
||||
ontime
|
||||
</span>
|
||||
<span className={style.emNote}>
|
||||
http://127.0.0.1:8088/API/?setHeadline=
|
||||
<span className={style.labelNoteInline}>$title</span>
|
||||
&setSub=<span className={style.labelNoteInline}>$presenter</span>
|
||||
</span>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className={style.labelNote} style={{ width: '30%' }}>
|
||||
Variable
|
||||
</td>
|
||||
<td className={style.labelNote}>Value</td>
|
||||
</tr>
|
||||
{ontimeVars.map((v) => (
|
||||
<tr>
|
||||
<td className={style.noteItem}>{v.name}</td>
|
||||
<td className={style.labelNote}>{v.name}</td>
|
||||
<td>{v.description}</td>
|
||||
</tr>
|
||||
))}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<FormLabel>
|
||||
On Load
|
||||
<span className={style.notes}>When a new event loads</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onLoad' className={style.modalInline}>
|
||||
<Input
|
||||
{...integrationInputProps}
|
||||
name='onLoadURL'
|
||||
value={formData?.onLoad?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onLoad: {
|
||||
...formData.onLoad,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onLoadEnable'
|
||||
value={formData?.onLoad?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onLoad: {
|
||||
...formData.onLoad,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>
|
||||
On Start
|
||||
<span className={style.notes}>
|
||||
When an timer starts / resumes
|
||||
</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onStart' className={style.modalInline}>
|
||||
<Input
|
||||
{...integrationInputProps}
|
||||
name='onStartURL'
|
||||
value={formData?.onStart?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStart: {
|
||||
...formData.onStart,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onStartEnable'
|
||||
value={formData?.onStart?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStart: {
|
||||
...formData.onStart,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>
|
||||
On Update
|
||||
<span className={style.notes}>At every clock tick</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onUpdate' className={style.modalInline}>
|
||||
<Input
|
||||
{...integrationInputProps}
|
||||
name='onUpdateURL'
|
||||
value={formData?.onUpdate?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onUpdate: {
|
||||
...formData.onUpdate,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onUpdateEnable'
|
||||
value={formData?.onUpdate?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onUpdate: {
|
||||
...formData.onUpdate,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>
|
||||
On Pause
|
||||
<span className={style.notes}>When a timer pauses</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onPause' className={style.modalInline}>
|
||||
<Input
|
||||
{...integrationInputProps}
|
||||
name='onPauseURL'
|
||||
value={formData?.onPause?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onPause: {
|
||||
...formData.onPause,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onPauseEnable'
|
||||
value={formData?.onPause?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onPause: {
|
||||
...formData.onPause,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>
|
||||
On Stop
|
||||
<span className={style.notes}>When an event is unloaded</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onStop' className={style.modalInline}>
|
||||
<Input
|
||||
{...integrationInputProps}
|
||||
name='onStopURL'
|
||||
value={formData?.onStop?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStop: {
|
||||
...formData.onStop,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onStopEnable'
|
||||
value={formData?.onStop?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStop: {
|
||||
...formData.onStop,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>
|
||||
On Finish
|
||||
<span className={style.notes}>When an event is finished</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onFinish' className={style.modalInline}>
|
||||
<Input
|
||||
{...integrationInputProps}
|
||||
name='onFinishURL'
|
||||
value={formData?.onFinish?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStop: {
|
||||
...formData.onFinish,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onFinishEnable'
|
||||
value={formData?.onFinish?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStop: {
|
||||
...formData.onStop,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</>
|
||||
</>
|
||||
<div className={style.submitContainer}>
|
||||
<Button
|
||||
colorScheme='blue'
|
||||
type='submit'
|
||||
isLoading={submitting}
|
||||
disabled={!changed || !ready}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<div className={style.hSeparator}>Send HTTP</div>
|
||||
<FormLabel style={{paddingLeft:'0.5em'}}>
|
||||
On Load
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
When a new event loads
|
||||
</span>
|
||||
</FormLabel>
|
||||
<div className={style.modalInline}>
|
||||
<Input
|
||||
{...inputProps}
|
||||
name='onLoadURL'
|
||||
value={formData?.onLoad?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onLoad: {
|
||||
...formData.onLoad,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onLoadEnable'
|
||||
value={formData?.onLoad?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onLoad: {
|
||||
...formData.onLoad,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<FormLabel style={{paddingLeft:'0.5em'}}>
|
||||
On Start
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
When an timer starts / resumes{' '}
|
||||
</span>
|
||||
</FormLabel>
|
||||
<div className={style.modalInline}>
|
||||
<Input
|
||||
{...inputProps}
|
||||
name='onStartURL'
|
||||
value={formData?.onStart?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStart: {
|
||||
...formData.onStart,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onStartEnable'
|
||||
value={formData?.onStart?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStart: {
|
||||
...formData.onStart,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<FormLabel style={{paddingLeft:'0.5em'}}>
|
||||
On Update
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
At every clock tick
|
||||
</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onUpdate' className={style.modalInline}>
|
||||
<Input
|
||||
{...inputProps}
|
||||
name='onUpdateURL'
|
||||
value={formData?.onUpdate?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onUpdate: {
|
||||
...formData.onUpdate,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onUpdateEnable'
|
||||
value={formData?.onUpdate?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onUpdate: {
|
||||
...formData.onUpdate,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel style={{paddingLeft:'0.5em'}}>
|
||||
On Pause
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
When a timer pauses
|
||||
</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onPause' className={style.modalInline}>
|
||||
<Input
|
||||
{...inputProps}
|
||||
name='onPauseURL'
|
||||
value={formData?.onPause?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onPause: {
|
||||
...formData.onPause,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onPauseEnable'
|
||||
value={formData?.onPause?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onPause: {
|
||||
...formData.onPause,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel style={{paddingLeft:'0.5em'}}>
|
||||
On Stop
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
When an event is unloaded
|
||||
</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onStop' className={style.modalInline}>
|
||||
<Input
|
||||
{...inputProps}
|
||||
name='onStopURL'
|
||||
value={formData?.onStop?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStop: {
|
||||
...formData.onStop,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onStopEnable'
|
||||
value={formData?.onStop?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStop: {
|
||||
...formData.onStop,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel style={{paddingLeft:'0.5em'}}>
|
||||
On Finish
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
When an event is finished
|
||||
</span>
|
||||
</FormLabel>
|
||||
<FormControl id='onFinish' className={style.modalInline}>
|
||||
<Input
|
||||
{...inputProps}
|
||||
name='onFinishURL'
|
||||
value={formData?.onFinish?.url}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStop: {
|
||||
...formData.onFinish,
|
||||
url: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
colorScheme='green'
|
||||
id='onFinishEnable'
|
||||
value={formData?.onFinish?.enabled}
|
||||
onChange={(event) => {
|
||||
setChanged(true);
|
||||
setFormData({
|
||||
...formData,
|
||||
onStop: {
|
||||
...formData.onStop,
|
||||
enabled: event.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
<SubmitContainer
|
||||
revert={revert}
|
||||
submitting={submitting}
|
||||
changed={changed}
|
||||
status={status}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
</ModalBody>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ import {
|
||||
} from '@chakra-ui/modal';
|
||||
import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/tabs';
|
||||
import EventSettingsModal from './EventSettingsModal';
|
||||
import AppSettingsModal from './AppSettingsModal';
|
||||
import OscSettingsModal from './OscSettingsModal';
|
||||
import AliasesModal from './AliasesModal';
|
||||
import IntegrationSettingsModal from './IntegrationSettingsModal';
|
||||
import AppSettingsModal from './AppSettingsModal';
|
||||
|
||||
export default function ModalManager(props) {
|
||||
const { isOpen, onClose } = props;
|
||||
@@ -20,7 +21,8 @@ export default function ModalManager(props) {
|
||||
onClose={onClose}
|
||||
closeOnOverlayClick={false}
|
||||
motionPreset={'slideInBottom'}
|
||||
size='lg'
|
||||
size='xl'
|
||||
scrollBehavior='inside'
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
@@ -29,21 +31,25 @@ export default function ModalManager(props) {
|
||||
|
||||
<Tabs size='sm' isLazy>
|
||||
<TabList>
|
||||
<Tab style={{ fontSize: '0.9em' }}>App Settings</Tab>
|
||||
<Tab style={{ fontSize: '0.9em' }}>Event Data</Tab>
|
||||
<Tab style={{ fontSize: '0.9em' }}>Application Settings</Tab>
|
||||
<Tab style={{ fontSize: '0.9em' }}>URL Aliases</Tab>
|
||||
<Tab style={{ fontSize: '0.9em' }}>OSC</Tab>
|
||||
{/*<Tab style={{ fontSize: '0.9em' }}>Integration</Tab>*/}
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<EventSettingsModal />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<AppSettingsModal />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<EventSettingsModal />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<AliasesModal />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<OscSettingsModal />
|
||||
</TabPanel>
|
||||
{/*<TabPanel>*/}
|
||||
{/* <IntegrationSettingsModal />*/}
|
||||
{/*</TabPanel>*/}
|
||||
|
||||
@@ -1,83 +1,155 @@
|
||||
@use '../../styles/variables' as *;
|
||||
@use '../../styles/main' as *;
|
||||
|
||||
//////////////////////////////////// main
|
||||
|
||||
.modalBody,
|
||||
.modalBodyDisabled {
|
||||
.modalBody {
|
||||
font-weight: 400;
|
||||
|
||||
.notes {
|
||||
font-weight: 400;
|
||||
color: $light-bg;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
.separator {
|
||||
border: 1px solid $light-bg-transparent;
|
||||
width: 50%;
|
||||
margin: 0.5em auto;
|
||||
.modalFields {
|
||||
min-height: 45vh;
|
||||
max-height: 45vh;
|
||||
overflow-y: auto;
|
||||
scrollbar-color: rgba($light-bg, 0.35) rgba($light-bg, 0.15);
|
||||
padding-right: 6px;
|
||||
|
||||
label {
|
||||
//font-weight: 400;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.inlineAlias,
|
||||
.inlineAliasPlaceholder {
|
||||
display: grid;
|
||||
grid-template-columns: 20% 1fr 1em 1.5em 1.5em;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.error {
|
||||
font-size: 0.8em;
|
||||
color: $error-red;
|
||||
}
|
||||
|
||||
.inlineAliasPlaceholder {
|
||||
grid-template-columns: 20% 1fr 4em;
|
||||
|
||||
.placeholder {
|
||||
background: $light-text;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba($light-bg, 0.15);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba($light-bg, 0.35);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba($light-bg, 0.45);
|
||||
}
|
||||
|
||||
.modalInline {
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
align-items: center;
|
||||
padding: 0 0.5em 0.5em 0.5em;
|
||||
}
|
||||
|
||||
.spacedEntry {
|
||||
padding: 0 0.5em 0.5em 0.5em;
|
||||
}
|
||||
|
||||
.pin {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
border-radius: 50%;
|
||||
|
||||
input {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.submitContainer {
|
||||
margin-top: 2em;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
button {
|
||||
margin-top: 1em;
|
||||
}
|
||||
justify-content: flex-end;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.modalBody > *,
|
||||
.modalBodyDisabled > * {
|
||||
.modalBody > * {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
//////////////////////////////////// notes
|
||||
p {
|
||||
&.notes {
|
||||
text-align: center;
|
||||
border-color: $light-bg-transparent;
|
||||
border-width: 0 2px;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
border-color: $light-bg-transparent;
|
||||
border-width: 0 2px;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
&.notes {
|
||||
font-size: 0.8em;
|
||||
font-size: 0.9em;
|
||||
padding-left: 0.4em;
|
||||
}
|
||||
}
|
||||
|
||||
.highNotes {
|
||||
background-color: $light-text;
|
||||
.blockNotes {
|
||||
background-color: $bg-gray;
|
||||
margin: 1em 0;
|
||||
padding: 0.3em;
|
||||
font-size: 0.9em;
|
||||
padding: 0.5em;
|
||||
font-size: 0.8em;
|
||||
border-radius: 2px;
|
||||
|
||||
table {
|
||||
background-color: $light-text;
|
||||
background-color: #fff;
|
||||
border-left: 4px solid lighten($ontime-pink, 5%);
|
||||
width: 100%;
|
||||
margin-top: 0.3em;
|
||||
margin: 0.5em 0;
|
||||
border-radius: 2px;
|
||||
|
||||
:first-child {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
td {
|
||||
user-select: text;
|
||||
}
|
||||
}
|
||||
|
||||
.noteItem {
|
||||
user-select: text;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.flexNote {
|
||||
user-select: text;
|
||||
padding-bottom: 0.3em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.emNote {
|
||||
@@ -87,13 +159,18 @@ span {
|
||||
}
|
||||
}
|
||||
|
||||
// Define style for a link
|
||||
a {
|
||||
&::after {
|
||||
content: ' \2197';
|
||||
color: $accent;
|
||||
}
|
||||
&:hover {
|
||||
color: $accent;
|
||||
}
|
||||
.labelNote {
|
||||
color: $light-bg;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.labelNoteInline {
|
||||
color: $light-bg;
|
||||
}
|
||||
|
||||
.inlineFlex {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
align-items: center;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
import { ModalBody } from '@chakra-ui/modal';
|
||||
import { FormControl, FormLabel, Input } from '@chakra-ui/react';
|
||||
import { getOSC, oscPlaceholderSettings, postOSC } from 'app/api/ontimeApi';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { useFetch } from 'app/hooks/useFetch';
|
||||
import { OSC_SETTINGS } from 'app/api/apiConstants';
|
||||
import style from './Modals.module.scss';
|
||||
import { LoggingContext } from '../../app/context/LoggingContext';
|
||||
import SubmitContainer from './SubmitContainer';
|
||||
import { inputProps, portInputProps } from './modalHelper';
|
||||
|
||||
|
||||
export default function OscSettingsModal() {
|
||||
const { data, status, refetch } = useFetch(OSC_SETTINGS, getOSC);
|
||||
const { emitError } = useContext(LoggingContext);
|
||||
const [formData, setFormData] = useState(oscPlaceholderSettings);
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
/**
|
||||
* Set formdata from server state
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (data == null) return;
|
||||
if (changed) return;
|
||||
setFormData({ ...data });
|
||||
}, [changed, data]);
|
||||
|
||||
/**
|
||||
* Validate and submit data
|
||||
*/
|
||||
const submitHandler = async (event) => {
|
||||
event.preventDefault();
|
||||
setSubmitting(true);
|
||||
|
||||
const f = formData;
|
||||
let e = { status: false, message: '' };
|
||||
|
||||
// Validate fields
|
||||
if (f.port < 1024 || f.port > 65535) {
|
||||
// Port in incorrect range
|
||||
e.status = true;
|
||||
e.message += 'OSC IN Port in incorrect range (1024 - 65535)';
|
||||
} else if (f.portOut < 1024 || f.portOut > 65535) {
|
||||
// Port in incorrect range
|
||||
e.status = true;
|
||||
e.message += 'OSC OUT Port in incorrect range (1024 - 65535)';
|
||||
} else if (f.port === f.portOut) {
|
||||
// Cant use the same port
|
||||
e.status = true;
|
||||
e.message += 'OSC IN and OUT Ports cant be the same';
|
||||
}
|
||||
|
||||
// set fields with error
|
||||
if (e.status) {
|
||||
emitError(`Invalid Input: ${e.message}`);
|
||||
} else {
|
||||
// Post here
|
||||
await postOSC(formData);
|
||||
setChanged(false);
|
||||
}
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts local state equals to server state
|
||||
*/
|
||||
const revert = async () => {
|
||||
setChanged(false);
|
||||
await refetch();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles change of input field in local state
|
||||
* @param {string} field - object parameter to update
|
||||
* @param {(string | number)} value - new object parameter value
|
||||
*/
|
||||
const handleChange = (field, value) => {
|
||||
const temp = { ...formData };
|
||||
temp[field] = value;
|
||||
setFormData(temp);
|
||||
setChanged(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalBody className={style.modalBody}>
|
||||
<p className={style.notes}>
|
||||
Options related to Open Sound Control
|
||||
<br />
|
||||
🔥 Changes take effect after app restart 🔥
|
||||
</p>
|
||||
<form onSubmit={submitHandler}>
|
||||
<div className={style.modalFields}>
|
||||
<div className={style.hSeparator}>OSC Input (control)</div>
|
||||
<div className={style.spacedEntry}>
|
||||
<FormLabel htmlFor='port'>
|
||||
OSC In Port
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
Open port for 3rd party control over OSC - Default 8888
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...portInputProps}
|
||||
name='port'
|
||||
placeholder='8888'
|
||||
value={formData.port}
|
||||
onChange={(event) =>
|
||||
handleChange('port', parseInt(event.target.value))
|
||||
}
|
||||
style={{ width: '6em', textAlign: 'center' }}
|
||||
/>
|
||||
</div>
|
||||
<div className={style.hSeparator}>OSC Output (feedback)</div>
|
||||
<div className={style.modalInline}>
|
||||
<FormControl id='targetIP'>
|
||||
<FormLabel htmlFor='targetIP'>
|
||||
OSC Out Target IP
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
Default 127.0.0.1
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...inputProps}
|
||||
size='sm'
|
||||
name='targetIP'
|
||||
placeholder='127.0.0.1'
|
||||
autoComplete='off'
|
||||
value={formData.targetIP}
|
||||
onChange={(event) =>
|
||||
handleChange('targetIP', event.target.value)
|
||||
}
|
||||
isDisabled={submitting}
|
||||
style={{ width: '12em', textAlign: 'right' }}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl id='portOut'>
|
||||
<FormLabel htmlFor='portOut'>
|
||||
OSC Out Port
|
||||
<span className={style.labelNote}>
|
||||
<br />
|
||||
Default 9999
|
||||
</span>
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...portInputProps}
|
||||
name='portOut'
|
||||
placeholder='9999'
|
||||
value={formData.portOut}
|
||||
onChange={(event) =>
|
||||
handleChange('portOut', parseInt(event.target.value))
|
||||
}
|
||||
style={{ width: '6em', textAlign: 'left' }}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<SubmitContainer
|
||||
revert={revert}
|
||||
submitting={submitting}
|
||||
changed={changed}
|
||||
status={status}
|
||||
/>
|
||||
</form>
|
||||
</ModalBody>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import style from './Modals.module.scss';
|
||||
import { Button } from '@chakra-ui/button';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function SubmitContainer(props) {
|
||||
const { submitting, changed, revert, status } = props;
|
||||
|
||||
return (
|
||||
<div className={style.submitContainer}>
|
||||
<Button
|
||||
type='submit'
|
||||
isDisabled={submitting || !changed}
|
||||
variant='ghosted'
|
||||
onClick={() => revert()}
|
||||
>
|
||||
Revert
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme='blue'
|
||||
type='submit'
|
||||
isLoading={submitting}
|
||||
disabled={!changed || status !== 'success'}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
SubmitContainer.propTypes = {
|
||||
submitting: PropTypes.bool,
|
||||
changed: PropTypes.bool,
|
||||
status: PropTypes.string,
|
||||
revert: PropTypes.func.isRequired,
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
export const inputProps = {
|
||||
size: 'sm',
|
||||
autoComplete: 'off',
|
||||
};
|
||||
|
||||
export const portInputProps = {
|
||||
...inputProps,
|
||||
type: 'number',
|
||||
min: '1024',
|
||||
max: '65535',
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
|
||||
import { fetchAllEvents } from 'app/api/eventsApi';
|
||||
import { fetchEvent } from 'app/api/eventApi';
|
||||
import { useSocket } from 'app/context/socketContext';
|
||||
import { stringFromMillis } from 'ontime-server/utils/time';
|
||||
import { stringFromMillis } from 'ontime-utils/time';
|
||||
import { useFetch } from 'app/hooks/useFetch';
|
||||
import { EVENTS_TABLE, EVENT_TABLE } from 'app/api/apiConstants';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import LowerClean from './LowerClean';
|
||||
import LowerLines from './LowerLines';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
const isEqual = require('react-fast-compare');
|
||||
|
||||
const areEqual = (prevProps, nextProps) => {
|
||||
@@ -12,6 +13,7 @@ const areEqual = (prevProps, nextProps) => {
|
||||
|
||||
const Lower = (props) => {
|
||||
const { title } = props;
|
||||
const [searchParams,] = useSearchParams();
|
||||
const [titles, setTitles] = useState({
|
||||
titleNow: '',
|
||||
titleNext: '',
|
||||
@@ -61,61 +63,59 @@ const Lower = (props) => {
|
||||
// eg. http://localhost:3000/lower?bg=ff2&text=f00&size=0.6&transition=5
|
||||
// Check for user options
|
||||
useEffect(() => {
|
||||
// get parameters
|
||||
const params = new URLSearchParams(props.location.search);
|
||||
// create aux
|
||||
let options = {};
|
||||
|
||||
// preset: selector
|
||||
// Should be a number 1-n
|
||||
let p = parseInt(params.get('preset'));
|
||||
let p = parseInt(searchParams.get('preset'));
|
||||
if (!isNaN(p)) setPreset(p);
|
||||
|
||||
// size: multiplier
|
||||
// Should be a number 0.0-n
|
||||
let s = params.get('size');
|
||||
let s = searchParams.get('size');
|
||||
if (s) options.size = s;
|
||||
|
||||
// transitionIn: seconds
|
||||
// Should be a number 0-n
|
||||
let t = parseInt(params.get('transition'));
|
||||
let t = parseInt(searchParams.get('transition'));
|
||||
if (!isNaN(t)) options.transitionIn = t;
|
||||
|
||||
// textColour: string
|
||||
// Should be a hex string '#ffffff'
|
||||
let c = params.get('text');
|
||||
let c = searchParams.get('text');
|
||||
if (c) options.textColour = `#${c}`;
|
||||
|
||||
// bgColour: string
|
||||
// Should be a hex string '#ffffff'
|
||||
let b = params.get('bg');
|
||||
let b = searchParams.get('bg');
|
||||
if (b) options.bgColour = `#${b}`;
|
||||
|
||||
// key: string
|
||||
// Should be a hex string '#00FF00' with key colour
|
||||
let k = params.get('key');
|
||||
let k = searchParams.get('key');
|
||||
if (k) options.keyColour = `#${k}`;
|
||||
|
||||
// fadeOut: seconds
|
||||
// Should be a number 0-n
|
||||
let f = parseInt(params.get('fadeout'));
|
||||
let f = parseInt(searchParams.get('fadeout'));
|
||||
if (!isNaN(f)) options.fadeOut = f;
|
||||
|
||||
// x: pixels
|
||||
// Should be a number 0-n
|
||||
let x = parseInt(params.get('x'));
|
||||
let x = parseInt(searchParams.get('x'));
|
||||
if (!isNaN(x)) options.posX = x;
|
||||
|
||||
// y: pixels
|
||||
// Should be a number 0-n
|
||||
let y = parseInt(params.get('y'));
|
||||
let y = parseInt(searchParams.get('y'));
|
||||
if (!isNaN(y)) options.posY = y;
|
||||
|
||||
setLowerOptions({
|
||||
...options,
|
||||
set: true,
|
||||
});
|
||||
}, [props.location.search]);
|
||||
}, [searchParams]);
|
||||
|
||||
// Defer rendering until we have data ready
|
||||
if (!lowerOptions.set) return null;
|
||||
|
||||
+13
-3
@@ -5,16 +5,26 @@ import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { ChakraProvider } from '@chakra-ui/react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { AppContextProvider } from './app/context/AppContext';
|
||||
import SocketProvider from './app/context/socketContext';
|
||||
|
||||
// Load Open Sans typeface
|
||||
require('typeface-open-sans');
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<ChakraProvider resetCSS>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
<SocketProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AppContextProvider>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</AppContextProvider>
|
||||
</QueryClientProvider>
|
||||
</SocketProvider>
|
||||
</ChakraProvider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
//////////////////////////////////// general app style
|
||||
//////////////////////////////////// general app colours
|
||||
|
||||
$ontime-accent: #4bffabcc;
|
||||
$ontime-pink: #ff7597;
|
||||
$ontime-roll: #2b6cb0;
|
||||
|
||||
$notes-color: #d69e2e;
|
||||
|
||||
$header-gray: #ccc;
|
||||
$label-gray: #aaa;
|
||||
$bg-gray: #f4f4f8;
|
||||
|
||||
$light-bg: #2b6cb0;
|
||||
$light-bg-transparent: #2b6cb055;
|
||||
$light-text: #2b6cb022;
|
||||
|
||||
$error-red: #E53E3E;
|
||||
|
||||
//////////////////////////////////// general app element overriders
|
||||
|
||||
// no decoration on lists
|
||||
ul {
|
||||
@@ -8,4 +26,29 @@ ul {
|
||||
// no resizing on text areas
|
||||
textarea {
|
||||
resize: none !important;
|
||||
}
|
||||
|
||||
// Define style for a link
|
||||
a {
|
||||
&::after {
|
||||
content: ' \2197';
|
||||
color: $ontime-pink;
|
||||
}
|
||||
&:hover {
|
||||
color: $ontime-pink;
|
||||
}
|
||||
}
|
||||
|
||||
// horizontal separator
|
||||
.hSeparator {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid $light-text;
|
||||
margin: 1em auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// inline vertical separator
|
||||
.vSpan {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//////////////////////////////////// general app elements
|
||||
|
||||
@mixin container-bg {
|
||||
background-color: rgba(0, 0, 0, 0.13);
|
||||
border-radius: 2px;
|
||||
padding: 0 0.5em;
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
$light-bg: #2b6cb0;
|
||||
$light-bg-transparent: #2b6cb055;
|
||||
$light-text: #2b6cb022;
|
||||
$accent: #ff7597;
|
||||
+232
-282
@@ -1786,7 +1786,7 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.13.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
|
||||
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
|
||||
@@ -1800,7 +1800,7 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.16.3":
|
||||
"@babel/runtime@^7.16.3", "@babel/runtime@^7.7.6":
|
||||
version "7.16.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.5.tgz#7f3e34bf8bdbbadf03fbb7b1ea0d929569c9487a"
|
||||
integrity sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==
|
||||
@@ -1877,24 +1877,24 @@
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@chakra-ui/accordion@1.4.1":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/accordion/-/accordion-1.4.1.tgz#bd5373dcfd11572e5e2c535eca51aee4fc24c828"
|
||||
integrity sha512-/E0FW5YHNVD6WwMGiuQuXpA70P2CKAV+MzcMITnSGPWsh9XD0mcXvMkIALVojfFk9tcCFdIGnxX/HWr41LzgIg==
|
||||
"@chakra-ui/accordion@1.4.2":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/accordion/-/accordion-1.4.2.tgz#e851d5e0f748646e20efde909b5f70a93679283b"
|
||||
integrity sha512-BAGMvcm2sFE5Ft7jwC9nF03/Yv7qztuhzwKBBy4iL0p1nCPh6kV54RBXUcoj3VWe+yrmNiAVYKRTdqQBTJFwOw==
|
||||
dependencies:
|
||||
"@chakra-ui/descendant" "2.1.1"
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/icon" "1.2.1"
|
||||
"@chakra-ui/icon" "2.0.0"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/transition" "1.4.1"
|
||||
"@chakra-ui/transition" "1.4.2"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/alert@1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/alert/-/alert-1.3.1.tgz#d091b36ee4a7e3684220cd6f86fd2e08ca71cb2a"
|
||||
integrity sha512-BeR6l/1CLZarA3uAe+5Q3hioYf7SixYfy9rOte/29ck1lx9PLjjuPYYmuDPtZNbGibhUCh48z4U/uK2x8mbpKQ==
|
||||
"@chakra-ui/alert@1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/alert/-/alert-1.3.2.tgz#6c038b0bd58f3964e0a3af213c162c108176a8b0"
|
||||
integrity sha512-+OMeVeGtydpj6nry0zH7qFDt36zEaxckRnufx1BGiCfWdUg6ahVwKXl8qX93Q8w82od7eAoBKMgGJz7IVL5NPw==
|
||||
dependencies:
|
||||
"@chakra-ui/icon" "1.2.1"
|
||||
"@chakra-ui/icon" "2.0.0"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
@@ -1950,18 +1950,18 @@
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/close-button@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/close-button/-/close-button-1.2.1.tgz#1109665e73cddcb643bd66e208b2aac75e8af40f"
|
||||
integrity sha512-A/cuFtJPF8rp5p6tCIGlQdHB89gLCSOzxWssoTXAGJnmlwY2YunFHxgkYZXwPbDqFrM8ndya7Ys+AuL1JZsa3g==
|
||||
"@chakra-ui/close-button@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/close-button/-/close-button-1.2.2.tgz#43e91b34d51f75dc08ff9a56cbfc7ed5ad158b8a"
|
||||
integrity sha512-SqeLib0qgMjK3OsO1g5OnAHUmdCC8GMjToNEea7TeSrA44bH9EXVhFTkMMu2PnDVHbQmi4Ee1cuulNJt0UhQ3g==
|
||||
dependencies:
|
||||
"@chakra-ui/icon" "1.2.1"
|
||||
"@chakra-ui/icon" "2.0.0"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/color-mode@1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/color-mode/-/color-mode-1.3.1.tgz#08402d28a9c5b4dd3e1a64f69faad319d55dc9d6"
|
||||
integrity sha512-UuuYMfYxaBE5fPtIR6dwIJn3snT0tVX67RN2RHv4/LQgUeiddq4VziXT3bd+tyIe9+zcsqrZhV7J7YP0h/Id/Q==
|
||||
"@chakra-ui/color-mode@1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/color-mode/-/color-mode-1.3.2.tgz#e87f9c17ae891c426fa8c9dbdd3c2ba80669fa48"
|
||||
integrity sha512-/rWcbrzbaWCyyUnT07Qjz0xf/ltHS31CHOKtVCWr2uTgfn2gOQpdxsKRbjrLYPOYZGTMdINUHNiAsqQjLoAoTQ==
|
||||
dependencies:
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/react-env" "1.1.1"
|
||||
@@ -2011,13 +2011,13 @@
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
react-focus-lock "2.5.2"
|
||||
|
||||
"@chakra-ui/form-control@1.5.1":
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/form-control/-/form-control-1.5.1.tgz#235267088026690d6994fa406c9f2618fa0351ec"
|
||||
integrity sha512-ASZYQFOs5mAoaNXAN/ZaesMy3XV07F0/Eba5PQ7Dejdn91aep6lqF889hmr8yqcR646xCOY7ISyYsskfh9QHrQ==
|
||||
"@chakra-ui/form-control@1.5.2":
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/form-control/-/form-control-1.5.2.tgz#4ce34f03165cfafacecabdce3be82011b74308a7"
|
||||
integrity sha512-uWv0/f+JEM0ZE5Hnj3TzCnJ09EB+A+DSs9QgyECOuxx9Ju6gnns2uaRki2BfxksQL9ZZomPCkMtXazY9Wa81ag==
|
||||
dependencies:
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/icon" "1.2.1"
|
||||
"@chakra-ui/icon" "2.0.0"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
@@ -2031,10 +2031,10 @@
|
||||
compute-scroll-into-view "1.0.14"
|
||||
copy-to-clipboard "3.3.1"
|
||||
|
||||
"@chakra-ui/icon@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/icon/-/icon-1.2.1.tgz#b47c0016531da8bef81061d2b96a2d5282b07ff8"
|
||||
integrity sha512-uZxFsiY4Tld+LvGIX7cky0H6oMRac8udPMQRzIk/UQeNZcsWisGetatbQsew3y1lWV/iH/8+TlDuW13GWGyGGQ==
|
||||
"@chakra-ui/icon@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/icon/-/icon-2.0.0.tgz#a2468736117139f94c6ed65eb86b9838e554c90e"
|
||||
integrity sha512-/GuU+xIcOIy9uSUUUCu249ZJB/nLDbjWGkfpoSdBwqT4+ytJrKt+0Ckh3Ub14sz3BJD+Z6IiIt6ySOA9+7lbsA==
|
||||
dependencies:
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
@@ -2046,21 +2046,21 @@
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/input@1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/input/-/input-1.3.1.tgz#a407242239f7bb5fdf3b237ab90355c7d77aff9e"
|
||||
integrity sha512-Z+LqkwVPMeUBuvB9dLDPKkBnWV52Q1PVl3KW9ouDIFg7SoemeYkBt3p4ttEKE+eIPsPlrcH1u2A/RGcCTZOe1g==
|
||||
"@chakra-ui/input@1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/input/-/input-1.3.2.tgz#59d49ab5c8645c51cd591d46ca23d5cb95e03392"
|
||||
integrity sha512-VMxmQgFiQ2UnBlkgLX/336G0IfYfw8YWF2ZoEFj5WL9kDSrrL1FXSBgjFGxrper74G4W20tESBCfU1S891y6cg==
|
||||
dependencies:
|
||||
"@chakra-ui/form-control" "1.5.1"
|
||||
"@chakra-ui/form-control" "1.5.2"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/layout@1.5.1":
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/layout/-/layout-1.5.1.tgz#be33e8fb5fb187f13c5c0ef66fe94cd293094da6"
|
||||
integrity sha512-nKiyZ5adjNTbBV3oFIUGIPijwutO1NGdev1jHtnZc3xo2urCIkBvKU8+mVjlX04IwZ7oLKoP3EiDDv0g7+o41Q==
|
||||
"@chakra-ui/layout@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/layout/-/layout-1.6.0.tgz#fa45d72420f01f4f6082f5a5a0c202bf7b09a059"
|
||||
integrity sha512-WUfQ104y1wNueU33/hPlZsMzYJGjO0dXMpVkQf5ZNhNX3IGDO+5+MO2x2xloP+j45yNPi3p8ti/HBnm3dXI+3Q==
|
||||
dependencies:
|
||||
"@chakra-ui/icon" "1.2.1"
|
||||
"@chakra-ui/icon" "2.0.0"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
@@ -2071,51 +2071,51 @@
|
||||
dependencies:
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/media-query@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/media-query/-/media-query-1.2.1.tgz#c1df05cbe8bd909a410d134240f9bceee760ec09"
|
||||
integrity sha512-Ho/qiPGTjNukFTE9WBdYV9FIXU7KFTJPqdRQPWANkz+j275n6sqSE1j5LRJllP+ett21KeuWLN4zL33pP0Ox+g==
|
||||
"@chakra-ui/media-query@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/media-query/-/media-query-1.2.2.tgz#46db9059de1367a3aa85c1fb973a78f95fca6cc5"
|
||||
integrity sha512-xSmDVleE1drWiGH/MX3RqyVm29x/8Vf6G0UGaI2kCpbNmon+Q1zHW/yDHvptIuctLrPHYO8LOBxuUjfnIXwC2g==
|
||||
dependencies:
|
||||
"@chakra-ui/react-env" "1.1.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/menu@1.8.1":
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/menu/-/menu-1.8.1.tgz#ad26e12843914747de1c36228f4f45c2be668a4d"
|
||||
integrity sha512-fgzzFukBj4sQzTRf4q/+nHiVTKhrMtJdofnluqce/SCRJ1G+bbovUySblTzfI8iFlTSZt/eWc/Nju4JB1S+3Yg==
|
||||
"@chakra-ui/menu@1.8.2":
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/menu/-/menu-1.8.2.tgz#849f51beef6220667eb47c831ddf2c65919ded0e"
|
||||
integrity sha512-u2GfkwTqbWa8L/7i/kOFbU3JANiT2HStR+gsYKuiuOPiuBcUb8OlgfJfP70OtVKegNKmVEMjvzXtld3wCCo/1g==
|
||||
dependencies:
|
||||
"@chakra-ui/clickable" "1.2.1"
|
||||
"@chakra-ui/descendant" "2.1.1"
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/popper" "2.4.1"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/transition" "1.4.1"
|
||||
"@chakra-ui/transition" "1.4.2"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/modal@1.10.1":
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/modal/-/modal-1.10.1.tgz#849243b6f700845ef04e8247d3326e3e0324b789"
|
||||
integrity sha512-cboC2ITm+5FjhrBc6yJ5cW4VXnfwlLhFa1EkPqF1k4kvYGyUHArvPN1q8AiPYOIrupHYu2Iu6YmQPg7TJwNImg==
|
||||
"@chakra-ui/modal@1.10.2":
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/modal/-/modal-1.10.2.tgz#578c30812c63863bef25f4b5c279cacbb34528ec"
|
||||
integrity sha512-ZlmYetPHwHW4CAM09j4/+Ui54dXR1nzU6mOwhWe4/IzLvEyoEU6fHJeKyGxVUpYTG/7wltG/wKFRJpYa77tiBg==
|
||||
dependencies:
|
||||
"@chakra-ui/close-button" "1.2.1"
|
||||
"@chakra-ui/close-button" "1.2.2"
|
||||
"@chakra-ui/focus-lock" "1.2.1"
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/portal" "1.3.1"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/transition" "1.4.1"
|
||||
"@chakra-ui/transition" "1.4.2"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
aria-hidden "^1.1.1"
|
||||
react-remove-scroll "2.4.1"
|
||||
|
||||
"@chakra-ui/number-input@1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/number-input/-/number-input-1.3.1.tgz#1c46b2748240c780617955da4f2f20a5414edf5c"
|
||||
integrity sha512-4vBRSShT5pedElgP9YGVC+9RHzQGmUVZqu3p0gZW0fLGVVQ9C1EGrO7djL+k3tgklyu8RvSwkRDJqEPvbQKDgQ==
|
||||
"@chakra-ui/number-input@1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/number-input/-/number-input-1.3.2.tgz#580035e56a13c30b04034ab02cb9c1132a1cf09c"
|
||||
integrity sha512-7x7AoqwPXU1odyDcqIwjBwf0MJUwYMM2fa+6YZ52F941GKlvkDiiJOhK6xfhhBzkLUQD6DN8zgAmmGhaZ6UQXw==
|
||||
dependencies:
|
||||
"@chakra-ui/counter" "1.2.1"
|
||||
"@chakra-ui/form-control" "1.5.1"
|
||||
"@chakra-ui/form-control" "1.5.2"
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/icon" "1.2.1"
|
||||
"@chakra-ui/icon" "2.0.0"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
@@ -2129,12 +2129,12 @@
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/popover@1.10.1":
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/popover/-/popover-1.10.1.tgz#03a172201d46bde73eeff7e40e141d82876df493"
|
||||
integrity sha512-/dMUQfd+h9j3GBtkA/nYaQ5xeu4vk0psUClFvLOAJRwXGN3aMrzn/mhrvHWQ/cJuwQrO1WzxH2+g6pwsFOm9ng==
|
||||
"@chakra-ui/popover@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/popover/-/popover-1.11.0.tgz#3ad9faf26019c750a5f8c619ee80dd97bcc4ee3e"
|
||||
integrity sha512-cCHXAfhIRir+M0ehlYIjDw3mHpiCxDTJ9WV0H1zHQV8nDYVIlZw3nEntaq8oJrv0wpIzq2WCW5ss+bBR7nLZ1A==
|
||||
dependencies:
|
||||
"@chakra-ui/close-button" "1.2.1"
|
||||
"@chakra-ui/close-button" "1.2.2"
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/popper" "2.4.1"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
@@ -2165,24 +2165,24 @@
|
||||
"@chakra-ui/theme-tools" "1.3.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/provider@1.7.1":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/provider/-/provider-1.7.1.tgz#d49c91abd91df8cd2824255f10353a7c17b8df9e"
|
||||
integrity sha512-E2z56BAYytg+sA7arxUtg54AzhYPpR5QUcR4L679aahgiod+5y6H5TSZcTnxTFRLXVLZbBbP/eH63seIcRqCRQ==
|
||||
"@chakra-ui/provider@1.7.3":
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/provider/-/provider-1.7.3.tgz#625c0b7c1265515399bb501bc929d8851bba2958"
|
||||
integrity sha512-D1SrQ7do4yzAv9/OTF3yj/BkLm7kFo5DdeuOCyvXGpVJumnvbtjltRmC7rFQH4R+y9qXPvfQP4LKMNBqSxPNng==
|
||||
dependencies:
|
||||
"@chakra-ui/css-reset" "1.1.1"
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/portal" "1.3.1"
|
||||
"@chakra-ui/react-env" "1.1.1"
|
||||
"@chakra-ui/system" "1.8.1"
|
||||
"@chakra-ui/system" "1.8.3"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/radio@1.4.1":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/radio/-/radio-1.4.1.tgz#62de496295f30a6b264cc3906dcfd17bc85a0e85"
|
||||
integrity sha512-Rq7goauffMNqwGVuPh4zh0kX0AuvTll66PhOwPu+GYHzsYO5+L9X875Q9QBnJEFtDjNF/MVIrH9WrKWjdWLqWw==
|
||||
"@chakra-ui/radio@1.4.3":
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/radio/-/radio-1.4.3.tgz#d87788fe688d0d58252e9efeb926d0417a70e158"
|
||||
integrity sha512-TQdyfdUD3BLklOP67n82JN8ksQv1BYjvaYsK0m6WCa0LDJr9aCC+XtUPgVq/1L2t4HqHdiGOrGBooF4vvy/+BA==
|
||||
dependencies:
|
||||
"@chakra-ui/form-control" "1.5.1"
|
||||
"@chakra-ui/form-control" "1.5.2"
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
@@ -2202,81 +2202,81 @@
|
||||
dependencies:
|
||||
"@chakra-ui/utils" "^1.9.1"
|
||||
|
||||
"@chakra-ui/react@^1.7.1":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/react/-/react-1.7.1.tgz#f88417edb4d08766bf102a8d8cc294cbd38443de"
|
||||
integrity sha512-mbU6M/lxD9BCuXXSEcscPpWqR4mWxXyTk0vkWcgLG59lMFAZ4/Ll+rG1xRHaCuRsAndWNws8h4+NcxY0eECfLQ==
|
||||
"@chakra-ui/react@^1.7.3":
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/react/-/react-1.7.3.tgz#358a3ff9847b78a798384db5c64cc222c9a48ae8"
|
||||
integrity sha512-6mrfDUOa9MoQ44Xvi7xgdDq48jTTTjW9BupCGf2R3DI+z6RbUKIHzbcoDJZt2HGY6j9EarMVNRoQJzvzGUKpoQ==
|
||||
dependencies:
|
||||
"@chakra-ui/accordion" "1.4.1"
|
||||
"@chakra-ui/alert" "1.3.1"
|
||||
"@chakra-ui/accordion" "1.4.2"
|
||||
"@chakra-ui/alert" "1.3.2"
|
||||
"@chakra-ui/avatar" "1.3.1"
|
||||
"@chakra-ui/breadcrumb" "1.3.1"
|
||||
"@chakra-ui/button" "1.5.1"
|
||||
"@chakra-ui/checkbox" "1.6.1"
|
||||
"@chakra-ui/close-button" "1.2.1"
|
||||
"@chakra-ui/close-button" "1.2.2"
|
||||
"@chakra-ui/control-box" "1.1.1"
|
||||
"@chakra-ui/counter" "1.2.1"
|
||||
"@chakra-ui/css-reset" "1.1.1"
|
||||
"@chakra-ui/editable" "1.3.1"
|
||||
"@chakra-ui/form-control" "1.5.1"
|
||||
"@chakra-ui/form-control" "1.5.2"
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/icon" "1.2.1"
|
||||
"@chakra-ui/icon" "2.0.0"
|
||||
"@chakra-ui/image" "1.1.1"
|
||||
"@chakra-ui/input" "1.3.1"
|
||||
"@chakra-ui/layout" "1.5.1"
|
||||
"@chakra-ui/input" "1.3.2"
|
||||
"@chakra-ui/layout" "1.6.0"
|
||||
"@chakra-ui/live-region" "1.1.1"
|
||||
"@chakra-ui/media-query" "1.2.1"
|
||||
"@chakra-ui/menu" "1.8.1"
|
||||
"@chakra-ui/modal" "1.10.1"
|
||||
"@chakra-ui/number-input" "1.3.1"
|
||||
"@chakra-ui/media-query" "1.2.2"
|
||||
"@chakra-ui/menu" "1.8.2"
|
||||
"@chakra-ui/modal" "1.10.2"
|
||||
"@chakra-ui/number-input" "1.3.2"
|
||||
"@chakra-ui/pin-input" "1.7.1"
|
||||
"@chakra-ui/popover" "1.10.1"
|
||||
"@chakra-ui/popover" "1.11.0"
|
||||
"@chakra-ui/popper" "2.4.1"
|
||||
"@chakra-ui/portal" "1.3.1"
|
||||
"@chakra-ui/progress" "1.2.1"
|
||||
"@chakra-ui/provider" "1.7.1"
|
||||
"@chakra-ui/radio" "1.4.1"
|
||||
"@chakra-ui/provider" "1.7.3"
|
||||
"@chakra-ui/radio" "1.4.3"
|
||||
"@chakra-ui/react-env" "1.1.1"
|
||||
"@chakra-ui/select" "1.2.1"
|
||||
"@chakra-ui/skeleton" "1.2.1"
|
||||
"@chakra-ui/slider" "1.5.1"
|
||||
"@chakra-ui/select" "1.2.2"
|
||||
"@chakra-ui/skeleton" "1.2.3"
|
||||
"@chakra-ui/slider" "1.5.2"
|
||||
"@chakra-ui/spinner" "1.2.1"
|
||||
"@chakra-ui/stat" "1.2.1"
|
||||
"@chakra-ui/stat" "1.2.2"
|
||||
"@chakra-ui/switch" "1.3.1"
|
||||
"@chakra-ui/system" "1.8.1"
|
||||
"@chakra-ui/system" "1.8.3"
|
||||
"@chakra-ui/table" "1.3.1"
|
||||
"@chakra-ui/tabs" "1.6.1"
|
||||
"@chakra-ui/tag" "1.2.1"
|
||||
"@chakra-ui/textarea" "1.2.1"
|
||||
"@chakra-ui/theme" "1.12.1"
|
||||
"@chakra-ui/toast" "1.4.1"
|
||||
"@chakra-ui/tooltip" "1.4.1"
|
||||
"@chakra-ui/transition" "1.4.1"
|
||||
"@chakra-ui/tag" "1.2.2"
|
||||
"@chakra-ui/textarea" "1.2.2"
|
||||
"@chakra-ui/theme" "1.12.2"
|
||||
"@chakra-ui/toast" "1.5.0"
|
||||
"@chakra-ui/tooltip" "1.4.2"
|
||||
"@chakra-ui/transition" "1.4.2"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
"@chakra-ui/visually-hidden" "1.1.1"
|
||||
|
||||
"@chakra-ui/select@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/select/-/select-1.2.1.tgz#a014c44efdc992ced96a4a74364148a4846c3d1f"
|
||||
integrity sha512-GqRmYGjVnw/Z/2RQiW7Ywuu9O5E0spmMUBjeE/v0rqjixBqrmdApjg5pmJ4YmUMvUI/WkGtR3FR5W9Y5PpvfKw==
|
||||
"@chakra-ui/select@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/select/-/select-1.2.2.tgz#1c2956c21a012e7c9ea294db42d3cab0d05bed24"
|
||||
integrity sha512-EchJW3St1DtSWHe//DHwKjGsQYL2zbKcNCLnJWQKGMPZsQhAD2wsm4xjowFrV8AkY7jbVM/U2v68puN7YTC3hg==
|
||||
dependencies:
|
||||
"@chakra-ui/form-control" "1.5.1"
|
||||
"@chakra-ui/form-control" "1.5.2"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/skeleton@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/skeleton/-/skeleton-1.2.1.tgz#6083aadb3ad62e8d63eb64d3985b00a1fdcd9746"
|
||||
integrity sha512-08yOmINorbxtv1xgZpHiTE7YCYdicZ5dCkc7nCVxvYQihH5efkEiLbP3EefZzFCTqa2KFN8BvgD5eA76TqleXA==
|
||||
"@chakra-ui/skeleton@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/skeleton/-/skeleton-1.2.3.tgz#f7be28e3af214473fb6cdec45d0715a049f82575"
|
||||
integrity sha512-u5ASkzPiBjfvKxKuBienUfmyYDTHziSWQ8Ny6k83LbwLv9IcmBNGsSkmsp7hesgi9cMHGBQ3hY2GTqG9ljndIg==
|
||||
dependencies:
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/media-query" "1.2.1"
|
||||
"@chakra-ui/system" "1.8.1"
|
||||
"@chakra-ui/media-query" "1.2.2"
|
||||
"@chakra-ui/system" "1.8.3"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/slider@1.5.1":
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/slider/-/slider-1.5.1.tgz#2bb3ef0321d81577132a67ef2d0aa1f594d2242a"
|
||||
integrity sha512-3C0DnUapVdVq9hU1VWAsk0qc6ws1TesjQl/KTkxlP4yBJxKAja1HtyeN9c1x4jizl1a1Q7BJrYICupy70Ju76A==
|
||||
"@chakra-ui/slider@1.5.2":
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/slider/-/slider-1.5.2.tgz#173218b55befe0f7200fe7f332234b3824d19b08"
|
||||
integrity sha512-zP07TMew61GkJe47Nu7zEg/SUEwPHpN4alW6VUM6Y8UaVpQaDx7InarbWTc/bXdTP03SfE+hQ6WD9Oy7noe4hQ==
|
||||
dependencies:
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
@@ -2290,19 +2290,19 @@
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
"@chakra-ui/visually-hidden" "1.1.1"
|
||||
|
||||
"@chakra-ui/stat@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/stat/-/stat-1.2.1.tgz#76be860aa5ae7ef66717ab7a7fcf5862268a05e7"
|
||||
integrity sha512-BTZFeh/8VdgUX080taCQj1g/rS4wGc+y3GQnklqlZ9N/bEv0gyLqQga7TFC/NkVl3cvjRiMnCCPj6vRih9x+Og==
|
||||
"@chakra-ui/stat@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/stat/-/stat-1.2.2.tgz#4a690b9a01986a838eb38ff20ee9cfaccdff0cdd"
|
||||
integrity sha512-0StsPDC56MjzhdlBl0R8wU0uwj9L1tvhQzge/ELSDn4tQDI7VovrxpFzVH0qsj7EZDwZa0BRQaSrstzWvgmJ/Q==
|
||||
dependencies:
|
||||
"@chakra-ui/icon" "1.2.1"
|
||||
"@chakra-ui/icon" "2.0.0"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
"@chakra-ui/visually-hidden" "1.1.1"
|
||||
|
||||
"@chakra-ui/styled-system@1.14.1":
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/styled-system/-/styled-system-1.14.1.tgz#78f49de1842a3675c44b17c9f392d7172880209a"
|
||||
integrity sha512-dgXFYZdJicsddUnPV1X7lQksgMD0z5EvwGaIh2JHJERqNRIvth/CBAnVLQQvy/xSJK5YaSEmeuVVU0veUOQcXg==
|
||||
"@chakra-ui/styled-system@1.15.0":
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/styled-system/-/styled-system-1.15.0.tgz#fe117ace12b452379e4df69f4779395ead55917f"
|
||||
integrity sha512-LnsKeiYkUuJ+NMTwueiX0Mj8CW9XAMJrJxpQm/X3GY5L5PO7Hv6wW725Ovqdy4mhG3IK7S8444FthpsDv/luHw==
|
||||
dependencies:
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
csstype "^3.0.9"
|
||||
@@ -2315,14 +2315,14 @@
|
||||
"@chakra-ui/checkbox" "1.6.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/system@1.8.1":
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/system/-/system-1.8.1.tgz#ce6c5af67840e20f261894c0310a0547314b262c"
|
||||
integrity sha512-CFHdAjXuEDIAvNBatTBo1Tfu/HJ1LfbmjGWI4uTRfA4sDIt+cCRc/VKSfl0IKQwy70yJOMoTehZBjS8g3RTymQ==
|
||||
"@chakra-ui/system@1.8.3":
|
||||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/system/-/system-1.8.3.tgz#bad447f4fda427fc28e234470d9fcc5a9b524d62"
|
||||
integrity sha512-6MaevsT7A2ifgOGQQCQsfvzPVd0kEXqFrX1Oxd842bawaqthmbFdo2bBTdaia/+Ivq/8Xot2uAQSbU+3NuRiUA==
|
||||
dependencies:
|
||||
"@chakra-ui/color-mode" "1.3.1"
|
||||
"@chakra-ui/color-mode" "1.3.2"
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/styled-system" "1.14.1"
|
||||
"@chakra-ui/styled-system" "1.15.0"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
react-fast-compare "3.2.0"
|
||||
|
||||
@@ -2344,20 +2344,20 @@
|
||||
"@chakra-ui/react-utils" "1.2.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/tag@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/tag/-/tag-1.2.1.tgz#2e5b7bd9895ece35d495b83bb29c34a3142cfe01"
|
||||
integrity sha512-O068n+qBc+CSyvpRBJ6Lwep6SydQ9UysRqw1ETF+4fJSp9dMrBp8vOcl2SVacKaCu13qdv8UdRMBxUiTz3lh7A==
|
||||
"@chakra-ui/tag@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/tag/-/tag-1.2.2.tgz#25519544cdbc0a32b2f8f44c7a8bae3bda912cb1"
|
||||
integrity sha512-H25y9nEyUAUdwQDND9P4mMXKf1wf9UH4A3DyP237qVKIyYBpa4aCH8eJU4dunh2yIzASB0DWcr7lsul/HAHxmg==
|
||||
dependencies:
|
||||
"@chakra-ui/icon" "1.2.1"
|
||||
"@chakra-ui/icon" "2.0.0"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/textarea@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/textarea/-/textarea-1.2.1.tgz#cf1594ce6496817530106f70a5d787c1a9e40f59"
|
||||
integrity sha512-3xDsL1qQ+eY5r4GcRL4bg90vtV/xxVlw0Z3PFehFP5JW7VwXNZIRjauR/+HlOA8eYq0cF6ch2boR1GPso6rQtw==
|
||||
"@chakra-ui/textarea@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/textarea/-/textarea-1.2.2.tgz#3e62612e4b1bc0724ba084b0b08b247ac2bbdbab"
|
||||
integrity sha512-DoLdKxHk0DyrQDnj1la9wjl2AW3/SK62nfWDYLAm0ouFsw1VKPw9nU+Yyj0dPruQTzI19nLaYF26i97rtnT27g==
|
||||
dependencies:
|
||||
"@chakra-ui/form-control" "1.5.1"
|
||||
"@chakra-ui/form-control" "1.5.2"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/theme-tools@1.3.1", "@chakra-ui/theme-tools@^1.3.1":
|
||||
@@ -2368,32 +2368,32 @@
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
"@ctrl/tinycolor" "^3.4.0"
|
||||
|
||||
"@chakra-ui/theme@1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/theme/-/theme-1.12.1.tgz#48a61fe5b2b0f8f422255d3049e16bb0b2dd6e60"
|
||||
integrity sha512-8yDril3rSzv42eKR0x7KdnrpN1ubY0m6q37CVUADgtboJqoJwWWX2/hqkv8CX6WJf8ZwPwFL5QIwS2FPSGgi+g==
|
||||
"@chakra-ui/theme@1.12.2":
|
||||
version "1.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/theme/-/theme-1.12.2.tgz#191e168d83e7a66bc3e65be802ad4e4e2a9bbeb5"
|
||||
integrity sha512-LVjSf16yYHD40ILrsDEd3idVQRvJSY7JY8lvTGWo2p6v+JQESWF+zXlYi9Le+TXRpZuFvJuuQ1SEvoqVwdcJ8Q==
|
||||
dependencies:
|
||||
"@chakra-ui/anatomy" "1.2.1"
|
||||
"@chakra-ui/theme-tools" "1.3.1"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
"@chakra-ui/toast@1.4.1":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/toast/-/toast-1.4.1.tgz#3e3885dd039e19f8ce60446f4843e0d8ec074b24"
|
||||
integrity sha512-vzQkYwnGq2nx0bOKIQ6XpJaGzUwnWKmUjcVrz9NzGwVI4g93PS7+13515R0m1NrDp30132OeDXQ+tmQwCRRe6w==
|
||||
"@chakra-ui/toast@1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/toast/-/toast-1.5.0.tgz#c6456c0a4f24669e16946de318ad568e24a5d497"
|
||||
integrity sha512-rTsFx/Qos5oVPN6aZMbT/wTxwZlFNSXQqrTpJYaRcRFQGzxIDDxmGkKYfPnyJjRP9i6EqynJhXEIyhMA0xO0dw==
|
||||
dependencies:
|
||||
"@chakra-ui/alert" "1.3.1"
|
||||
"@chakra-ui/close-button" "1.2.1"
|
||||
"@chakra-ui/alert" "1.3.2"
|
||||
"@chakra-ui/close-button" "1.2.2"
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/theme" "1.12.1"
|
||||
"@chakra-ui/transition" "1.4.1"
|
||||
"@chakra-ui/theme" "1.12.2"
|
||||
"@chakra-ui/transition" "1.4.2"
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
"@reach/alert" "0.13.2"
|
||||
|
||||
"@chakra-ui/tooltip@1.4.1":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/tooltip/-/tooltip-1.4.1.tgz#21e148b7107a92abbf90c19a3eead718b875a544"
|
||||
integrity sha512-KvTuqSqIpIgE+YNUwN7ONDRkSGR6SK9+dgSx2PfKy0Sel7UgDPVtxByuZ6tfJ9O1VTRYEdF9k+s6Gf8eRFQbNA==
|
||||
"@chakra-ui/tooltip@1.4.2":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/tooltip/-/tooltip-1.4.2.tgz#fce61c7ad4c3e6e5833487ddf75f03c774913fb9"
|
||||
integrity sha512-+wyYXG8qenKkFy2YSFfOBf3rlWADnu6S9EUxP+3Rmm78unOWXDuTJWzqy2QlXs2BwoQoifaz1LVwzmMb7WLVgQ==
|
||||
dependencies:
|
||||
"@chakra-ui/hooks" "1.7.1"
|
||||
"@chakra-ui/popper" "2.4.1"
|
||||
@@ -2402,10 +2402,10 @@
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
"@chakra-ui/visually-hidden" "1.1.1"
|
||||
|
||||
"@chakra-ui/transition@1.4.1":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/transition/-/transition-1.4.1.tgz#be31c02a285d182cc9c6dcd95548f8f35de64952"
|
||||
integrity sha512-s/VFucc6grNdP1bxw0oQLzy167gjAgyl/GiGH9nt54nioDEiSsvn70qKg7sjajNTvpoot+urQUdr4Qh+fIUFZQ==
|
||||
"@chakra-ui/transition@1.4.2":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@chakra-ui/transition/-/transition-1.4.2.tgz#de59833aa5727c0dd328b828145e4b0dabd005b3"
|
||||
integrity sha512-S+BNmpErHlntl//uaqv0sJegzMsQms0OnJapeZaRsvZL4s1SVYrR8kMrXigkdpeh4lAUqGsLpQHPKkzaKGbBOw==
|
||||
dependencies:
|
||||
"@chakra-ui/utils" "1.9.1"
|
||||
|
||||
@@ -2454,16 +2454,16 @@
|
||||
source-map "^0.5.7"
|
||||
stylis "^4.0.3"
|
||||
|
||||
"@emotion/cache@^11.5.0":
|
||||
version "11.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.5.0.tgz#a5eb78cbef8163939ee345e3ddf0af217b845e62"
|
||||
integrity sha512-mAZ5QRpLriBtaj/k2qyrXwck6yeoz1V5lMt/jfj6igWU35yYlNKs2LziXVgvH81gnJZ+9QQNGelSsnuoAy6uIw==
|
||||
"@emotion/cache@^11.7.1":
|
||||
version "11.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.7.1.tgz#08d080e396a42e0037848214e8aa7bf879065539"
|
||||
integrity sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.7.4"
|
||||
"@emotion/sheet" "^1.0.3"
|
||||
"@emotion/sheet" "^1.1.0"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
"@emotion/weak-memoize" "^0.2.5"
|
||||
stylis "^4.0.10"
|
||||
stylis "4.0.13"
|
||||
|
||||
"@emotion/hash@^0.8.0":
|
||||
version "0.8.0"
|
||||
@@ -2477,10 +2477,10 @@
|
||||
dependencies:
|
||||
"@emotion/memoize" "0.7.4"
|
||||
|
||||
"@emotion/is-prop-valid@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.1.0.tgz#29ef6be1e946fb4739f9707def860f316f668cde"
|
||||
integrity sha512-9RkilvXAufQHsSsjQ3PIzSns+pxuX4EW8EbGeSPjZMHuMx6z/MOzb9LpqNieQX4F3mre3NWS2+X3JNRHTQztUQ==
|
||||
"@emotion/is-prop-valid@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.1.1.tgz#cbd843d409dfaad90f9404e7c0404c55eae8c134"
|
||||
integrity sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw==
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.7.4"
|
||||
|
||||
@@ -2494,15 +2494,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
|
||||
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
|
||||
|
||||
"@emotion/react@^11.5.0":
|
||||
version "11.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.5.0.tgz#19b5771bbfbda5e8517e948a2d9064810f0022bd"
|
||||
integrity sha512-MYq/bzp3rYbee4EMBORCn4duPQfgpiEB5XzrZEBnUZAL80Qdfr7CEv/T80jwaTl/dnZmt9SnTa8NkTrwFNpLlw==
|
||||
"@emotion/react@^11.7.1":
|
||||
version "11.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.7.1.tgz#3f800ce9b20317c13e77b8489ac4a0b922b2fe07"
|
||||
integrity sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@emotion/cache" "^11.5.0"
|
||||
"@emotion/cache" "^11.7.1"
|
||||
"@emotion/serialize" "^1.0.2"
|
||||
"@emotion/sheet" "^1.0.3"
|
||||
"@emotion/sheet" "^1.1.0"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
"@emotion/weak-memoize" "^0.2.5"
|
||||
hoist-non-react-statics "^3.3.1"
|
||||
@@ -2518,19 +2518,19 @@
|
||||
"@emotion/utils" "^1.0.0"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@emotion/sheet@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.3.tgz#00c326cd7985c5ccb8fe2c1b592886579dcfab8f"
|
||||
integrity sha512-YoX5GyQ4db7LpbmXHMuc8kebtBGP6nZfRC5Z13OKJMixBEwdZrJ914D6yJv/P+ZH/YY3F5s89NYX2hlZAf3SRQ==
|
||||
"@emotion/sheet@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.0.tgz#56d99c41f0a1cda2726a05aa6a20afd4c63e58d2"
|
||||
integrity sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==
|
||||
|
||||
"@emotion/styled@^11.3.0":
|
||||
version "11.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.3.0.tgz#d63ee00537dfb6ff612e31b0e915c5cf9925a207"
|
||||
integrity sha512-fUoLcN3BfMiLlRhJ8CuPUMEyKkLEoM+n+UyAbnqGEsCd5IzKQ7VQFLtzpJOaCD2/VR2+1hXQTnSZXVJeiTNltA==
|
||||
"@emotion/styled@^11.6.0":
|
||||
version "11.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.6.0.tgz#9230d1a7bcb2ebf83c6a579f4c80e0664132d81d"
|
||||
integrity sha512-mxVtVyIOTmCAkFbwIp+nCjTXJNgcz4VWkOYQro87jE2QBTydnkiYusMrRGFtzuruiGK4dDaNORk4gH049iiQuw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@emotion/babel-plugin" "^11.3.0"
|
||||
"@emotion/is-prop-valid" "^1.1.0"
|
||||
"@emotion/is-prop-valid" "^1.1.1"
|
||||
"@emotion/serialize" "^1.0.2"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
|
||||
@@ -5301,10 +5301,10 @@ encodeurl@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||
|
||||
engine.io-client@~6.0.1:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.0.2.tgz#ccfc059051e65ca63845e65929184757754cc34e"
|
||||
integrity sha512-cAep9lhZV6Q8jMXx3TNSU5cydMzMed8/O7Tz5uzyqZvpNPtQ3WQXrLYGADxlsuaFmOLN7wZLmT7ImiFhUOku8g==
|
||||
engine.io-client@~6.1.1:
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.1.1.tgz#800d4b9db5487d169686729e5bd887afa78d36b0"
|
||||
integrity sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==
|
||||
dependencies:
|
||||
"@socket.io/component-emitter" "~3.0.0"
|
||||
debug "~4.3.1"
|
||||
@@ -6306,19 +6306,14 @@ hey-listen@^1.0.8:
|
||||
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
|
||||
integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==
|
||||
|
||||
history@^4.9.0:
|
||||
version "4.10.1"
|
||||
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
|
||||
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
|
||||
history@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/history/-/history-5.2.0.tgz#7cdd31cf9bac3c5d31f09c231c9928fad0007b7c"
|
||||
integrity sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
loose-envify "^1.2.0"
|
||||
resolve-pathname "^3.0.0"
|
||||
tiny-invariant "^1.0.2"
|
||||
tiny-warning "^1.0.0"
|
||||
value-equal "^1.0.1"
|
||||
"@babel/runtime" "^7.7.6"
|
||||
|
||||
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
@@ -6853,11 +6848,6 @@ is-wsl@^2.2.0:
|
||||
dependencies:
|
||||
is-docker "^2.0.0"
|
||||
|
||||
isarray@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
||||
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
||||
|
||||
isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
@@ -7670,7 +7660,7 @@ lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
@@ -7832,14 +7822,6 @@ min-indent@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
|
||||
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
|
||||
|
||||
mini-create-react-context@^0.4.0:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e"
|
||||
integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.1"
|
||||
tiny-warning "^1.0.3"
|
||||
|
||||
mini-css-extract-plugin@^2.4.5:
|
||||
version "2.4.5"
|
||||
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.5.tgz#191d6c170226037212c483af1180b4010b7b9eef"
|
||||
@@ -8133,6 +8115,10 @@ onetime@^5.1.2:
|
||||
dependencies:
|
||||
mimic-fn "^2.1.0"
|
||||
|
||||
"ontime-utils@link: ../server/utils":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
open@^8.0.9, open@^8.4.0:
|
||||
version "8.4.0"
|
||||
resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
|
||||
@@ -8323,13 +8309,6 @@ path-to-regexp@0.1.7:
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
||||
|
||||
path-to-regexp@^1.7.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
|
||||
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
|
||||
dependencies:
|
||||
isarray "0.0.1"
|
||||
|
||||
path-type@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
@@ -9220,15 +9199,15 @@ react-icons@^4.3.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||
|
||||
react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||
react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-qr-code@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-qr-code/-/react-qr-code-2.0.2.tgz#64107c869079aceb897c97496d163720ab2820e8"
|
||||
integrity sha512-73VGe81MgeE5FJNFgdY42ez/wPPJTHuooU3iE4CX+6F8M88O1Gg4zNA0L4bKEpoySQ0QjqreJgyXjFrG/QfsdA==
|
||||
react-qr-code@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/react-qr-code/-/react-qr-code-2.0.3.tgz#cc80785e08f817d1ab066ca4035262f77d049648"
|
||||
integrity sha512-6GDH0l53lksf2JgZwwcoS0D60a1OAal/GQRyNFkMBW19HjSqvtD5S20scmSQsKl+BgWM85Wd5DCcUYoHd+PZnQ==
|
||||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
qr.js "0.0.0"
|
||||
@@ -9278,34 +9257,20 @@ react-remove-scroll@2.4.1:
|
||||
use-callback-ref "^1.2.3"
|
||||
use-sidecar "^1.0.1"
|
||||
|
||||
react-router-dom@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
|
||||
integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
|
||||
react-router-dom@^6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.2.1.tgz#32ec81829152fbb8a7b045bf593a22eadf019bec"
|
||||
integrity sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
history "^4.9.0"
|
||||
loose-envify "^1.3.1"
|
||||
prop-types "^15.6.2"
|
||||
react-router "5.2.0"
|
||||
tiny-invariant "^1.0.2"
|
||||
tiny-warning "^1.0.0"
|
||||
history "^5.2.0"
|
||||
react-router "6.2.1"
|
||||
|
||||
react-router@5.2.0, react-router@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293"
|
||||
integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
|
||||
react-router@6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.2.1.tgz#be2a97a6006ce1d9123c28934e604faef51448a3"
|
||||
integrity sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
history "^4.9.0"
|
||||
hoist-non-react-statics "^3.1.0"
|
||||
loose-envify "^1.3.1"
|
||||
mini-create-react-context "^0.4.0"
|
||||
path-to-regexp "^1.7.0"
|
||||
prop-types "^15.6.2"
|
||||
react-is "^16.6.0"
|
||||
tiny-invariant "^1.0.2"
|
||||
tiny-warning "^1.0.0"
|
||||
history "^5.2.0"
|
||||
|
||||
react-scripts@5.0.0:
|
||||
version "5.0.0"
|
||||
@@ -9577,11 +9542,6 @@ resolve-from@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
|
||||
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
|
||||
|
||||
resolve-pathname@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
|
||||
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
|
||||
|
||||
resolve-url-loader@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz#d50d4ddc746bb10468443167acf800dcd6c3ad57"
|
||||
@@ -9903,15 +9863,15 @@ slash@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
|
||||
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
|
||||
|
||||
socket.io-client@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.3.2.tgz#9cfdb8fecac8a24d5723daf8c8749e70c8fdeb25"
|
||||
integrity sha512-2B9LqSunN60yV8F7S84CCEEcgbYNfrn7ejIInZtLZ7ppWtiX8rGZAjvdCvbnC8bqo/9RlCNOUsORLyskxSFP1g==
|
||||
socket.io-client@4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.4.0.tgz#d6568ebd79ac12e2d6b628e7e90e60f1d48d99ff"
|
||||
integrity sha512-g7riSEJXi7qCFImPow98oT8X++MSsHz6MMFRXkWNJ6uEROSHOa3kxdrsYWMq85dO+09CFMkcqlpjvbVXQl4z6g==
|
||||
dependencies:
|
||||
"@socket.io/component-emitter" "~3.0.0"
|
||||
backo2 "~1.0.2"
|
||||
debug "~4.3.2"
|
||||
engine.io-client "~6.0.1"
|
||||
engine.io-client "~6.1.1"
|
||||
parseuri "0.0.6"
|
||||
socket.io-parser "~4.1.1"
|
||||
|
||||
@@ -10219,10 +10179,10 @@ stylehacks@^5.0.1:
|
||||
browserslist "^4.16.0"
|
||||
postcss-selector-parser "^6.0.4"
|
||||
|
||||
stylis@^4.0.10:
|
||||
version "4.0.10"
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240"
|
||||
integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==
|
||||
stylis@4.0.13:
|
||||
version "4.0.13"
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
|
||||
integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==
|
||||
|
||||
stylis@^4.0.3:
|
||||
version "4.0.9"
|
||||
@@ -10409,16 +10369,11 @@ timsort@^0.3.0:
|
||||
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||
|
||||
tiny-invariant@^1.0.2, tiny-invariant@^1.0.6:
|
||||
tiny-invariant@^1.0.6:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
||||
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
||||
|
||||
tiny-warning@^1.0.0, tiny-warning@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||
|
||||
tmp@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
|
||||
@@ -10729,11 +10684,6 @@ v8-to-istanbul@^8.1.0:
|
||||
convert-source-map "^1.6.0"
|
||||
source-map "^0.7.3"
|
||||
|
||||
value-equal@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
|
||||
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
|
||||
|
||||
vary@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
|
||||
Reference in New Issue
Block a user