1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-18 07:52:23 +01:00

products page in front

This commit is contained in:
Du Peng 2022-07-13 19:34:38 +02:00
parent 1e3e7854b2
commit e23e83000d
11 changed files with 244 additions and 10 deletions

View File

@ -0,0 +1,30 @@
import apiClient from './clients/api-client';
import { AxiosResponse } from 'axios';
import { Product } from '../models/product';
export default class ProductAPI {
static async index (): Promise<Array<Product>> {
const res: AxiosResponse<Array<Product>> = await apiClient.get('/api/products');
return res?.data;
}
static async get (id: number): Promise<Product> {
const res: AxiosResponse<Product> = await apiClient.get(`/api/products/${id}`);
return res?.data;
}
static async create (product: Product): Promise<Product> {
const res: AxiosResponse<Product> = await apiClient.post('/api/products', { product });
return res?.data;
}
static async update (product: Product): Promise<Product> {
const res: AxiosResponse<Product> = await apiClient.patch(`/api/products/${product.id}`, { product });
return res?.data;
}
static async destroy (productId: number): Promise<void> {
const res: AxiosResponse<void> = await apiClient.delete(`/api/products/${productId}`);
return res?.data;
}
}

View File

@ -0,0 +1,50 @@
import React from 'react';
import { FabButton } from '../base/fab-button';
import { Product } from '../../models/product';
interface ProductsListProps {
products: Array<Product>,
onEdit: (product: Product) => void,
onDelete: (productId: number) => void,
}
/**
* This component shows a list of all Products
*/
export const ProductsList: React.FC<ProductsListProps> = ({ products, onEdit, onDelete }) => {
/**
* Init the process of editing the given product
*/
const editProduct = (product: Product): () => void => {
return (): void => {
onEdit(product);
};
};
/**
* Init the process of delete the given product
*/
const deleteProduct = (productId: number): () => void => {
return (): void => {
onDelete(productId);
};
};
return (
<div>
{products.map((product) => (
<div key={product.id}>
{product.name}
<div className="buttons">
<FabButton className="edit-btn" onClick={editProduct(product)}>
<i className="fa fa-edit" />
</FabButton>
<FabButton className="delete-btn" onClick={deleteProduct(product.id)}>
<i className="fa fa-trash" />
</FabButton>
</div>
</div>
))}
</div>
);
};

View File

@ -0,0 +1,77 @@
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { react2angular } from 'react2angular';
import { HtmlTranslate } from '../base/html-translate';
import { Loader } from '../base/loader';
import { IApplication } from '../../models/application';
import { FabAlert } from '../base/fab-alert';
import { FabButton } from '../base/fab-button';
import { ProductsList } from './products-list';
import { Product } from '../../models/product';
import ProductAPI from '../../api/product';
declare const Application: IApplication;
interface ProductsProps {
onSuccess: (message: string) => void,
onError: (message: string) => void,
}
/**
* This component shows all Products and filter
*/
const Products: React.FC<ProductsProps> = ({ onSuccess, onError }) => {
const { t } = useTranslation('admin');
const [products, setProducts] = useState<Array<Product>>([]);
const [product, setProduct] = useState<Product>(null);
useEffect(() => {
ProductAPI.index().then(data => {
setProducts(data);
});
}, []);
/**
* Open edit the product modal
*/
const editProduct = (product: Product) => {
setProduct(product);
};
/**
* Delete a product
*/
const deleteProduct = async (productId: number): Promise<void> => {
try {
await ProductAPI.destroy(productId);
const data = await ProductAPI.index();
setProducts(data);
onSuccess(t('app.admin.store.products.successfully_deleted'));
} catch (e) {
onError(t('app.admin.store.products.unable_to_delete') + e);
}
};
return (
<div>
<h2>{t('app.admin.store.products.all_products')}</h2>
<FabButton className="save">{t('app.admin.store.products.create_a_product')}</FabButton>
<ProductsList
products={products}
onEdit={editProduct}
onDelete={deleteProduct}
/>
</div>
);
};
const ProductsWrapper: React.FC<ProductsProps> = ({ onSuccess, onError }) => {
return (
<Loader>
<Products onSuccess={onSuccess} onError={onError} />
</Loader>
);
};
Application.Components.component('products', react2angular(ProductsWrapper, ['onSuccess', 'onError']));

View File

