Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ GEM
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
railties (>= 3.2)
elasticsearch (7.13.3)
elasticsearch-api (= 7.13.3)
elasticsearch-transport (= 7.13.3)
elasticsearch-api (7.13.3)
multi_json
elasticsearch-transport (7.13.3)
faraday (~> 1)
multi_json
erubi (1.10.0)
execjs (2.8.1)
factory_bot (6.2.0)
Expand Down Expand Up @@ -412,6 +420,9 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
searchkick (5.0.3)
activemodel (>= 5.2)
hashie
selenium-webdriver (4.1.0)
childprocess (>= 0.5, < 5.0)
rexml (~> 3.2, >= 3.2.5)
Expand Down Expand Up @@ -495,6 +506,7 @@ DEPENDENCIES
devise_invitable (~> 2.0, >= 2.0.6)
discard (~> 1.2)
dotenv-rails
elasticsearch (< 7.14)
factory_bot_rails
faker
foreman
Expand Down Expand Up @@ -525,6 +537,7 @@ DEPENDENCIES
rubocop-rails
rubocop-rspec (~> 2.8)
sass-rails
searchkick
selenium-webdriver (>= 4.0.0)
shoulda-callback-matchers (~> 1.1.1)
shoulda-matchers (~> 5.1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

class InternalApi::V1::Invoices::BulkDeletionController < InternalApi::V1::ApplicationController
def create
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the action name be destroy?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apoorv1316 We can add a destroy_multiple action in invoices_controller instead of creating a separate controller just for this.

cc: @rohitjoshixyz

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mohd-anas-ansari that action won't be RESTful

Copy link
Collaborator Author

@apoorv1316 apoorv1316 May 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rohitjoshixyz Destroy action is a member route and we want to use a collection route for bulk_delete that's why we are using create action.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use collection route for delete as well by explicitly mentioning it in routes.rb

authorize :create, policy_class: Invoices::BulkDeletionPolicy
invoices = Invoice.where(id: params[:invoices_ids])
invoices.destroy_all
head :no_content
end
end
5 changes: 5 additions & 0 deletions app/controllers/internal_api/v1/invoices_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ def show
}
end

def destroy
authorize invoice
invoice.destroy
end

def send_invoice
authorize invoice

Expand Down
6 changes: 5 additions & 1 deletion app/javascript/src/apis/invoices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ const post = async (body) => axios.post(`${path}`, body);

const patch = async (id, body) => axios.post(`${path}/${id}`, body);

const destroy = (id) => axios.delete(`${path}/${id}`);

const destroyBulk = (invoice_ids) => axios.post(`${path}/bulk_deletion`, invoice_ids);

const getInvoice = async (id) => axios.get(`${path}/${id}`);

const sendInvoice = async (id, payload) =>
axios.post(`${path}/${id}/send_invoice`, payload);

const invoicesApi = { get, post, patch, sendInvoice, getInvoice };
const invoicesApi = { get, post, patch, destroy, sendInvoice, getInvoice, destroyBulk };

export default invoicesApi;
2 changes: 1 addition & 1 deletion app/javascript/src/components/Invoices/Generate/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Header from "./Header";

import Invoice_settings from "./Invoice_settings";
import { mapGenerateInvoice, unmapGenerateInvoice } from "../../../mapper/generateInvoice.mapper";
import SendInvoice from "../modals/SendInvoice";
import SendInvoice from "../Modals/SendInvoice";

