Skip to content

Commit

Permalink
#1009 create list promotion page
Browse files Browse the repository at this point in the history
  • Loading branch information
nguyenvanhadncntt committed Sep 16, 2024
1 parent 1e5cacc commit 70f3647
Show file tree
Hide file tree
Showing 39 changed files with 651 additions and 89 deletions.
25 changes: 17 additions & 8 deletions backoffice/asset/data/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import {
COUNTRY_URL,
INVENTORY_WAREHOUSE_PRODUCTS_URL,
INVENTORY_WAREHOUSE_STOCKS_URL,
STATE_OR_PROVINCE_URL,
TAX_CLASS_URL,
TAX_RATE_URL,
WAREHOUSE_URL,
MANAGER_PROMOTIONS_URL,
SALES_GIFT_CARDS_URL,
SALES_ORDERS_URL,
SALES_SHIPMENTS_URL,
SALES_RETURN_REQUESTS_URL,
SALES_RECURRING_PAYMENTS_URL,
SALES_GIFT_CARDS_URL,
SALES_RETURN_REQUESTS_URL,
SALES_SHIPMENTS_URL,
SALES_SHOPPING_CARTS_AND_WISHLISTS_URL,
STATE_OR_PROVINCE_URL,
SYSTEM_PAYMENT_PROVIDERS,
SYSTEM_SETTINGS,
TAX_CLASS_URL,
TAX_RATE_URL,
WAREHOUSE_URL,
WEBHOOKS_URL,
} from '@constants/Common';

Expand Down Expand Up @@ -139,7 +140,7 @@ export const menu_sale_item_data = [
link: SALES_GIFT_CARDS_URL,
},
{
id: 5,
id: 6,
name: 'Shopping carts and wishlists',
link: SALES_SHOPPING_CARTS_AND_WISHLISTS_URL,
},
Expand All @@ -162,3 +163,11 @@ export const menu_system_item_data = [
link: WEBHOOKS_URL,
},
];

export const menu_promotion_item_data = [
{
id: 1,
name: 'Manager Promotions',
link: MANAGER_PROMOTIONS_URL,
},
];
35 changes: 29 additions & 6 deletions backoffice/common/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import Head from 'next/head';
import Link from 'next/link';
import { MouseEventHandler, useState } from 'react';
import { Navbar } from 'react-bootstrap';
import { ToastContainer } from 'react-toastify';
import styles from '../../styles/Layout.module.css';
import AuthenticationInfo from './AuthenticationInfo';
import { MouseEventHandler, useState } from 'react';
import Link from 'next/link';
import {
menu_catalog_item_data,
menu_customer_item_data,
menu_inventory_item_data,
menu_location_item_data,
menu_tax_item_data,
menu_promotion_item_data,
menu_sale_item_data,
menu_system_item_data,
menu_tax_item_data,
} from '../../asset/data/sidebar';
import styles from '../../styles/Layout.module.css';
import AuthenticationInfo from './AuthenticationInfo';

