Bulk deletion of pages
This commit is contained in:
parent
24e2527c1b
commit
08e1467bb1
@ -2,6 +2,7 @@ export type Nullable<T> = { [P in keyof T]: T[P] | null }
|
|||||||
|
|
||||||
export type Subset<T> = { [K in keyof T]?: Subset<T[K]> }
|
export type Subset<T> = { [K in keyof T]?: Subset<T[K]> }
|
||||||
export type Dictionary<T> = { [key: string]: T };
|
export type Dictionary<T> = { [key: string]: T };
|
||||||
|
export type OneOrMany<T> = T | T[];
|
||||||
|
|
||||||
export type Index = string | symbol | number;
|
export type Index = string | symbol | number;
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ export function throttle<TArgs extends any[]>(decorated: (...args: TArgs) => voi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encapsulate<T>(value: T|T[]): T[] {
|
export function encapsulate<T>(value: OneOrMany<T>): T[] {
|
||||||
if (value instanceof Array) {
|
if (value instanceof Array) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -35,7 +36,7 @@ export function encapsulate<T>(value: T|T[]): T[] {
|
|||||||
return [ value ];
|
return [ value ];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function one<T>(value: T|T[]): T {
|
export function one<T>(value: OneOrMany<T>): T {
|
||||||
if (value instanceof Array) {
|
if (value instanceof Array) {
|
||||||
return value[0];
|
return value[0];
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import pageDtoTransformer, { PageDTO } from "@/api/dto/page";
|
|||||||
import { axios } from "@/api";
|
import { axios } from "@/api";
|
||||||
import { STATIC_PAGE_ENDPOINT } from "@/api/page";
|
import { STATIC_PAGE_ENDPOINT } from "@/api/page";
|
||||||
import { prepare } from "@/routing";
|
import { prepare } from "@/routing";
|
||||||
|
import { encapsulate, OneOrMany } from "@/helpers";
|
||||||
|
|
||||||
const STATIC_PAGE_INDEX_ENDPOINT = "/staticPage";
|
const STATIC_PAGE_INDEX_ENDPOINT = "/staticPage";
|
||||||
|
|
||||||
@ -13,8 +14,10 @@ export async function all(): Promise<Page[]> {
|
|||||||
return response.data.map(dto => pageDtoTransformer.transform(dto));
|
return response.data.map(dto => pageDtoTransformer.transform(dto));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remove(page: Pick<Page, "slug">): Promise<void> {
|
export async function remove(page: OneOrMany<Pick<Page, "slug">>): Promise<void> {
|
||||||
await axios.delete(prepare(STATIC_PAGE_ENDPOINT, { slug: page.slug }));
|
const pages = encapsulate(page);
|
||||||
|
|
||||||
|
await Promise.all(pages.map(page => axios.delete(prepare(STATIC_PAGE_ENDPOINT, { slug: page.slug }))));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(page: Page): Promise<Page> {
|
export async function save(page: Page): Promise<Page> {
|
||||||
|
@ -9,7 +9,7 @@ import { Async } from "@/components/async";
|
|||||||
import MaterialTable, { Action, Column } from "material-table";
|
import MaterialTable, { Action, Column } from "material-table";
|
||||||
import { default as StaticPage } from "@/data/page";
|
import { default as StaticPage } from "@/data/page";
|
||||||
import { Delete, FileFind, Pencil, Refresh } from "mdi-material-ui";
|
import { Delete, FileFind, Pencil, Refresh } from "mdi-material-ui";
|
||||||
import { encapsulate, one } from "@/helpers";
|
import { encapsulate, one, OneOrMany } from "@/helpers";
|
||||||
import { Actions } from "@/components";
|
import { Actions } from "@/components";
|
||||||
import { useSpacing } from "@/styles";
|
import { useSpacing } from "@/styles";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
@ -21,6 +21,7 @@ import { Confirm } from "@/components/confirm";
|
|||||||
export const StaticPageManagement = () => {
|
export const StaticPageManagement = () => {
|
||||||
const { t } = useTranslation("management");
|
const { t } = useTranslation("management");
|
||||||
const [ result, setPagesPromise ] = useAsyncState<StaticPage[]>();
|
const [ result, setPagesPromise ] = useAsyncState<StaticPage[]>();
|
||||||
|
const [ selected, setSelected ] = useState<StaticPage[]>([]);
|
||||||
const spacing = useSpacing(2);
|
const spacing = useSpacing(2);
|
||||||
|
|
||||||
const updatePageList = () => {
|
const updatePageList = () => {
|
||||||
@ -67,20 +68,28 @@ export const StaticPageManagement = () => {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
const DeleteStaticPageAction = ({ page }: { page: StaticPage }) => {
|
const DeleteStaticPageAction = ({ page, children }: { page: OneOrMany<StaticPage>, children?: (action: any) => React.ReactNode }) => {
|
||||||
const handlePageDeletion = async () => {
|
const handlePageDeletion = async () => {
|
||||||
await api.page.remove(page);
|
await api.page.remove(page);
|
||||||
updatePageList();
|
updatePageList();
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmation = <>
|
const confirmation = <>
|
||||||
<Trans i18nKey="page.confirm.delete">
|
{ !Array.isArray(page)
|
||||||
Czy na pewno chcesz usunąć stronę <strong>{ page.title.pl }</strong>?
|
? <Trans i18nKey="page.confirm.delete">
|
||||||
</Trans>
|
Czy na pewno chcesz usunąć stronę <strong>{ page.title.pl }</strong>?
|
||||||
|
</Trans>
|
||||||
|
: <>
|
||||||
|
{ t("page.confirm.bulk-delete") }
|
||||||
|
<ul>
|
||||||
|
{ page.map(page => <li key={ page.slug }>{ page.title.pl }</li>) }
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
}
|
||||||
</>;
|
</>;
|
||||||
|
|
||||||
return <Confirm onConfirm={ handlePageDeletion } content={ confirmation }>
|
return <Confirm onConfirm={ handlePageDeletion } content={ confirmation }>
|
||||||
{ action => <Tooltip title={ t("actions.delete") as string }><IconButton onClick={ action }><Delete /></IconButton></Tooltip> }
|
{ action => children ? children(action) : <Tooltip title={ t("actions.delete") as string }><IconButton onClick={ action }><Delete /></IconButton></Tooltip> }
|
||||||
</Confirm>;
|
</Confirm>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,12 +152,19 @@ export const StaticPageManagement = () => {
|
|||||||
<CreateStaticPageAction />
|
<CreateStaticPageAction />
|
||||||
<Button onClick={ updatePageList } startIcon={ <Refresh /> }>{ t("refresh") }</Button>
|
<Button onClick={ updatePageList } startIcon={ <Refresh /> }>{ t("refresh") }</Button>
|
||||||
</Actions>
|
</Actions>
|
||||||
|
{ selected.length > 0 && <Actions>
|
||||||
|
<DeleteStaticPageAction page={ selected }>
|
||||||
|
{ action => <Button startIcon={ <Delete /> } onClick={ action }>{ t("actions.delete") }</Button> }
|
||||||
|
</DeleteStaticPageAction>
|
||||||
|
</Actions> }
|
||||||
<Async async={ result } keepValue>{
|
<Async async={ result } keepValue>{
|
||||||
pages => <MaterialTable
|
pages => <MaterialTable
|
||||||
title={ <div style={{ display: "flex", alignItems: "center" }}>{ t("page.index.title") } { result.isLoading && <CircularProgress size="1.5rem" style={{ marginLeft: "1rem" }}/> }</div> }
|
title={ <div style={{ display: "flex", alignItems: "center" }}>{ t("page.index.title") } { result.isLoading && <CircularProgress size="1.5rem" style={{ marginLeft: "1rem" }}/> }</div> }
|
||||||
columns={ columns }
|
columns={ columns }
|
||||||
data={ pages }
|
data={ pages }
|
||||||
detailPanel={ page => <PagePreview page={ page } /> }
|
detailPanel={ page => <PagePreview page={ page } /> }
|
||||||
|
onSelectionChange={ pages => setSelected(pages) }
|
||||||
|
options={{ selection: true }}
|
||||||
/>
|
/>
|
||||||
}</Async>
|
}</Async>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -31,3 +31,5 @@ page:
|
|||||||
title: Utwórz stronę statyczną
|
title: Utwórz stronę statyczną
|
||||||
edit:
|
edit:
|
||||||
title: Zmień stronę statyczną
|
title: Zmień stronę statyczną
|
||||||
|
confirm:
|
||||||
|
bulk-delete: Czy na pewno chcesz usunąć wszystkie wybrane strony?
|
||||||
|
Loading…
Reference in New Issue
Block a user