diff --git a/src/App.js b/src/App.js index 91aef56..78614c0 100644 --- a/src/App.js +++ b/src/App.js @@ -12,6 +12,7 @@ import { ForgotPasswordPage } from "./_containers/ForgotPasswordPage"; import RegisterPage from "./_containers/RegisterPage"; import HomePage from "./_containers/HomePage"; import ListCustomer from "./_containers/ListCustomer"; +import ListGrantPage from "./_containers/ListGrantPage"; function App() { const dispatch = useDispatch(); @@ -58,6 +59,12 @@ function App() { component={ListCustomer} authentication={authentication} /> + diff --git a/src/_components/Auth/InputSelect/index.js b/src/_components/Auth/InputSelect/index.js index 5a17b2e..257f45b 100644 --- a/src/_components/Auth/InputSelect/index.js +++ b/src/_components/Auth/InputSelect/index.js @@ -38,7 +38,7 @@ const InputRadio = (props) => {
{ }} >
{ return ( <> - - "header-item" + (isActive ? " header-item-selected" : "") - } - onClick={() => setIsOpenMenuMobile(false)} - > - Thông tin chung - - - "header-item" + (isActive ? " header-item-selected" : "") - } - onClick={() => setIsOpenMenuMobile(false)} - > - Danh sách khách hàng - + {listHeader.map(item => ( + + "header-item" + (isActive ? " header-item-selected" : "") + } + onClick={() => setIsOpenMenuMobile(false)} + key={item.id} + > + {item.name} + + ))} ) } diff --git a/src/_constants/headerNews.js b/src/_constants/headerNews.js index 113a8f1..30a72cb 100644 --- a/src/_constants/headerNews.js +++ b/src/_constants/headerNews.js @@ -21,3 +21,22 @@ export const NameTitleNewsItem = { PROFILE: "Hồ sơ", // LOGIN: "login_news", }; + +export const listHeader = [ + { + id: 1, + name: 'Thông tin chung', + href: '/', + exact: true + }, + { + id: 2, + name: 'Danh sách khách hàng', + href: '/customers', + }, + { + id: 3, + name: 'Danh sách tuyến dưới', + href: '/grants' + } +] \ No newline at end of file diff --git a/src/_containers/ListCustomer/ListCustomer.style.scss b/src/_containers/ListCustomer/ListCustomer.style.scss index 8062d73..91747e3 100644 --- a/src/_containers/ListCustomer/ListCustomer.style.scss +++ b/src/_containers/ListCustomer/ListCustomer.style.scss @@ -12,13 +12,13 @@ .filter-form { display: flex; - align-items: center; + // align-items: center; gap: 24px; @include screen_mobile { flex-direction: column; gap: 0; - align-items: unset; + // align-items: unset; } .input-base-filter { @@ -86,7 +86,7 @@ } .list-customer-container { - margin-top: 64px; + margin-top: 20px; .total-customer { font-weight: 700; @@ -97,6 +97,7 @@ font-weight: 700; font-size: 18px; text-align: center; + margin-top: 64px; } .list-customer { @@ -114,7 +115,7 @@ .customer-name { font-size: 24px; font-weight: 700; - margin-bottom: 16px; + margin-bottom: 8px; } .customer-row { diff --git a/src/_containers/ListCustomer/index.js b/src/_containers/ListCustomer/index.js index f0b8ef1..0a3e4c2 100644 --- a/src/_containers/ListCustomer/index.js +++ b/src/_containers/ListCustomer/index.js @@ -13,6 +13,11 @@ export default function ListCustomer() { start_date: '', end_date: '' }) + const [errFilter, setErrFilter] = useState({ + keyword: '', + start_date: '', + end_date: '' + }) const [listCustomer, setListCustomer] = useState([]) const [isLoading, setIsLoading] = useState(false) const [isFilter, setIsFilter] = useState(false) @@ -39,6 +44,10 @@ export default function ListCustomer() { ...prev, [key]: value })) + setErrFilter(prev => ({ + ...prev, + [key]: '' + })) } const validateFilter = () => { @@ -51,12 +60,23 @@ export default function ListCustomer() { keyword: '', start_date: '' } - setIsFilter(false) + setErrFilter(iniFilter) setFilter(iniFilter) + if(!isFilter) { + return; + } + setIsFilter(false) getData(iniFilter) } const handleFilter = () => { + if(!!filter.keyword && filter.keyword.length < 3) { + setErrFilter(prev => ({ + ...prev, + keyword: 'Nhập tối thiểu 3 kí tự' + })) + return; + } setIsFilter(true) getData(filter) } @@ -87,15 +107,18 @@ export default function ListCustomer() {
changeFilter('keyword', text)} - type="text" - name="keyword" - placeholder="Nhập tên / số điện thoại / email" - autoFocus={true} - renderLabelIcon={renderIconKeyWord} + classNameContainer='input-container' + className='input-base-filter' + value={filter.keyword} + setValue={(text) => changeFilter('keyword', text)} + type="text" + name="keyword" + placeholder="Nhập tên / số điện thoại / email" + autoFocus={true} + renderLabelIcon={renderIconKeyWord} + errorText={errFilter.keyword} + typeErrText='underAbsolute' + errorAbsolute={true} />
@@ -122,7 +145,7 @@ export default function ListCustomer() {
- diff --git a/src/_containers/ListGrantPage/ListGrantPage.style.scss b/src/_containers/ListGrantPage/ListGrantPage.style.scss new file mode 100644 index 0000000..2fe70ac --- /dev/null +++ b/src/_containers/ListGrantPage/ListGrantPage.style.scss @@ -0,0 +1,194 @@ +@import "/src/_styles/mixin"; + +.list-grant-main-container { + + @include screen_mobile { + padding: 0 16px; + } + + .filter-container { + display: flex; + flex-direction: column; + + .filter-form { + display: flex; + flex-direction: column; + + .filter-form-row { + display: flex; + gap: 24px; + + @include screen_mobile { + flex-direction: column; + gap: 0; + } + } + + .grant-level-filter-form { + flex: 1; + display: flex; + gap: 24px; + + @include screen_mobile { + flex-direction: column; + gap: 0; + } + } + + .input-base-filter { + height: 48px; + + .icon_label { + margin-right: 12px; + } + } + + .keyword-filter-form { + flex: 1; + } + + .date-filter-form { + flex: 1; + display: flex; + gap: 24px; + + @include screen_mobile { + gap: 16px; + } + + .input_date_base { + margin-bottom: 0; + } + } + + .input-container { + flex: 1; + } + + .input_radio_base { + margin-bottom: 24px; + } + } + + .filter-action { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 24px; + + @include screen_mobile { + justify-content: center; + } + + .btn-filter { + display: flex; + align-items: center; + height: 40px; + border-radius: 20px; + line-height: 40px; + padding: 0 25px; + font-size: 16px; + border: none; + font-family: 'Myriadpro-SemiBold'; + + svg { + margin-right: 8px; + } + } + + .btn-cancel-filter { + color: #4b4a4a; + background-color: #DDDDDD; + } + + .btn-cancel-filter:hover { + opacity: 0.6; + } + } + + .filter-form-row-second { + display: flex; + gap: 24px; + align-items: center; + + @include screen_mobile { + flex-direction: column; + } + } + } + + .list-grant-container { + margin-top: 20px; + + .total-grant { + font-weight: 700; + margin-bottom: 16px; + } + + .empty-text { + font-weight: 700; + font-size: 18px; + text-align: center; + margin-top: 64px; + } + + .list-grant { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(48.6%, 1fr)); + gap: 24px; + + .grant-item { + // flex: 1 1 300px; + padding: 16px; + border-radius: 24px; + border: 1px solid #E1E1E1; + box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; + + .grant-name { + font-size: 24px; + font-weight: 700; + margin-bottom: 8px; + } + + .grant-row { + display: flex; + gap: 16px; + margin-bottom: 8px; + } + + .grant-row.message .grant-info { + width: 100%; + } + + .grant-row-mobile { + @include screen_mobile { + flex-direction: column; + gap: 8px; + } + + .grant-info { + @include screen_mobile { + width: 100%; + } + } + } + + .grant-info { + width: 50%; + display: flex; + gap: 8px; + + svg { + width: 24px; + height: 24px; + } + + span { + flex: 1; + text-align: justify; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/_containers/ListGrantPage/index.js b/src/_containers/ListGrantPage/index.js new file mode 100644 index 0000000..48838a2 --- /dev/null +++ b/src/_containers/ListGrantPage/index.js @@ -0,0 +1,273 @@ +import { useEffect, useState } from "react"; +import InputText from "../../_components/Auth/InputText"; +import HeaderMain from "../../_components/Header/HeaderMain"; +import InputDate from "../../_components/Auth/InputDate"; +import { apiCaller } from "../../_helpers"; +import { configConstants } from "../../_constants"; +import dayjs from "dayjs"; +import './ListGrantPage.style.scss' +import InputRadio from "../../_components/Auth/InputSelect"; +import { useSelector } from "react-redux"; +// role agents +export default function ListGrantPage() { + const authentication = useSelector((state) => state.authentication); + const [listLevel] = useState(Array(authentication?.role === 'agents' ? 2 : 9).fill(0).map((_, i) => ({ + title: `Cấp ${i + 1}`, + value: (i + 1).toString(), + }))) + const [filter, setFilter] = useState({ + keyword: '', + up_grant: '', + grant: null, + start_date: '', + end_date: '', + }) + const [errFilter, setErrFilter] = useState({ + keyword: '', + up_grant: '', + grant: null, + start_date: '', + end_date: '', + }) + const [listGrant, setListGrant] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const [isFilter, setIsFilter] = useState(false) + + const getData = async (dataFilter) => { + setIsLoading(true) + const endPoint = '/agent/agent_of_general?' + + (!!dataFilter?.keyword ? `keyword=${dataFilter.keyword}&` : '') + + (!!dataFilter?.start_date ? `start_date=${dayjs(dataFilter.start_date).format('YYYY-MM-DD')}&` : '') + + (!!dataFilter?.end_date ? `end_date=${dayjs(dataFilter.end_date).format('YYYY-MM-DD')}&` : '') + + (!!dataFilter?.grant ? `grant=${dataFilter.grant?.value}&` : '') + + (!!dataFilter?.up_grant ? `up_grant=${dataFilter.up_grant}` : ''); + + try { + const res = await apiCaller(endPoint, 'GET', {}, null, false, configConstants.API_URL_SETEST, true, true) + setIsLoading(false) + if(res?.status) { + setListGrant(res?.data) + } + } catch (err) { + setIsLoading(false) + } + } + + const changeFilter = (key, value) => { + setFilter(prev => ({ + ...prev, + [key]: value + })) + setErrFilter(prev => ({ + ...prev, + [key]: '' + })) + } + + const validateFilter = () => { + return Object.values(filter).some(item => !!item) + } + + const handleCancelFilter = () => { + const iniFilter = { + keyword: '', + up_grant: '', + grant: null, + start_date: '', + end_date: '', + } + setErrFilter(iniFilter) + setFilter(iniFilter) + if(!isFilter) { + return; + } + setIsFilter(false) + getData(iniFilter) + } + + const handleFilter = () => { + if(!!filter.keyword && filter.keyword.length < 3) { + setErrFilter(prev => ({ + ...prev, + keyword: 'Nhập tối thiểu 3 kí tự' + })) + return; + } + if(!!filter.up_grant && filter.up_grant.length < 3) { + setErrFilter(prev => ({ + ...prev, + up_grant: 'Nhập tối thiểu 3 kí tự' + })) + return; + } + setIsFilter(true) + getData(filter) + } + + useEffect(() => { + getData(filter) + }, []) + + const renderIconKeyWord = () => { + return ( + + ) + } + + const renderIconDate = () => { + return ( + + ) + } + + const renderIconUpGrant = () => { + return ( + + ) + } + + const renderIconLevel = () => { + return ( + + ) + } + + return ( +
+ +
+
+
+
+
+
+
+ changeFilter('keyword', text)} + type="text" + name="keyword" + placeholder="Nhập tên / số điện thoại / email / mã đại lý" + autoFocus={true} + renderLabelIcon={renderIconKeyWord} + errorText={errFilter.keyword} + typeErrText='underAbsolute' + errorAbsolute={true} + /> +
+
+
+ changeFilter('up_grant', text)} + type="text" + name="up_grant" + placeholder="Nhập mã tuyến trên" + renderLabelIcon={renderIconUpGrant} + errorText={errFilter.up_grant} + typeErrText='underAbsolute' + errorAbsolute={true} + /> +
+ changeFilter('grant', option)} + name="grant" + renderLabelIcon={renderIconLevel} + placeholder={"Chọn cấp đại lý"} + /> +
+
+
+
+ changeFilter('start_date', date)} + name="start_date" + renderLabelIcon={renderIconDate} + placeholder={"Từ ngày"} + maxDate={new Date()} + /> + changeFilter('end_date', date)} + name="end_date" + renderLabelIcon={renderIconDate} + placeholder={"Đến ngày"} + maxDate={new Date()} + /> +
+
+ + +
+
+
+
+ +
+ {!!listGrant.length &&

{`Tổng số tuyến dưới: ${listGrant.length}`}

} +
+ {listGrant?.map(item => ( +
+

{item?.name}

+
+
+ + {item?.agents_code} +
+
+ + {item?.grant} +
+
+ +
+
+ + {item?.up_grant} +
+
+ + {item?.phone} +
+
+ +
+
+ + {item?.email} +
+
+ + {dayjs(item?.created_date).format('DD/MM/YYYY')} +
+
+
+ ))} +
+ {!listGrant.length && !isLoading &&

Không có dữ liệu.

} +
+
+
+
+
+ ) +} \ No newline at end of file