shadimage
mui-treasury
itemapp-analyticssource: ok · refreshed

App Analytics

A app-analytics item.

npx shadcn add "https://mui-treasury.com/r/app-analytics.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
@mui/icons-material@mui/material@emotion/react@emotion/styled

Registry dependencies

https://www.mui-treasury.com/r/layout-core.json

Files8 · source cached 10/8/2026, 4:45:06 AM

layout/app-analytics/app-analytics.tsx
'use client';
import React from 'react';

import ChevronLeftRounded from '@mui/icons-material/ChevronLeftRounded';
import MenuRounded from '@mui/icons-material/MenuRounded';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import IconButton from '@mui/material/IconButton';
import Tab from '@mui/material/Tab';
import Typography from '@mui/material/Typography';
import useScrollTrigger from '@mui/material/useScrollTrigger';

import {
  Content,
  EdgeDrawerClose,
  EdgeDrawerTrigger,
  EdgeSidebar,
  EdgeSidebarCollapser,
  EdgeSidebarContent,
  Header,
  Root,
} from '../layout-core';
import { layoutVisibility } from '../layout-core/layoutVisibility';
import {
  IconNavMockup,
  LinkNavMockup,
  SideNavUserInfoMockup,
  StatCardMockup,
  TopUsersCardMockup,
  UserActiveCardMockup,
} from './mockups/dashboard';
import { UnderlineTabs } from './mockups/tabs';
import { Word } from './mockups/typography';

export function LayoutV6AppAnalytics() {
  const [tabIndex, setTabIndex] = React.useState(0);
  const trigger = useScrollTrigger({
    threshold: 0,
    disableHysteresis: true,
  });
  return (
    <Root>
      <Header
        height={{ xs: '48px', sm: '64px', md: '72px' }}
        sx={{
          bgcolor: 'rgba(255,255,255,0.5)',
          backdropFilter: 'blur(12px)',
          ...(trigger && {
            transition: '0.2s',
            boxShadow: '0 0 4px 0 #e2e8f0',
            '&:before': {
              content: '""',
              position: 'absolute',
              display: 'block',
              width: '100%',
              height: '1px',
              bgcolor: 'grey.200',
              bottom: 0,
            },
          }),
        }}
      >
        <Box
          sx={{
            px: { xs: 1, md: 2.5 },
            display: 'flex',
            alignItems: 'center',
            gap: 1,
          }}
        >
          <EdgeDrawerTrigger
            render={
              <IconButton>
                <MenuRounded />
              </IconButton>
            }
          />

          <EdgeSidebarCollapser
            render={
              <IconButton>
                <ChevronLeftRounded
                  className={layoutVisibility.visibleOnEdgePermanentUncollapsed}
                />
                <MenuRounded
                  className={layoutVisibility.visibleOnEdgePermanentCollapsed}
                />
              </IconButton>
            }
          />

          <Typography
            variant="h6"
            sx={{ fontSize: 'clamp(18px, 1vw + 1rem, 24px)' }}
          >
            <b>Analytics</b>
          </Typography>
        </Box>
      </Header>
      <EdgeSidebar
        permanentAutoCollapse="xl"
        variant={{
          xs: ['drawer'],
          lg: [
            'permanent',
            {
              width: '300px',
              collapsedWidth: '80px',
              hoverUncollapse: true,
            },
          ],
        }}
      >
        <EdgeDrawerClose />

        <EdgeSidebarContent>
          <SideNavUserInfoMockup />
          <Box
            sx={{
              borderTop: '1px solid',
              borderColor: 'grey.200',
              display: 'flex',
              height: '100%',
            }}
          >
            <IconNavMockup size="small" />
            <LinkNavMockup />
          </Box>
        </EdgeSidebarContent>
      </EdgeSidebar>
      <Content>
        <UnderlineTabs
          value={tabIndex}
          onChange={(_event, index) => setTabIndex(index)}
          sx={{
            minHeight: { xs: 44, md: 48 },
            px: 2,
            '& .MuiTab-root': {
              minHeight: { xs: 44, md: 48 },
              minWidth: 0,
              fontSize: { md: 16 },
            },
          }}
        >
          <Tab label={<Word cached />} disableTouchRipple />
          <Tab label={<Word cached />} disableTouchRipple />
          <Tab label={<Word cached />} disableTouchRipple />
        </UnderlineTabs>
        <Box
          sx={{
            height: '16vh',
            minHeight: 240,
            borderRadius: 2,
            bgcolor: 'rgba(0 0 0 / 0.12)',
            m: 2,
          }}
        />
        <Box sx={{ px: 2 }}>
          <Grid container spacing={2}>
            <Grid size={{ xs: 12, sm: 6, md: 8 }}>
              <Grid container spacing={2}>
                <Grid size={6}>
                  <StatCardMockup />
                </Grid>
                <Grid size={6}>
                  <StatCardMockup />
                </Grid>
                <Grid size={6}>
                  <StatCardMockup />
                </Grid>
                <Grid size={6}>
                  <StatCardMockup />
                </Grid>
                <Grid size={{ xs: 12, md: 6 }}>
                  <UserActiveCardMockup />
                </Grid>
              </Grid>
            </Grid>
            <Grid size={{ xs: 12, sm: 6, md: 4 }}>
              <TopUsersCardMockup sx={{ height: '100%' }} />
            </Grid>
          </Grid>
        </Box>
        <br />
        <br />
        <br />
      </Content>
    </Root>
  );
}
layout/app-analytics/mockups/dashboard.tsx
'use client';
import React from 'react';

