feishin/src/renderer/features/playlists/components/playlist-query-builder.tsx

347 lines
8.7 KiB
TypeScript
Raw Normal View History

2023-01-04 04:09:24 -08:00
import { useState, useImperativeHandle, forwardRef } from 'react';
2023-01-04 15:54:25 -08:00
import { Flex, Group, ScrollArea } from '@mantine/core';
2023-01-04 04:09:24 -08:00
import clone from 'lodash/clone';
import get from 'lodash/get';
2023-01-04 15:54:25 -08:00
import setWith from 'lodash/setWith';
2023-01-04 04:09:24 -08:00
import { nanoid } from 'nanoid';
import { NDSongQueryFields } from '/@/renderer/api/navidrome.types';
2023-01-04 15:54:25 -08:00
import { Button, DropdownMenu, NumberInput, QueryBuilder } from '/@/renderer/components';
import {
convertNDQueryToQueryGroup,
convertQueryGroupToNDQuery,
} from '/@/renderer/features/playlists/utils';
import { QueryBuilderGroup, QueryBuilderRule } from '/@/renderer/types';
import { RiMore2Fill } from 'react-icons/ri';
2023-01-04 04:09:24 -08:00
type AddArgs = {
groupIndex: number[];
level: number;
};
type DeleteArgs = {
groupIndex: number[];
level: number;
uniqueId: string;
};
2023-01-04 15:54:25 -08:00
interface PlaylistQueryBuilderProps {
onSave: (parsedFilter: any) => void;
onSaveAs: (parsedFilter: any) => void;
query: any;
}
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
export const PlaylistQueryBuilder = forwardRef(
({ query, onSave, onSaveAs }: PlaylistQueryBuilderProps, ref) => {
const [filters, setFilters] = useState<any>(
convertNDQueryToQueryGroup(query) || {
2023-01-04 04:09:24 -08:00
all: [],
2023-01-04 15:54:25 -08:00
},
);
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
useImperativeHandle(ref, () => ({
reset() {
setFilters({
all: [],
});
},
}));
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const setFilterHandler = (newFilters: QueryBuilderGroup) => {
setFilters(newFilters);
// onSave(newFilters);
2023-01-04 04:09:24 -08:00
};
2023-01-04 15:54:25 -08:00
const handleSave = () => {
onSave(convertQueryGroupToNDQuery(filters));
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const handleSaveAs = () => {
onSaveAs(convertQueryGroupToNDQuery(filters));
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const handleAddRuleGroup = (args: AddArgs) => {
const { level, groupIndex } = args;
const filtersCopy = clone(filters);
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const getPath = (level: number) => {
if (level === 0) return 'group';
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const str = [];
for (const index of groupIndex) {
str.push(`group[${index}]`);
2023-01-04 04:09:24 -08:00
}
2023-01-04 15:54:25 -08:00
return `${str.join('.')}.group`;
};
const path = getPath(level);
const updatedFilters = setWith(
filtersCopy,
path,
[
...get(filtersCopy, path),
{
group: [],
rules: [
{
field: '',
operator: '',
uniqueId: nanoid(),
value: '',
},
],
type: 'any',
uniqueId: nanoid(),
},
],
clone,
);
setFilterHandler(updatedFilters);
2023-01-04 04:09:24 -08:00
};
2023-01-04 15:54:25 -08:00
const handleDeleteRuleGroup = (args: DeleteArgs) => {
const { uniqueId, level, groupIndex } = args;
const filtersCopy = clone(filters);
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const getPath = (level: number) => {
if (level === 0) return 'group';
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const str = [];
for (let i = 0; i < groupIndex.length; i += 1) {
if (i !== groupIndex.length - 1) {
str.push(`group[${groupIndex[i]}]`);
} else {
str.push(`group`);
}
}
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
return `${str.join('.')}`;
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const path = getPath(level);
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const updatedFilters = setWith(
filtersCopy,
path,
[
...get(filtersCopy, path).filter(
(group: QueryBuilderGroup) => group.uniqueId !== uniqueId,
),
],
clone,
);
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
setFilterHandler(updatedFilters);
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const getRulePath = (level: number, groupIndex: number[]) => {
if (level === 0) return 'rules';
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const str = [];
for (const index of groupIndex) {
str.push(`group[${index}]`);
}
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
return `${str.join('.')}.rules`;
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const handleAddRule = (args: AddArgs) => {
const { level, groupIndex } = args;
const filtersCopy = clone(filters);
const path = getRulePath(level, groupIndex);
const updatedFilters = setWith(
filtersCopy,
path,
[
...get(filtersCopy, path),
{
field: 'title',
operator: 'contains',
uniqueId: nanoid(),
value: null,
},
],
clone,
);
setFilterHandler(updatedFilters);
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const handleDeleteRule = (args: DeleteArgs) => {
const { uniqueId, level, groupIndex } = args;
const filtersCopy = clone(filters);
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const path = getRulePath(level, groupIndex);
const updatedFilters = setWith(
filtersCopy,
path,
get(filtersCopy, path).filter((rule: QueryBuilderRule) => rule.uniqueId !== uniqueId),
clone,
);
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
setFilterHandler(updatedFilters);
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const handleChangeField = (args: any) => {
const { uniqueId, level, groupIndex, value } = args;
const filtersCopy = clone(filters);
const path = getRulePath(level, groupIndex);
const updatedFilters = setWith(
filtersCopy,
path,
get(filtersCopy, path).map((rule: QueryBuilderGroup) => {
if (rule.uniqueId !== uniqueId) return rule;
// const defaultOperator = FILTER_OPTIONS_DATA.find(
// (option) => option.value === value,
// )?.default;
return {
...rule,
field: value,
operator: '',
value: '',
};
}),
clone,
);
setFilterHandler(updatedFilters);
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const handleChangeType = (args: any) => {
const { level, groupIndex, value } = args;
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const filtersCopy = clone(filters);
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
if (level === 0) {
return setFilterHandler({ ...filtersCopy, type: value });
2023-01-04 04:09:24 -08:00
}
2023-01-04 15:54:25 -08:00
const getTypePath = () => {
const str = [];
for (let i = 0; i < groupIndex.length; i += 1) {
str.push(`group[${groupIndex[i]}]`);
}
return `${str.join('.')}`;
};
const path = getTypePath();
const updatedFilters = setWith(
filtersCopy,
path,
{
...get(filtersCopy, path),
type: value,
},
clone,
);
return setFilterHandler(updatedFilters);
2023-01-04 04:09:24 -08:00
};
2023-01-04 15:54:25 -08:00
const handleChangeOperator = (args: any) => {
const { uniqueId, level, groupIndex, value } = args;
const filtersCopy = clone(filters);
const path = getRulePath(level, groupIndex);
const updatedFilters = setWith(
filtersCopy,
path,
get(filtersCopy, path).map((rule: QueryBuilderRule) => {
if (rule.uniqueId !== uniqueId) return rule;
return {
...rule,
operator: value,
};
}),
clone,
);
setFilterHandler(updatedFilters);
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
const handleChangeValue = (args: any) => {
const { uniqueId, level, groupIndex, value } = args;
const filtersCopy = clone(filters);
const path = getRulePath(level, groupIndex);
console.log('path', path);
const updatedFilters = setWith(
filtersCopy,
path,
get(filtersCopy, path).map((rule: QueryBuilderRule) => {
if (rule.uniqueId !== uniqueId) return rule;
return {
...rule,
value,
};
}),
clone,
);
setFilterHandler(updatedFilters);
};
2023-01-04 04:09:24 -08:00
2023-01-04 15:54:25 -08:00
return (
<Flex
direction="column"
h="100%"
justify="space-between"
>
<ScrollArea h="100%">
<QueryBuilder
data={filters}
filters={NDSongQueryFields}
groupIndex={[]}
level={0}
uniqueId={filters.uniqueId}
onAddRule={handleAddRule}
onAddRuleGroup={handleAddRuleGroup}
onChangeField={handleChangeField}
onChangeOperator={handleChangeOperator}
onChangeType={handleChangeType}
onChangeValue={handleChangeValue}
onDeleteRule={handleDeleteRule}
onDeleteRuleGroup={handleDeleteRuleGroup}
/>
</ScrollArea>
<Group
align="flex-end"
p="1rem 1rem 0"
position="apart"
>
<NumberInput
label="Limit to"
width={75}
/>
<Group>
<Button
variant="filled"
onClick={handleSave}
>
Save
</Button>
<DropdownMenu position="bottom-end">
<DropdownMenu.Target>
<Button
p="0.5em"
variant="default"
>
<RiMore2Fill size={15} />
</Button>
</DropdownMenu.Target>
<DropdownMenu.Dropdown>
<DropdownMenu.Item onClick={handleSaveAs}>Save as</DropdownMenu.Item>
</DropdownMenu.Dropdown>
</DropdownMenu>
</Group>
</Group>
</Flex>
2023-01-04 04:09:24 -08:00
);
2023-01-04 15:54:25 -08:00
},
);