mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-06 01:08:21 +01:00
delete & disable packs
This commit is contained in:
parent
1a65b97653
commit
0345d22582
@ -47,6 +47,6 @@ class API::PrepaidPacksController < API::ApiController
|
|||||||
def pack_params
|
def pack_params
|
||||||
pack_params = params
|
pack_params = params
|
||||||
pack_params[:pack][:amount] = pack_params[:pack][:amount].to_f * 100.0 if pack_params[:pack][:amount]
|
pack_params[:pack][:amount] = pack_params[:pack][:amount].to_f * 100.0 if pack_params[:pack][:amount]
|
||||||
params.require(:pack).permit(:priceable_id, :priceable_type, :group_id, :amount, :minutes)
|
params.require(:pack).permit(:priceable_id, :priceable_type, :group_id, :amount, :minutes, :disabled)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -7,6 +7,7 @@ import PrepaidPackAPI from '../../api/prepaid-pack';
|
|||||||
import { IFablab } from '../../models/fablab';
|
import { IFablab } from '../../models/fablab';
|
||||||
import { duration } from 'moment';
|
import { duration } from 'moment';
|
||||||
import { FabButton } from '../base/fab-button';
|
import { FabButton } from '../base/fab-button';
|
||||||
|
import { DeletePack } from './delete-pack';
|
||||||
|
|
||||||
declare var Fablab: IFablab;
|
declare var Fablab: IFablab;
|
||||||
|
|
||||||
@ -30,7 +31,6 @@ export const ConfigurePacksButton: React.FC<ConfigurePacksButtonProps> = ({ pack
|
|||||||
const [showList, setShowList] = useState<boolean>(false);
|
const [showList, setShowList] = useState<boolean>(false);
|
||||||
const [addPackModal, setAddPackModal] = useState<boolean>(false);
|
const [addPackModal, setAddPackModal] = useState<boolean>(false);
|
||||||
const [editPackModal, setEditPackModal] = useState<boolean>(false);
|
const [editPackModal, setEditPackModal] = useState<boolean>(false);
|
||||||
const [deletePackModal, setDeletePackModal] = useState<boolean>(false);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the formatted localized amount for the given price (e.g. 20.5 => "20,50 €")
|
* Return the formatted localized amount for the given price (e.g. 20.5 => "20,50 €")
|
||||||
@ -68,17 +68,10 @@ export const ConfigurePacksButton: React.FC<ConfigurePacksButtonProps> = ({ pack
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open/closes the "confirm delete pack" modal
|
* Callback triggered when the PrepaidPack was successfully created/deleted/updated.
|
||||||
|
* We refresh the list of packs for the current tooltip to display the new data.
|
||||||
*/
|
*/
|
||||||
const toggleRemovePackModal = (): void => {
|
const handleSuccess = (message: string) => {
|
||||||
setDeletePackModal(!deletePackModal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback triggered when the PrepaidPack was successfully created.
|
|
||||||
* We refresh the list of packs for the current button to get the new one.
|
|
||||||
*/
|
|
||||||
const handlePackCreated = (message: string) => {
|
|
||||||
onSuccess(message);
|
onSuccess(message);
|
||||||
PrepaidPackAPI.index({ group_id: groupId, priceable_id: priceableId, priceable_type: priceableType })
|
PrepaidPackAPI.index({ group_id: groupId, priceable_id: priceableId, priceable_type: priceableType })
|
||||||
.then(data => setPacks(data))
|
.then(data => setPacks(data))
|
||||||
@ -100,11 +93,11 @@ export const ConfigurePacksButton: React.FC<ConfigurePacksButtonProps> = ({ pack
|
|||||||
{showList && <FabPopover title={t('app.admin.configure_packs_button.packs')} headerButton={renderAddButton()}>
|
{showList && <FabPopover title={t('app.admin.configure_packs_button.packs')} headerButton={renderAddButton()}>
|
||||||
<ul>
|
<ul>
|
||||||
{packs?.map(p =>
|
{packs?.map(p =>
|
||||||
<li key={p.id}>
|
<li key={p.id} className={p.disabled ? 'disabled' : ''}>
|
||||||
{formatDuration(p.minutes)} - {formatPrice(p.amount)}
|
{formatDuration(p.minutes)} - {formatPrice(p.amount)}
|
||||||
<span className="pack-actions">
|
<span className="pack-actions">
|
||||||
<FabButton className="edit-pack-button" onClick={toggleEditPackModal}><i className="fas fa-edit"/></FabButton>
|
<FabButton className="edit-pack-button" onClick={toggleEditPackModal}><i className="fas fa-edit"/></FabButton>
|
||||||
<FabButton className="remove-pack-button" onClick={toggleRemovePackModal}><i className="fas fa-trash"/></FabButton>
|
<DeletePack onSuccess={handleSuccess} onError={onError} pack={p} />
|
||||||
</span>
|
</span>
|
||||||
</li>)}
|
</li>)}
|
||||||
</ul>
|
</ul>
|
||||||
@ -112,7 +105,7 @@ export const ConfigurePacksButton: React.FC<ConfigurePacksButtonProps> = ({ pack
|
|||||||
</FabPopover>}
|
</FabPopover>}
|
||||||
<NewPackModal isOpen={addPackModal}
|
<NewPackModal isOpen={addPackModal}
|
||||||
toggleModal={toggleAddPackModal}
|
toggleModal={toggleAddPackModal}
|
||||||
onSuccess={handlePackCreated}
|
onSuccess={handleSuccess}
|
||||||
onError={onError}
|
onError={onError}
|
||||||
groupId={groupId}
|
groupId={groupId}
|
||||||
priceableId={priceableId}
|
priceableId={priceableId}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FabButton } from '../base/fab-button';
|
||||||
|
import { FabModal } from '../base/fab-modal';
|
||||||
|
import { Loader } from '../base/loader';
|
||||||
|
import { PrepaidPack } from '../../models/prepaid-pack';
|
||||||
|
import PrepaidPackAPI from '../../api/prepaid-pack';
|
||||||
|
|
||||||
|
|
||||||
|
interface DeletePackProps {
|
||||||
|
onSuccess: (message: string) => void,
|
||||||
|
onError: (message: string) => void,
|
||||||
|
pack: PrepaidPack,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component shows a button.
|
||||||
|
* When clicked, we show a modal dialog to ask the user for confirmation about the deletion of the provided pack.
|
||||||
|
*/
|
||||||
|
const DeletePackComponent: React.FC<DeletePackProps> = ({ onSuccess, onError, pack }) => {
|
||||||
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
|
const [deletionModal, setDeletionModal] = useState<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens/closes the deletion modal
|
||||||
|
*/
|
||||||
|
const toggleDeletionModal = (): void => {
|
||||||
|
setDeletionModal(!deletionModal);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The deletion has been confirmed by the user.
|
||||||
|
* Call the API to trigger the deletion of the temporary set plan-category
|
||||||
|
*/
|
||||||
|
const onDeleteConfirmed = (): void => {
|
||||||
|
PrepaidPackAPI.destroy(pack.id).then(() => {
|
||||||
|
onSuccess(t('app.admin.delete_pack.pack_deleted'));
|
||||||
|
}).catch((error) => {
|
||||||
|
onError(t('app.admin.delete_pack.unable_to_delete') + error);
|
||||||
|
});
|
||||||
|
toggleDeletionModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="delete-pack">
|
||||||
|
<FabButton type='button' className="remove-pack-button" icon={<i className="fa fa-trash" />} onClick={toggleDeletionModal} />
|
||||||
|
<FabModal title={t('app.admin.delete_pack.delete_pack')}
|
||||||
|
isOpen={deletionModal}
|
||||||
|
toggleModal={toggleDeletionModal}
|
||||||
|
closeButton={true}
|
||||||
|
confirmButton={t('app.admin.delete_pack.confirm_delete')}
|
||||||
|
onConfirm={onDeleteConfirmed}>
|
||||||
|
<span>{t('app.admin.delete_pack.delete_confirmation')}</span>
|
||||||
|
</FabModal>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const DeletePack: React.FC<DeletePackProps> = ({ onSuccess, onError, pack }) => {
|
||||||
|
return (
|
||||||
|
<Loader>
|
||||||
|
<DeletePackComponent onSuccess={onSuccess} onError={onError} pack={pack} />
|
||||||
|
</Loader>
|
||||||
|
);
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import React, { BaseSyntheticEvent } from 'react';
|
import React, { BaseSyntheticEvent } from 'react';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
|
import Switch from 'react-switch';
|
||||||
import { PrepaidPack } from '../../models/prepaid-pack';
|
import { PrepaidPack } from '../../models/prepaid-pack';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useImmer } from 'use-immer';
|
import { useImmer } from 'use-immer';
|
||||||
@ -85,6 +86,15 @@ export const PackForm: React.FC<PackFormProps> = ({ formId, onSubmit, packData }
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback triggered when the user disables the pack.
|
||||||
|
*/
|
||||||
|
const handleUpdateDisabled = (checked: boolean) => {
|
||||||
|
updatePack(draft => {
|
||||||
|
draft.disabled = checked;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form id={formId} onSubmit={handleSubmit} className="pack-form">
|
<form id={formId} onSubmit={handleSubmit} className="pack-form">
|
||||||
<label htmlFor="hours">{t('app.admin.pack_form.hours')} *</label>
|
<label htmlFor="hours">{t('app.admin.pack_form.hours')} *</label>
|
||||||
@ -119,6 +129,10 @@ export const PackForm: React.FC<PackFormProps> = ({ formId, onSubmit, packData }
|
|||||||
onChange={handleUpdateValidityInterval}
|
onChange={handleUpdateValidityInterval}
|
||||||
options={buildOptions()} />
|
options={buildOptions()} />
|
||||||
</div>
|
</div>
|
||||||
|
<label htmlFor="disabled">{t('app.admin.pack_form.disabled')}</label>
|
||||||
|
<div>
|
||||||
|
<Switch checked={pack?.disabled || false} onChange={handleUpdateDisabled} id="disabled" />
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,4 +15,5 @@ export interface PrepaidPack {
|
|||||||
minutes: number,
|
minutes: number,
|
||||||
amount: number,
|
amount: number,
|
||||||
usages?: number,
|
usages?: number,
|
||||||
|
disabled?: boolean,
|
||||||
}
|
}
|
||||||
|
@ -60,5 +60,6 @@
|
|||||||
@import "modules/pricing/editable-price";
|
@import "modules/pricing/editable-price";
|
||||||
@import "modules/pricing/configure-packs-button";
|
@import "modules/pricing/configure-packs-button";
|
||||||
@import "modules/pricing/pack-form";
|
@import "modules/pricing/pack-form";
|
||||||
|
@import "modules/pricing/delete-pack";
|
||||||
|
|
||||||
@import "app.responsive";
|
@import "app.responsive";
|
||||||
|
@ -44,16 +44,16 @@
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pack-actions > button {
|
.pack-actions button {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
line-height: 10px;
|
line-height: 10px;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
||||||
&.remove-pack-button {
|
|
||||||
background-color: #cb1117;
|
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
color: #999999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
.delete-pack {
|
||||||
|
display: inline;
|
||||||
|
|
||||||
|
.remove-pack-button {
|
||||||
|
background-color: #cb1117;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
json.extract! pack, :id, :priceable_id, :priceable_type, :group_id, :validity_interval, :validity_count, :minutes
|
json.extract! pack, :id, :priceable_id, :priceable_type, :group_id, :validity_interval, :validity_count, :minutes, :disabled
|
||||||
json.amount pack.amount / 100.0
|
json.amount pack.amount / 100.0
|
||||||
json.usages pack.statistic_profile_prepaid_packs.count
|
json.usages pack.statistic_profile_prepaid_packs.count
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
json.array! @packs do |pack|
|
json.array! @packs do |pack|
|
||||||
json.extract! pack, :priceable_id, :priceable_type, :group_id, :minutes
|
json.extract! pack, :id, :priceable_id, :priceable_type, :group_id, :minutes, :disabled
|
||||||
json.amount pack.amount / 100.0
|
json.amount pack.amount / 100.0
|
||||||
end
|
end
|
||||||
|
@ -381,6 +381,7 @@ en:
|
|||||||
pack_form:
|
pack_form:
|
||||||
hours: "Hours"
|
hours: "Hours"
|
||||||
amount: "Price"
|
amount: "Price"
|
||||||
|
disabled: "Disabled"
|
||||||
validity_count: "Maximum validity"
|
validity_count: "Maximum validity"
|
||||||
select_interval: "Interval..."
|
select_interval: "Interval..."
|
||||||
intervals:
|
intervals:
|
||||||
@ -393,6 +394,12 @@ en:
|
|||||||
new_pack_info: "A prepaid pack allows users to buy {TYPE, select, Machine{machine} Space{space} other{}} hours before booking any slots. These packs can provide discounts on volumes purchases."
|
new_pack_info: "A prepaid pack allows users to buy {TYPE, select, Machine{machine} Space{space} other{}} hours before booking any slots. These packs can provide discounts on volumes purchases."
|
||||||
create_pack: "Create this pack"
|
create_pack: "Create this pack"
|
||||||
pack_successfully_created: "The new prepaid pack was successfully created."
|
pack_successfully_created: "The new prepaid pack was successfully created."
|
||||||
|
delete_pack:
|
||||||
|
pack_deleted: "The prepaid pack was successfully deleted."
|
||||||
|
unable_to_delete: "Unable to delete the prepaid pack: "
|
||||||
|
delete_pack: "Delete the prepaid pack"
|
||||||
|
confirm_delete: "Delete"
|
||||||
|
delete_confirmation: "Are you sure you want to delete this prepaid pack? This won't be possible if the pack was already bought by users."
|
||||||
#ajouter un code promotionnel
|
#ajouter un code promotionnel
|
||||||
coupons_new:
|
coupons_new:
|
||||||
add_a_coupon: "Add a coupon"
|
add_a_coupon: "Add a coupon"
|
||||||
|
@ -10,6 +10,7 @@ class CreatePrepaidPacks < ActiveRecord::Migration[5.2]
|
|||||||
t.integer :minutes
|
t.integer :minutes
|
||||||
t.string :validity_interval
|
t.string :validity_interval
|
||||||
t.integer :validity_count
|
t.integer :validity_count
|
||||||
|
t.boolean :disabled
|
||||||
|
|
||||||
t.timestamps
|
t.timestamps
|
||||||
end
|
end
|
||||||
|
@ -1727,6 +1727,7 @@ CREATE TABLE public.prepaid_packs (
|
|||||||
minutes integer,
|
minutes integer,
|
||||||
validity_interval character varying,
|
validity_interval character varying,
|
||||||
validity_count integer,
|
validity_count integer,
|
||||||
|
disabled boolean,
|
||||||
created_at timestamp without time zone NOT NULL,
|
created_at timestamp without time zone NOT NULL,
|
||||||
updated_at timestamp without time zone NOT NULL
|
updated_at timestamp without time zone NOT NULL
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user