1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-11-29 10:24:20 +01:00

WIP: payment schedules dashboard

This commit is contained in:
Sylvain 2021-02-09 17:18:33 +01:00
parent d767cf5f85
commit 16bb4bc76d
16 changed files with 147 additions and 8 deletions

View File

@ -6,6 +6,15 @@ class API::PaymentSchedulesController < API::ApiController
before_action :set_payment_schedule, only: %i[download cancel]
before_action :set_payment_schedule_item, only: %i[cash_check refresh_item pay_item]
def index
@payment_schedules = PaymentSchedule.where('invoicing_profile_id = ?', current_user.invoicing_profile.id)
.includes(:invoicing_profile, :payment_schedule_items, :subscription)
.joins(:invoicing_profile)
.order('payment_schedules.created_at DESC')
.page(params[:page])
.per(params[:size])
end
def list
authorize PaymentSchedule

View File

@ -14,6 +14,11 @@ export default class PaymentScheduleAPI {
return res?.data;
}
async index (query: PaymentScheduleIndexRequest): Promise<Array<PaymentSchedule>> {
const res: AxiosResponse = await apiClient.get(`/api/payment_schedules?page=${query.query.page}&size=${query.query.size}`);
return res?.data;
}
async cashCheck(paymentScheduleItemId: number): Promise<CashCheckResponse> {
const res: AxiosResponse = await apiClient.post(`/api/payment_schedules/items/${paymentScheduleItemId}/cash_check`);
return res?.data;
@ -38,5 +43,10 @@ export default class PaymentScheduleAPI {
const api = new PaymentScheduleAPI();
return wrapPromise(api.list(query));
}
static index(query: PaymentScheduleIndexRequest): IWrapPromise<Array<PaymentSchedule>> {
const api = new PaymentScheduleAPI();
return wrapPromise(api.index(query));
}
}

View File

@ -0,0 +1,88 @@
/**
* This component shows a list of all payment schedules with their associated deadlines (aka. PaymentScheduleItem) and invoices
* for the currentUser
*/
import React, { useState } from 'react';
import { IApplication } from '../models/application';
import { useTranslation } from 'react-i18next';
import { Loader } from './loader';
import { react2angular } from 'react2angular';
import PaymentScheduleAPI from '../api/payment-schedule';
import { PaymentSchedulesTable } from './payment-schedules-table';
import { FabButton } from './fab-button';
import { User } from '../models/user';
declare var Application: IApplication;
interface PaymentSchedulesDashboardProps {
currentUser: User
}
const PAGE_SIZE = 20;
const paymentSchedulesIndex = PaymentScheduleAPI.index({ query: { page: 1, size: PAGE_SIZE } });
const PaymentSchedulesDashboard: React.FC<PaymentSchedulesDashboardProps> = ({ currentUser }) => {
const { t } = useTranslation('logged');
const [paymentSchedules, setPaymentSchedules] = useState(paymentSchedulesIndex.read());
const [pageNumber, setPageNumber] = useState(1);
/**
* Fetch from the API the next payment schedules to display, for the current filters, and append them to the current results table.
*/
const handleLoadMore = (): void => {
setPageNumber(pageNumber + 1);
const api = new PaymentScheduleAPI();
api.index({ query: { page: pageNumber + 1, size: PAGE_SIZE }}).then((res) => {
const list = paymentSchedules.concat(res);
setPaymentSchedules(list);
});
}
/**
* Reload from te API all the currently displayed payment schedules
*/
const handleRefreshList = (): void => {
const api = new PaymentScheduleAPI();
api.index({ query: { page: 1, size: PAGE_SIZE * pageNumber }}).then((res) => {
setPaymentSchedules(res);
});
}
/**
* Check if the current collection of payment schedules is empty or not.
*/
const hasSchedules = (): boolean => {
return paymentSchedules.length > 0;
}
/**
* Check if there are some results for the current filters that aren't currently shown.
*/
const hasMoreSchedules = (): boolean => {
return hasSchedules() && paymentSchedules.length < paymentSchedules[0].max_length;
}
return (
<div className="payment-schedules-dashboard">
{!hasSchedules() && <div>{t('app.admin.invoices.payment_schedules.no_payment_schedules')}</div>}
{hasSchedules() && <div className="schedules-list">
<PaymentSchedulesTable paymentSchedules={paymentSchedules} showCustomer={false} refreshList={handleRefreshList} operator={currentUser} />
{hasMoreSchedules() && <FabButton className="load-more" onClick={handleLoadMore}>{t('app.admin.invoices.payment_schedules.load_more')}</FabButton>}
</div>}
</div>
);
}
const PaymentSchedulesDashboardWrapper: React.FC<PaymentSchedulesDashboardProps> = ({ currentUser }) => {
return (
<Loader>
<PaymentSchedulesDashboard currentUser={currentUser} />
</Loader>
);
}
Application.Components.component('paymentSchedulesDashboard', react2angular(PaymentSchedulesDashboardWrapper, ['currentUser']));

View File

