From 22e3002265742ffc57742c9471fc7cbff168e7d6 Mon Sep 17 00:00:00 2001 From: Navaneeth Pk Date: Fri, 25 Jun 2021 23:13:44 +0530 Subject: [PATCH] Feature: Delete apps (#323) --- app/controllers/apps_controller.rb | 6 +++ app/models/app.rb | 2 + config/routes.rb | 2 +- frontend/src/HomePage/AppMenu.jsx | 7 +++- frontend/src/HomePage/HomePage.jsx | 43 +++++++++++++++++++++- frontend/src/_components/ConfirmDialog.jsx | 43 ++++++++++++++++++++++ frontend/src/_components/index.js | 1 + frontend/src/_services/app.service.js | 6 +++ 8 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 frontend/src/_components/ConfirmDialog.jsx diff --git a/app/controllers/apps_controller.rb b/app/controllers/apps_controller.rb index 328196ff28..53316db7c6 100644 --- a/app/controllers/apps_controller.rb +++ b/app/controllers/apps_controller.rb @@ -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]) diff --git a/app/models/app.rb b/app/models/app.rb index 2de962af53..9ed37e4118 100644 --- a/app/models/app.rb +++ b/app/models/app.rb @@ -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 diff --git a/config/routes.rb b/config/routes.rb index 99463cfc57..89ae0988a3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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' diff --git a/frontend/src/HomePage/AppMenu.jsx b/frontend/src/HomePage/AppMenu.jsx index cfc4516ec0..c49f3cda24 100644 --- a/frontend/src/HomePage/AppMenu.jsx +++ b/frontend/src/HomePage/AppMenu.jsx @@ -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 handleToggle(status)} overlay={ @@ -50,6 +50,9 @@ export const AppMenu = function AppMenu({ {!addToFolder &&
setAddToFolder(true)}>Add to folder +

+

+ deleteApp()}>Delete app
} diff --git a/frontend/src/HomePage/HomePage.jsx b/frontend/src/HomePage/HomePage.jsx index 3c208c89d3..1fc86a08e9 100644 --- a/frontend/src/HomePage/HomePage.jsx +++ b/frontend/src/HomePage/HomePage.jsx @@ -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 (
+ this.executeAppDeletion()} + onCancel={() => {}} + /> +
@@ -189,6 +229,7 @@ class HomePage extends React.Component { app={app} folders={this.state.folders} foldersChanged={this.foldersChanged} + deleteApp={() => this.deleteApp(app)} /> )) diff --git a/frontend/src/_components/ConfirmDialog.jsx b/frontend/src/_components/ConfirmDialog.jsx new file mode 100644 index 0000000000..e532ef87b6 --- /dev/null +++ b/frontend/src/_components/ConfirmDialog.jsx @@ -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 ( + <> + +
+ {message} + + + + +
+ + ); +} diff --git a/frontend/src/_components/index.js b/frontend/src/_components/index.js index f91147e91e..9d510e39da 100644 --- a/frontend/src/_components/index.js +++ b/frontend/src/_components/index.js @@ -1,3 +1,4 @@ export * from './PrivateRoute'; export * from './Pagination'; export * from './Header'; +export * from './ConfirmDialog'; diff --git a/frontend/src/_services/app.service.js b/frontend/src/_services/app.service.js index 72975a642c..a0a491359d 100644 --- a/frontend/src/_services/app.service.js +++ b/frontend/src/_services/app.service.js @@ -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);