Feature: Delete apps (#323)

This commit is contained in:
Navaneeth Pk 2021-06-25 23:13:44 +05:30 committed by GitHub
parent 80d5ec536d
commit 22e3002265
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 106 additions and 4 deletions

View file

@ -49,6 +49,12 @@ class AppsController < ApplicationController
end
end
def destroy
app = App.find params[:id]
app.update(current_version: nil)
app.destroy
end
def slugs
@app = App.find_by(slug: params[:slug])

View file

@ -3,8 +3,10 @@
class App < ApplicationRecord
belongs_to :organization
has_many :data_queries, dependent: :destroy
has_many :data_sources, dependent: :destroy
has_many :app_users, dependent: :destroy
has_many :app_versions, dependent: :destroy
has_many :folder_apps, dependent: :destroy
belongs_to :current_version, class_name: "AppVersion", optional: true
belongs_to :user, optional: true

View file

@ -1,5 +1,5 @@
Rails.application.routes.draw do
resources :apps, only: %i[index create show update] do
resources :apps, only: %i[index create show update destroy] do
resources :versions, only: %i[index create update]
get '/users', to: 'apps#users'

View file

@ -6,7 +6,7 @@ import { folderService } from '@/_services';
import { toast } from 'react-toastify';
export const AppMenu = function AppMenu({
app, folders, foldersChanged
app, folders, foldersChanged, deleteApp
}) {
const [addToFolder, setAddToFolder] = useState(false);
@ -40,7 +40,7 @@ export const AppMenu = function AppMenu({
return <OverlayTrigger
trigger="click"
placement="right"
placement="top"
rootClose
onToggle={(status) => handleToggle(status)}
overlay={
@ -50,6 +50,9 @@ export const AppMenu = function AppMenu({
{!addToFolder &&
<div className="field mb-2">
<span role="button" onClick={() => setAddToFolder(true)}>Add to folder </span>
<br></br>
<br></br>
<span class="my-3 text-danger" role="button" onClick={() => deleteApp()}>Delete app </span>
</div>
}

View file

@ -7,6 +7,8 @@ import { AppMenu } from './AppMenu';
import { BlankPage } from './BlankPage';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import { renderTooltip } from '@/_helpers/appUtils';
import { ConfirmDialog } from '@/_components';
import { toast } from 'react-toastify';
class HomePage extends React.Component {
constructor(props) {
@ -18,6 +20,7 @@ class HomePage extends React.Component {
isLoading: true,
creatingApp: false,
currentFolder: {},
showAppDeletionConfirmation: false,
apps: [],
folders: [],
meta: {
@ -56,6 +59,7 @@ class HomePage extends React.Component {
}
pageChanged = (page) => {
this.setState({ currentPage: page });
this.fetchApps(page, this.state.currentFolder.id);
}
@ -77,13 +81,49 @@ class HomePage extends React.Component {
});
};
deleteApp = (app) => {
this.setState({ showAppDeletionConfirmation: true, appToBeDeleted: app })
}
executeAppDeletion = () => {
this.setState({ isDeletingApp: true });
appService.deleteApp(this.state.appToBeDeleted.id).then((data) => {
toast.info('App deleted successfully.', {
hideProgressBar: true,
position: 'top-center'
});
this.setState({
isDeletingApp: false,
appToBeDeleted: null,
showAppDeletionConfirmation: false
});
this.fetchApps(this.state.currentPage || 0, this.state.currentFolder.id)
}).catch(({ error }) => {
toast.error('Could not delete the app.', { hideProgressBar: true, position: 'top-center' });
this.setState({
isDeletingApp: false,
appToBeDeleted: null,
showAppDeletionConfirmation: false
});
});
;
}
render() {
const {
apps, isLoading, creatingApp, meta, currentFolder
apps, isLoading, creatingApp, meta, currentFolder, showAppDeletionConfirmation, isDeletingApp
} = this.state;
return (
<div className="wrapper home-page">
<ConfirmDialog
show={showAppDeletionConfirmation}
message={'The app and the associated data will be permanently deleted, do you want to continue?'}
confirmButtonLoading={isDeletingApp}
onConfirm={() => this.executeAppDeletion()}
onCancel={() => {}}
/>
<Header
/>
@ -189,6 +229,7 @@ class HomePage extends React.Component {
app={app}
folders={this.state.folders}
foldersChanged={this.foldersChanged}
deleteApp={() => this.deleteApp(app)}
/>
</td>
</tr>))

View file

@ -0,0 +1,43 @@
import React, { useState, useEffect } from 'react';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
export function ConfirmDialog({
show, message, onConfirm, onCancel, confirmButtonLoading
}) {
const [showModal, setShow] = useState(show);
useEffect(() => {
setShow(show);
}, [show]);
const handleClose = () => {
setShow(false);
};
const handleConfirm = () => {
onConfirm();
};
const handleCancel = () => {
onCancel();
handleClose();
};
return (
<>
<Modal show={showModal} onHide={handleClose} size="sm" centered={true}>
<div className="modal-status bg-danger"></div>
<Modal.Body>{message}</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleCancel}>
Cancel
</Button>
<Button variant="danger" autoFocus className={`${confirmButtonLoading ? 'btn-loading' : ''}`} onClick={handleConfirm}>
Yes
</Button>
</Modal.Footer>
</Modal>
</>
);
}

View file

@ -1,3 +1,4 @@
export * from './PrivateRoute';
export * from './Pagination';
export * from './Header';
export * from './ConfirmDialog';

View file

@ -4,6 +4,7 @@ import { authHeader, handleResponse } from '@/_helpers';
export const appService = {
getAll,
createApp,
deleteApp,
getApp,
getAppBySlug,
saveApp,
@ -31,6 +32,11 @@ function getApp(id) {
return fetch(`${config.apiUrl}/apps/${id}`, requestOptions).then(handleResponse);
}
function deleteApp(id) {
const requestOptions = { method: 'DELETE', headers: authHeader() };
return fetch(`${config.apiUrl}/apps/${id}`, requestOptions).then(handleResponse);
}
function getAppBySlug(slug) {
const requestOptions = { method: 'GET', headers: authHeader() };
return fetch(`${config.apiUrl}/apps/slugs/${slug}`, requestOptions).then(handleResponse);