ui(admin): add allocation delete button

This commit is contained in:
Matthew Penner 2021-10-03 16:28:58 -06:00
parent 6df90a12d8
commit b070efce98
No known key found for this signature in database
GPG key ID: BAB67850901908A8
7 changed files with 100 additions and 8 deletions

View file

@ -0,0 +1,9 @@
import http from '@/api/http';
export default (nodeId: number, allocationId: number): Promise<void> => {
return new Promise((resolve, reject) => {
http.delete(`/api/application/nodes/${nodeId}/allocations/${allocationId}`)
.then(() => resolve())
.catch(reject);
});
};

View file

@ -12,7 +12,7 @@ export interface Filters {
export const Context = createContext<Filters>();
export default (id: string | number, include: string[] = []) => {
export default (id: number, include: string[] = []) => {
const { page, filters, sort, sortDirection } = useContext(Context);
const params = {};

View file

@ -13,12 +13,12 @@ export default () => {
<>
<div css={tw`w-full grid grid-cols-12 gap-x-8`}>
<div css={tw`w-full flex col-span-8`}>
<AllocationTable nodeId={match.params.id}/>
<AllocationTable nodeId={Number(match.params.id)}/>
</div>
<div css={tw`w-full flex col-span-4`}>
<AdminBox icon={faNetworkWired} title={'Allocations'} css={tw`h-auto w-full`}>
<CreateAllocationForm nodeId={match.params.id}/>
<CreateAllocationForm nodeId={Number(match.params.id)}/>
</AdminBox>
</div>
</div>

View file

@ -5,6 +5,7 @@ import tw from 'twin.macro';
import getAllocations, { Context as AllocationsContext, Filters } from '@/api/admin/nodes/allocations/getAllocations';
import AdminCheckbox from '@/components/admin/AdminCheckbox';
import AdminTable, { ContentWrapper, Loading, NoItems, Pagination, TableBody, TableHead, TableHeader, useTableHooks } from '@/components/admin/AdminTable';
import DeleteAllocationButton from '@/components/admin/nodes/allocations/DeleteAllocationButton';
import CopyOnClick from '@/components/elements/CopyOnClick';
import useFlash from '@/plugins/useFlash';
@ -29,7 +30,7 @@ function RowCheckbox ({ id }: { id: number }) {
}
interface Props {
nodeId: string;
nodeId: number;
filters?: Filters;
}
@ -37,7 +38,7 @@ function AllocationsTable ({ nodeId, filters }: Props) {
const { clearFlashes, clearAndAddHttpError } = useFlash();
const { page, setPage, setFilters, sort, setSort, sortDirection } = useContext(AllocationsContext);
const { data: allocations, error, isValidating } = getAllocations(nodeId, [ 'server' ]);
const { data: allocations, error, isValidating, mutate } = getAllocations(nodeId, [ 'server' ]);
const length = allocations?.items?.length || 0;
@ -49,7 +50,7 @@ function AllocationsTable ({ nodeId, filters }: Props) {
};
const onSearch = (query: string): Promise<void> => {
return new Promise((resolve) => {
return new Promise(resolve => {
if (query.length < 2) {
setFilters(filters || null);
} else {
@ -87,6 +88,7 @@ function AllocationsTable ({ nodeId, filters }: Props) {
<TableHeader name={'Alias'}/>
<TableHeader name={'Port'} direction={sort === 'port' ? (sortDirection ? 1 : 2) : null} onClick={() => setSort('port')}/>
<TableHeader name={'Assigned To'}/>
<TableHeader/>
</TableHead>
<TableBody>
@ -128,6 +130,24 @@ function AllocationsTable ({ nodeId, filters }: Props) {
:
<td/>
}
<td>
<DeleteAllocationButton
nodeId={nodeId}
allocationId={allocation.id}
onDeleted={async () => {
await mutate(allocations => ({
pagination: allocations!.pagination,
items: allocations!.items.filter(a => a.id === allocation.id),
}));
// Go back a page if no more items will exist on the current page.
if (allocations?.items.length - 1 % 10 === 0) {
setPage(p => p - 1);
}
}}
/>
</td>
</tr>
))
}

View file

@ -19,7 +19,7 @@ const distinct = (value: any, index: any, self: any) => {
return self.indexOf(value) === index;
};
function CreateAllocationForm ({ nodeId }: { nodeId: string | number }) {
function CreateAllocationForm ({ nodeId }: { nodeId: number }) {
const [ ips, setIPs ] = useState<Option[]>([]);
const [ ports ] = useState<Option[]>([]);

View file

@ -0,0 +1,62 @@
import React, { useState } from 'react';
import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
import ConfirmationModal from '@/components/elements/ConfirmationModal';
import deleteAllocation from '@/api/admin/nodes/allocations/deleteAllocation';
interface Props {
nodeId: number;
allocationId: number;
onDeleted?: () => void;
}
export default ({ nodeId, allocationId, onDeleted }: Props) => {
const [ visible, setVisible ] = useState(false);
const [ loading, setLoading ] = useState(false);
const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
const onDelete = () => {
setLoading(true);
clearFlashes('allocation');
deleteAllocation(nodeId, allocationId)
.then(() => {
setLoading(false);
setVisible(false);
if (onDeleted !== undefined) {
onDeleted();
}
})
.catch(error => {
console.error(error);
clearAndAddHttpError({ key: 'allocation', error });
setLoading(false);
setVisible(false);
});
};
return (
<>
<ConfirmationModal
visible={visible}
title={'Delete allocation?'}
buttonText={'Yes, delete allocation'}
onConfirmed={onDelete}
showSpinnerOverlay={loading}
onModalDismissed={() => setVisible(false)}
>
Are you sure you want to delete this allocation?
</ConfirmationModal>
<Button type={'button'} size={'inline'} color={'red'} onClick={() => setVisible(true)}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" css={tw`h-5 w-5`}>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</Button>
</>
);
};

View file

@ -4,7 +4,7 @@ import Spinner from '@/components/elements/Spinner';
interface Props {
isLoading?: boolean;
size?: 'xsmall' | 'small' | 'large' | 'xlarge';
size?: 'inline' | 'xsmall' | 'small' | 'large' | 'xlarge';
color?: 'green' | 'red' | 'primary' | 'grey';
isSecondary?: boolean;
}
@ -60,6 +60,7 @@ const ButtonStyle = styled.button<Omit<Props, 'isLoading'>>`
`};
`};
${props => props.size === 'inline' && tw`p-1 text-xs`};
${props => props.size === 'xsmall' && tw`p-2 text-xs`};
${props => (!props.size || props.size === 'small') && tw`p-3`};
${props => props.size === 'large' && tw`p-4 text-sm`};