bazarr/frontend/src/components/MassEditor.tsx

122 lines
3.5 KiB
TypeScript

import { useIsAnyMutationRunning, useLanguageProfiles } from "@/apis/hooks";
import { GetItemId } from "@/utilities";
import { faCheck, faUndo } from "@fortawesome/free-solid-svg-icons";
import { uniqBy } from "lodash";
import { useCallback, useMemo, useState } from "react";
import { Container, Dropdown, Row } from "react-bootstrap";
import { UseMutationResult } from "react-query";
import { useNavigate } from "react-router-dom";
import { Column, useRowSelect } from "react-table";
import { ContentHeader, SimpleTable } from ".";
import { useCustomSelection } from "./tables/plugins";
interface MassEditorProps<T extends Item.Base = Item.Base> {
columns: Column<T>[];
data: T[];
mutation: UseMutationResult<void, unknown, FormType.ModifyItem>;
}
function MassEditor<T extends Item.Base>(props: MassEditorProps<T>) {
const { columns, data: raw, mutation } = props;
const [selections, setSelections] = useState<T[]>([]);
const [dirties, setDirties] = useState<T[]>([]);
const hasTask = useIsAnyMutationRunning();
const { data: profiles } = useLanguageProfiles();
const navigate = useNavigate();
const onEnded = useCallback(() => navigate(".."), [navigate]);
const data = useMemo(
() => uniqBy([...dirties, ...(raw ?? [])], GetItemId),
[dirties, raw]
);
const profileOptions = useMemo(() => {
const items: JSX.Element[] = [];
if (profiles) {
items.push(
<Dropdown.Item key="clear-profile">Clear Profile</Dropdown.Item>
);
items.push(<Dropdown.Divider key="dropdown-divider"></Dropdown.Divider>);
items.push(
...profiles.map((v) => (
<Dropdown.Item key={v.profileId} eventKey={v.profileId.toString()}>
{v.name}
</Dropdown.Item>
))
);
}
return items;
}, [profiles]);
const { mutateAsync } = mutation;
const save = useCallback(() => {
const form: FormType.ModifyItem = {
id: [],
profileid: [],
};
dirties.forEach((v) => {
const id = GetItemId(v);
if (id) {
form.id.push(id);
form.profileid.push(v.profileId);
}
});
return mutateAsync(form);
}, [dirties, mutateAsync]);
const setProfiles = useCallback(
(key: Nullable<string>) => {
const id = key ? parseInt(key) : null;
const newItems = selections.map((v) => ({ ...v, profileId: id }));
setDirties((dirty) => {
return uniqBy([...newItems, ...dirty], GetItemId);
});
},
[selections]
);
return (
<Container fluid>
<ContentHeader scroll={false}>
<ContentHeader.Group pos="start">
<Dropdown onSelect={setProfiles}>
<Dropdown.Toggle disabled={selections.length === 0} variant="light">
Change Profile
</Dropdown.Toggle>
<Dropdown.Menu>{profileOptions}</Dropdown.Menu>
</Dropdown>
</ContentHeader.Group>
<ContentHeader.Group pos="end">
<ContentHeader.Button icon={faUndo} onClick={onEnded}>
Cancel
</ContentHeader.Button>
<ContentHeader.AsyncButton
icon={faCheck}
disabled={dirties.length === 0 || hasTask}
promise={save}
onSuccess={onEnded}
>
Save
</ContentHeader.AsyncButton>
</ContentHeader.Group>
</ContentHeader>
<Row>
<SimpleTable
columns={columns}
data={data}
onSelect={setSelections}
plugins={[useRowSelect, useCustomSelection]}
></SimpleTable>
</Row>
</Container>
);
}
export default MassEditor;