@ -20,7 +20,7 @@ interface PaymentSchedulesListProps {
}
const PAGE_SIZE = 20;
const paymentSchedulesList = PaymentScheduleAPI.list({ query: { page: 1, size: 20 } });
const paymentSchedulesList = PaymentScheduleAPI.list({ query: { page: 1, size: PAGE_SIZE } });
const PaymentSchedulesList: React.FC<PaymentSchedulesListProps> = ({ currentUser }) => {
const { t } = useTranslation('admin');

View File

@ -30,13 +30,13 @@ Application.Controllers.controller('DashboardController', ['$scope', 'memberProm
const initialize = () => $scope.social.networks = filterNetworks();
/**
* Filter social network or website that are associated with the profile of the user provided in promise
* Filter the social networks or websites that are associated with the profile of the user provided in promise
* and return the filtered networks
* @return {Array}
*/
var filterNetworks = function () {
const filterNetworks = function () {
const networks = [];
for (let network of Array.from(SocialNetworks)) {
for (const network of Array.from(SocialNetworks)) {
if ($scope.user.profile[network] && ($scope.user.profile[network].length > 0)) {
networks.push(network);
}

View File

@ -205,6 +205,15 @@ angular.module('application.router', ['ui.router'])
}
}
})
.state('app.logged.dashboard.payment_schedules', {
url: '/payment_schedules',
views: {
'main@': {
templateUrl: '/dashboard/payment_schedules.html',
controller: 'DashboardController'
}
}
})
.state('app.logged.dashboard.wallet', {
url: '/wallet',
abstract: !Fablab.walletModule,

View File

@ -16,6 +16,7 @@
<li ui-sref-active="active"><a class="text-black" href="#" ui-sref="app.logged.dashboard.trainings" translate>{{ 'app.public.common.my_trainings' }}</a></li>
<li ui-sref-active="active"><a class="text-black" href="#" ui-sref="app.logged.dashboard.events" translate>{{ 'app.public.common.my_events' }}</a></li>
<li ui-sref-active="active" ng-show="$root.modules.invoicing"><a class="text-black" href="#" ui-sref="app.logged.dashboard.invoices" translate>{{ 'app.public.common.my_invoices' }}</a></li>
<li ui-sref-active="active" ng-show="$root.modules.invoicing"><a class="text-black" href="#" ui-sref="app.logged.dashboard.payment_schedules" translate>{{ 'app.public.common.my_payment_schedules' }}</a></li>
<li ng-show="$root.modules.wallet" ui-sref-active="active"><a class="text-black" href="#" ui-sref="app.logged.dashboard.wallet" translate>{{ 'app.public.common.my_wallet' }}</a></li>
</ul>
</section>

View File

@ -0,0 +1,11 @@
<div>
<section class="heading">
<div class="row no-gutter">
<ng-include src="'/dashboard/nav.html'"></ng-include>
</div>
</section>
<payment-schedules-dashboard current-user="currentUser" />
</div>

View File

@ -10,7 +10,7 @@
<span class="avatar avatar-block text-center">
<fab-user-avatar ng-model="member.profile.user_avatar" avatar-class="thumb-50"></fab-user-avatar>
<!-- <i class="on b-white bottom"></i> -->
<a ><span class="user-name m-l-sm m-t-xs">{{member.name}}</span></a>
<a><span class="user-name m-l-sm m-t-xs">{{member.name}}</span></a>
</span>
</div>

View File

@ -40,6 +40,7 @@
<li><a ui-sref="app.logged.dashboard.trainings" translate>{{ 'app.public.common.my_trainings' }}</a></li>
<li><a ui-sref="app.logged.dashboard.events" translate>{{ 'app.public.common.my_events' }}</a></li>
<li><a ui-sref="app.logged.dashboard.invoices" ng-show="$root.modules.invoicing" translate>{{ 'app.public.common.my_invoices' }}</a></li>
<li><a ui-sref="app.logged.dashboard.payment_schedules" ng-show="$root.modules.invoicing" translate>{{ 'app.public.common.my_payment_schedules' }}</a></li>
<li ng-show="$root.modules.wallet"><a ui-sref="app.logged.dashboard.wallet" translate>{{ 'app.public.common.my_wallet' }}</a></li>
<li class="divider" ng-if="isAuthorized(['admin', 'manager'])"></li>
<li><a class="text-black pointer" ng-click="help($event)" ng-if="isAuthorized(['admin', 'manager'])"><i class="fa fa-question-circle"></i> <span translate>{{ 'app.public.common.help' }}</span> </a></li>

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
max_schedules = @payment_schedules.except(:offset, :limit, :order).count
json.array! @payment_schedules do |ps|
json.max_length max_schedules
json.partial! 'api/payment_schedules/payment_schedule', payment_schedule: ps
end

View File

@ -7,4 +7,4 @@
DATE: I18n.l(@attached_object.due_date, format: :long)) %>
<%= t('.body.error') %>
</p>
<p><%= t('.body.action', DASHBOARD: link_to(t('.body.your_dashboard'), "#{root_url}#!/dashboard/invoices")) %></p>
<p><%= t('.body.action', DASHBOARD: link_to(t('.body.your_dashboard'), "#{root_url}#!/dashboard/payment_schedules")) %></p>

View File

@ -14,7 +14,7 @@
<p><%= t('.body.schedule_in_your_dashboard_html',
DASHBOARD: link_to(
t('.body.your_dashboard'),
"#{root_url}#!/dashboard/invoices"
"#{root_url}#!/dashboard/payment_schedules"
)
) %>
</p>

View File

@ -19,6 +19,7 @@ en:
my_trainings: "My Trainings"
my_events: "My Events"
my_invoices: "My Invoices"
my_payment_schedules: "My payment schedules"
my_wallet: "My Wallet"
#contextual help
help: "Help"

View File

@ -19,6 +19,7 @@ fr:
my_trainings: "Mes formations"
my_events: "Mes événements"
my_invoices: "Mes factures"
my_payment_schedules: "Mes échéanciers de paiement"
my_wallet: "Mon porte-monnaie"
#contextual help
help: "Aide"

View File

@ -111,7 +111,7 @@ Rails.application.routes.draw do
get 'first', action: 'first', on: :collection
end
resources :payment_schedules, only: %i[show] do
resources :payment_schedules, only: %i[index show] do
post 'list', action: 'list', on: :collection
put 'cancel', on: :member
get 'download', on: :member