import AppRegistrationRounded from '@mui/icons-material/AppRegistrationRounded';
import EventNoteRounded from '@mui/icons-material/EventNoteRounded';
import ForumRounded from '@mui/icons-material/ForumRounded';
import HomeRounded from '@mui/icons-material/HomeRounded';
import SearchRounded from '@mui/icons-material/SearchRounded';
import SettingsRounded from '@mui/icons-material/SettingsRounded';
import Avatar from '@mui/material/Avatar';
import Box, { BoxProps } from '@mui/material/Box';
import Divider from '@mui/material/Divider';
import IconButton, { IconButtonProps } from '@mui/material/IconButton';
import LinearProgress from '@mui/material/LinearProgress';
import List, { ListProps } from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemAvatar from '@mui/material/ListItemAvatar';
import ListItemButton from '@mui/material/ListItemButton';
import Typography from '@mui/material/Typography';
import { alpha } from '@mui/material/styles';

import { Circle, Square } from './shapes';
import { Word } from './typography';
import { getLightGrey, getLightestGrey } from './utils';

// --- IconNav ---

const defaultIcons = [
  <SearchRounded key="search" />,
  <HomeRounded key="home" />,
  <ForumRounded key="forum" />,
  <SettingsRounded key="settings" />,
  <AppRegistrationRounded key="app" />,
  <EventNoteRounded key="event" />,
];

export const IconNavMockup = ({
  activeIndex = 1,
  icons = defaultIcons,
  size,
  ...props
}: { icons?: Array<React.ReactNode>; activeIndex?: number } & Pick<
  IconButtonProps,
  'size'
> &
  BoxProps) => {
  return (
    <Box
      {...props}
      sx={{
        display: 'inline-flex',
        flexDirection: 'column',
        gap: 2,
        p: 2,
        bgcolor: getLightestGrey,
        '& .MuiIconButton-root': {
          borderRadius: 2,
        },
        ...props.sx,
      }}
    >
      {icons.map((icon, index) => (
        <IconButton
          key={index}
          disableTouchRipple
          size={size}
          sx={{
            ...(index === 0 && { mb: 0 }),
            ...(index === activeIndex && {
              bgcolor: (theme) => alpha(theme.palette.primary.main, 0.12),
              '& svg': {
                color: (theme) => theme.palette.primary.main,
              },
            }),
          }}
        >
          {icon}
        </IconButton>
      ))}
    </Box>
  );
};

// --- LinkNav ---

export const LinkNavMockup = ({
  lineCount = 5,
  ...props
}: { lineCount?: number } & ListProps) => {
  return (
    <List
      {...props}
      sx={{
        flexGrow: 1,
        p: 2,
        '& .MuiListItemButton-root': {
          borderRadius: 2,
          '&:hover': {
            bgcolor: (theme) => alpha(theme.palette.grey[300], 0.2),
            color: 'grey.500',
          },
        },
        ...props.sx,
      }}
    >
      {[...Array(lineCount).fill(undefined)].map((_, index) => (
        <ListItem key={index} disablePadding>
          <ListItemButton>
            <Word sx={{ my: 1 }} />
          </ListItemButton>
        </ListItem>
      ))}
    </List>
  );
};

// --- SideNavUserInfo ---