@ -4,9 +4,35 @@
*/
'use strict';
Application.Controllers.controller('AdminStoreController', ['$scope', 'CSRF', 'growl',
function ($scope, CSRF, growl) {
Application.Controllers.controller('AdminStoreController', ['$scope', 'CSRF', 'growl', '$state',
function ($scope, CSRF, growl, $state) {
/* PRIVATE SCOPE */
// Map of tab state and index
const TABS = {
'app.admin.store.settings': 0,
'app.admin.store.products': 1,
'app.admin.store.categories': 2,
'app.admin.store.orders': 3
};
/* PUBLIC SCOPE */
// default tab: products
$scope.tabs = {
active: TABS[$state.current.name]
};
/**
* Callback triggered in click tab
*/
$scope.selectTab = () => {
setTimeout(function () {
const currentTab = _.keys(TABS)[$scope.tabs.active];
if (currentTab !== $state.current.name) {
$state.go(currentTab, { location: true, notify: false, reload: false });
}
});
};
/**
* Callback triggered in case of error
*/

View File

@ -84,7 +84,7 @@ Application.Controllers.controller('MainNavController', ['$scope', 'settingsProm
authorizedRoles: ['admin', 'manager']
},
{
state: 'app.admin.store',
state: 'app.admin.store.products',
linkText: 'app.public.common.manage_the_store',
linkIcon: 'cart-plus',
authorizedRoles: ['admin', 'manager']

View File

@ -0,0 +1,24 @@
export enum StockType {
internal = 'internal',
external = 'external'
}
export interface Stock {
internal: number,
external: number,
}
export interface Product {
id: number,
name: string,
slug: string,
sku: string,
description: string,
is_active: boolean,
product_category_id: number,
amount: number,
quantity_min: number,
stock: Stock,
low_stock_alert: boolean,
low_stock_threshold: number,
}

View File

@ -1105,6 +1105,7 @@ angular.module('application.router', ['ui.router'])
})
.state('app.admin.store', {
abstract: true,
url: '/admin/store',
views: {
'main@': {
@ -1114,6 +1115,22 @@ angular.module('application.router', ['ui.router'])
}
})
.state('app.admin.store.settings', {
url: '/settings'
})
.state('app.admin.store.products', {
url: '/products'
})
.state('app.admin.store.categories', {
url: '/categories'
})
.state('app.admin.store.orders', {
url: '/orders'
})
// OpenAPI Clients
.state('app.admin.open_api_clients', {
url: '/open_api_clients',

View File

@ -20,19 +20,19 @@
<div class="col-md-12">
<uib-tabset justified="true" active="tabs.active">
<uib-tab heading="{{ 'app.admin.store.settings' | translate }}" index="0">
<uib-tab heading="{{ 'app.admin.store.settings' | translate }}" index="0" select="selectTab()">
<ng-include src="'/admin/store/settings.html'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'app.admin.store.all_products' | translate }}" index="1">
<uib-tab heading="{{ 'app.admin.store.all_products' | translate }}" index="1" select="selectTab()">
<ng-include src="'/admin/store/products.html'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'app.admin.store.categories_of_store' | translate }}" index="2">
<uib-tab heading="{{ 'app.admin.store.categories_of_store' | translate }}" index="2" select="selectTab()">
<ng-include src="'/admin/store/categories.html'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'app.admin.store.the_orders' | translate }}" index="3">
<uib-tab heading="{{ 'app.admin.store.the_orders' | translate }}" index="3" select="selectTab()">
<ng-include src="'/admin/store/orders.html'"></ng-include>
</uib-tab>
</uib-tabset>

View File

@ -1 +1 @@
<h2>Products page</h2>
<products on-success="onSuccess" on-error="onError"/>

View File

@ -1925,4 +1925,9 @@ en:
success: "The category has been successfully deleted"
save: "Save"
required: "This field is required"
slug_pattern: "Only lowercase alphanumeric groups of characters separated by an hyphen"
slug_pattern: "Only lowercase alphanumeric groups of characters separated by an hyphen"
products:
all_products: "All products"
create_a_product: "Create a product"
successfully_deleted: "The product has been successfully deleted"
unable_to_delete: "Unable to delete the product: "

View File

@ -1925,4 +1925,9 @@ fr:
success: "La catégorie a bien été supprimée"
save: "Enregistrer"
required: "This field is required"
slug_pattern: "Only lowercase alphanumeric groups of characters separated by an hyphen"
slug_pattern: "Only lowercase alphanumeric groups of characters separated by an hyphen"
products:
all_products: "Tous les produits"
create_a_product: "Créer un produit"
successfully_deleted: "Le produit a bien été supprimé"
unable_to_delete: "Impossible de supprimer le produit: "