1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-03-21 12:29:03 +01:00

(feat) pagination

This commit is contained in:
vincent 2022-09-09 13:48:20 +02:00
parent d5d8a972cf
commit 6678412cd6
11 changed files with 136 additions and 14 deletions

View File

@ -8,6 +8,7 @@ class API::ProductsController < API::ApiController
def index
@products = ProductService.list(params)
@pages = ProductService.pages if params[:page].present?
end
def show

View File

@ -1,12 +1,12 @@
import apiClient from './clients/api-client';
import { AxiosResponse } from 'axios';
import { serialize } from 'object-to-formdata';
import { Product, ProductIndexFilter } from '../models/product';
import { Product, ProductIndexFilter, ProductsIndex } from '../models/product';
import ApiLib from '../lib/api';
export default class ProductAPI {
static async index (filters?: ProductIndexFilter): Promise<Array<Product>> {
const res: AxiosResponse<Array<Product>> = await apiClient.get(`/api/products${ApiLib.filtersToQuery(filters)}`);
static async index (filters?: ProductIndexFilter): Promise<ProductsIndex> {
const res: AxiosResponse<ProductsIndex> = await apiClient.get(`/api/products${ApiLib.filtersToQuery(filters)}`);
return res?.data;
}

View File

@ -0,0 +1,43 @@
import React from 'react';
import { CaretDoubleLeft, CaretLeft, CaretRight, CaretDoubleRight } from 'phosphor-react';
interface FabPaginationProps {
pageCount: number,
currentPage: number,
selectPage: (page: number) => void
}
/**
* Renders a pagination navigation
*/
export const FabPagination: React.FC<FabPaginationProps> = ({ pageCount, currentPage, selectPage }) => {
return (
<nav className='fab-pagination'>
{currentPage - 2 > 1 &&
<button onClick={() => selectPage(1)}><CaretDoubleLeft size={24} /></button>
}
{currentPage - 1 >= 1 &&
<button onClick={() => selectPage(currentPage - 1)}><CaretLeft size={24} /></button>
}
{currentPage - 2 >= 1 &&
<button onClick={() => selectPage(currentPage - 2)}>{currentPage - 2}</button>
}
{currentPage - 1 >= 1 &&
<button onClick={() => selectPage(currentPage - 1)}>{currentPage - 1}</button>
}
<button className='is-active'>{currentPage}</button>
{currentPage + 1 <= pageCount &&
<button onClick={() => selectPage(currentPage + 1)}>{currentPage + 1}</button>
}
{currentPage + 2 <= pageCount &&
<button onClick={() => selectPage(currentPage + 2)}>{currentPage + 2}</button>
}
{currentPage + 1 <= pageCount &&
<button onClick={() => selectPage(currentPage + 1)}><CaretRight size={24} /></button>
}
{currentPage + 2 < pageCount &&
<button onClick={() => selectPage(pageCount)}><CaretDoubleRight size={24} /></button>
}
</nav>
);
};

View File

@ -1,10 +1,11 @@
import React from 'react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { react2angular } from 'react2angular';
import { Loader } from '../base/loader';
import { IApplication } from '../../models/application';
import { StoreListHeader } from './store-list-header';
import { OrderItem } from './order-item';
import { FabPagination } from '../base/fab-pagination';
declare const Application: IApplication;
@ -25,6 +26,11 @@ type selectOption = { value: number, label: string };
export const OrdersDashboard: React.FC<OrdersDashboardProps> = ({ onError }) => {
const { t } = useTranslation('public');
// TODO: delete next eslint disable
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [pageCount, setPageCount] = useState<number>(0);
const [currentPage, setCurrentPage] = useState<number>(1);
/**
* Creates sorting options to the react-select format
*/
@ -56,6 +62,9 @@ export const OrdersDashboard: React.FC<OrdersDashboardProps> = ({ onError }) =>
<div className="orders-list">
<OrderItem />
</div>
{pageCount > 1 &&
<FabPagination pageCount={pageCount} currentPage={currentPage} selectPage={setCurrentPage} />
}
</div>
</section>
);

View File

@ -14,6 +14,7 @@ import MachineAPI from '../../api/machine';
import { AccordionItem } from './accordion-item';
import { X } from 'phosphor-react';
import { StoreListHeader } from './store-list-header';
import { FabPagination } from '../base/fab-pagination';
declare const Application: IApplication;
@ -33,8 +34,6 @@ interface ProductsProps {
const Products: React.FC<ProductsProps> = ({ onSuccess, onError }) => {
const { t } = useTranslation('admin');
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [products, setProducts] = useState<Array<Product>>([]);
const [filteredProductsList, setFilteredProductList] = useImmer<Array<Product>>([]);
const [features, setFeatures] = useImmer<Filters>(initFilters);
const [filterVisible, setFilterVisible] = useState<boolean>(false);
@ -44,11 +43,13 @@ const Products: React.FC<ProductsProps> = ({ onSuccess, onError }) => {
const [machines, setMachines] = useState<checklistOption[]>([]);
const [update, setUpdate] = useState(false);
const [accordion, setAccordion] = useState({});
const [pageCount, setPageCount] = useState<number>(0);
const [currentPage, setCurrentPage] = useState<number>(1);
useEffect(() => {
ProductAPI.index().then(data => {
setProducts(data);
setFilteredProductList(data);
ProductAPI.index({ page: 1 }).then(data => {
setPageCount(data.total_pages);
setFilteredProductList(data.products);
});
ProductCategoryAPI.index().then(data => {
@ -71,6 +72,14 @@ const Products: React.FC<ProductsProps> = ({ onSuccess, onError }) => {
}).catch(onError);
}, []);
useEffect(() => {
ProductAPI.index({ page: currentPage }).then(data => {
setFilteredProductList(data.products);
setPageCount(data.total_pages);
window.document.getElementById('content-main').scrollTo({ top: 100, behavior: 'smooth' });
});
}, [currentPage]);
useEffect(() => {
applyFilters();
setClearFilters(false);
@ -91,7 +100,7 @@ const Products: React.FC<ProductsProps> = ({ onSuccess, onError }) => {
try {
await ProductAPI.destroy(productId);
const data = await ProductAPI.index();
setProducts(data);
setFilteredProductList(data.products);
onSuccess(t('app.admin.store.products.successfully_deleted'));
} catch (e) {
onError(t('app.admin.store.products.unable_to_delete') + e);
@ -307,6 +316,9 @@ const Products: React.FC<ProductsProps> = ({ onSuccess, onError }) => {
/>
))}
</div>
{pageCount > 1 &&
<FabPagination pageCount={pageCount} currentPage={currentPage} selectPage={setCurrentPage} />
}
</div>
</div>
);

View File

@ -16,6 +16,7 @@ import { User } from '../../models/user';
import { Order } from '../../models/order';
import { AccordionItem } from './accordion-item';
import { StoreListHeader } from './store-list-header';
import { FabPagination } from '../base/fab-pagination';
declare const Application: IApplication;
@ -45,10 +46,13 @@ const Store: React.FC<StoreProps> = ({ onError, onSuccess, currentUser }) => {
const [filterVisible, setFilterVisible] = useState<boolean>(false);
const [machines, setMachines] = useState<checklistOption[]>([]);
const [accordion, setAccordion] = useState({});
const [pageCount, setPageCount] = useState<number>(0);
const [currentPage, setCurrentPage] = useState<number>(1);
useEffect(() => {
ProductAPI.index({ is_active: true }).then(data => {
setProducts(data);
ProductAPI.index({ page: 1 }).then(data => {
setPageCount(data.total_pages);
setProducts(data.products);
}).catch(() => {
onError(t('app.public.store.unexpected_error_occurred'));
});
@ -71,6 +75,14 @@ const Store: React.FC<StoreProps> = ({ onError, onSuccess, currentUser }) => {
emitCustomEvent('CartUpdate', cart);
}, [cart]);
useEffect(() => {
ProductAPI.index({ page: currentPage }).then(data => {
setProducts(data.products);
setPageCount(data.total_pages);
window.document.getElementById('content-main').scrollTo({ top: 100, behavior: 'smooth' });
});
}, [currentPage]);
/**
* Create categories tree (parent/children)
*/
@ -237,6 +249,9 @@ const Store: React.FC<StoreProps> = ({ onError, onSuccess, currentUser }) => {
<StoreProductItem key={product.id} product={product} cart={cart} onSuccessAddProductToCart={addToCart} />
))}
</div>
{pageCount > 1 &&
<FabPagination pageCount={pageCount} currentPage={currentPage} selectPage={setCurrentPage} />
}
</div>
</div>
);

View File

@ -2,7 +2,8 @@ import { TDateISO } from '../typings/date-iso';
import { ApiFilter } from './api';
export interface ProductIndexFilter extends ApiFilter {
is_active: boolean,
is_active?: boolean,
page?: number
}
export enum StockType {
@ -15,6 +16,11 @@ export interface Stock {
external: number,
}
export interface ProductsIndex {
total_pages?: number,
products: Array<Product>
}
export interface Product {
id: number,
name: string,

View File

@ -25,6 +25,7 @@
@import "modules/base/fab-input";
@import "modules/base/fab-modal";
@import "modules/base/fab-output-copy";
@import "modules/base/fab-pagination";
@import "modules/base/fab-panel";
@import "modules/base/fab-popover";
@import "modules/base/fab-state-label";

View File

@ -0,0 +1,25 @@
.fab-pagination {
display: grid;
grid-auto-flow: column;
grid-template-columns: repeat(9, min-content);
justify-content: center;
gap: 1.6rem;
button {
min-width: 4rem;
height: 4rem;
display: flex;
justify-content: center;
align-items: center;
background-color: transparent;
border: none;
border-radius: var(--border-radius-sm);
@include text-lg(500);
&:hover:not(.is-active) {
background-color: var(--gray-soft);
}
}
.is-active {
background-color: var(--main);
color: var(--main-text-color);
}
}

View File

@ -2,15 +2,24 @@
# Provides methods for Product
class ProductService
PRODUCTS_PER_PAGE = 2
def self.list(filters)
products = Product.includes(:product_images)
if filters[:is_active].present?
state = filters[:disabled] == 'false' ? [nil, false] : true
products = products.where(is_active: state)
end
if filters[:page].present?
products = products.page(filters[:page]).per(PRODUCTS_PER_PAGE)
end
products
end
def self.pages
Product.page(1).per(PRODUCTS_PER_PAGE).total_pages
end
# amount params multiplied by hundred
def self.amount_multiplied_by_hundred(amount)
if amount.present?

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true
json.array! @products do |product|
json.total_pages @pages if @pages.present?
json.products @products do |product|
json.extract! product, :id, :name, :slug, :sku, :is_active, :product_category_id, :quantity_min, :stock, :machine_ids,
:low_stock_threshold
json.amount product.amount / 100.0 if product.amount.present?