export const SideNavUserInfoMockup = ({
  collapsed,
  ...props
}: { collapsed?: boolean } & BoxProps) => {
  return (
    <Box
      {...props}
      sx={{
        bgcolor: getLightestGrey,
        mr: '1px',
        p: 1.5,
        display: 'flex',
        alignItems: 'center',
        ...props.sx,
      }}
    >
      <Circle sx={{ fontSize: collapsed ? 36 : 48, transition: '0.2s' }} />
      {!collapsed && (
        <Box sx={{ ml: 1.5, minWidth: '0px' }}>
          <Word />
          <Word
            sx={{
              height: 10,
              borderRadius: 2,
              mt: 0.75,
              bgcolor: getLightGrey,
            }}
          />
        </Box>
      )}
    </Box>
  );
};

// --- StatCard ---

export const StatCardMockup = (props: BoxProps) => {
  return (
    <Box
      {...props}
      sx={{
        borderRadius: 1,
        border: '1px solid',
        borderColor: getLightGrey,
        bgcolor: 'background.paper',
        p: 1.5,
        display: 'flex',
        alignItems: 'center',
        boxShadow: '0 2px 6px 0 rgba(0,0,0,0.08)',
        ...props.sx,
      }}
    >
      <Square />
      <Box sx={{ ml: 1.5, minWidth: '0px' }}>
        <Word
          sx={{
            height: 10,
            borderRadius: 3,
            bgcolor: getLightGrey,
            m: 0,
          }}
        />
        <Word sx={{ mt: 0.5, mb: 0 }} />
      </Box>
    </Box>
  );
};

// --- TopUsersCard ---

export const TopUsersCardMockup = ({
  userCount = 5,
  ...props
}: { userCount?: number } & BoxProps) => {
  return (
    <Box
      {...props}
      sx={{
        borderRadius: 1,
        border: '1px solid',
        borderColor: getLightGrey,
        bgcolor: 'background.paper',
        boxShadow: '0 2px 6px 0 rgba(0,0,0,0.08)',
        ...props.sx,
      }}
    >
      <Box sx={{ display: 'flex', p: 1.5, alignItems: 'center' }}>
        <Box>
          <Word
            sx={{
              height: 10,
              borderRadius: 3,
              bgcolor: getLightGrey,
              m: 0,
              width: 40,
            }}
          />
          <Word sx={{ mt: 0.5, mb: 0 }} />
        </Box>
        <Circle sx={{ ml: 'auto', fontSize: 24 }} />
      </Box>
      <Divider />
      <List>
        {[...Array(userCount).fill(undefined)].map((_, index) => (
          <ListItem key={index}>
            <ListItemAvatar>
              <Avatar />
            </ListItemAvatar>
            <Word />
          </ListItem>
        ))}
      </List>
    </Box>
  );
};

// --- UserActiveCard ---

export const UserActiveCardMockup = (props: BoxProps) => {
  return (
    <Box
      {...props}
      sx={{
        borderRadius: 1,
        border: '1px solid',
        borderColor: getLightGrey,
        bgcolor: 'background.paper',
        p: 1.5,
        boxShadow: '0 2px 6px 0 rgba(0,0,0,0.08)',
        ...props.sx,
      }}
    >
      <Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
        <Word sx={{ bgcolor: getLightGrey }} />
        <Circle sx={{ fontSize: 20, ml: 'auto' }} />
      </Box>
      <br />
      <Box sx={{ display: 'flex', justifyContent: 'space-around' }}>
        <Box>
          <Box sx={{ display: 'flex', alignItems: 'center' }}>
            <Square sx={{ fontSize: 16, bgcolor: 'primary.main', mr: 1 }} />
            <Typography variant="h4">
              <b>92%</b>
            </Typography>
          </Box>
          <Word
            sx={{
              bgcolor: getLightGrey,
              ml: 3,
              height: 12,
              borderRadius: 2,
              width: 48,
            }}
          />
        </Box>
        <Box>
          <Box sx={{ display: 'flex', alignItems: 'center' }}>
            <Square sx={{ fontSize: 16, bgcolor: 'error.main', mr: 1 }} />
            <Typography variant="h4">
              <b>8%</b>
            </Typography>
          </Box>
          <Word
            sx={{
              bgcolor: getLightGrey,
              ml: 3,
              height: 12,
              borderRadius: 2,
              width: 48,
            }}
          />
        </Box>
      </Box>
      <br />
      <LinearProgress
        variant="determinate"
        value={92}
        sx={{
          height: 10,
          borderRadius: 3,
          bgcolor: getLightGrey,
          '& > .MuiLinearProgress-bar': {
            borderRadius: 3,
          },
        }}
      />
      <Box
        sx={{
          height: 16,
        }}
      />
      <LinearProgress
        variant="determinate"
        value={8}
        sx={{
          height: 10,
          borderRadius: 3,
          bgcolor: getLightGrey,
          '& > .MuiLinearProgress-bar': {
            borderRadius: 3,
            bgcolor: 'error.main',
          },
        }}
      />
      <br />
    </Box>
  );
};
layout/app-analytics/mockups/index.ts
export * from './utils';
export * from './shapes';
export * from './typography';
export * from './dashboard';
export * from './tabs';
layout/app-analytics/mockups/shapes.tsx
'use client';
import React from 'react';

