feat(flows): add flow filters
This commit is contained in:
121
packages/web/src/components/FlowFilters/index.jsx
Normal file
121
packages/web/src/components/FlowFilters/index.jsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Select,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
Collapse,
|
||||
} from '@mui/material';
|
||||
import FilterAltIcon from '@mui/icons-material/FilterAlt';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
|
||||
import Can from 'components/Can';
|
||||
import useCurrentUserRuleConditions from 'hooks/useCurrentUserRuleConditions';
|
||||
import useFlowFilters from 'hooks/useFlowFilters';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
|
||||
export default function FlowFilters({ onFilterChange }) {
|
||||
const theme = useTheme();
|
||||
const formatMessage = useFormatMessage();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const currentUserRuleConditions = useCurrentUserRuleConditions();
|
||||
|
||||
const { filters, filterByStatus, filterByOwnership } = useFlowFilters();
|
||||
|
||||
const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false);
|
||||
|
||||
const currentUserReadFlowsConditions = currentUserRuleConditions(
|
||||
'read',
|
||||
'Flow',
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* Mobile: Toggle Button for Filters */}
|
||||
{isMobile && (
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<FilterAltIcon />}
|
||||
onClick={() => setMobileFiltersOpen((prev) => !prev)}
|
||||
fullWidth
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
{mobileFiltersOpen
|
||||
? formatMessage('flowFilters.hideFilters')
|
||||
: formatMessage('flowFilters.showFilters')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Filters Box (Always Visible on Large Screens) */}
|
||||
<Collapse in={!isMobile || mobileFiltersOpen}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: { xs: 'column', md: 'row' },
|
||||
gap: 2,
|
||||
mb: 2,
|
||||
alignItems: { md: 'center' },
|
||||
}}
|
||||
>
|
||||
{/* User Flows Filter */}
|
||||
{currentUserReadFlowsConditions &&
|
||||
!currentUserReadFlowsConditions?.isCreator && (
|
||||
<FormControl
|
||||
fullWidth
|
||||
sx={{ maxWidth: { md: 200 } }}
|
||||
variant="outlined"
|
||||
>
|
||||
<InputLabel shrink>
|
||||
{formatMessage('flowFilters.flowsFilterLabel')}
|
||||
</InputLabel>
|
||||
|
||||
<Select
|
||||
label={formatMessage('flowFilters.flowsFilterLabel')}
|
||||
value={filters.onlyOwnedFlows}
|
||||
displayEmpty
|
||||
onChange={(e) => filterByOwnership(e.target.value)}
|
||||
key={filters.onlyOwnedFlows}
|
||||
>
|
||||
<MenuItem value={undefined}>
|
||||
{formatMessage('flowFilters.flowsFilterAllOption')}
|
||||
</MenuItem>
|
||||
<MenuItem value={true}>
|
||||
{formatMessage('flowFilters.flowsFilterOnlyMineOption')}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{/* Status Filter */}
|
||||
<FormControl fullWidth sx={{ maxWidth: { md: 200 } }}>
|
||||
<InputLabel shrink>
|
||||
{formatMessage('flowFilters.statusFilterLabel')}
|
||||
</InputLabel>
|
||||
|
||||
<Select
|
||||
label={formatMessage('flowFilters.statusFilterLabel')}
|
||||
value={filters.status}
|
||||
displayEmpty
|
||||
onChange={(e) => filterByStatus(e.target.value)}
|
||||
key={filters.status}
|
||||
>
|
||||
<MenuItem value={undefined}>
|
||||
{formatMessage('flowFilters.statusFilterAnyOption')}
|
||||
</MenuItem>
|
||||
<MenuItem value="published">
|
||||
{formatMessage('flowFilters.statusFilterPublishedOption')}
|
||||
</MenuItem>
|
||||
<MenuItem value="draft">
|
||||
{formatMessage('flowFilters.statusFilterDraftOption')}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { Link, useSearchParams, useNavigate } from 'react-router-dom';
|
||||
import Box from '@mui/material/Box';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Card from '@mui/material/Card';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
@@ -23,6 +24,8 @@ import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useFolders from 'hooks/useFolders';
|
||||
import useDeleteFolder from 'hooks/useDeleteFolder';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import objectifyUrlSearchParams from 'helpers/objectifyUrlSearchParams';
|
||||
import useFlowFilters from 'hooks/useFlowFilters';
|
||||
|
||||
import { ListItemIcon } from './style';
|
||||
|
||||
@@ -35,6 +38,8 @@ export default function Folders() {
|
||||
const { data: folders } = useFolders();
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
|
||||
const { enhanceExistingSearchParams } = useFlowFilters();
|
||||
|
||||
const {
|
||||
mutateAsync: deleteFolder,
|
||||
error: deleteFolderError,
|
||||
@@ -53,8 +58,8 @@ export default function Folders() {
|
||||
(folder) => folder.id === selectedFolderId,
|
||||
);
|
||||
|
||||
const allFlowsFolder = new URLSearchParams().toString();
|
||||
const unassignedFlowsFolder = new URLSearchParams('folderId=null').toString();
|
||||
const allFlowsFolder = enhanceExistingSearchParams('folderId', undefined);
|
||||
const unassignedFlowsFolder = enhanceExistingSearchParams('folderId', 'null');
|
||||
|
||||
const allFlowsFolderSelected = selectedFolderId === null;
|
||||
const unassignedFlowsFolderSelected = selectedFolderId === 'null'; // intendedly stringified
|
||||
@@ -86,9 +91,7 @@ export default function Folders() {
|
||||
};
|
||||
|
||||
const getFolderSearchParams = (folderId) => {
|
||||
const searchParams = new URLSearchParams(`folderId=${folderId}`);
|
||||
|
||||
return searchParams.toString();
|
||||
return enhanceExistingSearchParams('folderId', folderId).toString();
|
||||
};
|
||||
|
||||
const generateFolderItem = (folder) => {
|
||||
@@ -148,10 +151,7 @@ export default function Folders() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
component={Card}
|
||||
// sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}
|
||||
>
|
||||
<Box component={Card}>
|
||||
<List component="nav" aria-label="static folders">
|
||||
<ListItemButton
|
||||
component={Link}
|
||||
|
||||
Reference in New Issue
Block a user