interface DataProps {
id: number;
Expand Down Expand Up @@ -175,7 +176,7 @@ const Sidebar = (menu: MenuProps) => {
data-target="#salesSubmenu"
data-bs-toggle="collapse"
aria-controls="salesSubmenu"
aria-expanded="false"
aria-expanded="true"
className="dropdown-toggle"
>
<span className="fa fa-shopping-cart"></span> Sales
Expand Down Expand Up @@ -235,6 +236,28 @@ const Sidebar = (menu: MenuProps) => {
</ul>
</li>

<li onClick={() => changeMenu('promotion')}
className={menuActive == 'promotion' ? 'active' : ''}
>
<Link
href="#promotionSubmenu"
data-target="#promotionSubmenu"
data-bs-toggle="collapse"
aria-controls="promotionSubmenu"
aria-expanded="false"
className="dropdown-toggle"
>
<span className="fa fa-money"></span> Promotion
</Link>
<ul className="collapse list-unstyled" id="promotionSubmenu">
<ListItem
data={menu_promotion_item_data}
childActive={menu.childActive}
changeChildMenu={menu.changeChildMenu}
/>
</ul>
</li>

<li className={menuActive == 'tax' ? 'active' : ''} onClick={() => changeMenu('tax')}>
<Link
href="#taxSubmenu"
Expand Down
1 change: 1 addition & 0 deletions backoffice/constants/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const SALES_RETURN_REQUESTS_URL = '/sales/return-requests';
export const SALES_RECURRING_PAYMENTS_URL = '/sales/recurring-payments';
export const SALES_GIFT_CARDS_URL = '/sales/gift-cards';
export const SALES_SHOPPING_CARTS_AND_WISHLISTS_URL = '/sales/shopping-carts-and-wishlists';
export const MANAGER_PROMOTIONS_URL = '/promotion/manager-promotion';

export const SYSTEM_PAYMENT_PROVIDERS = '/system/payment-providers';
export const SYSTEM_SETTINGS = '/system/settings';
Expand Down
6 changes: 6 additions & 0 deletions backoffice/modules/promotion/models/Brand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type BrandVm = {
id: number;
name: string;
slug: string;
isPublish: boolean;
};
6 changes: 6 additions & 0 deletions backoffice/modules/promotion/models/Category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type CategoryGetVm = {
id: number;
name: string;
slug: string;
parentId: number;
};
11 changes: 11 additions & 0 deletions backoffice/modules/promotion/models/Product.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type ProductVm = {
id: number;
name: string;
slug: string;
isAllowedToOrder: boolean;
isPublished: boolean;
isFeatured: boolean;
isVisibleIndividually: boolean;
createdOn: Date;
taxClassId: number;
};
39 changes: 39 additions & 0 deletions backoffice/modules/promotion/models/Promotion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { BrandVm } from './Brand';
import { CategoryGetVm } from './Category';
import { ProductVm } from './Product';

export type PromotionDetail = {
id: number;
name: string;
slug: string;
description: string;
couponCode: string;
usageLimit: number;
usageCount: number;
discountType: string;
applyTo: string;
usageType: string;
discountPercentage: number;
discountAmount: number;
isActive: boolean;
startDate: Date;
endDate: Date;
brands: BrandVm[];
categories: CategoryGetVm[];
products: ProductVm[];
};

export type PromotionPage = {
promotionDetailVmList: PromotionDetail[];
pageNo: number;
pageSize: number;
totalPage: number;
totalElements: number;
};

export type PromotionListRequest = {
promotionName: string;
couponCode: string;
pageNo: number;
pageSize: number;
};
17 changes: 17 additions & 0 deletions backoffice/modules/promotion/services/PromotionService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import apiClientService from '@commonServices/ApiClientService';
import { PromotionListRequest } from '../models/Promotion';

const baseUrl = '/api/promotion/backoffice/promotions';

export async function getPromotions(request: PromotionListRequest) {
const url = `${baseUrl}?${createRequestFromObject(request)}`;
return (await apiClientService.get(url)).json();
}

function createRequestFromObject(request: any): string {
return Object.keys(request)
.map(function (key) {
return key + '=' + request[key];
})
.join('&');
}
149 changes: 149 additions & 0 deletions backoffice/pages/promotion/manager-promotion/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE } from '@constants/Common';
import { PromotionListRequest, PromotionPage } from 'modules/promotion/models/Promotion';
import { getPromotions } from 'modules/promotion/services/PromotionService';
import { NextPage } from 'next';
import Link from 'next/link';
import { useEffect, useState } from 'react';
import { Button, Table } from 'react-bootstrap';
import ReactPaginate from 'react-paginate';

const PromotionList: NextPage = () => {
const [isLoading, setLoading] = useState(false);
const [promotionPage, setPromotionPage] = useState<PromotionPage>();
const [couponCode, setCouponCode] = useState<string>('');
const [promotionName, setPromotionName] = useState<string>('');
const [pageNo, setPageNo] = useState<number>(DEFAULT_PAGE_NUMBER);
const [totalPage, setTotalPage] = useState<number>(1);

useEffect(() => {
setLoading(true);
getPromotionList();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageNo]);

const getPromotionList = () => {
getPromotions(createRequestParams()).then((data) => {
setLoading(false);
setTotalPage(data.totalPage);
setPromotionPage(data);
});
};

const createRequestParams = (): PromotionListRequest => {
return {
couponCode: couponCode,
pageNo: pageNo,
pageSize: DEFAULT_PAGE_SIZE,
promotionName: promotionName,
};
};

const changePage = ({ selected }: any) => {
setPageNo(selected);
};

const convertToStringDate = (date: Date) => {
if (typeof date === 'string') {
date = new Date(date);
}
const month = date.getMonth() + 1;
return `${date.getFullYear()}-${month > 9 ? month : `0${month}`}-${date.getDate()}`;
};

return (
<>
<div className="row mt-5">
<div className="col-md-8">
<h2 className="text-danger font-weight-bold mb-3">Promotions</h2>
</div>
<div className="col-md-4 text-right">
<Link href="/catalog/brands/create">
<Button>Add new Promotion</Button>
</Link>
</div>
</div>
<div className="row mt-5">
<div className="col-md-4">
<input
type="text"
className="form-control"
placeholder="Coupon Code"
value={couponCode}
onChange={(e) => setCouponCode(e.target.value)}
/>
</div>
<div className="col-md-4">
<input
type="text"
className="form-control"
placeholder="Promotion Name"
value={promotionName}
onChange={(e) => setPromotionName(e.target.value)}
/>
</div>
<div className="col-md-4">
<Button onClick={getPromotionList}>Search</Button>
</div>
</div>
{!isLoading && (
<Table striped bordered hover>
<thead>
<tr>
<th>Name</th>
<th>Coupon code</th>
<th>Slug</th>
<th>Description</th>
<th>Is active</th>
<th>Discount Type</th>
<th>Usage count</th>
<th>Usage limit</th>
<th>Apply To</th>
<th>Start date</th>
<th>End date</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{promotionPage?.promotionDetailVmList?.map((promotion) => (
<tr key={promotion.id}>
<td>{promotion.name}</td>
<td>{promotion.couponCode}</td>
<td>{promotion.slug}</td>
<td>{promotion.description}</td>
<td>{promotion.isActive}</td>
<td>{promotion.discountType}</td>
<td>{promotion.usageCount}</td>
<td>{promotion.usageLimit}</td>
<td>{promotion.applyTo}</td>
<td>{convertToStringDate(promotion.startDate)}</td>
<td>{convertToStringDate(promotion.endDate)}</td>
<td>
<Link href={`/promotion/manager-promotion/${promotion.id}`}>
<button className="btn btn-outline-primary btn-sm" type="button">
Edit
</button>
</Link>
</td>
</tr>
))}
</tbody>
</Table>
)}
{totalPage > 1 && (
<ReactPaginate
forcePage={pageNo}
previousLabel={'Previous'}
nextLabel={'Next'}
pageCount={totalPage}
onPageChange={changePage}
containerClassName={'pagination-container'}
previousClassName={'previous-btn'}
nextClassName={'next-btn'}
disabledClassName={'pagination-disabled'}
activeClassName={'pagination-active'}
/>
)}
</>
);
};
export default PromotionList;
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ public BrandController(BrandRepository brandRepository, BrandService brandServic
}

@GetMapping({"/backoffice/brands", "/storefront/brands"})
public ResponseEntity<List<BrandVm>> listBrands() {
public ResponseEntity<List<BrandVm>> listBrands(@RequestParam(required = false) String brandName) {
log.info("[Test logging with trace] Got a request");
List<BrandVm> brandVms = brandRepository.findAll().stream()
List<BrandVm> brandVms = brandRepository.findByNameContainingIgnoreCase(brandName).stream()
.map(BrandVm::fromModel)
.toList();
return ResponseEntity.ok(brandVms);
Expand Down Expand Up @@ -118,4 +118,14 @@ public ResponseEntity<Void> deleteBrand(@PathVariable long id) {
brandRepository.deleteById(id);
return ResponseEntity.noContent().build();
}

@GetMapping("/backoffice/brands/by-ids")
@ApiResponses(value = {
@ApiResponse(responseCode = "204", description = "No content", content = @Content()),
@ApiResponse(responseCode = "404", description = "Not found",
content = @Content(schema = @Schema(implementation = ErrorVm.class)))})
public ResponseEntity<List<BrandVm>> getBrandsByIds(@RequestParam List<Long> ids) {
return ResponseEntity.ok(brandService.getBrandsByIds(ids));
}

}
Loading

0 comments on commit 70f3647

Please sign in to comment.