mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-16 12:53:32 +00:00
Feat/table (#105)
* feat/table add react-table * feat/table add protected table route * feat/table upgrade dependencies * feat/table support parsing of new properties Co-authored-by: DeepSource Bot <bot@deepsource.io>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { createContext, useCallback, useEffect, useState } from 'react';
|
||||
import React, { createContext, useCallback, useEffect, useState } from 'react';
|
||||
import { useFetch } from '../hooks/useFetch';
|
||||
import { APP_SETTINGS } from '../api/apiConstants';
|
||||
import { getSettings } from '../api/ontimeApi';
|
||||
@@ -6,11 +6,11 @@ import { getSettings } from '../api/ontimeApi';
|
||||
export const AppContext = createContext({
|
||||
auth: false,
|
||||
data: {
|
||||
pinCode: null
|
||||
}
|
||||
pinCode: null,
|
||||
},
|
||||
});
|
||||
|
||||
export const AppContextProvider = (props) => {
|
||||
export const AppContextProvider = ({ children }) => {
|
||||
const [auth, setAuth] = useState(true);
|
||||
const { data } = useFetch(APP_SETTINGS, getSettings);
|
||||
|
||||
@@ -21,17 +21,16 @@ export const AppContextProvider = (props) => {
|
||||
} 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>
|
||||
const validate = useCallback(
|
||||
(pin) => {
|
||||
const correct = pin === data.pinCode;
|
||||
setAuth(correct);
|
||||
return correct;
|
||||
},
|
||||
[data]
|
||||
);
|
||||
};
|
||||
|
||||
return <AppContext.Provider value={{ auth, validate }}>{children}</AppContext.Provider>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import React, { createContext, useCallback } from 'react';
|
||||
import { useLocalStorage } from '../hooks/useLocalStorage';
|
||||
|
||||
export const CollapseContext = createContext({
|
||||
collapsed: {},
|
||||
setCollapsed: () => undefined,
|
||||
clearCollapsed: () => undefined,
|
||||
isCollapsed: () => undefined,
|
||||
});
|
||||
|
||||
export const CollapseProvider = ({ children }) => {
|
||||
const [collapsed, saveCollapsed] = useLocalStorage('collapsed', {});
|
||||
|
||||
/**
|
||||
* @description Sets collapsed state for a single id
|
||||
* @param {string} - id
|
||||
* @param {boolean} - collapsed / not collapsed
|
||||
*/
|
||||
const setCollapsed = useCallback(
|
||||
(id, isCollapsed) => {
|
||||
if (isCollapsed) {
|
||||
saveCollapsed((prev) => ({ ...prev, [id]: true }));
|
||||
} else {
|
||||
saveCollapsed((prev) => {
|
||||
const newObject = { ...prev };
|
||||
delete newObject[id];
|
||||
return { ...newObject };
|
||||
});
|
||||
}
|
||||
},
|
||||
[saveCollapsed]
|
||||
);
|
||||
|
||||
/**
|
||||
* @description Clears collapsed state in local-storage
|
||||
*/
|
||||
const collapseMultiple = useCallback(
|
||||
(events) => {
|
||||
const newOptions = {};
|
||||
for (const event of events) {
|
||||
newOptions[event.id] = true;
|
||||
}
|
||||
saveCollapsed(newOptions);
|
||||
},
|
||||
[saveCollapsed]
|
||||
);
|
||||
|
||||
/**
|
||||
* @description Clears collapsed state in local-storage
|
||||
*/
|
||||
const expandAll = useCallback(() => {
|
||||
saveCollapsed({});
|
||||
}, [saveCollapsed]);
|
||||
|
||||
/**
|
||||
* @description Clears collapsed state in local-storage
|
||||
* @return {boolean} - collapsed / not collapsed
|
||||
*/
|
||||
const isCollapsed = useCallback(
|
||||
(id) => {
|
||||
return id in collapsed;
|
||||
},
|
||||
[collapsed]
|
||||
);
|
||||
|
||||
return (
|
||||
<CollapseContext.Provider value={{ setCollapsed, collapseMultiple, expandAll, isCollapsed }}>
|
||||
{children}
|
||||
</CollapseContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, useCallback, useMemo, useState } from 'react';
|
||||
import React, { createContext, useCallback, useMemo, useState } from 'react';
|
||||
import { useLocalStorage } from '../hooks/useLocalStorage';
|
||||
|
||||
export const CursorContext = createContext({
|
||||
@@ -10,7 +10,7 @@ export const CursorContext = createContext({
|
||||
moveCursorDown: () => undefined,
|
||||
});
|
||||
|
||||
export const CursorProvider = (props) => {
|
||||
export const CursorProvider = ({ children }) => {
|
||||
const [cursor, setCursor] = useState(0);
|
||||
const [_cursorLocked, _setCursorLocked] = useLocalStorage('isCursorLocked', 'locked');
|
||||
const isCursorLocked = useMemo(() => _cursorLocked === 'locked', [_cursorLocked]);
|
||||
@@ -57,7 +57,7 @@ export const CursorProvider = (props) => {
|
||||
moveCursorDown,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</CursorContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, useState } from 'react';
|
||||
import React, { createContext, useState } from 'react';
|
||||
|
||||
export const LocalEventSettingsContext = createContext({
|
||||
showQuickEntry: false,
|
||||
@@ -10,7 +10,7 @@ export const LocalEventSettingsContext = createContext({
|
||||
setDefaultPublic: () => undefined,
|
||||
});
|
||||
|
||||
export const LocalEventSettingsProvider = (props) => {
|
||||
export const LocalEventSettingsProvider = ({ children }) => {
|
||||
const [showQuickEntry, setShowQuickEntry] = useState(false);
|
||||
const [starTimeIsLastEnd, setStarTimeIsLastEnd] = useState(true);
|
||||
const [defaultPublic, setDefaultPublic] = useState(false);
|
||||
@@ -26,7 +26,7 @@ export const LocalEventSettingsProvider = (props) => {
|
||||
setDefaultPublic,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</LocalEventSettingsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { createContext, useCallback, useEffect, useState } from 'react';
|
||||
import { useSocket } from './socketContext';
|
||||
import { createContext, useCallback, useEffect, useState } from 'react';
|
||||
import { generateId } from 'ontime-utils/generate_id';
|
||||
import { nowInMillis, stringFromMillis } from 'ontime-utils/time';
|
||||
|
||||
@@ -8,10 +8,10 @@ export const LoggingContext = createContext({
|
||||
emitInfo: () => undefined,
|
||||
emitWarning: () => undefined,
|
||||
emitError: () => undefined,
|
||||
clearLog: () => undefined
|
||||
clearLog: () => undefined,
|
||||
});
|
||||
|
||||
export const LoggingProvider = (props) => {
|
||||
export const LoggingProvider = ({ children }) => {
|
||||
const MAX_MESSAGES = 100;
|
||||
const socket = useSocket();
|
||||
const [logData, setLogData] = useState([]);
|
||||
@@ -40,57 +40,69 @@ export const LoggingProvider = (props) => {
|
||||
* @param level
|
||||
* @private
|
||||
*/
|
||||
const _send = useCallback((text, level) => {
|
||||
if (socket != null) {
|
||||
const m = {
|
||||
id: generateId(),
|
||||
origin,
|
||||
time: stringFromMillis(nowInMillis()),
|
||||
level,
|
||||
text
|
||||
const _send = useCallback(
|
||||
(text, level) => {
|
||||
if (socket != null) {
|
||||
const m = {
|
||||
id: generateId(),
|
||||
origin,
|
||||
time: stringFromMillis(nowInMillis()),
|
||||
level,
|
||||
text,
|
||||
};
|
||||
setLogData((l) => [m, ...l]);
|
||||
socket.emit('logger', m);
|
||||
}
|
||||
setLogData((l) => [m, ...l]);
|
||||
socket.emit('logger', m);
|
||||
}
|
||||
if (logData.length > MAX_MESSAGES) {
|
||||
setLogData((l) => l.pop());
|
||||
}
|
||||
},[logData, socket]);
|
||||
if (logData.length > MAX_MESSAGES) {
|
||||
setLogData((l) => l.pop());
|
||||
}
|
||||
},
|
||||
[logData, socket]
|
||||
);
|
||||
|
||||
/**
|
||||
* Sends a message with level INFO
|
||||
* @param text
|
||||
*/
|
||||
const emitInfo = useCallback((text) => {
|
||||
_send(text, 'INFO');
|
||||
}, [_send]);
|
||||
const emitInfo = useCallback(
|
||||
(text) => {
|
||||
_send(text, 'INFO');
|
||||
},
|
||||
[_send]
|
||||
);
|
||||
|
||||
/**
|
||||
* Sends a message with level WARN
|
||||
* @param text
|
||||
*/
|
||||
const emitWarning = useCallback((text) => {
|
||||
_send(text, 'WARN');
|
||||
}, [_send]);
|
||||
const emitWarning = useCallback(
|
||||
(text) => {
|
||||
_send(text, 'WARN');
|
||||
},
|
||||
[_send]
|
||||
);
|
||||
|
||||
/**
|
||||
* Sends a message with level ERROR
|
||||
* @param text
|
||||
*/
|
||||
const emitError = useCallback((text) => {
|
||||
_send(text, 'ERROR');
|
||||
}, [_send]);
|
||||
const emitError = useCallback(
|
||||
(text) => {
|
||||
_send(text, 'ERROR');
|
||||
},
|
||||
[_send]
|
||||
);
|
||||
|
||||
/**
|
||||
* Clears running log
|
||||
*/
|
||||
const clearLog = useCallback(() => {
|
||||
setLogData([])
|
||||
setLogData([]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LoggingContext.Provider value = {{ emitInfo, logData, emitWarning, emitError, clearLog }}>
|
||||
{props.children}
|
||||
<LoggingContext.Provider value={{ emitInfo, logData, emitWarning, emitError, clearLog }}>
|
||||
{children}
|
||||
</LoggingContext.Provider>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import React, { createContext, useCallback, useState } from 'react';
|
||||
import { useLocalStorage } from '../hooks/useLocalStorage';
|
||||
|
||||
export const TableSettingsContext = createContext({
|
||||
theme: '',
|
||||
showSettings: false,
|
||||
followSelected: false,
|
||||
|
||||
toggleSettings: () => undefined,
|
||||
toggleTheme: () => undefined,
|
||||
toggleFollow: () => undefined,
|
||||
});
|
||||
|
||||
export const TableSettingsProvider = ({ children }) => {
|
||||
const [theme, setTheme] = useLocalStorage('table-color-theme', 'dark');
|
||||
const [followSelected, setFollowSelected] = useLocalStorage('table-follow-selected', false);
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
|
||||
/**
|
||||
* @description Toggles the current value of dark mode
|
||||
* @param {string} val - 'light' or 'dark'
|
||||
*/
|
||||
const toggleTheme = useCallback(
|
||||
(val) => {
|
||||
if (val === undefined) {
|
||||
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
|
||||
} else {
|
||||
setTheme(val);
|
||||
}
|
||||
},
|
||||
[setTheme]
|
||||
);
|
||||
|
||||
/**
|
||||
* @description Toggles visibility state for settings
|
||||
* @param {boolean} val - whether the settings window is visible
|
||||
*/
|
||||
const toggleSettings = useCallback(
|
||||
(val) => {
|
||||
if (val === undefined) {
|
||||
setShowSettings((prev) => !prev);
|
||||
} else {
|
||||
setShowSettings(val);
|
||||
}
|
||||
},
|
||||
[setShowSettings]
|
||||
);
|
||||
|
||||
/**
|
||||
* @description Toggles follow option
|
||||
* @param {boolean} val - whether the window follows selected event
|
||||
*/
|
||||
const toggleFollow = useCallback(
|
||||
(val) => {
|
||||
if (val === undefined) {
|
||||
setFollowSelected((prev) => !prev);
|
||||
} else {
|
||||
setFollowSelected(val);
|
||||
}
|
||||
},
|
||||
[setFollowSelected]
|
||||
);
|
||||
|
||||
return (
|
||||
<TableSettingsContext.Provider
|
||||
value={{
|
||||
theme,
|
||||
showSettings,
|
||||
followSelected,
|
||||
toggleSettings,
|
||||
toggleTheme,
|
||||
toggleFollow,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</TableSettingsContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
const { atom } = require('jotai');
|
||||
|
||||
const PATH = 'option-collapse';
|
||||
const initialValue = {};
|
||||
|
||||
// collapse options object, initialised from local storage
|
||||
// REFRACT: When do we read from local storage?
|
||||
// on every render?
|
||||
|
||||
export const collapseAtom = atom(
|
||||
(get) => {
|
||||
const storedOptions = localStorage.getItem(PATH);
|
||||
if (storedOptions == null) return initialValue;
|
||||
|
||||
return JSON.parse(storedOptions);
|
||||
},
|
||||
(get, set, newValues) => {
|
||||
set(collapseAtom, newValues);
|
||||
localStorage.setItem(PATH, JSON.stringify(newValues));
|
||||
}
|
||||
);
|
||||
|
||||
// get a single option, if it exists
|
||||
export const SelectCollapse = (id) => {
|
||||
return atom((get) => get(collapseAtom)[id]);
|
||||
};
|
||||
|
||||
// change a single item in object
|
||||
export const HandleCollapse = atom(null, (get, set, payload) => {
|
||||
const updatedVal = {
|
||||
...get(collapseAtom),
|
||||
...payload,
|
||||
};
|
||||
set(collapseAtom, updatedVal);
|
||||
localStorage.setItem(PATH, JSON.stringify(updatedVal));
|
||||
});
|
||||
|
||||
// change collapsed in several items
|
||||
export const BatchOperation = atom(null, (get, set, payload) => {
|
||||
let prevOptions = get(collapseAtom);
|
||||
|
||||
let newOptions = {};
|
||||
|
||||
for (const item of payload.items) {
|
||||
newOptions[item.id] = payload.isCollapsed;
|
||||
}
|
||||
|
||||
if (payload.clear) {
|
||||
// clear object
|
||||
prevOptions = {};
|
||||
// clear localstorage
|
||||
localStorage.removeItem(PATH);
|
||||
}
|
||||
|
||||
const options = { ...prevOptions, ...newOptions };
|
||||
|
||||
set(collapseAtom, options);
|
||||
|
||||
localStorage.setItem(PATH, JSON.stringify(options));
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
const { atom } = require('jotai');
|
||||
|
||||
const PATH = 'option-gen';
|
||||
const initialValue = {
|
||||
cursor: 'locked',
|
||||
};
|
||||
|
||||
export const settingsAtom = atom(
|
||||
(get) => {
|
||||
const storedOptions = localStorage.getItem(PATH);
|
||||
if (storedOptions == null) return initialValue;
|
||||
|
||||
return JSON.parse(storedOptions);
|
||||
},
|
||||
(get, set, newValues) => {
|
||||
set(settingsAtom, newValues);
|
||||
localStorage.setItem(PATH, JSON.stringify(newValues));
|
||||
}
|
||||
);
|
||||
|
||||
// get a single option, if it exists
|
||||
export const SelectSetting = (setting) => {
|
||||
return atom((get) => get(settingsAtom)[setting]);
|
||||
};
|
||||
|
||||
// change a single item in object
|
||||
export const HandleOptions = atom(null, (get, set, payload) => {
|
||||
const updatedVal = {
|
||||
...get(settingsAtom),
|
||||
...payload,
|
||||
};
|
||||
set(settingsAtom, updatedVal);
|
||||
localStorage.setItem(PATH, JSON.stringify(updatedVal));
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, useContext, useEffect, useState } from 'react';
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import io from 'socket.io-client';
|
||||
import { serverURL } from 'app/api/apiConstants';
|
||||
|
||||
@@ -8,7 +8,7 @@ export const useSocket = () => {
|
||||
return useContext(SocketContext);
|
||||
};
|
||||
|
||||
function SocketProvider(props) {
|
||||
function SocketProvider({ children }) {
|
||||
const [socket, setSocket] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -17,10 +17,7 @@ function SocketProvider(props) {
|
||||
return () => s.disconnect();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SocketContext.Provider value={socket}>
|
||||
{props.children}
|
||||
</SocketContext.Provider>
|
||||
);
|
||||
return <SocketContext.Provider value={socket}>{children}</SocketContext.Provider>;
|
||||
}
|
||||
|
||||
export default SocketProvider;
|
||||
|
||||
Reference in New Issue
Block a user