Add profile page, refactor code (#421)
This commit is contained in:
parent
b4e7046aac
commit
f658c229f4
|
@ -34,7 +34,7 @@
|
|||
"devDependencies": {
|
||||
"@craco/craco": "^7.0.0-alpha.0",
|
||||
"@speedy-tuner/eslint-config": "^0.1.3",
|
||||
"@types/node": "^17.0.16",
|
||||
"@types/node": "^17.0.17",
|
||||
"@types/pako": "^1.0.3",
|
||||
"@types/react": "^17.0.39",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
|
@ -5093,9 +5093,9 @@
|
|||
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "17.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.16.tgz",
|
||||
"integrity": "sha512-ydLaGVfQOQ6hI1xK2A5nVh8bl0OGoIfYMxPWHqqYe9bTkWCfqiVvZoh2I/QF2sNSkZzZyROBoTefIEI+PB6iIA=="
|
||||
"version": "17.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.17.tgz",
|
||||
"integrity": "sha512-e8PUNQy1HgJGV3iU/Bp2+D/DXh3PYeyli8LgIwsQcs1Ar1LoaWHSIT6Rw+H2rNJmiq6SNWiDytfx8+gYj7wDHw=="
|
||||
},
|
||||
"node_modules/@types/pako": {
|
||||
"version": "1.0.3",
|
||||
|
@ -24138,9 +24138,9 @@
|
|||
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "17.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.16.tgz",
|
||||
"integrity": "sha512-ydLaGVfQOQ6hI1xK2A5nVh8bl0OGoIfYMxPWHqqYe9bTkWCfqiVvZoh2I/QF2sNSkZzZyROBoTefIEI+PB6iIA=="
|
||||
"version": "17.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.17.tgz",
|
||||
"integrity": "sha512-e8PUNQy1HgJGV3iU/Bp2+D/DXh3PYeyli8LgIwsQcs1Ar1LoaWHSIT6Rw+H2rNJmiq6SNWiDytfx8+gYj7wDHw=="
|
||||
},
|
||||
"@types/pako": {
|
||||
"version": "1.0.3",
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
"devDependencies": {
|
||||
"@craco/craco": "^7.0.0-alpha.0",
|
||||
"@speedy-tuner/eslint-config": "^0.1.3",
|
||||
"@types/node": "^17.0.16",
|
||||
"@types/node": "^17.0.17",
|
||||
"@types/pako": "^1.0.3",
|
||||
"@types/react": "^17.0.39",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
|
|
14
src/App.less
14
src/App.less
|
@ -64,6 +64,20 @@ html, body {
|
|||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.small-container,
|
||||
.large-container {
|
||||
padding: 20px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.small-container {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.large-container {
|
||||
max-width: 1400px;
|
||||
}
|
||||
|
||||
.ant-tabs-tabpane {
|
||||
height: calc(100vh - @layout-header-height - @layout-footer-height - @layout-trigger-height - @tabs-nav-height);
|
||||
overflow-y: auto;
|
||||
|
|
81
src/App.tsx
81
src/App.tsx
|
@ -6,7 +6,6 @@ import {
|
|||
import {
|
||||
Layout,
|
||||
Result,
|
||||
Skeleton,
|
||||
} from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
|
@ -22,7 +21,7 @@ import { Routes } from './routes';
|
|||
import { loadTune } from './utils/api';
|
||||
import store from './store';
|
||||
import Logs from './pages/Logs';
|
||||
import './App.less';
|
||||
import Loader from './components/Loader';
|
||||
import {
|
||||
AppState,
|
||||
NavigationState,
|
||||
|
@ -33,6 +32,7 @@ import Info from './pages/Info';
|
|||
import Hub from './pages/Hub';
|
||||
|
||||
import 'react-perfect-scrollbar/dist/css/styles.css';
|
||||
import './App.less';
|
||||
|
||||
// TODO: fix this
|
||||
// lazy loading this component causes a weird Curve canvas scaling
|
||||
|
@ -41,6 +41,7 @@ import 'react-perfect-scrollbar/dist/css/styles.css';
|
|||
const Tune = lazy(() => import('./pages/Tune'));
|
||||
const Diagnose = lazy(() => import('./pages/Diagnose'));
|
||||
const Login = lazy(() => import('./pages/auth/Login'));
|
||||
const Profile = lazy(() => import('./pages/auth/Profile'));
|
||||
const SignUp = lazy(() => import('./pages/auth/SignUp'));
|
||||
const ResetPassword = lazy(() => import('./pages/auth/ResetPassword'));
|
||||
const Upload = lazy(() => import('./pages/Upload'));
|
||||
|
@ -84,22 +85,15 @@ const App = ({ ui, navigation }: { ui: UIState, navigation: NavigationState }) =
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [tuneId]);
|
||||
|
||||
const ContentFor = useCallback((props: { children: ReactNode, marginLeft?: number }) => {
|
||||
const { children, marginLeft } = props;
|
||||
const ContentFor = useCallback((props: { element: ReactNode, marginLeft?: number }) => {
|
||||
const { element, marginLeft } = props;
|
||||
|
||||
return (
|
||||
<Layout style={{ marginLeft }}>
|
||||
<Layout className="app-content">
|
||||
<Content>
|
||||
<Suspense fallback={<Skeleton
|
||||
active
|
||||
style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
padding: 20,
|
||||
}}
|
||||
/>}>
|
||||
{children}
|
||||
<Suspense fallback={<Loader />}>
|
||||
{element}
|
||||
</Suspense>
|
||||
</Content>
|
||||
</Layout>
|
||||
|
@ -112,57 +106,18 @@ const App = ({ ui, navigation }: { ui: UIState, navigation: NavigationState }) =
|
|||
<Layout>
|
||||
<TopBar tuneId={navigation.tuneId} />
|
||||
<ReactRoutes>
|
||||
<Route path={Routes.ROOT} element={
|
||||
<ContentFor>
|
||||
<Hub />
|
||||
</ContentFor>
|
||||
} />
|
||||
<Route path={Routes.TUNE_ROOT} element={
|
||||
<ContentFor>
|
||||
<Info />
|
||||
</ContentFor>
|
||||
} />
|
||||
<Route path={`${Routes.TUNE_TUNE}/*`} element={
|
||||
<ContentFor marginLeft={margin}>
|
||||
<Tune />
|
||||
</ContentFor>
|
||||
} />
|
||||
<Route path={Routes.TUNE_LOGS} element={
|
||||
<ContentFor marginLeft={margin}>
|
||||
<Logs />
|
||||
</ContentFor>
|
||||
} />
|
||||
<Route path={Routes.TUNE_DIAGNOSE} element={
|
||||
<ContentFor marginLeft={margin}>
|
||||
<Diagnose />
|
||||
</ContentFor>
|
||||
} />
|
||||
<Route path={Routes.LOGIN} element={
|
||||
<ContentFor>
|
||||
<Login />
|
||||
</ContentFor>
|
||||
} />
|
||||
<Route path={Routes.SIGN_UP} element={
|
||||
<ContentFor>
|
||||
<SignUp />
|
||||
</ContentFor>
|
||||
} />
|
||||
<Route path={Routes.RESET_PASSWORD} element={
|
||||
<ContentFor>
|
||||
<ResetPassword />
|
||||
</ContentFor>
|
||||
} />
|
||||
<Route path={Routes.UPLOAD} element={
|
||||
<ContentFor>
|
||||
<Upload />
|
||||
</ContentFor>
|
||||
} />
|
||||
<Route path={Routes.HUB} element={<ContentFor element={<Hub />} />} />
|
||||
<Route path={Routes.TUNE_ROOT} element={<ContentFor element={<Info />} />} />
|
||||
<Route path={`${Routes.TUNE_TUNE}/*`} element={<ContentFor marginLeft={margin} element={<Tune />} />} />
|
||||
<Route path={Routes.TUNE_LOGS} element={<ContentFor marginLeft={margin} element={<Logs />} />} />
|
||||
<Route path={Routes.TUNE_DIAGNOSE} element={<ContentFor marginLeft={margin} element={<Diagnose />} />} />
|
||||
<Route path={Routes.LOGIN} element={<ContentFor element={<Login />} />} />
|
||||
<Route path={Routes.PROFILE} element={<ContentFor element={<Profile />} />} />
|
||||
<Route path={Routes.SIGN_UP} element={<ContentFor element={<SignUp />} />} />
|
||||
<Route path={Routes.RESET_PASSWORD} element={<ContentFor element={<ResetPassword />} />} />
|
||||
<Route path={Routes.UPLOAD} element={<ContentFor element={<Upload />} />} />
|
||||
</ReactRoutes>
|
||||
<Result
|
||||
status="warning"
|
||||
title="Page not found"
|
||||
style={{ marginTop: 50 }}
|
||||
/>
|
||||
<Result status="warning" title="Page not found" style={{ marginTop: 50 }} />
|
||||
</Layout>
|
||||
<StatusBar />
|
||||
</>
|
||||
|
|
|
@ -322,7 +322,7 @@ const CommandPalette = (props: CommandPaletteProps) => {
|
|||
name: 'Hub',
|
||||
subtitle: 'Public tunes and logs.',
|
||||
icon: <CarOutlined />,
|
||||
perform: () => navigate(Routes.ROOT),
|
||||
perform: () => navigate(Routes.HUB),
|
||||
},
|
||||
{
|
||||
id: 'ToggleSidebar',
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { Skeleton } from 'antd';
|
||||
|
||||
const Loader = () => (
|
||||
<div className="small-container">
|
||||
<Skeleton active />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Loader;
|
|
@ -17,9 +17,9 @@ import {
|
|||
import LandscapeNotice from '../Tune/Dialog/LandscapeNotice';
|
||||
import { Colors } from '../../utils/colors';
|
||||
import touchZoomPlugin from '../../utils/uPlot/touchZoomPlugin';
|
||||
import { isNumber } from '../../utils/tune/expression';
|
||||
|
||||
import 'uplot/dist/uPlot.min.css';
|
||||
import { isNumber } from '../../utils/tune/expression';
|
||||
|
||||
const { useBreakpoint } = Grid;
|
||||
|
||||
|
|
|
@ -73,20 +73,22 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
|
|||
const navigate = useNavigate();
|
||||
const { query } = useKBar();
|
||||
const buildTuneUrl = useCallback((route: string) => tuneId ? generatePath(route, { tuneId }) : null, [tuneId]);
|
||||
const rootPathMatch = useMatch(Routes.ROOT);
|
||||
const hubPathMatch = useMatch(Routes.HUB);
|
||||
const tuneRootMatch = useMatch(`${Routes.TUNE_ROOT}/*`);
|
||||
const tuneTuneMatch = useMatch(`${Routes.TUNE_TUNE}/*`);
|
||||
const tabMatch = useMatch(`${Routes.TUNE_TAB}/*`);
|
||||
const uploadMatch = useMatch(Routes.UPLOAD);
|
||||
const hubMatch = useMatch(Routes.HUB);
|
||||
const logoutClick = useCallback(async () => {
|
||||
try {
|
||||
await logout();
|
||||
navigate(Routes.HUB);
|
||||
logout();
|
||||
logOutSuccessful();
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
logOutFailed(error as Error);
|
||||
}
|
||||
}, [logout]);
|
||||
}, [logout, navigate]);
|
||||
|
||||
const toggleCommandPalette = useCallback(() => query.toggle(), [query]);
|
||||
|
||||
|
@ -109,15 +111,15 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
|
|||
});
|
||||
|
||||
const tabs = useMemo(() => (
|
||||
<Col span={16} md={12} sm={16} style={{ textAlign: 'center' }}>
|
||||
<Col span={16} md={16} sm={16}>
|
||||
<Radio.Group
|
||||
key={pathname}
|
||||
defaultValue={tuneTuneMatch?.pathnameBase || tabMatch?.pathname || tuneRootMatch?.pathname || rootPathMatch?.pathname}
|
||||
defaultValue={tuneTuneMatch?.pathnameBase || tabMatch?.pathname || tuneRootMatch?.pathname || hubPathMatch?.pathname}
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
onChange={(e) => navigate(e.target.value)}
|
||||
>
|
||||
<Radio.Button value={buildTuneUrl(Routes.ROOT)}>
|
||||
<Radio.Button value={buildTuneUrl(Routes.HUB)}>
|
||||
<Space>
|
||||
<CarOutlined />
|
||||
{lg && 'Hub'}
|
||||
|
@ -149,7 +151,7 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
|
|||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Col>
|
||||
), [buildTuneUrl, lg, navigate, pathname, rootPathMatch?.pathname, tabMatch?.pathname, tuneRootMatch?.pathname, tuneTuneMatch?.pathnameBase]);
|
||||
), [buildTuneUrl, lg, navigate, pathname, hubPathMatch?.pathname, tabMatch?.pathname, tuneRootMatch?.pathname, tuneTuneMatch?.pathnameBase]);
|
||||
|
||||
const rightMenuColProps = tuneId ? {
|
||||
span: 8,
|
||||
|
@ -178,8 +180,13 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
|
|||
return (
|
||||
<Header className="app-top-bar">
|
||||
<Row>
|
||||
<Col span={0} md={4} sm={0} />
|
||||
{tuneId ? tabs : <Col span={10} md={10} sm={16} />}
|
||||
{tuneId ? tabs : (
|
||||
<Col span={10} md={14} sm={16}>
|
||||
<Link to={Routes.HUB}>
|
||||
<Button icon={<CarOutlined />} type={hubMatch ? 'primary' : 'default'}>Hub</Button>
|
||||
</Link>
|
||||
</Col>
|
||||
)}
|
||||
<Col {...rightMenuColProps} style={{ textAlign: 'right' }}>
|
||||
<Space>
|
||||
{sm && <Tooltip title={
|
||||
|
@ -239,9 +246,14 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
|
|||
overlay={
|
||||
<Menu>
|
||||
{currentUser ? (
|
||||
<Menu.Item key="logout" icon={<LogoutOutlined />} onClick={logoutClick}>
|
||||
Logout
|
||||
</Menu.Item>
|
||||
<>
|
||||
<Menu.Item key="profile" icon={<UserOutlined />}>
|
||||
<Link to={Routes.PROFILE}>Profile</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="logout" icon={<LogoutOutlined />} onClick={logoutClick}>
|
||||
Logout
|
||||
</Menu.Item>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Menu.Item key="login" icon={<LoginOutlined />}>
|
||||
|
@ -252,9 +264,6 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
|
|||
</Menu.Item>
|
||||
</>
|
||||
)}
|
||||
<Menu.Item key="preferences" disabled icon={<SettingOutlined />}>
|
||||
Preferences
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
placement="bottomCenter"
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
Form,
|
||||
Skeleton,
|
||||
Divider,
|
||||
Col,
|
||||
Row,
|
||||
|
@ -43,6 +42,7 @@ import Map from './Dialog/Map';
|
|||
import { evaluateExpression } from '../../utils/tune/expression';
|
||||
import useBrowserStorage from '../../hooks/useBrowserStorage';
|
||||
import useConfig from '../../hooks/useConfig';
|
||||
import Loader from '../Loader';
|
||||
|
||||
interface DialogsAndCurves {
|
||||
[name: string]: DialogType | CurveType | TableType,
|
||||
|
@ -82,18 +82,6 @@ const mapStateToProps = (state: AppState) => ({
|
|||
ui: state.ui,
|
||||
});
|
||||
|
||||
const containerStyle = {
|
||||
padding: 20,
|
||||
maxWidth: 1400,
|
||||
width: '100%',
|
||||
margin: '0 auto',
|
||||
};
|
||||
|
||||
const skeleton = (<div style={containerStyle}>
|
||||
<Skeleton active />
|
||||
<Skeleton active />
|
||||
</div>);
|
||||
|
||||
// TODO: refactor this
|
||||
const Dialog = ({
|
||||
ui,
|
||||
|
@ -364,7 +352,7 @@ const Dialog = ({
|
|||
}, [isDataReady, url, ui.sidebarCollapsed]);
|
||||
|
||||
if (!isDataReady) {
|
||||
return skeleton;
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
const dialogConfig = config.dialogs[name];
|
||||
|
@ -375,7 +363,7 @@ const Dialog = ({
|
|||
if (!dialogConfig) {
|
||||
if (curveConfig) {
|
||||
return (
|
||||
<div ref={containerRef} style={containerStyle}>
|
||||
<div ref={containerRef} className="large-container">
|
||||
<Divider>{curveConfig.title}</Divider>
|
||||
{renderCurve(curveConfig)}
|
||||
</div>
|
||||
|
@ -384,7 +372,7 @@ const Dialog = ({
|
|||
|
||||
if (tableConfig) {
|
||||
return (
|
||||
<div ref={containerRef} style={containerStyle}>
|
||||
<div ref={containerRef} className="large-container">
|
||||
{renderHelp(tableConfig.help)}
|
||||
<Divider>{tableConfig.title}</Divider>
|
||||
{renderTable(tableConfig)}
|
||||
|
@ -402,7 +390,7 @@ const Dialog = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<div ref={containerRef} style={containerStyle}>
|
||||
<div ref={containerRef} className="large-container">
|
||||
{renderHelp(dialogConfig?.help)}
|
||||
<Form
|
||||
labelCol={{ span: 10 }}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
Layout,
|
||||
Menu,
|
||||
Skeleton,
|
||||
} from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
|
@ -23,7 +22,6 @@ import {
|
|||
import store from '../../store';
|
||||
import Icon from '../SideBar/Icon';
|
||||
import { Routes } from '../../routes';
|
||||
import useConfig from '../../hooks/useConfig';
|
||||
import {
|
||||
AppState,
|
||||
NavigationState,
|
||||
|
@ -77,7 +75,6 @@ const SideBar = ({ config, tune, ui, navigation, matchedPath }: SideBarProps) =>
|
|||
collapsed: ui.sidebarCollapsed,
|
||||
onCollapse: (collapsed: boolean) => store.dispatch({ type: 'ui/sidebarCollapsed', payload: collapsed }),
|
||||
} as any;
|
||||
const { isConfigReady } = useConfig(config);
|
||||
const [menus, setMenus] = useState<any[]>([]);
|
||||
|
||||
const menusList = useCallback((types: MenusType) => (
|
||||
|
@ -123,20 +120,6 @@ const SideBar = ({ config, tune, ui, navigation, matchedPath }: SideBarProps) =>
|
|||
}
|
||||
}, [config.menus, menusList, tune.constants]);
|
||||
|
||||
if (!isConfigReady) {
|
||||
return (
|
||||
<Sider {...siderProps} className="app-sidebar" >
|
||||
<div style={{ paddingLeft: 10 }}>
|
||||
<Skeleton active />
|
||||
<Skeleton active />
|
||||
<Skeleton active />
|
||||
<Skeleton active />
|
||||
<Skeleton active />
|
||||
</div>
|
||||
</Sider>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Sider {...siderProps} className="app-sidebar">
|
||||
<PerfectScrollbar options={{ suppressScrollX: true }}>
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
import {
|
||||
Layout,
|
||||
Tabs,
|
||||
Skeleton,
|
||||
Progress,
|
||||
Steps,
|
||||
Space,
|
||||
|
@ -44,6 +43,7 @@ import TriggerLogsParser, {
|
|||
ToothLogEntry,
|
||||
} from '../utils/logs/TriggerLogsParser';
|
||||
import ToothCanvas from '../components/TriggerLogs/ToothCanvas';
|
||||
import Loader from '../components/Loader';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const { Content } = Layout;
|
||||
|
@ -144,7 +144,7 @@ const Diagnose = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loa
|
|||
<>
|
||||
<Sider {...(siderProps as any)} className="app-sidebar">
|
||||
{!logs && !loadedLogs.length ?
|
||||
<div style={{ padding: 20 }}><Skeleton active /></div>
|
||||
<Loader />
|
||||
:
|
||||
!ui.sidebarCollapsed &&
|
||||
<Tabs defaultActiveKey="files" style={{ marginLeft: 20 }}>
|
||||
|
|
|
@ -31,12 +31,6 @@ import { TuneDbData } from '../types/dbData';
|
|||
import { Routes } from '../routes';
|
||||
import { generateShareUrl } from '../utils/url';
|
||||
|
||||
const containerStyle = {
|
||||
padding: 20,
|
||||
maxWidth: 1200,
|
||||
margin: '0 auto',
|
||||
};
|
||||
|
||||
const { useBreakpoint } = Grid;
|
||||
|
||||
const loadingCards = (
|
||||
|
@ -60,6 +54,7 @@ const Hub = () => {
|
|||
const [tunes, setTunes] = useState<TuneDbData[]>([]);
|
||||
const [dataSource, setDataSource] = useState<any[]>([]);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const goToTune = (tuneId: string) => navigate(generatePath(Routes.TUNE_ROOT, { tuneId }));
|
||||
|
||||
|
@ -90,6 +85,7 @@ const Hub = () => {
|
|||
publishedAt: new Date((tune.createdAt as Timestamp).seconds * 1000).toLocaleString(),
|
||||
stars: 0,
|
||||
})));
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, [listTunes]);
|
||||
|
||||
|
@ -144,14 +140,14 @@ const Hub = () => {
|
|||
}, []); // TODO: fix this
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className="large-container">
|
||||
<Typography.Title>Hub</Typography.Title>
|
||||
<Input style={{ marginBottom: 10, height: 50 }} placeholder="Search..." />
|
||||
<Input style={{ marginBottom: 10, height: 40 }} placeholder="Search..." />
|
||||
{md ?
|
||||
<Table dataSource={dataSource} columns={columns} />
|
||||
<Table dataSource={dataSource} columns={columns} loading={isLoading} />
|
||||
:
|
||||
<Row gutter={[16, 16]}>
|
||||
{tunes.length === 0 ? loadingCards : (
|
||||
{isLoading ? loadingCards : (
|
||||
tunes.map((tune) => (
|
||||
<Col span={16} sm={8} key={tune.tuneFile}>
|
||||
<Card
|
||||
|
|
|
@ -7,19 +7,14 @@ import {
|
|||
Input,
|
||||
Row,
|
||||
Select,
|
||||
Skeleton,
|
||||
} from 'antd';
|
||||
import {
|
||||
AppState,
|
||||
TuneDataState,
|
||||
} from '../types/state';
|
||||
import Loader from '../components/Loader';
|
||||
|
||||
const { Item } = Form;
|
||||
const containerStyle = {
|
||||
padding: 20,
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
};
|
||||
const rowProps = { gutter: 10 };
|
||||
const colProps = { span: 24, sm: 12 };
|
||||
|
||||
|
@ -29,15 +24,11 @@ const mapStateToProps = (state: AppState) => ({
|
|||
|
||||
const Info = ({ tuneData }: { tuneData: TuneDataState }) => {
|
||||
if (!tuneData.details) {
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<Skeleton active />
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className="small-container">
|
||||
<Divider>Details</Divider>
|
||||
<Form>
|
||||
<Row {...rowProps}>
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
Tabs,
|
||||
Checkbox,
|
||||
Row,
|
||||
Skeleton,
|
||||
Progress,
|
||||
Steps,
|
||||
Space,
|
||||
|
@ -52,6 +51,7 @@ import {
|
|||
AppState,
|
||||
UIState,
|
||||
} from '../types/state';
|
||||
import Loader from '../components/Loader';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const { Content } = Layout;
|
||||
|
@ -220,7 +220,7 @@ const Logs = ({
|
|||
<>
|
||||
<Sider {...(siderProps as any)} className="app-sidebar">
|
||||
{!logs && !loadedLogs.length ?
|
||||
<div style={{ padding: 20 }}><Skeleton active /></div>
|
||||
<Loader />
|
||||
:
|
||||
!ui.sidebarCollapsed &&
|
||||
<Tabs defaultActiveKey="fields" style={{ marginLeft: 20 }}>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Skeleton } from 'antd';
|
||||
import {
|
||||
generatePath,
|
||||
useMatch,
|
||||
|
@ -18,6 +17,7 @@ import {
|
|||
AppState,
|
||||
NavigationState,
|
||||
} from '../types/state';
|
||||
import Loader from '../components/Loader';
|
||||
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
navigation: state.navigation,
|
||||
|
@ -54,13 +54,8 @@ const Tune = ({ navigation, config }: { navigation: NavigationState, config: Con
|
|||
}
|
||||
}, [firstDialogPath, navigate, tuneRootMatch, isConfigReady]);
|
||||
|
||||
// TODO: unify loading indicators across the app
|
||||
if (!isConfigReady || !dialogMatch) {
|
||||
return (
|
||||
<div>
|
||||
<Skeleton active />
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
notification,
|
||||
Row,
|
||||
Select,
|
||||
Skeleton,
|
||||
Space,
|
||||
Switch,
|
||||
Tabs,
|
||||
|
@ -56,6 +55,7 @@ import LogParser from '../utils/logs/LogParser';
|
|||
import useDb from '../hooks/useDb';
|
||||
import useServerStorage from '../hooks/useServerStorage';
|
||||
import { generateShareUrl } from '../utils/url';
|
||||
import Loader from '../components/Loader';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
|
@ -83,11 +83,6 @@ interface ValidationResult {
|
|||
|
||||
type ValidateFile = (file: File) => Promise<ValidationResult>;
|
||||
|
||||
const containerStyle = {
|
||||
padding: 20,
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
};
|
||||
const rowProps = { gutter: 10 };
|
||||
const colProps = { span: 24, sm: 12 };
|
||||
|
||||
|
@ -666,23 +661,19 @@ const UploadPage = () => {
|
|||
);
|
||||
|
||||
if (!isUserAuthorized) {
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<Skeleton active />
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
if (isPublished) {
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className="small-container">
|
||||
{shareSection}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className="small-container">
|
||||
<Form
|
||||
onFinish={publish}
|
||||
initialValues={{
|
||||
|
|
|
@ -27,7 +27,6 @@ import {
|
|||
logInFailed,
|
||||
logInSuccessful,
|
||||
} from './notifications';
|
||||
import { containerStyle } from './common';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
|
@ -39,7 +38,7 @@ const Login = () => {
|
|||
const { login, googleAuth, githubAuth } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const isAnythingLoading = isEmailLoading || isGoogleLoading || isGithubLoading;
|
||||
const redirectAfterLogin = useCallback(() => navigate(Routes.UPLOAD), [navigate]);
|
||||
const redirectAfterLogin = useCallback(() => navigate(Routes.HUB), [navigate]);
|
||||
|
||||
const googleLogin = useCallback(async () => {
|
||||
setIsGoogleLoading(true);
|
||||
|
@ -85,10 +84,9 @@ const Login = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className="small-container">
|
||||
<Divider>Log In using email</Divider>
|
||||
<Form
|
||||
initialValues={{ remember: true }}
|
||||
onFinish={emailLogin}
|
||||
validateMessages={validateMessages}
|
||||
autoComplete="off"
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
Button,
|
||||
Divider,
|
||||
} from 'antd';
|
||||
import { UserOutlined } from '@ant-design/icons';
|
||||
import validateMessages from './validateMessages';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { restrictedPage } from './notifications';
|
||||
import { Routes } from '../../routes';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
const Profile = () => {
|
||||
const { currentUser } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentUser) {
|
||||
restrictedPage();
|
||||
navigate(Routes.LOGIN);
|
||||
}
|
||||
}, [currentUser, navigate]);
|
||||
|
||||
return (
|
||||
<div className="small-container">
|
||||
<Divider>Your Profile</Divider>
|
||||
<Form
|
||||
validateMessages={validateMessages}
|
||||
form={form}
|
||||
autoComplete="off"
|
||||
>
|
||||
<Item
|
||||
name="username"
|
||||
rules={[{ required: true }]}
|
||||
hasFeedback
|
||||
>
|
||||
<Input prefix={<UserOutlined />} placeholder="Username" />
|
||||
</Item>
|
||||
<Item>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Profile;
|
|
@ -17,7 +17,6 @@ import {
|
|||
resetFailed,
|
||||
resetSuccessful,
|
||||
} from './notifications';
|
||||
import { containerStyle } from './common';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
|
@ -42,7 +41,7 @@ const ResetPassword = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className="small-container">
|
||||
<Divider>Reset password</Divider>
|
||||
<Form
|
||||
initialValues={{ remember: true }}
|
||||
|
|
|
@ -21,7 +21,6 @@ import {
|
|||
signUpFailed,
|
||||
signUpSuccessful,
|
||||
} from './notifications';
|
||||
import { containerStyle } from './common';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
|
@ -39,7 +38,7 @@ const SignUp = () => {
|
|||
await signUp(email, password);
|
||||
signUpSuccessful();
|
||||
emailNotVerified();
|
||||
navigate(Routes.ROOT);
|
||||
navigate(Routes.HUB);
|
||||
} catch (error) {
|
||||
form.resetFields();
|
||||
console.warn(error);
|
||||
|
@ -49,10 +48,9 @@ const SignUp = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className="small-container">
|
||||
<Divider>Sign Up</Divider>
|
||||
<Form
|
||||
initialValues={{ remember: true }}
|
||||
onFinish={onFinish}
|
||||
validateMessages={validateMessages}
|
||||
autoComplete="off"
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
const containerStyle = {
|
||||
padding: 20,
|
||||
maxWidth: 370,
|
||||
margin: '0 auto',
|
||||
};
|
||||
|
||||
export {
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
containerStyle,
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export enum Routes {
|
||||
ROOT = '/',
|
||||
HUB = '/',
|
||||
|
||||
TUNE_ROOT = '/t/:tuneId',
|
||||
TUNE_TAB = '/t/:tuneId/:tab',
|
||||
|
@ -11,6 +12,7 @@ export enum Routes {
|
|||
|
||||
LOGIN = '/auth/login',
|
||||
LOGOUT = '/auth/logout',
|
||||
PROFILE = '/auth/profile',
|
||||
SIGN_UP = '/auth/sign-up',
|
||||
FORGOT_PASSWORD = '/auth/forgot-password',
|
||||
RESET_PASSWORD = '/auth/reset-password',
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
@border-radius-base: 6px;
|
||||
|
||||
@layout-header-padding: 0 15px;
|
||||
@layout-header-height: 45px;
|
||||
@layout-header-height: 50px;
|
||||
|
||||
@layout-footer-padding: 2px 10px;
|
||||
@layout-footer-height: 28px;
|
||||
|
|
Loading…
Reference in New Issue