mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
add GetStartedChecklist
This commit is contained in:
@@ -94,6 +94,12 @@ export { Table };
|
||||
import { Leaf } from "mdi-material-ui";
|
||||
export { Leaf };
|
||||
|
||||
import { FormatListChecks } from "mdi-material-ui";
|
||||
export { FormatListChecks as Checklist };
|
||||
|
||||
import { FileTableBoxOutline } from "mdi-material-ui";
|
||||
export { FileTableBoxOutline as Project };
|
||||
|
||||
export * from "./AddRow";
|
||||
export * from "./AddRowTop";
|
||||
export * from "./ChevronDown";
|
||||
|
||||
@@ -143,6 +143,9 @@ export const tableSettingsDialogSchemaAtom = atom(async (get) => {
|
||||
return getTableSchema(tableId);
|
||||
});
|
||||
|
||||
/** Open the Get Started checklist from anywhere */
|
||||
export const getStartedChecklistAtom = atom(false);
|
||||
|
||||
/** Persist the state of the add row ID type */
|
||||
export const tableAddRowIdTypeAtom = atomWithStorage<
|
||||
"decrement" | "random" | "custom"
|
||||
|
||||
52
src/components/SocialLogo.tsx
Normal file
52
src/components/SocialLogo.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Box, BoxProps, SvgIcon } from "@mui/material";
|
||||
import { Discord as DiscordIcon } from "@src/assets/icons";
|
||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||
import TwitterIcon from "@mui/icons-material/Twitter";
|
||||
|
||||
import { spreadSx } from "@src/utils/ui";
|
||||
|
||||
const SOCIAL_PLATFORMS = Object.freeze({
|
||||
discord: { bg: "#5865F2", fg: "#fff", icon: <DiscordIcon /> },
|
||||
gitHub: { bg: "#24292E", fg: "#fff", icon: <GitHubIcon /> },
|
||||
twitter: { bg: "#1d9bf0", fg: "#fff", icon: <TwitterIcon /> },
|
||||
productHunt: {
|
||||
bg: "#fff",
|
||||
fg: "#DA552F",
|
||||
icon: (
|
||||
<SvgIcon viewBox="0 0 240 240">
|
||||
<path d="M240 120c0 66.274-53.726 120-120 120S0 186.274 0 120 53.726 0 120 0s120 53.726 120 120m-104 0h-34V84h34c9.941 0 18 8.059 18 18 0 9.94-8.059 18-18 18m0-60H78v120h24v-36h34c23.196 0 42-18.804 42-42s-18.804-42-42-42" />
|
||||
</SvgIcon>
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
export interface ISocialLogoProps extends Partial<BoxProps> {
|
||||
platform: keyof typeof SOCIAL_PLATFORMS;
|
||||
}
|
||||
|
||||
export default function SocialLogo({ platform, ...props }: ISocialLogoProps) {
|
||||
return (
|
||||
<Box
|
||||
{...props}
|
||||
sx={[
|
||||
{
|
||||
borderRadius: 1.5,
|
||||
p: 0.5,
|
||||
bgcolor: SOCIAL_PLATFORMS[platform].bg,
|
||||
color: SOCIAL_PLATFORMS[platform].fg,
|
||||
boxShadow: (theme) => `0 0 0 1px ${
|
||||
theme.palette.action.inputOutline
|
||||
} inset,
|
||||
0 ${theme.palette.mode === "dark" ? "" : "-"}1px 0 0 ${
|
||||
theme.palette.action.inputOutline
|
||||
} inset`,
|
||||
|
||||
"& svg": { display: "block" },
|
||||
},
|
||||
...spreadSx(props.sx),
|
||||
]}
|
||||
>
|
||||
{SOCIAL_PLATFORMS[platform].icon}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -94,18 +94,20 @@ export default function SteppedAccordion({
|
||||
>
|
||||
<StepLabel error={error} {...labelProps}>
|
||||
{title}
|
||||
<ExpandIcon sx={{ mr: -0.5 }} />
|
||||
{content && <ExpandIcon sx={{ mr: -0.5 }} />}
|
||||
</StepLabel>
|
||||
</StepButton>
|
||||
|
||||
<StepContent
|
||||
TransitionProps={
|
||||
disableUnmount ? { unmountOnExit: false } : undefined
|
||||
}
|
||||
{...contentProps}
|
||||
>
|
||||
{content}
|
||||
</StepContent>
|
||||
{content && (
|
||||
<StepContent
|
||||
TransitionProps={
|
||||
disableUnmount ? { unmountOnExit: false } : undefined
|
||||
}
|
||||
{...contentProps}
|
||||
>
|
||||
{content}
|
||||
</StepContent>
|
||||
)}
|
||||
</Step>
|
||||
)
|
||||
)}
|
||||
|
||||
@@ -10,6 +10,7 @@ export const EXTERNAL_LINKS = {
|
||||
gitHub: meta.repository.url.replace(".git", ""),
|
||||
discord: "https://discord.gg/B8yAD5PDX4",
|
||||
twitter: "https://twitter.com/rowyio",
|
||||
productHunt: "https://www.producthunt.com/products/rowy-2",
|
||||
|
||||
rowyRun: meta.repository.url.replace(".git", "Run"),
|
||||
rowyRunGitHub: meta.repository.url.replace(".git", "Run"),
|
||||
|
||||
116
src/layouts/Navigation/CommunityMenu.tsx
Normal file
116
src/layouts/Navigation/CommunityMenu.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
import {
|
||||
Menu,
|
||||
MenuProps,
|
||||
ListSubheader,
|
||||
MenuItem,
|
||||
ListItemIcon,
|
||||
ListItemSecondaryAction,
|
||||
} from "@mui/material";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import SocialLogo from "@src/components/SocialLogo";
|
||||
|
||||
import { EXTERNAL_LINKS } from "@src/constants/externalLinks";
|
||||
import { logEvent, analytics } from "analytics";
|
||||
|
||||
export default function HelpMenu({
|
||||
anchorEl,
|
||||
onClose,
|
||||
}: Pick<MenuProps, "anchorEl" | "onClose">) {
|
||||
const open = Boolean(anchorEl);
|
||||
useEffect(() => {
|
||||
if (open) logEvent(analytics, "open_community_menu");
|
||||
}, [open]);
|
||||
|
||||
const externalLinkIcon = (
|
||||
<ListItemSecondaryAction sx={{ right: 10, color: "text.disabled" }}>
|
||||
<InlineOpenInNewIcon />
|
||||
</ListItemSecondaryAction>
|
||||
);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
id="community-menu"
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
||||
transformOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||
sx={{ "& .MuiPaper-root": { mt: 0.5, py: 1 } }}
|
||||
PaperProps={{ elevation: 10 }}
|
||||
>
|
||||
<ListSubheader
|
||||
sx={{
|
||||
pb: 0.5,
|
||||
typography: "subtitle1",
|
||||
}}
|
||||
>
|
||||
Join our community
|
||||
</ListSubheader>
|
||||
<ListSubheader
|
||||
sx={{
|
||||
pb: 1.5,
|
||||
maxWidth: 260,
|
||||
typography: "body2",
|
||||
color: "text.secondary",
|
||||
}}
|
||||
>
|
||||
Reach out for help, engage with our community, or shout us out!
|
||||
</ListSubheader>
|
||||
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.discord}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<ListItemIcon sx={{ mr: 1 }}>
|
||||
<SocialLogo platform="discord" />
|
||||
</ListItemIcon>
|
||||
Discord
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.gitHub}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<ListItemIcon sx={{ mr: 1 }}>
|
||||
<SocialLogo platform="gitHub" />
|
||||
</ListItemIcon>
|
||||
GitHub
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.twitter}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<ListItemIcon sx={{ mr: 1 }}>
|
||||
<SocialLogo platform="twitter" />
|
||||
</ListItemIcon>
|
||||
Twitter
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.productHunt}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<ListItemIcon sx={{ mr: 1 }}>
|
||||
<SocialLogo platform="productHunt" />
|
||||
</ListItemIcon>
|
||||
Product Hunt
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import { useAtom, useSetAtom } from "jotai";
|
||||
|
||||
import { Typography, Button } from "@mui/material";
|
||||
import UncheckedIcon from "@mui/icons-material/RadioButtonUnchecked";
|
||||
import CheckedIcon from "@mui/icons-material/CheckCircleOutline";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import MembersIcon from "@mui/icons-material/AccountCircleOutlined";
|
||||
import { Project as ProjectIcon } from "@src/assets/icons";
|
||||
|
||||
import Modal, { IModalProps } from "@src/components/Modal";
|
||||
import SteppedAccordion from "@src/components/SteppedAccordion";
|
||||
import Progress from "./Progress";
|
||||
|
||||
import {
|
||||
globalScope,
|
||||
getStartedChecklistAtom,
|
||||
tableSettingsDialogAtom,
|
||||
} from "@src/atoms/globalScope";
|
||||
import {
|
||||
NAV_DRAWER_WIDTH,
|
||||
NAV_DRAWER_COLLAPSED_WIDTH,
|
||||
} from "@src/layouts/Navigation/NavDrawer";
|
||||
|
||||
export interface IGetStartedChecklistProps extends Partial<IModalProps> {
|
||||
navOpen: boolean;
|
||||
navPermanent: boolean;
|
||||
}
|
||||
|
||||
export default function GetStartedChecklist({
|
||||
navOpen,
|
||||
navPermanent,
|
||||
...props
|
||||
}: IGetStartedChecklistProps) {
|
||||
const [open, setOpen] = useAtom(getStartedChecklistAtom, globalScope);
|
||||
const openTableSettingsDialog = useSetAtom(
|
||||
tableSettingsDialogAtom,
|
||||
globalScope
|
||||
);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
onClose={() => setOpen(false)}
|
||||
title="Get started"
|
||||
hideBackdrop
|
||||
maxWidth="xs"
|
||||
PaperProps={{ elevation: 8 }}
|
||||
fullScreen={false}
|
||||
sx={[
|
||||
{
|
||||
"& .MuiDialog-container": {
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-end",
|
||||
},
|
||||
|
||||
"& .MuiDialog-paper": {
|
||||
marginLeft: {
|
||||
xs: `max(env(safe-area-inset-left), 8px)`,
|
||||
sm: `max(env(safe-area-inset-left), ${
|
||||
(navPermanent
|
||||
? navOpen
|
||||
? NAV_DRAWER_WIDTH
|
||||
: NAV_DRAWER_COLLAPSED_WIDTH
|
||||
: 0) + 8
|
||||
}px)`,
|
||||
},
|
||||
marginBottom: `max(env(safe-area-inset-bottom), 8px)`,
|
||||
marginRight: `max(env(safe-area-inset-right), 8px)`,
|
||||
width: 360,
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Progress sx={{ mb: 2 }} />
|
||||
|
||||
<SteppedAccordion
|
||||
steps={[
|
||||
{
|
||||
id: "workspace",
|
||||
title: "Create a workspace",
|
||||
labelButtonProps: { icon: <CheckedIcon color="success" /> },
|
||||
content: null,
|
||||
},
|
||||
{
|
||||
id: "tutorial",
|
||||
title: "Complete the table tutorial",
|
||||
labelButtonProps: { icon: <UncheckedIcon color="action" /> },
|
||||
content: (
|
||||
<>
|
||||
<Typography>This is why you should</Typography>
|
||||
<Button variant="contained" color="primary">
|
||||
Begin tutorial
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "project",
|
||||
title: "Create a project",
|
||||
labelButtonProps: { icon: <UncheckedIcon color="action" /> },
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
You’re ready to create a project and connect to a data source
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<ProjectIcon />}
|
||||
>
|
||||
Create project
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "table",
|
||||
title: "Create a table",
|
||||
labelButtonProps: { icon: <UncheckedIcon color="action" /> },
|
||||
content: (
|
||||
<>
|
||||
<Typography>This is why you should</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<AddIcon />}
|
||||
onClick={() => openTableSettingsDialog({ open: true })}
|
||||
>
|
||||
Create table
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "members",
|
||||
title: "Invite team members",
|
||||
labelButtonProps: { icon: <UncheckedIcon color="action" /> },
|
||||
content: (
|
||||
<>
|
||||
<Typography>
|
||||
Go to the members page to invite someone
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<MembersIcon />}
|
||||
>
|
||||
Members
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
sx={{
|
||||
"& .MuiStepConnector-root": {
|
||||
my: -10 / 8,
|
||||
},
|
||||
"& .Mui-active + .MuiStep-root:not(:last-of-type) .MuiStepContent-root":
|
||||
{
|
||||
mt: -10 / 8,
|
||||
pt: 10 / 8,
|
||||
mb: 10 / 8,
|
||||
pb: 2,
|
||||
},
|
||||
"& .MuiStepContent-root .MuiCollapse-wrapperInner > * + *": {
|
||||
mt: 1,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
23
src/layouts/Navigation/GetStartedChecklist/Progress.tsx
Normal file
23
src/layouts/Navigation/GetStartedChecklist/Progress.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Box, BoxProps, Typography } from "@mui/material";
|
||||
import { spreadSx } from "@src/utils/ui";
|
||||
|
||||
export default function Progress({ sx }: Partial<BoxProps>) {
|
||||
return (
|
||||
<Box
|
||||
sx={[
|
||||
{ display: "flex", alignItems: "center", gap: 0.5 },
|
||||
...spreadSx(sx),
|
||||
]}
|
||||
>
|
||||
<Typography style={{ flex: 3 }}>1/5</Typography>
|
||||
|
||||
<Box
|
||||
sx={{ flex: 1, borderRadius: 1, height: 8, bgcolor: "success.light" }}
|
||||
/>
|
||||
<Box sx={{ flex: 1, borderRadius: 1, height: 8, bgcolor: "divider" }} />
|
||||
<Box sx={{ flex: 1, borderRadius: 1, height: 8, bgcolor: "divider" }} />
|
||||
<Box sx={{ flex: 1, borderRadius: 1, height: 8, bgcolor: "divider" }} />
|
||||
<Box sx={{ flex: 1, borderRadius: 1, height: 8, bgcolor: "divider" }} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
2
src/layouts/Navigation/GetStartedChecklist/index.ts
Normal file
2
src/layouts/Navigation/GetStartedChecklist/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./GetStartedChecklist";
|
||||
export { default } from "./GetStartedChecklist";
|
||||
@@ -1,30 +1,39 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
import {
|
||||
Menu,
|
||||
MenuProps,
|
||||
ListSubheader,
|
||||
MenuItem,
|
||||
ListItemIcon,
|
||||
ListItemSecondaryAction,
|
||||
Divider,
|
||||
} from "@mui/material";
|
||||
import DocsIcon from "@mui/icons-material/LibraryBooksOutlined";
|
||||
import { Discord as DiscordIcon } from "@src/assets/icons";
|
||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||
import TwitterIcon from "@mui/icons-material/Twitter";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import SocialLogo from "@src/components/SocialLogo";
|
||||
|
||||
import { EXTERNAL_LINKS } from "@src/constants/externalLinks";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import { analytics } from "analytics";
|
||||
import { logEvent, analytics } from "analytics";
|
||||
|
||||
export default function HelpMenu({
|
||||
anchorEl,
|
||||
onClose,
|
||||
}: Pick<MenuProps, "anchorEl" | "onClose">) {
|
||||
// useEffect(() => {
|
||||
// analytics.logEvent("open_help_menu");
|
||||
// }, []);
|
||||
const open = Boolean(anchorEl);
|
||||
useEffect(() => {
|
||||
if (open) logEvent(analytics, "open_help_menu");
|
||||
}, [open]);
|
||||
|
||||
const externalLinkIcon = (
|
||||
<ListItemSecondaryAction sx={{ right: 10, color: "text.disabled" }}>
|
||||
<ListItemSecondaryAction
|
||||
sx={{
|
||||
position: "static",
|
||||
transform: "none",
|
||||
ml: "auto",
|
||||
pl: 2,
|
||||
color: "text.disabled",
|
||||
}}
|
||||
>
|
||||
<InlineOpenInNewIcon />
|
||||
</ListItemSecondaryAction>
|
||||
);
|
||||
@@ -32,50 +41,60 @@ export default function HelpMenu({
|
||||
return (
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
id="help-menu"
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
||||
transformOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||
sx={{ "& .MuiPaper-root": { mt: 0.5, minWidth: 160 } }}
|
||||
sx={{ "& .MuiPaper-root": { mt: 0.5, py: 1 } }}
|
||||
PaperProps={{ elevation: 10 }}
|
||||
>
|
||||
<ListSubheader
|
||||
sx={{
|
||||
mb: 0.5,
|
||||
typography: "subtitle1",
|
||||
}}
|
||||
>
|
||||
Get support
|
||||
</ListSubheader>
|
||||
<ListSubheader
|
||||
sx={{
|
||||
mb: 1.5,
|
||||
maxWidth: 260,
|
||||
typography: "body2",
|
||||
color: "text.secondary",
|
||||
}}
|
||||
>
|
||||
Reach out for help and find FAQs on GitHub Discussions
|
||||
</ListSubheader>
|
||||
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.discord}
|
||||
href={EXTERNAL_LINKS.gitHub + "/discussions"}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<DiscordIcon />
|
||||
<ListItemIcon sx={{ mr: 1 }}>
|
||||
<SocialLogo platform="gitHub" />
|
||||
</ListItemIcon>
|
||||
Discord
|
||||
GitHub Discussions
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
|
||||
<Divider variant="middle" />
|
||||
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.gitHub}
|
||||
href={EXTERNAL_LINKS.gitHub + "/issues/new/choose"}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<GitHubIcon />
|
||||
<ListItemIcon sx={{ mr: 1 }}>
|
||||
<SocialLogo platform="gitHub" />
|
||||
</ListItemIcon>
|
||||
GitHub
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
component="a"
|
||||
href={EXTERNAL_LINKS.twitter}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={onClose as any}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<TwitterIcon />
|
||||
</ListItemIcon>
|
||||
Twitter
|
||||
Feature request
|
||||
{externalLinkIcon}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { find, groupBy } from "lodash-es";
|
||||
import { colord } from "colord";
|
||||
|
||||
import {
|
||||
alpha,
|
||||
Drawer,
|
||||
DrawerProps,
|
||||
Stack,
|
||||
@@ -17,28 +18,31 @@ import {
|
||||
} from "@mui/material";
|
||||
import CloseIcon from "@mui/icons-material/MenuOpen";
|
||||
import HomeIcon from "@mui/icons-material/HomeOutlined";
|
||||
import SettingsIcon from "@mui/icons-material/SettingsOutlined";
|
||||
import UserManagementIcon from "@mui/icons-material/AccountCircleOutlined";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
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 {
|
||||
ChevronRight as ChevronRightIcon,
|
||||
Checklist as ChecklistIcon,
|
||||
} from "@src/assets/icons";
|
||||
|
||||
import Logo from "@src/assets/Logo";
|
||||
import NavItem from "./NavItem";
|
||||
import SettingsNav from "./SettingsNav";
|
||||
import NavTableSection from "./NavTableSection";
|
||||
import UpdateCheckBadge from "./UpdateCheckBadge";
|
||||
import Progress from "./GetStartedChecklist/Progress";
|
||||
import CommunityMenu from "./CommunityMenu";
|
||||
import HelpMenu from "./HelpMenu";
|
||||
|
||||
import {
|
||||
globalScope,
|
||||
userRolesAtom,
|
||||
userSettingsAtom,
|
||||
tablesAtom,
|
||||
tableSettingsDialogAtom,
|
||||
getStartedChecklistAtom,
|
||||
} from "@src/atoms/globalScope";
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import { ROUTES } from "@src/constants/routes";
|
||||
@@ -61,16 +65,23 @@ export default function NavDrawer({
|
||||
}: INavDrawerProps) {
|
||||
const [tables] = useAtom(tablesAtom, globalScope);
|
||||
const [userSettings] = useAtom(userSettingsAtom, globalScope);
|
||||
const [userRoles] = useAtom(userRolesAtom, globalScope);
|
||||
const openTableSettingsDialog = useSetAtom(
|
||||
tableSettingsDialogAtom,
|
||||
globalScope
|
||||
);
|
||||
const openGetStartedChecklist = useSetAtom(
|
||||
getStartedChecklistAtom,
|
||||
globalScope
|
||||
);
|
||||
|
||||
const [hover, setHover] = useState(false);
|
||||
const [communityMenuAnchorEl, setCommunityMenuAnchorEl] =
|
||||
useState<HTMLButtonElement | null>(null);
|
||||
const [helpMenuAnchorEl, setHelpMenuAnchorEl] =
|
||||
useState<HTMLButtonElement | null>(null);
|
||||
|
||||
const menuOpen = communityMenuAnchorEl || helpMenuAnchorEl;
|
||||
|
||||
const favorites = Array.isArray(userSettings.favoriteTables)
|
||||
? userSettings.favoriteTables
|
||||
: [];
|
||||
@@ -82,7 +93,7 @@ export default function NavDrawer({
|
||||
};
|
||||
|
||||
const collapsed = !open && isPermanent;
|
||||
const tempExpanded = hover && collapsed;
|
||||
const tempExpanded = (hover || menuOpen) && collapsed;
|
||||
const width =
|
||||
collapsed && !tempExpanded ? NAV_DRAWER_COLLAPSED_WIDTH : NAV_DRAWER_WIDTH;
|
||||
const closeDrawer = isPermanent
|
||||
@@ -111,8 +122,15 @@ export default function NavDrawer({
|
||||
"& .MuiDrawer-paper": {
|
||||
width,
|
||||
pt: 0,
|
||||
pb: 1,
|
||||
scrollbarWidth: "thin",
|
||||
|
||||
"--nav-bg": (theme) =>
|
||||
colord(theme.palette.background.paper)
|
||||
.mix("#fff", 0.09) // elevation 8
|
||||
.alpha(1)
|
||||
.toHslString(),
|
||||
bgcolor: "var(--nav-bg)",
|
||||
backgroundImage: "none",
|
||||
},
|
||||
},
|
||||
isPermanent && {
|
||||
@@ -135,7 +153,7 @@ export default function NavDrawer({
|
||||
overflowX: "hidden",
|
||||
|
||||
borderRight: "none",
|
||||
bgcolor: "background.default",
|
||||
"--nav-bg": (theme) => theme.palette.background.default,
|
||||
},
|
||||
|
||||
"& .MuiListItemSecondaryAction-root": {
|
||||
@@ -158,9 +176,9 @@ export default function NavDrawer({
|
||||
tempExpanded && {
|
||||
zIndex: "drawer",
|
||||
"& .MuiDrawer-paper": {
|
||||
bgcolor: (theme) =>
|
||||
"--nav-bg": (theme) =>
|
||||
colord(theme.palette.background.paper)
|
||||
.mix("#fff", 0.09)
|
||||
.mix("#fff", 0.09) // elevation 8
|
||||
.alpha(1)
|
||||
.toHslString(),
|
||||
boxShadow: (theme) =>
|
||||
@@ -168,14 +186,25 @@ export default function NavDrawer({
|
||||
},
|
||||
},
|
||||
]}
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
PaperProps={{
|
||||
elevation: isPermanent ? 0 : 8,
|
||||
onMouseEnter: () => setHover(true),
|
||||
onMouseLeave: () => setHover(false),
|
||||
}}
|
||||
>
|
||||
{!isPermanent && (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
sx={{ height: TOP_BAR_HEIGHT, flexShrink: 0, px: 0.5 }}
|
||||
sx={{
|
||||
height: TOP_BAR_HEIGHT,
|
||||
flexShrink: 0,
|
||||
px: 0.5,
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
backgroundColor: "var(--nav-bg)",
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
aria-label="Close navigation drawer"
|
||||
@@ -191,8 +220,15 @@ export default function NavDrawer({
|
||||
|
||||
<nav style={{ flexGrow: 1 }}>
|
||||
<List
|
||||
component="ol"
|
||||
disablePadding
|
||||
style={{ height: "100%", display: "flex", flexDirection: "column" }}
|
||||
style={{
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
position: "static",
|
||||
backgroundColor: "var(--nav-bg)",
|
||||
}}
|
||||
>
|
||||
<li>
|
||||
<NavItem to={ROUTES.tables} onClick={closeDrawer}>
|
||||
@@ -203,47 +239,13 @@ export default function NavDrawer({
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavItem to={ROUTES.userSettings} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<SettingsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Settings" />
|
||||
{userRoles.includes("ADMIN") && (
|
||||
<UpdateCheckBadge sx={{ mr: 1.5 }} />
|
||||
)}
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
{userRoles.includes("ADMIN") && (
|
||||
<li>
|
||||
<NavItem to={ROUTES.members} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<UserManagementIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Members" />
|
||||
</NavItem>
|
||||
</li>
|
||||
)}
|
||||
<SettingsNav
|
||||
closeDrawer={closeDrawer as any}
|
||||
collapsed={isPermanent && !open && !tempExpanded}
|
||||
/>
|
||||
|
||||
<Divider variant="middle" sx={{ my: 1 }} />
|
||||
|
||||
<li>
|
||||
<NavItem
|
||||
{...({ component: "button" } as any)}
|
||||
style={{ textAlign: "left" }}
|
||||
onClick={(e: any) => {
|
||||
if (closeDrawer) closeDrawer(e);
|
||||
openTableSettingsDialog({});
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<AddIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Create table…" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
{sections &&
|
||||
Object.entries(sections)
|
||||
.filter(([, tables]) => tables.length > 0)
|
||||
@@ -253,61 +255,142 @@ export default function NavDrawer({
|
||||
section={section}
|
||||
tables={tables}
|
||||
closeDrawer={closeDrawer}
|
||||
hidden={isPermanent && !open && !tempExpanded}
|
||||
collapsed={isPermanent && !open && !tempExpanded}
|
||||
/>
|
||||
))}
|
||||
|
||||
<Divider variant="middle" sx={{ my: 1, mt: "auto" }} />
|
||||
|
||||
<li>
|
||||
<NavItem href={EXTERNAL_LINKS.docs}>
|
||||
<ListItemIcon>
|
||||
<DocsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Docs" />
|
||||
{externalLinkIcon}
|
||||
</NavItem>
|
||||
</li>
|
||||
<li>
|
||||
<NavItem href={WIKI_LINKS.howTo}>
|
||||
<ListItemIcon>
|
||||
<LearningIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Learning" />
|
||||
{externalLinkIcon}
|
||||
</NavItem>
|
||||
</li>
|
||||
<li>
|
||||
<NavItem
|
||||
onClick={(e: any) => setHelpMenuAnchorEl(e.currentTarget)}
|
||||
{...({ component: "button" } as any)}
|
||||
onClick={(e: any) => {
|
||||
if (closeDrawer) closeDrawer(e);
|
||||
openTableSettingsDialog({});
|
||||
}}
|
||||
sx={{ mb: 1 }}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<CommunityIcon />
|
||||
<AddIcon />
|
||||
</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>
|
||||
<ListItemText primary="Create table…" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<HelpMenu
|
||||
anchorEl={helpMenuAnchorEl}
|
||||
onClose={() => setHelpMenuAnchorEl(null)}
|
||||
/>
|
||||
<List
|
||||
component="li"
|
||||
disablePadding
|
||||
sx={{
|
||||
position: "sticky",
|
||||
bottom: 0,
|
||||
bgcolor: "var(--nav-bg)",
|
||||
mt: "auto",
|
||||
pb: 1,
|
||||
}}
|
||||
>
|
||||
<ol style={{ listStyle: "none", margin: 0, padding: 0 }}>
|
||||
<Divider variant="middle" sx={{ mb: 1 }} />
|
||||
|
||||
<li>
|
||||
<NavItem
|
||||
onClick={() => {
|
||||
openGetStartedChecklist(true);
|
||||
setHover(false);
|
||||
}}
|
||||
sx={{
|
||||
mb: 1,
|
||||
py: 0.5,
|
||||
bgcolor: (theme) =>
|
||||
alpha(
|
||||
theme.palette.primary.main,
|
||||
theme.palette.action.selectedOpacity
|
||||
),
|
||||
"&:hover": {
|
||||
bgcolor: (theme) =>
|
||||
alpha(
|
||||
theme.palette.primary.main,
|
||||
theme.palette.action.selectedOpacity +
|
||||
theme.palette.action.hoverOpacity
|
||||
),
|
||||
},
|
||||
"& *, &&:hover *": {
|
||||
color: (theme) =>
|
||||
theme.palette.primary[
|
||||
theme.palette.mode === "dark" ? "light" : "dark"
|
||||
],
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<ChecklistIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Get started"
|
||||
secondary={<Progress sx={{ mr: 3 }} />}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<ChevronRightIcon />
|
||||
</ListItemSecondaryAction>
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavItem href={EXTERNAL_LINKS.docs}>
|
||||
<ListItemIcon>
|
||||
<DocsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Docs" />
|
||||
{externalLinkIcon}
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavItem href={WIKI_LINKS.howTo}>
|
||||
<ListItemIcon>
|
||||
<LearningIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Learning" />
|
||||
{externalLinkIcon}
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavItem
|
||||
onClick={(e: any) =>
|
||||
setCommunityMenuAnchorEl(e.currentTarget)
|
||||
}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<CommunityIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Join our community" />
|
||||
<ListItemSecondaryAction>
|
||||
<ChevronRightIcon />
|
||||
</ListItemSecondaryAction>
|
||||
</NavItem>
|
||||
<CommunityMenu
|
||||
anchorEl={communityMenuAnchorEl}
|
||||
onClose={() => setCommunityMenuAnchorEl(null)}
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavItem
|
||||
onClick={(e: any) => setHelpMenuAnchorEl(e.currentTarget)}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<HelpIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Help" />
|
||||
<ListItemSecondaryAction>
|
||||
<ChevronRightIcon />
|
||||
</ListItemSecondaryAction>
|
||||
</NavItem>
|
||||
<HelpMenu
|
||||
anchorEl={helpMenuAnchorEl}
|
||||
onClose={() => setHelpMenuAnchorEl(null)}
|
||||
/>
|
||||
</li>
|
||||
</ol>
|
||||
</List>
|
||||
</List>
|
||||
</nav>
|
||||
</Drawer>
|
||||
|
||||
@@ -23,14 +23,14 @@ export interface INavTableSectionProps {
|
||||
section: string;
|
||||
tables: TableSettings[];
|
||||
closeDrawer?: (e: {}) => void;
|
||||
hidden?: boolean;
|
||||
collapsed?: boolean;
|
||||
}
|
||||
|
||||
export default function NavTableSection({
|
||||
section,
|
||||
tables,
|
||||
closeDrawer,
|
||||
hidden,
|
||||
collapsed,
|
||||
}: INavTableSectionProps) {
|
||||
const { pathname } = useLocation();
|
||||
const hasMatch = tables.map(getTableRoute).includes(pathname);
|
||||
@@ -39,16 +39,8 @@ export default function NavTableSection({
|
||||
const isFavorites = section === "Favorites";
|
||||
|
||||
return (
|
||||
<li
|
||||
style={{
|
||||
opacity: hidden ? 0.38 : 1,
|
||||
transitionProperty: "opacity",
|
||||
transitionTimingFunction: "var(--nav-transition-timing-function)",
|
||||
transitionDuration: "var(--nav-transition-duration)",
|
||||
}}
|
||||
>
|
||||
<li>
|
||||
<NavItem
|
||||
{...({ component: "button" } as any)}
|
||||
selected={!isFavorites && hasMatch && !open}
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
>
|
||||
@@ -71,7 +63,7 @@ export default function NavTableSection({
|
||||
</ListItemSecondaryAction>
|
||||
</NavItem>
|
||||
|
||||
<Collapse in={open}>
|
||||
<Collapse in={open && !collapsed}>
|
||||
<List style={{ paddingTop: 0 }}>
|
||||
{tables.map((table) => {
|
||||
const route = getTableRoute(table);
|
||||
|
||||
@@ -11,6 +11,7 @@ import ErrorFallback, {
|
||||
IErrorFallbackProps,
|
||||
} from "@src/components/ErrorFallback";
|
||||
import Loading from "@src/components/Loading";
|
||||
import GetStartedChecklist from "./GetStartedChecklist";
|
||||
|
||||
import {
|
||||
globalScope,
|
||||
@@ -51,6 +52,7 @@ export default function Navigation({ children }: React.PropsWithChildren<{}>) {
|
||||
isPermanent={isPermanent}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
<GetStartedChecklist navOpen={open} navPermanent={isPermanent} />
|
||||
|
||||
<ErrorBoundary FallbackComponent={StyledErrorFallback}>
|
||||
<Suspense
|
||||
|
||||
115
src/layouts/Navigation/SettingsNav.tsx
Normal file
115
src/layouts/Navigation/SettingsNav.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import { useState } from "react";
|
||||
import { useAtom } from "jotai";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
import {
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
ListItemSecondaryAction,
|
||||
Collapse,
|
||||
List,
|
||||
} from "@mui/material";
|
||||
import MembersIcon from "@mui/icons-material/AccountCircleOutlined";
|
||||
import SettingsIcon from "@mui/icons-material/SettingsOutlined";
|
||||
import { ChevronDown } from "@src/assets/icons";
|
||||
|
||||
import NavItem from "./NavItem";
|
||||
import UpdateCheckBadge from "./UpdateCheckBadge";
|
||||
|
||||
import { globalScope, userRolesAtom } from "@src/atoms/globalScope";
|
||||
import { ROUTES } from "@src/constants/routes";
|
||||
|
||||
export interface ISettingsNavProps {
|
||||
closeDrawer: () => void;
|
||||
collapsed: boolean;
|
||||
}
|
||||
|
||||
export default function SettingsNav({
|
||||
closeDrawer,
|
||||
collapsed,
|
||||
}: ISettingsNavProps) {
|
||||
const [userRoles] = useAtom(userRolesAtom, globalScope);
|
||||
const { pathname } = useLocation();
|
||||
const [open, setOpen] = useState(pathname.includes(ROUTES.settings));
|
||||
|
||||
if (!userRoles.includes("ADMIN"))
|
||||
return (
|
||||
<li>
|
||||
<NavItem to={ROUTES.userSettings} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<SettingsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Settings" />
|
||||
</NavItem>
|
||||
</li>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<li>
|
||||
<NavItem to={ROUTES.members} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<MembersIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Members" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavItem onClick={() => setOpen((o) => !o)}>
|
||||
<ListItemIcon>
|
||||
<SettingsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Settings" />
|
||||
|
||||
<ListItemSecondaryAction>
|
||||
<ChevronDown
|
||||
sx={{
|
||||
color: "action.active",
|
||||
m: 0.25,
|
||||
display: "block",
|
||||
transform: open ? "rotate(180deg)" : "rotate(0)",
|
||||
transition: (theme) => theme.transitions.create("transform"),
|
||||
}}
|
||||
/>
|
||||
</ListItemSecondaryAction>
|
||||
{userRoles.includes("ADMIN") && <UpdateCheckBadge sx={{ mr: 1.5 }} />}
|
||||
</NavItem>
|
||||
|
||||
<Collapse in={open && !collapsed}>
|
||||
<List style={{ paddingTop: 0 }}>
|
||||
<li>
|
||||
<NavItem to={ROUTES.userSettings} onClick={closeDrawer}>
|
||||
<ListItemIcon />
|
||||
<ListItemText
|
||||
primary="User"
|
||||
sx={{
|
||||
"&& .MuiListItemText-primary": {
|
||||
fontWeight: "normal",
|
||||
fontSize: ".8125rem",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavItem to={ROUTES.projectSettings} onClick={closeDrawer}>
|
||||
<ListItemIcon />
|
||||
<ListItemText
|
||||
primary="Project"
|
||||
sx={{
|
||||
"&& .MuiListItemText-primary": {
|
||||
fontWeight: "normal",
|
||||
fontSize: ".8125rem",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</NavItem>
|
||||
</li>
|
||||
</List>
|
||||
</Collapse>
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -134,7 +134,7 @@ export default function TopBar({
|
||||
ml: 1,
|
||||
},
|
||||
!(routeTitle as any)?.leftAligned && {
|
||||
ml: { xs: 1, sm: 208 / 8 + 1 },
|
||||
ml: { xs: 1, sm: 40 / 8 + 1, md: 208 / 8 + 1 },
|
||||
},
|
||||
isPermanent &&
|
||||
(routeTitle as any)?.leftAligned && {
|
||||
@@ -167,11 +167,16 @@ export default function TopBar({
|
||||
to={ROUTES.members}
|
||||
startIcon={<PersonAddIcon />}
|
||||
sx={{
|
||||
minWidth: 40,
|
||||
"&&": { px: { xs: 1, md: 2 } },
|
||||
mr: 1,
|
||||
"&.active": { visibility: "hidden" },
|
||||
|
||||
"& .text": { display: { xs: "none", md: "inline" } },
|
||||
"& .MuiButton-startIcon": { mr: { xs: 0, md: 1 } },
|
||||
}}
|
||||
>
|
||||
Invite team members
|
||||
<span className="text">Invite team members</span>
|
||||
</Button>
|
||||
|
||||
<UserMenu />
|
||||
|
||||
Reference in New Issue
Block a user