mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
update navigation
This commit is contained in:
@@ -52,7 +52,7 @@ const UserSettingsPage = lazy(() => import("@src/pages/Settings/UserSettingsPage
|
||||
// prettier-ignore
|
||||
const ProjectSettingsPage = lazy(() => import("@src/pages/Settings/ProjectSettingsPage" /* webpackChunkName: "ProjectSettingsPage" */));
|
||||
// prettier-ignore
|
||||
const UserManagementPage = lazy(() => import("@src/pages/Settings/UserManagementPage" /* webpackChunkName: "UserManagementPage" */));
|
||||
const MembersPage = lazy(() => import("@src/pages/Settings/MembersPage" /* webpackChunkName: "MembersPage" */));
|
||||
// prettier-ignore
|
||||
const DebugSettingsPage = lazy(() => import("@src/pages/Settings/DebugSettingsPage" /* webpackChunkName: "DebugSettingsPage" */));
|
||||
|
||||
@@ -141,10 +141,7 @@ export default function App() {
|
||||
path={ROUTES.projectSettings}
|
||||
element={<ProjectSettingsPage />}
|
||||
/>
|
||||
<Route
|
||||
path={ROUTES.userManagement}
|
||||
element={<UserManagementPage />}
|
||||
/>
|
||||
<Route path={ROUTES.members} element={<MembersPage />} />
|
||||
<Route
|
||||
path={ROUTES.debugSettings}
|
||||
element={<DebugSettingsPage />}
|
||||
|
||||
@@ -22,20 +22,20 @@ export default function Logo({ size = 1.5, ...props }: ILogoProps) {
|
||||
|
||||
<defs>
|
||||
<linearGradient x1="83.349%" y1="50%" x2="0%" y2="50%" id="b">
|
||||
<stop stop-color="#F0A" offset="0%" />
|
||||
<stop stop-color="#FA0" offset="100%" />
|
||||
<stop stopColor="#F0A" offset="0%" />
|
||||
<stop stopColor="#FA0" offset="100%" />
|
||||
</linearGradient>
|
||||
<linearGradient x1="50%" y1="16.276%" x2="50%" y2="100%" id="c">
|
||||
<stop stop-color="#4200FF" offset="0%" />
|
||||
<stop stop-color="#F0A" offset="100%" />
|
||||
<stop stopColor="#4200FF" offset="0%" />
|
||||
<stop stopColor="#F0A" offset="100%" />
|
||||
</linearGradient>
|
||||
<linearGradient x1="83.052%" y1="50%" x2="0%" y2="50%" id="d">
|
||||
<stop stop-color="#0AF" offset="0%" />
|
||||
<stop stop-color="#0FA" offset="100%" />
|
||||
<stop stopColor="#0AF" offset="0%" />
|
||||
<stop stopColor="#0FA" offset="100%" />
|
||||
</linearGradient>
|
||||
<linearGradient x1="100%" y1="50%" x2="16.614%" y2="50%" id="e">
|
||||
<stop stop-color="#0AF" offset="0%" />
|
||||
<stop stop-color="#4200FF" offset="100%" />
|
||||
<stop stopColor="#0AF" offset="0%" />
|
||||
<stop stopColor="#4200FF" offset="100%" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
|
||||
@@ -14,9 +14,15 @@ import githubLogo from "@src/assets/logos/github.svg";
|
||||
import appleLogo from "@src/assets/logos/apple.svg";
|
||||
import yahooLogo from "@src/assets/logos/yahoo.svg";
|
||||
|
||||
import { EXTERNAL_LINKS } from "@src/constants/externalLinks";
|
||||
|
||||
export const authOptions = {
|
||||
google: {
|
||||
provider: GoogleAuthProvider.PROVIDER_ID,
|
||||
customParameters: {
|
||||
// Forces account selection even when one account is available
|
||||
prompt: "select_account",
|
||||
},
|
||||
},
|
||||
twitter: {
|
||||
provider: TwitterAuthProvider.PROVIDER_ID,
|
||||
@@ -59,6 +65,8 @@ export const defaultUiConfig: firebaseui.auth.Config = {
|
||||
signInFlow: "popup",
|
||||
signInSuccessUrl: "/",
|
||||
signInOptions: [authOptions.google],
|
||||
// tosUrl: EXTERNAL_LINKS.terms,
|
||||
// privacyPolicyUrl: EXTERNAL_LINKS.privacy,
|
||||
};
|
||||
|
||||
export const getSignInOptions = (
|
||||
|
||||
@@ -35,7 +35,7 @@ export enum ROUTES {
|
||||
settings = "/settings",
|
||||
userSettings = "/settings/user",
|
||||
projectSettings = "/settings/project",
|
||||
userManagement = "/settings/userManagement",
|
||||
members = "/members",
|
||||
debugSettings = "/settings/debug",
|
||||
|
||||
test = "/test",
|
||||
@@ -62,7 +62,7 @@ export const ROUTE_TITLES = {
|
||||
[ROUTES.settings]: "Settings",
|
||||
[ROUTES.userSettings]: "Settings",
|
||||
[ROUTES.projectSettings]: "Project Settings",
|
||||
[ROUTES.userManagement]: "User Management",
|
||||
[ROUTES.members]: "Members",
|
||||
[ROUTES.debugSettings]: "Debug",
|
||||
|
||||
[ROUTES.test]: "Test",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useAtom } from "jotai";
|
||||
import { use100vh } from "react-div-100vh";
|
||||
|
||||
import {
|
||||
@@ -14,6 +15,7 @@ import Logo from "@src/assets/Logo";
|
||||
import bgTableLight from "@src/assets/bg-table-light.webp";
|
||||
import bgTableDark from "@src/assets/bg-table-dark.webp";
|
||||
|
||||
import { globalScope, projectIdAtom } from "@src/atoms/globalScope";
|
||||
import { EXTERNAL_LINKS } from "@src/constants/externalLinks";
|
||||
|
||||
export interface IAuthLayoutProps {
|
||||
@@ -35,6 +37,7 @@ export default function AuthLayout({
|
||||
children,
|
||||
loading,
|
||||
}: IAuthLayoutProps) {
|
||||
const [projectId] = useAtom(projectIdAtom, globalScope);
|
||||
const fullScreenHeight = use100vh() ?? 0;
|
||||
|
||||
const linkProps: LinkProps = {
|
||||
@@ -50,8 +53,8 @@ export default function AuthLayout({
|
||||
sx={{
|
||||
backgroundImage: (theme) =>
|
||||
`linear-gradient(to bottom,
|
||||
${alpha(theme.palette.background.default, 0.6)},
|
||||
${alpha(theme.palette.background.default, 0.6)}),
|
||||
${alpha(theme.palette.background.default, 0.75)},
|
||||
${alpha(theme.palette.background.default, 0.75)}),
|
||||
url(${theme.palette.mode === "dark" ? bgTableDark : bgTableLight})`,
|
||||
backgroundSize: { xs: "1920px 1080px", md: "cover" },
|
||||
backgroundRepeat: "no-repeat",
|
||||
@@ -59,20 +62,19 @@ export default function AuthLayout({
|
||||
|
||||
display: "grid",
|
||||
placeItems: "center",
|
||||
alignContent: "center",
|
||||
alignContent: "space-between",
|
||||
gap: 2,
|
||||
gridAutoRows: "max-content",
|
||||
minHeight: fullScreenHeight > 0 ? `${fullScreenHeight}px` : "100vh",
|
||||
|
||||
pt: (theme) => `max(env(safe-area-inset-top), ${theme.spacing(1)})`,
|
||||
pb: (theme) => `max(env(safe-area-inset-bottom), ${theme.spacing(1)})`,
|
||||
pl: (theme) => `max(env(safe-area-inset-left), ${theme.spacing(1)})`,
|
||||
pt: (theme) => `max(env(safe-area-inset-top), ${theme.spacing(2)})`,
|
||||
pb: (theme) => `max(env(safe-area-inset-bottom), ${theme.spacing(2)})`,
|
||||
pr: (theme) => `max(env(safe-area-inset-right), ${theme.spacing(1)})`,
|
||||
pl: (theme) => `max(env(safe-area-inset-left), ${theme.spacing(1)})`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
marginBottom: -8,
|
||||
display: hideLogo && hideLinks ? "none" : "block",
|
||||
visibility: hideLogo ? "hidden" : "visible",
|
||||
@@ -83,13 +85,13 @@ export default function AuthLayout({
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Logo />
|
||||
<Logo size={2} />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<Paper
|
||||
component="main"
|
||||
elevation={4}
|
||||
elevation={8}
|
||||
sx={
|
||||
{
|
||||
position: "relative",
|
||||
@@ -99,10 +101,11 @@ export default function AuthLayout({
|
||||
width: "100%",
|
||||
p: 4,
|
||||
minHeight: 300,
|
||||
borderRadius: 3,
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
textAlign: "center",
|
||||
// textAlign: "center",
|
||||
|
||||
"& > :not(style) + :not(style)": { mt: 4 },
|
||||
} as any
|
||||
@@ -140,13 +143,13 @@ export default function AuthLayout({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* <Typography
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
sx={{ pt: 1, display: hideProject ? "none" : "block" }}
|
||||
sx={{ display: hideProject ? "none" : "block", mb: -0.5 }}
|
||||
>
|
||||
Project: <span style={{ userSelect: "all" }}>{projectId}</span>
|
||||
</Typography> */}
|
||||
</Typography>
|
||||
</Paper>
|
||||
|
||||
<Stack
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import { useState, useRef } from "react";
|
||||
|
||||
import {
|
||||
IconButton,
|
||||
IconButtonProps,
|
||||
Menu,
|
||||
MenuProps,
|
||||
MenuItem,
|
||||
ListItemIcon,
|
||||
ListItemSecondaryAction,
|
||||
Divider,
|
||||
Grow,
|
||||
} from "@mui/material";
|
||||
import HelpIcon from "@mui/icons-material/HelpOutline";
|
||||
import DocsIcon from "@mui/icons-material/LibraryBooksOutlined";
|
||||
import { Discord as DiscordIcon } from "@src/assets/icons";
|
||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||
@@ -20,9 +15,13 @@ import { EXTERNAL_LINKS } from "@src/constants/externalLinks";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import { analytics } from "analytics";
|
||||
|
||||
export default function UserMenu(props: IconButtonProps) {
|
||||
const anchorEl = useRef<HTMLButtonElement>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
export default function HelpMenu({
|
||||
anchorEl,
|
||||
onClose,
|
||||
}: Pick<MenuProps, "anchorEl" | "onClose">) {
|
||||
// useEffect(() => {
|
||||
// analytics.logEvent("open_help_menu");
|
||||
// }, []);
|
||||
|
||||
const externalLinkIcon = (
|
||||
<ListItemSecondaryAction sx={{ right: 10, color: "text.disabled" }}>
|
||||
@@ -31,89 +30,54 @@ export default function UserMenu(props: IconButtonProps) {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grow in>
|
||||
<IconButton
|
||||
aria-label="Open help menu"
|
||||
aria-controls="help-menu"
|
||||
aria-haspopup="true"
|
||||
size="large"
|
||||
{...props}
|
||||
ref={anchorEl}
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
analytics.logEvent("open_help_menu");
|
||||
}}
|
||||
>
|
||||
<HelpIcon />
|
||||
</IconButton>
|
||||
</Grow>
|
||||
|
||||
<Menu
|
||||
anchorEl={anchorEl.current}
|
||||
id="help-menu"
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
||||
transformOrigin={{ vertical: "top", horizontal: "right" }}
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
sx={{ "& .MuiPaper-root": { minWidth: 160 } }}
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={onClose}
|
||||
id="help-menu"
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
||||
transformOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||
sx={{ "& .MuiPaper-root": { mt: 0.5, minWidth: 160 } }}
|
||||
>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.discord}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.docs}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<DocsIcon />
|
||||
</ListItemIcon>
|
||||
Docs
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
|
||||
<Divider variant="middle" sx={{ mt: 0.5, mb: 0.5 }} />
|
||||
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.discord}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<DiscordIcon />
|
||||
</ListItemIcon>
|
||||
Discord
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.gitHub}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<GitHubIcon />
|
||||
</ListItemIcon>
|
||||
GitHub
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.twitter}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<TwitterIcon />
|
||||
</ListItemIcon>
|
||||
Twitter
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
<ListItemIcon>
|
||||
<DiscordIcon />
|
||||
</ListItemIcon>
|
||||
Discord
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.gitHub}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<GitHubIcon />
|
||||
</ListItemIcon>
|
||||
GitHub
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.twitter}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<TwitterIcon />
|
||||
</ListItemIcon>
|
||||
Twitter
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,29 +11,27 @@ import {
|
||||
List,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Avatar,
|
||||
Divider,
|
||||
ListItemSecondaryAction,
|
||||
Box,
|
||||
Fade,
|
||||
} from "@mui/material";
|
||||
import CloseIcon from "@mui/icons-material/MenuOpen";
|
||||
import ArrowBackIcon from "@mui/icons-material/WorkspacesOutlined";
|
||||
import HomeIcon from "@mui/icons-material/HomeOutlined";
|
||||
import SettingsIcon from "@mui/icons-material/SettingsOutlined";
|
||||
import ProjectSettingsIcon from "@mui/icons-material/BuildCircleOutlined";
|
||||
import UserManagementIcon from "@mui/icons-material/AccountCircleOutlined";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import { Table as TableIcon } from "@src/assets/icons";
|
||||
import DocsIcon from "@mui/icons-material/LibraryBooksOutlined";
|
||||
import LearningIcon from "@mui/icons-material/LocalLibraryOutlined";
|
||||
import HelpIcon from "@mui/icons-material/HelpOutline";
|
||||
import CommunityIcon from "@mui/icons-material/SentimentSatisfiedAltOutlined";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import { ChevronRight as ChevronRightIcon } from "@src/assets/icons";
|
||||
|
||||
import Logo from "@src/assets/Logo";
|
||||
import NavItem from "./NavItem";
|
||||
import NavTableSection from "./NavTableSection";
|
||||
import UpdateCheckBadge from "./UpdateCheckBadge";
|
||||
import HelpMenu from "./HelpMenu";
|
||||
|
||||
import {
|
||||
globalScope,
|
||||
@@ -70,6 +68,8 @@ export default function NavDrawer({
|
||||
);
|
||||
|
||||
const [hover, setHover] = useState(false);
|
||||
const [helpMenuAnchorEl, setHelpMenuAnchorEl] =
|
||||
useState<HTMLButtonElement | null>(null);
|
||||
|
||||
const favorites = Array.isArray(userSettings.favoriteTables)
|
||||
? userSettings.favoriteTables
|
||||
@@ -89,8 +89,8 @@ export default function NavDrawer({
|
||||
? undefined
|
||||
: (e: {}) => onClose(e, "escapeKeyDown");
|
||||
|
||||
const externalLinkIcon = !collapsed && (
|
||||
<ListItemSecondaryAction sx={{ right: 10, color: "text.disabled" }}>
|
||||
const externalLinkIcon = (
|
||||
<ListItemSecondaryAction sx={{ right: 10 }}>
|
||||
<InlineOpenInNewIcon />
|
||||
</ListItemSecondaryAction>
|
||||
);
|
||||
@@ -194,48 +194,6 @@ export default function NavDrawer({
|
||||
disablePadding
|
||||
style={{ height: "100%", display: "flex", flexDirection: "column" }}
|
||||
>
|
||||
<li>
|
||||
<NavItem onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<ArrowBackIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Workspace" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavItem onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<Avatar
|
||||
sx={{
|
||||
borderRadius: 1,
|
||||
width: 24,
|
||||
height: 24,
|
||||
fontSize: "inherit",
|
||||
bgcolor: "primary.main",
|
||||
color: "primary.contrastText",
|
||||
}}
|
||||
>
|
||||
P
|
||||
</Avatar>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Project" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
{/* {userRoles.includes("ADMIN") && (
|
||||
<li>
|
||||
<NavItem to={ROUTES.userManagement} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<UserManagementIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="User Management" />
|
||||
</NavItem>
|
||||
</li>
|
||||
)} */}
|
||||
|
||||
<Divider variant="middle" sx={{ my: 1 }} />
|
||||
|
||||
<li>
|
||||
<NavItem to={ROUTES.tables} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
@@ -251,30 +209,30 @@ export default function NavDrawer({
|
||||
<SettingsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Settings" />
|
||||
{userRoles.includes("ADMIN") && (
|
||||
<UpdateCheckBadge sx={{ mr: 1.5 }} />
|
||||
)}
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
{userRoles.includes("ADMIN") && (
|
||||
<li>
|
||||
<NavItem to={ROUTES.projectSettings} onClick={closeDrawer}>
|
||||
<NavItem to={ROUTES.members} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<ProjectSettingsIcon />
|
||||
<UserManagementIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Project Settings" />
|
||||
<UpdateCheckBadge sx={{ mr: 1.5 }} />
|
||||
<ListItemText primary="Members" />
|
||||
</NavItem>
|
||||
</li>
|
||||
)}
|
||||
|
||||
<Divider variant="middle" sx={{ my: 1 }} />
|
||||
|
||||
{/* <ListSubheader>Your tables</ListSubheader> */}
|
||||
|
||||
<li>
|
||||
<NavItem
|
||||
{...({ component: "button" } as any)}
|
||||
style={{ textAlign: "left" }}
|
||||
onClick={(e) => {
|
||||
onClick={(e: any) => {
|
||||
if (closeDrawer) closeDrawer(e);
|
||||
openTableSettingsDialog({});
|
||||
}}
|
||||
@@ -286,15 +244,6 @@ export default function NavDrawer({
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
{/* <li>
|
||||
<NavItem to={ROUTES.tables} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<TableIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Table" />
|
||||
</NavItem>
|
||||
</li> */}
|
||||
|
||||
{sections &&
|
||||
Object.entries(sections)
|
||||
.filter(([, tables]) => tables.length > 0)
|
||||
@@ -329,13 +278,36 @@ export default function NavDrawer({
|
||||
</NavItem>
|
||||
</li>
|
||||
<li>
|
||||
<NavItem href={EXTERNAL_LINKS.docs}>
|
||||
<NavItem
|
||||
onClick={(e: any) => setHelpMenuAnchorEl(e.currentTarget)}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<CommunityIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Join our community" />
|
||||
<ListItemSecondaryAction>
|
||||
<ChevronRightIcon />
|
||||
</ListItemSecondaryAction>
|
||||
</NavItem>
|
||||
</li>
|
||||
<li>
|
||||
<NavItem
|
||||
onClick={(e: any) => setHelpMenuAnchorEl(e.currentTarget)}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<HelpIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Help" />
|
||||
<ListItemSecondaryAction>
|
||||
<ChevronRightIcon />
|
||||
</ListItemSecondaryAction>
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<HelpMenu
|
||||
anchorEl={helpMenuAnchorEl}
|
||||
onClose={() => setHelpMenuAnchorEl(null)}
|
||||
/>
|
||||
</List>
|
||||
</nav>
|
||||
</Drawer>
|
||||
|
||||
@@ -4,24 +4,29 @@ import { spreadSx } from "@src/utils/ui";
|
||||
|
||||
const linkProps = { target: "_blank", rel: "noopener noreferrer" };
|
||||
|
||||
export default function NavItem(props: MenuItemProps<typeof Link | "a">) {
|
||||
export default function NavItem(
|
||||
props: MenuItemProps<typeof Link | "a" | "button">
|
||||
) {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
component={"to" in props ? Link : "a"}
|
||||
component={"to" in props ? Link : "href" in props ? "a" : "button"}
|
||||
selected={"to" in props ? pathname === props.to : false}
|
||||
{...props}
|
||||
{...("href" in props ? linkProps : {})}
|
||||
sx={[
|
||||
{
|
||||
overflow: "hidden",
|
||||
textAlign: "left",
|
||||
color: "text.secondary",
|
||||
|
||||
"& .MuiListItemText-primary": {
|
||||
typography: "button",
|
||||
color: "text.secondary",
|
||||
"& .MuiListItemText-primary": { typography: "button" },
|
||||
"& .MuiSvgIcon-root": {
|
||||
color: "inherit",
|
||||
opacity: 0.87,
|
||||
display: "block",
|
||||
},
|
||||
"& .MuiListItemIcon-root": { opacity: 0.87 },
|
||||
|
||||
"&:hover, &.Mui-selected": {
|
||||
"& .MuiListItemText-primary": { color: "text.primary" },
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { useAtom } from "jotai";
|
||||
import { colord } from "colord";
|
||||
import { NavLink } from "react-router-dom";
|
||||
|
||||
import {
|
||||
useScrollTrigger,
|
||||
AppBar,
|
||||
Toolbar,
|
||||
IconButton,
|
||||
Button,
|
||||
Box,
|
||||
Typography,
|
||||
Grow,
|
||||
@@ -12,15 +15,15 @@ import {
|
||||
} from "@mui/material";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import MenuCloseIcon from "@mui/icons-material/MenuOpen";
|
||||
import PersonAddIcon from "@mui/icons-material/PersonAddOutlined";
|
||||
|
||||
import Logo from "@src/assets/Logo";
|
||||
import { NAV_DRAWER_WIDTH, NAV_DRAWER_COLLAPSED_WIDTH } from "./NavDrawer";
|
||||
import HelpMenu from "./HelpMenu";
|
||||
import UserMenu from "./UserMenu";
|
||||
import UpdateCheckBadge from "./UpdateCheckBadge";
|
||||
|
||||
import { globalScope, userRolesAtom } from "@src/atoms/globalScope";
|
||||
import { ROUTE_TITLES } from "@src/constants/routes";
|
||||
import { ROUTES, ROUTE_TITLES } from "@src/constants/routes";
|
||||
|
||||
export const TOP_BAR_HEIGHT = 56;
|
||||
|
||||
@@ -51,25 +54,14 @@ export default function TopBar({
|
||||
elevation={trigger ? 1 : 0}
|
||||
sx={{
|
||||
height: TOP_BAR_HEIGHT,
|
||||
backgroundImage:
|
||||
"linear-gradient(rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.09))", // Elevation 8
|
||||
|
||||
"&::before": {
|
||||
content: "''",
|
||||
display: "block",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
|
||||
bgcolor: "background.default",
|
||||
opacity: trigger ? 0 : 1,
|
||||
|
||||
transitionProperty: "opacity",
|
||||
transitionTimingFunction: "var(--nav-transition-timing-function)",
|
||||
transitionDuration: "var(--nav-transition-duration)",
|
||||
},
|
||||
backgroundColor: trigger
|
||||
? (theme) =>
|
||||
colord(theme.palette.background.paper)
|
||||
.mix("#fff", 0.09) // Elevation 8
|
||||
.alpha(1)
|
||||
.toHslString()
|
||||
: "transparent",
|
||||
transitionProperty: "box-shadow, background-color",
|
||||
}}
|
||||
>
|
||||
<Toolbar
|
||||
@@ -137,11 +129,12 @@ export default function TopBar({
|
||||
{
|
||||
flex: 1,
|
||||
overflowX: "auto",
|
||||
scrollbarWidth: "thin",
|
||||
userSelect: "none",
|
||||
ml: 1,
|
||||
},
|
||||
!(routeTitle as any)?.leftAligned && {
|
||||
ml: { xs: 1, sm: 6 },
|
||||
ml: { xs: 1, sm: 208 / 8 + 1 },
|
||||
},
|
||||
isPermanent &&
|
||||
(routeTitle as any)?.leftAligned && {
|
||||
@@ -169,7 +162,18 @@ export default function TopBar({
|
||||
</Box>
|
||||
</Fade>
|
||||
|
||||
<HelpMenu />
|
||||
<Button
|
||||
component={NavLink}
|
||||
to={ROUTES.members}
|
||||
startIcon={<PersonAddIcon />}
|
||||
sx={{
|
||||
mr: 1,
|
||||
"&.active": { visibility: "hidden" },
|
||||
}}
|
||||
>
|
||||
Invite team members
|
||||
</Button>
|
||||
|
||||
<UserMenu />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
import { Badge, BadgeProps } from "@mui/material";
|
||||
|
||||
import { globalScope, userRolesAtom } from "@src/atoms/globalScope";
|
||||
import useUpdateCheck from "@src/hooks/useUpdateCheck";
|
||||
|
||||
export default function UpdateCheckBadge(props: Partial<BadgeProps>) {
|
||||
const [userRoles] = useAtom(userRolesAtom, globalScope);
|
||||
const [latestUpdate] = useUpdateCheck();
|
||||
|
||||
if (!latestUpdate.rowy && !latestUpdate.rowyRun) return <>{props.children}</>;
|
||||
if (
|
||||
!userRoles.includes("ADMIN") ||
|
||||
(!latestUpdate.rowy && !latestUpdate.rowyRun)
|
||||
)
|
||||
return <>{props.children}</>;
|
||||
|
||||
return (
|
||||
<Badge
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
import { useMediaQuery, Stack, Typography, Link } from "@mui/material";
|
||||
import { Typography, Link } from "@mui/material";
|
||||
|
||||
import MarketingPanel from "@src/layouts/MarketingPanel";
|
||||
import AuthLayout from "@src/layouts/AuthLayout";
|
||||
import FirebaseUi from "@src/components/FirebaseUi";
|
||||
import { EXTERNAL_LINKS } from "@src/constants/externalLinks";
|
||||
@@ -16,61 +15,48 @@ export default function SignUpPage() {
|
||||
uiConfig.signInSuccessUrl = redirect;
|
||||
}
|
||||
|
||||
const isMobile = useMediaQuery((theme: any) => theme.breakpoints.down("md"));
|
||||
|
||||
return (
|
||||
<Stack direction="row">
|
||||
<MarketingPanel />
|
||||
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<AuthLayout
|
||||
hideLogo={!isMobile}
|
||||
hideLinks={!isMobile}
|
||||
title="Sign up"
|
||||
description={
|
||||
<AuthLayout
|
||||
title="Sign up"
|
||||
description={
|
||||
<>
|
||||
Welcome! To join this project, sign up with the email address
|
||||
{searchParams.get("email") ? (
|
||||
<>
|
||||
Welcome! To join this project, sign in with the email address
|
||||
{searchParams.get("email") ? (
|
||||
<>
|
||||
:{" "}
|
||||
<b style={{ userSelect: "all" }}>
|
||||
{searchParams.get("email")}
|
||||
</b>
|
||||
</>
|
||||
) : (
|
||||
" used to invite you."
|
||||
)}
|
||||
: <b style={{ userSelect: "all" }}>{searchParams.get("email")}</b>
|
||||
</>
|
||||
}
|
||||
) : (
|
||||
" used to invite you."
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<FirebaseUi uiConfig={uiConfig} />
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
style={{ marginTop: 16 }}
|
||||
>
|
||||
By signing up, you agree to our{" "}
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.terms}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="text.secondary"
|
||||
>
|
||||
<FirebaseUi uiConfig={uiConfig} />
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
style={{ marginTop: 16 }}
|
||||
>
|
||||
By signing up, you agree to our{" "}
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.terms}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="text.secondary"
|
||||
>
|
||||
Terms and Conditions
|
||||
</Link>{" "}
|
||||
and{" "}
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.privacy}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="text.secondary"
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
.
|
||||
</Typography>
|
||||
</AuthLayout>
|
||||
</div>
|
||||
</Stack>
|
||||
Terms and Conditions
|
||||
</Link>{" "}
|
||||
and{" "}
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.privacy}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="text.secondary"
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
.
|
||||
</Typography>
|
||||
</AuthLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
allUsersAtom,
|
||||
updateUserAtom,
|
||||
} from "@src/atoms/globalScope";
|
||||
import UserManagementSourceFirebase from "@src/sources/UserManagementSourceFirebase";
|
||||
import UserManagementSourceFirebase from "@src/sources/MembersSourceFirebase";
|
||||
import { firebaseDbAtom } from "@src/sources/ProjectSourceFirebase";
|
||||
import { USERS } from "@src/config/dbPaths";
|
||||
import { getTableSchemaPath } from "@src/utils/table";
|
||||
|
||||
@@ -19,18 +19,18 @@ import UserSkeleton from "@src/components/Settings/UserManagement/UserSkeleton";
|
||||
import InviteUser from "@src/components/Settings/UserManagement/InviteUser";
|
||||
|
||||
import { globalScope, allUsersAtom } from "@src/atoms/globalScope";
|
||||
import UserManagementSourceFirebase from "@src/sources/UserManagementSourceFirebase";
|
||||
import MembersSourceFirebase from "@src/sources/MembersSourceFirebase";
|
||||
import useBasicSearch from "@src/hooks/useBasicSearch";
|
||||
|
||||
const SEARCH_KEYS = ["id", "user.displayName", "user.email"];
|
||||
|
||||
function UserManagementPage() {
|
||||
function MembersPage() {
|
||||
const [users] = useAtom(allUsersAtom, globalScope);
|
||||
const [results, query, handleQuery] = useBasicSearch(users, SEARCH_KEYS);
|
||||
|
||||
return (
|
||||
<Container maxWidth="sm" sx={{ px: 1, pt: 1, pb: 7 + 3 + 3 }}>
|
||||
<UserManagementSourceFirebase />
|
||||
<MembersSourceFirebase />
|
||||
|
||||
<FloatingSearch
|
||||
label="Search users"
|
||||
@@ -71,7 +71,7 @@ function UserManagementPage() {
|
||||
);
|
||||
}
|
||||
|
||||
export default function SuspendedUserManagementPage() {
|
||||
export default function SuspendedMembersPage() {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
@@ -107,7 +107,7 @@ export default function SuspendedUserManagementPage() {
|
||||
</Fade>
|
||||
}
|
||||
>
|
||||
<UserManagementPage />
|
||||
<MembersPage />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
@@ -11,14 +11,12 @@ import { USERS } from "@src/config/dbPaths";
|
||||
/**
|
||||
* When rendered, provides atom values for top-level tables
|
||||
*/
|
||||
const UserManagementSourceFirebase = memo(
|
||||
function UserManagementSourceFirebase() {
|
||||
useFirestoreCollectionWithAtom(allUsersAtom, globalScope, USERS, {
|
||||
updateDocAtom: updateUserAtom,
|
||||
});
|
||||
const MembersSourceFirebase = memo(function MembersSourceFirebase() {
|
||||
useFirestoreCollectionWithAtom(allUsersAtom, globalScope, USERS, {
|
||||
updateDocAtom: updateUserAtom,
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
return null;
|
||||
});
|
||||
|
||||
export default UserManagementSourceFirebase;
|
||||
export default MembersSourceFirebase;
|
||||
Reference in New Issue
Block a user