const fetchGenerateInvoice = async (navigate, getInvoiceDetails) => {
try {
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/src/components/Invoices/Invoice/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import invoicesApi from "apis/invoices";
import Header from "./Header";
import InvoiceDetails from "./InvoiceDetails";
import { ApiStatus as InvoiceStatus } from "../../../constants";
import SendInvoice from "../modals/SendInvoice";
import SendInvoice from "../Modals/SendInvoice";

const Invoice = () => {
const params = useParams();
Expand Down
6 changes: 5 additions & 1 deletion app/javascript/src/components/Invoices/List/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const Header = ({
setFilterVisibilty,
isInvoiceSelected,
selectedInvoiceCount,
clearCheckboxes
clearCheckboxes,
setShowBulkDeleteDialog
}) => (
<div className="mt-6 mb-3 sm:flex sm:items-center sm:justify-between">
<h2 className="header__title">Invoices</h2>
Expand Down Expand Up @@ -65,6 +66,9 @@ const Header = ({
<div className="flex">
<button
type="button"
onClick={()=> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the status of the selected invoices is sent/viewed? They can still be deleted. Is this the expected behaviour?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, only draft and declined invoices can be deleted.

setShowBulkDeleteDialog(true);
}}
className="header__button border-miru-red-400 text-miru-red-400"
>
<Trash weight="fill" size={16} />
Expand Down
24 changes: 16 additions & 8 deletions app/javascript/src/components/Invoices/List/Table/TableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ const TableRow = ({
invoice,
isSelected,
selectInvoices,
deselectInvoices
deselectInvoices,
setShowDeleteDialog,
setInvoiceToDelete
}) => {
const [isSending, setIsSending] = useState<boolean>(false);

Expand Down Expand Up @@ -94,13 +96,19 @@ const TableRow = ({
</div>
</td>

<td className="px-2 py-4 text-sm font-medium text-right whitespace-nowrap">
<div className="flex items-center h-full">
<button className="hidden group-hover:block text-miru-han-purple-1000">
<Trash size={16} />
</button>
</div>
</td>
{(invoice.status == "draft" || invoice.status == "declined") && (
<td className="px-2 py-4 text-sm font-medium text-right whitespace-nowrap">
<div className="flex items-center h-full">
<button className="hidden group-hover:block text-miru-han-purple-1000"
onClick={()=> {
setShowDeleteDialog(true);
setInvoiceToDelete(invoice.id);
}}>
<Trash size={16} />
</button>
</div>
</td>
)}

{isSending && (
<SendInvoice invoice={invoice} setIsSending={setIsSending} isSending />
Expand Down
7 changes: 4 additions & 3 deletions app/javascript/src/components/Invoices/List/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ const Table = ({
invoices,
selectInvoices,
deselectInvoices,
selectedInvoices
selectedInvoices,
setShowDeleteDialog,
setInvoiceToDelete
}) => (
<table className="min-w-full mt-4 divide-y divide-gray-200">
<thead>
Expand All @@ -28,8 +30,7 @@ const Table = ({
isSelected={selectedInvoices.includes(invoice.id)}
invoice={invoice}
selectInvoices={selectInvoices}
deselectInvoices={deselectInvoices}
/>
deselectInvoices={deselectInvoices} setShowDeleteDialog={setShowDeleteDialog} setInvoiceToDelete={setInvoiceToDelete} />
))}
</tbody>
</table>
Expand Down
6 changes: 5 additions & 1 deletion app/javascript/src/components/Invoices/List/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ const Container = ({
invoices,
selectedInvoices,
selectInvoices,
deselectInvoices
deselectInvoices,
setShowDeleteDialog,
setInvoiceToDelete
}) =>
invoices.length > 0 ? (
<>
Expand All @@ -22,6 +24,8 @@ const Container = ({
selectedInvoices={selectedInvoices}
selectInvoices={selectInvoices}
deselectInvoices={deselectInvoices}
setShowDeleteDialog= {setShowDeleteDialog}
setInvoiceToDelete ={setInvoiceToDelete}
/>
</>
) : (
Expand Down
26 changes: 25 additions & 1 deletion app/javascript/src/components/Invoices/List/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import FilterSideBar from "./FilterSideBar";
import Header from "./Header";

import { ApiStatus as InvoicesStatus } from "../../../constants";
import BulkDeleteInvoices from "../Modals/BulkDeleteInvoices";
import DeleteInvoice from "../Modals/DeleteInvoice";

const Invoices: React.FC = () => {
const [status, setStatus] = React.useState<InvoicesStatus>(
Expand All @@ -28,6 +30,11 @@ const Invoices: React.FC = () => {
const [selectedInvoices, setSelectedInvoices] = React.useState<number[]>([]);

const [isFilterVisible, setFilterVisibilty] = React.useState<boolean>(false);
const [showDeleteDialog, setShowDeleteDialog] =
React.useState<boolean>(false);
const [showBulkDeleteDialaog, setShowBulkDeleteDialog] =
React.useState<boolean>(false);
const [invoiceToDelete, setInvoiceToDelete] = React.useState(null);

const selectedInvoiceCount = selectedInvoices.length;
const isInvoiceSelected = selectedInvoiceCount > 0;
Expand All @@ -47,7 +54,7 @@ const Invoices: React.FC = () => {
setInvoices(invoices);
setSummary(summary);
setPagy(pagy);

setSelectedInvoices([]);
setStatus(InvoicesStatus.SUCCESS);
} catch (error) {
setStatus(InvoicesStatus.ERROR);
Expand Down Expand Up @@ -75,6 +82,7 @@ const Invoices: React.FC = () => {
}
selectedInvoiceCount={selectedInvoiceCount}
isInvoiceSelected={isInvoiceSelected}
setShowBulkDeleteDialog={setShowBulkDeleteDialog}
/>

<Container
Expand All @@ -83,6 +91,8 @@ const Invoices: React.FC = () => {
selectedInvoices={selectedInvoices}
selectInvoices={selectInvoices}
deselectInvoices={deselectInvoices}
setShowDeleteDialog={setShowDeleteDialog}
setInvoiceToDelete={setInvoiceToDelete}
/>

{isFilterVisible && (
Expand All @@ -92,6 +102,20 @@ const Invoices: React.FC = () => {
{invoices.length && (
<Pagination pagy={pagy} params={params} setParams={setParams} />
)}
{showDeleteDialog && (
<DeleteInvoice
invoice={invoiceToDelete}
setShowDeleteDialog={setShowDeleteDialog}
fetchInvoices={fetchInvoices}
/>
)}
{showBulkDeleteDialaog && (
<BulkDeleteInvoices
invoices_ids={selectedInvoices}
setShowBulkDeleteDialog={setShowBulkDeleteDialog}
fetchInvoices={fetchInvoices}
/>
)}
</React.Fragment>
)
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from "react";
import invoicesApi from "apis/invoices";

interface IProps {
invoices_ids: any;
setShowBulkDeleteDialog : any;
fetchInvoices: any
}

const BulkDeleteInvoices = ({ invoices_ids, setShowBulkDeleteDialog, fetchInvoices }: IProps) => {
const destroyInvoices = async invoices_ids => {
try {
await invoicesApi.destroyBulk({ invoices_ids });
setShowBulkDeleteDialog(false);
fetchInvoices();
} catch (error) {
console.error(error);
}
};
return (
<div className="px-4 flex items-center justify-center">
<div
className="overflow-auto fixed top-0 left-0 right-0 bottom-0 inset-0 z-10 flex items-start justify-center"
style={{
backgroundColor: "rgba(29, 26, 49, 0.6)"
}}
>
<div className="relative px-4 h-full w-full md:flex md:items-center md:justify-center">
<div className="rounded-lg px-6 pb-6 bg-white shadow-xl transform transition-all sm:align-middle sm:max-w-md modal-width">
<div className="flex-col my-8">
<h6 className="text-2xl font-bold mb-2">Delete Invoices</h6>
<p className="font-normal mt-2">
Are you sure you want to delete these invoice?
<b className="font-bold"></b> This action cannot
be reversed.
</p>
</div>
<div className="flex justify-between">
<button
className="button__bg_transparent"
onClick={() => {
setShowBulkDeleteDialog (false);
}}
>
CANCEL
</button>
<button
className="button__bg_purple"
onClick={() => {
destroyInvoices(invoices_ids);
}}
>
DELETE
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default BulkDeleteInvoices;
62 changes: 62 additions & 0 deletions app/javascript/src/components/Invoices/Modals/DeleteInvoice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from "react";
import invoicesApi from "apis/invoices";

interface IProps {
invoice: any;
setShowDeleteDialog: any;
fetchInvoices: any;
}

const DeleteInvoice = ({ invoice, setShowDeleteDialog, fetchInvoices }: IProps) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BulkDeleteInvoices and DeleteInvoice can be combined in a single modal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, It's good to have a single modal. Will create a separate PR for it.

const destroyInvoice = async invoice => {
try {
await invoicesApi.destroy(invoice);
setShowDeleteDialog(false);
fetchInvoices();
} catch (error) {
console.error(error);
}
};
return (
<div className="px-4 flex items-center justify-center">
<div
className="overflow-auto fixed top-0 left-0 right-0 bottom-0 inset-0 z-10 flex items-start justify-center"
style={{
backgroundColor: "rgba(29, 26, 49, 0.6)"
}}
>
<div className="relative px-4 h-full w-full md:flex md:items-center md:justify-center">
<div className="rounded-lg px-6 pb-6 bg-white shadow-xl transform transition-all sm:align-middle sm:max-w-md modal-width">
<div className="flex-col my-8">
<h6 className="text-2xl font-bold mb-2">Delete Invoice</h6>
<p className="font-normal mt-2">
Are you sure you want to delete this invoice?
<b className="font-bold"></b> This action cannot
be reversed.
</p>
</div>
<div className="flex justify-between">
<button
className="button__bg_transparent"
onClick={() => {
setShowDeleteDialog(false);
}}
>
CANCEL
</button>
<button
className="button__bg_purple"
onClick={() => {
destroyInvoice(invoice);
}}
>
DELETE
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default DeleteInvoice;
Loading