mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-17 06:52:27 +01:00
integration of packs-summary
This commit is contained in:
parent
baf41588d3
commit
10aaf0042c
@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# API Controller for resources of type StatisticProfilePrepaidPack
|
||||
class UserPacksController < API::ApiController
|
||||
class API::UserPacksController < API::ApiController
|
||||
before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import _ from 'lodash';
|
||||
import { Machine } from '../../models/machine';
|
||||
import { User } from '../../models/user';
|
||||
import { UserPack } from '../../models/user-pack';
|
||||
@ -19,7 +20,7 @@ type PackableItem = Machine;
|
||||
interface PacksSummaryProps {
|
||||
item: PackableItem,
|
||||
itemType: 'Machine',
|
||||
customer: User,
|
||||
customer?: User,
|
||||
operator: User,
|
||||
onError: (message: string) => void,
|
||||
onSuccess: (message: string) => void,
|
||||
@ -33,13 +34,18 @@ const PacksSummaryComponent: React.FC<PacksSummaryProps> = ({ item, itemType, cu
|
||||
const [packsModal, setPacksModal] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
UserPackAPI.index({ user_id: customer.id, priceable_type: itemType, priceable_id: item.id })
|
||||
.then(data => setUserPacks(data))
|
||||
.catch(error => onError(error));
|
||||
SettingAPI.get(SettingName.RenewPackThreshold)
|
||||
.then(data => setThreshold(parseFloat(data.value)))
|
||||
.catch(error => onError(error));
|
||||
}, [item, itemType, customer])
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (_.isEmpty(customer)) return;
|
||||
|
||||
UserPackAPI.index({ user_id: customer.id, priceable_type: itemType, priceable_id: item.id })
|
||||
.then(data => setUserPacks(data))
|
||||
.catch(error => onError(error));
|
||||
}, [item, itemType, customer]);
|
||||
|
||||
/**
|
||||
* Total of minutes used by the customer
|
||||
@ -47,7 +53,7 @@ const PacksSummaryComponent: React.FC<PacksSummaryProps> = ({ item, itemType, cu
|
||||
const totalUsed = (): number => {
|
||||
if (!userPacks) return 0;
|
||||
|
||||
return userPacks.map(up => up.minutes_used).reduce((acc, curr) => acc + curr);
|
||||
return userPacks.map(up => up.minutes_used).reduce((acc, curr) => acc + curr, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,25 +62,26 @@ const PacksSummaryComponent: React.FC<PacksSummaryProps> = ({ item, itemType, cu
|
||||
const totalAvailable = (): number => {
|
||||
if (!userPacks) return 0;
|
||||
|
||||
return userPacks.map(up => up.prepaid_pack.minutes).reduce((acc, curr) => acc + curr);
|
||||
return userPacks.map(up => up.prepaid_pack.minutes).reduce((acc, curr) => acc + curr, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Total prepaid hours remaining for the current customer
|
||||
*/
|
||||
const totalHours = (): number => {
|
||||
return totalAvailable() - totalUsed() / 60;
|
||||
return (totalAvailable() - totalUsed()) / 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do we need to display the "renew pack" button?
|
||||
* Do we need to display the "buy new pack" button?
|
||||
*/
|
||||
const shouldDisplayRenew = (): boolean => {
|
||||
const shouldDisplayButton = (): boolean => {
|
||||
console.log(threshold);
|
||||
if (threshold < 1) {
|
||||
return totalAvailable() - totalUsed() >= totalAvailable() * threshold;
|
||||
return totalAvailable() - totalUsed() <= totalAvailable() * threshold;
|
||||
}
|
||||
|
||||
return totalAvailable() - totalUsed() >= threshold;
|
||||
return totalAvailable() - totalUsed() <= threshold * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,23 +102,29 @@ const PacksSummaryComponent: React.FC<PacksSummaryProps> = ({ item, itemType, cu
|
||||
.catch(error => onError(error));
|
||||
}
|
||||
|
||||
// prevent component rendering if no customer selected
|
||||
if (_.isEmpty(customer)) return <div />;
|
||||
|
||||
return (
|
||||
<div className="packs-summary">
|
||||
<span className="remaining-hours">{t('app.logged.packs_summary.remaining_HOURS', { HOURS: totalHours(), ITEM: itemType })}</span>
|
||||
{shouldDisplayRenew() && <div>
|
||||
<FabButton className="renew-button" onClick={togglePacksModal} icon={<i className="fa fa-shopping-cart"/>}>
|
||||
{t('app.logged.packs_summary.buy_a_new_pack')}
|
||||
</FabButton>
|
||||
<ProposePacksModal isOpen={packsModal}
|
||||
toggleModal={togglePacksModal}
|
||||
item={item}
|
||||
itemType={itemType}
|
||||
customer={customer}
|
||||
operator={operator}
|
||||
onError={onError}
|
||||
onDecline={togglePacksModal}
|
||||
onSuccess={handlePackBoughtSuccess} />
|
||||
</div>}
|
||||
<h3>{t('app.logged.packs_summary.prepaid_hours')}</h3>
|
||||
<div className="content">
|
||||
<span className="remaining-hours">{t('app.logged.packs_summary.remaining_HOURS', { HOURS: totalHours(), ITEM: itemType })}</span>
|
||||
{shouldDisplayButton() && <div className="button-wrapper">
|
||||
<FabButton className="buy-button" onClick={togglePacksModal} icon={<i className="fa fa-shopping-cart"/>}>
|
||||
{t('app.logged.packs_summary.buy_a_new_pack')}
|
||||
</FabButton>
|
||||
<ProposePacksModal isOpen={packsModal}
|
||||
toggleModal={togglePacksModal}
|
||||
item={item}
|
||||
itemType={itemType}
|
||||
customer={customer}
|
||||
operator={operator}
|
||||
onError={onError}
|
||||
onDecline={togglePacksModal}
|
||||
onSuccess={handlePackBoughtSuccess} />
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -98,8 +98,8 @@ class MachinesController {
|
||||
/**
|
||||
* Controller used in the public listing page, allowing everyone to see the list of machines
|
||||
*/
|
||||
Application.Controllers.controller('MachinesController', ['$scope', '$state', '_t', 'AuthService', 'Machine', '$uibModal', 'settingsPromise', 'Member', 'uiTourService', 'growl',
|
||||
function ($scope, $state, _t, AuthService, Machine, $uibModal, settingsPromise, Member, uiTourService, growl) {
|
||||
Application.Controllers.controller('MachinesController', ['$scope', '$state', '_t', 'AuthService', 'Machine', '$uibModal', 'settingsPromise', 'Member', 'uiTourService', 'machinesPromise', 'growl',
|
||||
function ($scope, $state, _t, AuthService, Machine, $uibModal, settingsPromise, Member, uiTourService, machinesPromise, growl) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
/**
|
||||
@ -164,7 +164,7 @@ Application.Controllers.controller('MachinesController', ['$scope', '$state', '_
|
||||
placement: 'bottom',
|
||||
orphan: true
|
||||
});
|
||||
if ($scope.machines.length > 0) {
|
||||
if (machinesPromise.length > 0) {
|
||||
uitour.createStep({
|
||||
selector: '.machines-list .show-button',
|
||||
stepId: 'view',
|
||||
@ -185,7 +185,7 @@ Application.Controllers.controller('MachinesController', ['$scope', '$state', '_
|
||||
orphan: true
|
||||
});
|
||||
}
|
||||
if ($scope.machines.length > 0) {
|
||||
if (machinesPromise.length > 0) {
|
||||
uitour.createStep({
|
||||
selector: '.machines-list .reserve-button',
|
||||
stepId: 'reserve',
|
||||
@ -357,8 +357,8 @@ Application.Controllers.controller('ShowMachineController', ['$scope', '$state',
|
||||
* This controller workflow is pretty similar to the trainings reservation controller.
|
||||
*/
|
||||
|
||||
Application.Controllers.controller('ReserveMachineController', ['$scope', '$stateParams', '_t', 'moment', 'Auth', '$timeout', 'Member', 'Availability', 'plansPromise', 'groupsPromise', 'machinePromise', 'settingsPromise', 'uiCalendarConfig', 'CalendarConfig', 'Reservation',
|
||||
function ($scope, $stateParams, _t, moment, Auth, $timeout, Member, Availability, plansPromise, groupsPromise, machinePromise, settingsPromise, uiCalendarConfig, CalendarConfig, Reservation) {
|
||||
Application.Controllers.controller('ReserveMachineController', ['$scope', '$stateParams', '_t', 'moment', 'Auth', '$timeout', 'Member', 'Availability', 'plansPromise', 'groupsPromise', 'machinePromise', 'settingsPromise', 'uiCalendarConfig', 'CalendarConfig', 'Reservation', 'growl',
|
||||
function ($scope, $stateParams, _t, moment, Auth, $timeout, Member, Availability, plansPromise, groupsPromise, machinePromise, settingsPromise, uiCalendarConfig, CalendarConfig, Reservation, growl) {
|
||||
/* PRIVATE STATIC CONSTANTS */
|
||||
|
||||
// Slot free to be booked
|
||||
@ -626,6 +626,20 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
|
||||
*/
|
||||
$scope.filterDisabledPlans = function (plan) { return !plan.disabled; };
|
||||
|
||||
/**
|
||||
* Callback triggered by react components
|
||||
*/
|
||||
$scope.onSuccess = function (message) {
|
||||
growl.success(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered by react components
|
||||
*/
|
||||
$scope.onError = function (message) {
|
||||
growl.error(message);
|
||||
};
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,6 @@ export interface Machine {
|
||||
current_user_is_trained?: boolean,
|
||||
current_user_next_training_reservation?: Reservation,
|
||||
current_user_has_packs?: boolean,
|
||||
current_user_available_for_packs_renewal?: boolean,
|
||||
has_prepaid_packs_for_current_user?: boolean,
|
||||
machine_projects?: Array<{
|
||||
id: number,
|
||||
|
@ -324,6 +324,7 @@ angular.module('application.router', ['ui.router'])
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
|
||||
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['feature_tour_display']" }).$promise; }]
|
||||
}
|
||||
})
|
||||
|
@ -55,7 +55,6 @@
|
||||
@import "modules/machines/machines-list";
|
||||
@import "modules/machines/machines-filters";
|
||||
@import "modules/machines/required-training-modal";
|
||||
@import "modules/machines/propose-packs-modal";
|
||||
@import "modules/user/avatar";
|
||||
@import "modules/pricing/machines-pricing";
|
||||
@import "modules/pricing/editable-price";
|
||||
@ -63,5 +62,7 @@
|
||||
@import "modules/pricing/pack-form";
|
||||
@import "modules/pricing/delete-pack";
|
||||
@import "modules/pricing/edit-pack";
|
||||
@import "modules/prepaid-packs/propose-packs-modal";
|
||||
@import "modules/prepaid-packs/packs-summary";
|
||||
|
||||
@import "app.responsive";
|
||||
|
@ -0,0 +1,27 @@
|
||||
.packs-summary {
|
||||
border: 1px solid #ddd;
|
||||
margin: 15px;
|
||||
padding: 0;
|
||||
border-radius: 6px;
|
||||
|
||||
h3 {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 15px;
|
||||
margin: 0;
|
||||
line-height: 1.8rem;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 15px;
|
||||
|
||||
.button-wrapper {
|
||||
text-align: center;
|
||||
|
||||
.buy-button {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -29,6 +29,14 @@
|
||||
<select-member></select-member>
|
||||
</div>
|
||||
|
||||
<packs-summary item="machine"
|
||||
item-type="'Machine'"
|
||||
customer="ctrl.member"
|
||||
operator="currentUser"
|
||||
on-error="onError"
|
||||
on-success="onSuccess">
|
||||
</packs-summary>
|
||||
|
||||
<cart slot="selectedEvent"
|
||||
slot-selection-time="selectionTime"
|
||||
events="events"
|
||||
|
@ -391,7 +391,7 @@ section#cookies-modal div.cookies-consent .cookies-actions button.accept {
|
||||
.machine-card {
|
||||
.machine-actions {
|
||||
button {
|
||||
color: $secondary;
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,7 @@ if current_user
|
||||
json.partial! 'api/reservations/reservation', reservation: current_user.next_training_reservation_by_machine(@machine)
|
||||
end
|
||||
end
|
||||
json.current_user_has_packs current_user.packs?(@machine, nil)
|
||||
json.current_user_available_for_packs_renewal current_user.packs?(@machine)
|
||||
json.current_user_has_packs current_user.packs?(@machine)
|
||||
json.has_prepaid_packs_for_current_user @machine.packs?(current_user)
|
||||
end
|
||||
|
||||
|
@ -3,6 +3,6 @@
|
||||
json.array!(@user_packs) do |user_pack|
|
||||
json.extract! user_pack, :minutes_used, :expires_at
|
||||
json.prepaid_pack do
|
||||
json.extract! user_pack.prepaid_pack :minutes
|
||||
json.extract! user_pack.prepaid_pack, :minutes
|
||||
end
|
||||
end
|
||||
|
@ -192,6 +192,7 @@ en:
|
||||
month: "{COUNT, plural, one{month} other{months}}"
|
||||
year: "{COUNT, plural, one{year} other{years}}"
|
||||
packs_summary:
|
||||
prepaid_hours: "Prepaid hours"
|
||||
remaining_HOURS: "You have {HOURS} prepaid hours remaining for this {ITEM, select, Machine{machine} Space{space} other{}}."
|
||||
buy_a_new_pack: "Buy a new pack"
|
||||
#book a training
|
||||
|
Loading…
x
Reference in New Issue
Block a user