import Box, { BoxProps } from '@mui/material/Box';

import { getBaseGrey } from './utils';

export const Circle = (props: BoxProps) => (
  <Box
    {...props}
    sx={{
      width: '1em',
      height: '1em',
      fontSize: '40px',
      backgroundColor: getBaseGrey,
      borderRadius: 10,
      flexShrink: 0,
      ...props.sx,
    }}
  />
);

export const Pill = (props: BoxProps) => (
  <Box
    {...props}
    sx={{
      width: 56,
      height: 24,
      backgroundColor: getBaseGrey,
      borderRadius: 5,
      ...props.sx,
    }}
  />
);

export const Square = (props: BoxProps) => (
  <Box
    {...props}
    sx={{
      width: '1em',
      height: '1em',
      fontSize: '40px',
      backgroundColor: getBaseGrey,
      borderRadius: 1,
      flexShrink: 0,
      ...props.sx,
    }}
  />
);
layout/app-analytics/mockups/tabs.tsx
'use client';
import Tabs from '@mui/material/Tabs';
import { styled } from '@mui/material/styles';

export const UnderlineTabs = styled(Tabs)(({ theme }) => ({
  '&:before': {
    content: '""',
    display: 'block',
    height: '1px',
    backgroundColor: theme.palette.grey[200],
    left: 0,
    bottom: 0,
    width: '100%',
    position: 'absolute',
  },
  '& .MuiTab-root': {
    textTransform: 'none',
    paddingLeft: 0,
    paddingRight: 0,
    marginLeft: theme.spacing(2),
    marginRight: theme.spacing(2),
    '&:first-of-type': {
      marginLeft: 0,
    },
    '&:last-of-type': {
      marginRight: 0,
    },
  },
}));
layout/app-analytics/mockups/typography.tsx
'use client';
import React from 'react';

import Box, { BoxProps } from '@mui/material/Box';
import { SxProps, Theme } from '@mui/material/styles';

import { getBaseGrey } from './utils';

const createTypography = (generateSx: () => BoxProps['sx']) => {
  const Component = ({
    cached,
    sx = [],
    ...props
  }: { cached?: boolean } & BoxProps) => {
    const ref = React.useRef(generateSx());
    const resolvedSx: SxProps<Theme> =
      (cached ? ref.current : generateSx()) || [];
    return (
      <Box
        {...props}
        sx={[
          {
            height: 16,
            borderRadius: 1,
            bgcolor: getBaseGrey,
          },
          ...(Array.isArray(resolvedSx) ? resolvedSx : [resolvedSx]),
          ...(Array.isArray(sx) ? sx : [sx]),
        ]}
      />
    );
  };
  return Component;
};

export const H1 = createTypography(() => ({
  height: 40,
  width: '16ch',
  maxWidth: '100%',
  mt: 3,
  mb: 1,
}));

export const Text = createTypography(() => ({
  height: 16,
  mt: 1.5,
  mb: 1.5,
  width: '100%',
  maxWidth: '100%',
}));

export const Word = createTypography(() => ({
  height: 16,
  width: '10ch',
  maxWidth: '100%',
}));
layout/app-analytics/mockups/utils.ts
'use client';
import { Theme } from '@mui/material/styles';

export const getBaseGrey = (theme: Theme): string =>
  theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.12)' : 'grey.300';

export const getLightGrey = (theme: Theme): string =>
  theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.09)' : 'grey.200';

export const getLightestGrey = (theme: Theme): string =>
  theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.07)' : 'grey.50';

let seed = 0;
function seededRandom() {
  seed = (seed * 9301 + 49297) % 233280;
  return seed / 233280;
}

export const randomBetween = (min: number, max: number, unit?: string) => {
  const result = Math.floor(seededRandom() * (max - min)) + min;
  if (unit) return `${result}${unit}`;
  return result;
};
layout/app-analytics/index.ts
export * from './app-analytics';
export * from './dashboard';
export * from './index';
export * from './shapes';
export * from './tabs';
export * from './typography';
export * from './utils';

More from mui-treasury

Browse all components in this library →