Merge branch 'quy_fe' into uat

uat
Quy_FE 3 months ago
commit 8c13a2c108
  1. 1
      src/_components/Auth/InputDate/index.js
  2. 12
      src/_components/Auth/InputDate/index.scss
  3. 30
      src/_components/Header/index.js
  4. 158
      src/_components/boxChart/BoxDoughnutBarChart.js
  5. 21
      src/_components/boxChart/BoxDoughnutChar.js
  6. 28
      src/_components/boxChart/boxChart.scss
  7. 8
      src/_components/chart/RDoughnutChart.js
  8. 9
      src/_components/chart/VerticalBarChart.js
  9. 35
      src/_components/renderIcon/index.js
  10. 9
      src/_screens/criteria/criteria-manage/index.js
  11. 142
      src/_screens/criteria/criteria-setting/index.js
  12. 6
      src/_screens/home/admin/index.js
  13. 2
      src/_screens/home/detail-grade/index.js
  14. 2
      src/_screens/home/detail-room-education/detailRoomEducation.style.scss
  15. 140
      src/_screens/home/detail-room-education/index.js
  16. 11
      src/_screens/home/education-department/educationDepartmentHome.style.scss
  17. 95
      src/_screens/home/education-department/index.js
  18. 12
      src/_screens/home/headmaster/index.js
  19. 8
      src/_screens/home/outstanding-teacher/index.js
  20. 1
      src/_screens/home/outstanding-teacher/outstandingTeacher.style.scss
  21. 1
      src/_screens/home/teacher/index.js

@ -72,6 +72,7 @@ const InputDate = (props) => {
}`}
selected={props.value}
onChange={(date) => changeValue(date)}
disabledKeyboardNavigation
// showMonthDropdown
showYearDropdown
showMonthYearPicker={!!props?.isMonthPicker}

@ -53,6 +53,18 @@ $border-color: #4a4848;
}
}
.react-datepicker {
&__month-text {
&--selected,
&--in-selecting-range,
&--in-range {
border-radius: 8px !important;
}
}
}
input {
height: 100%;
border: none;

@ -1,17 +1,41 @@
import { useSelector } from "react-redux";
import "./header.style.scss";
import { configConstants } from "../../_constants";
import PrimaryButton from "../Button/PrimaryButton";
import { renderIconButtonLeft } from "../renderIcon";
import { history } from "../../_helpers";
export default function Header({ icon, title, subtitles = [] }) {
export default function Header({ icon, title, subtitles = [], manager = false, isBack = false }) {
const authentication = useSelector((state) => state.authentication);
const { fullname, organization_name, role } = authentication?.user || {};
const hasFullName = fullname || organization_name;
const fullName = role === "organization_admin" ? `Hiệu trưởng ${hasFullName}` : fullname;
const handleFullName = () =>{
let fullName
if(manager){
fullName = 'Giám đốc ' + hasFullName
}else{
fullName = role === "organization_admin"
? `${organization_name ? 'Hiệu trưởng ' : ''}${hasFullName}`
: fullname;
}
return fullName
}
return (
<div className="header-container">
<div className="header-left-side">
{isBack && (
<div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}>
<PrimaryButton style={{ alignSelf: "center", justifyContent:'flex end', marginRight:30 }} onClick={()=>{
history.goBack()
}}>
<div style={{marginRight: 8, paddingBottom: 4}}>{renderIconButtonLeft()}</div>
Quay lại
</PrimaryButton>
</div>
)}
{icon}
<p className="header-title">
{title}
@ -25,7 +49,7 @@ export default function Header({ icon, title, subtitles = [] }) {
</p>
</div>
<div className="header-right-side">
<p className="header-name">{fullName}</p>
<p className="header-name">{handleFullName()}</p>
<img
src={!!authentication?.user?.avatar ? (configConstants.BASE_URL + authentication?.user?.avatar) : '/assets/imgs/avatar_auth.png'}
className="header-avatar"

@ -1,75 +1,125 @@
import { Subtitles } from '@material-ui/icons'
import { PRIMARY_COLOR } from '../../_constants/common'
import RDoughnutChart from '../chart/RDoughnutChart'
import './boxChart.scss'
import { VerticalBarChart } from '../chart/VerticalBarChart'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import InputDate from '../Auth/InputDate'
import { renderIconButton, renderIconButtonLeft, renderIconDate } from '../renderIcon'
export default function BoxDoughnutBarChart({dataDoughnut = [], dataBarChart = [], labelsBarChart = [], dateBarChat, onSetDateBarChart, titleDoughnut, subtitleDoughnut, titleLine, subTitleLine}) {
const Btn = ({ icon, isDisabled = false, onClick }) => {
return (
<div className='d-flex justify-content-center align-items-center'>
<button className={`d-flex custom-button ${isDisabled ? "button-disable" : ""}`} onClick={onClick} disabled={isDisabled}>
<div>{icon}</div>
</button>
</div>
);
};
export default function BoxDoughnutBarChart({
dataDoughnut = [],
dataBarChart = [],
labelsBarChart = [],
dateBarChat,
onSetDateBarChart,
titleDoughnut,
subtitleDoughnut,
titleLine,
subTitleLine,
}) {
const [month, setMonth] = useState(dateBarChat)
const [monthError, setMonthError] = useState('')
const [current, target] = dataDoughnut
const changeMonth = (date) => {
const handleMonthChange = (date) => {
setMonth(date)
!!onSetDateBarChart && onSetDateBarChart(date)
}
const _dataDoughnut = (dataDoughnut?.[0] / dataDoughnut?.[1] === 1 || dataDoughnut?.[0] > dataDoughnut?.[1]) ? [dataDoughnut?.[0]] : [dataDoughnut?.[0], dataDoughnut?.[1] - dataDoughnut?.[0]]
const renderDoughnutSection = () => (
<div style={{borderBottom: '1px solid #c4c4c4', paddingBottom: '1.6rem', flex: 0.38, display: 'flex', flexDirection: 'column'}}>
{titleDoughnut && <p className='box-chart-title'>{titleDoughnut}</p>}
{subtitleDoughnut && <p className='box-chart-subtitle'>{subtitleDoughnut}</p>}
<div className='d-flex flex-1'>
<div className='doughnut-chart-content flex-1'>
<RDoughnutChart data={dataDoughnut} />
</div>
<div className='origin-vertical justify-content-center align-item-center flex-1'>
<p style={{color: PRIMARY_COLOR, fontSize: '2.2rem', fontWeight: 800}}>{current}</p>
<p style={{color: '#01AEF0', fontSize: '2.2rem', fontWeight: 800}}>
{target > 0 ? target : '...'}
</p>
</div>
</div>
</div>
)
const renderBarSection = () => (
<div className='d-flex' style={{marginTop: '1.2rem', flexDirection: 'column', flex: 0.62}}>
{titleLine && <p className='box-chart-title'>{titleLine}</p>}
{subTitleLine && <p className='box-chart-subtitle'>{subTitleLine}</p>}
{dataBarChart.length > 0 ? (
<div className='flex-1' style={{padding: '1.6rem 0'}}>
<VerticalBarChart data={dataBarChart} labels={labelsBarChart}/>
</div>
) : (
<div className="d-flex justify-content-center align-items-center flex-1">
Không dữ liệu để hiển thị
</div>
)}
</div>
)
const handleClickNext = () => {
const newMonth = month.getMonth() + 1;
const newYear = month.getFullYear();
if (newMonth > 11) {
setMonth(new Date(newYear + 1, 0, 1));
} else {
setMonth(new Date(newYear, newMonth, 1));
}
};
const handleClickPrev = () => {
const newMonth = month.getMonth() - 1;
const newYear = month.getFullYear();
if (newMonth < 0) {
setMonth(new Date(newYear - 1, 11, 1));
} else {
setMonth(new Date(newYear, newMonth, 1));
}
};
useEffect(() => {
onSetDateBarChart?.(month);
}, [month]);
const renderIconDate = () => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 20 20">
<path
fill="#00bbb5"
d="M5.673 0a.7.7 0 0 1 .7.7v1.309h7.517v-1.3a.7.7 0 0 1 1.4 0v1.3H18a2 2 0 0 1 2 1.999v13.993A2 2 0 0 1 18 20H2a2 2 0 0 1-2-1.999V4.008a2 2 0 0 1 2-1.999h2.973V.699a.7.7 0 0 1 .7-.699M1.4 7.742v10.259a.6.6 0 0 0 .6.6h16a.6.6 0 0 0 .6-.6V7.756zm5.267 6.877v1.666H5v-1.666zm4.166 0v1.666H9.167v-1.666zm4.167 0v1.666h-1.667v-1.666zm-8.333-3.977v1.666H5v-1.666zm4.166 0v1.666H9.167v-1.666zm4.167 0v1.666h-1.667v-1.666zM4.973 3.408H2a.6.6 0 0 0-.6.6v2.335l17.2.014V4.008a.6.6 0 0 0-.6-.6h-2.71v.929a.7.7 0 0 1-1.4 0v-.929H6.373v.92a.7.7 0 0 1-1.4 0z"
/>
</svg>
)
}
const isDisabledNext = new Date(month).getMonth() === new Date().getMonth() && new Date(month).getFullYear() === new Date().getFullYear();
return (
<div className="box-chart-container">
<div style={{borderBottom: '1px solid #c4c4c4', paddingBottom: '1.6rem', flex: 0.38, display: 'flex', flexDirection: 'column'}}>
{!!titleDoughnut && <p className='box-chart-title'>{titleDoughnut}</p>}
{!!subtitleDoughnut && <p className='box-chart-subtitle'>{subtitleDoughnut}</p>}
<div className='d-flex flex-1'>
<div className='doughnut-chart-content flex-1'>
<RDoughnutChart data={_dataDoughnut} />
</div>
<div className='origin-vertical justify-content-center align-item-center flex-1'>
<p style={{fontSize: '1.8rem'}}>{dataDoughnut?.[0] + '/' + dataDoughnut?.[1]}</p>
<p style={{color: PRIMARY_COLOR, fontSize: '2.2rem', fontWeight: 800}}>{Math.round(dataDoughnut?.[0]/dataDoughnut?.[1] * 100) + '%'}</p>
</div>
{renderDoughnutSection()}
{renderBarSection()}
<div className='d-flex justify-content-center align-items-center'>
<Btn icon={renderIconButtonLeft()} isDisabled={false} onClick={handleClickPrev} />
<div className='d-flex justify-content-center align-item-center' style={{width: 140, alignSelf: 'center'}}>
<InputDate
maxDate={new Date()}
label={''}
styleContainer={{flex: 1}}
value={month}
setValue={handleMonthChange}
name="month"
renderLabelIcon={renderIconDate}
errorText={monthError}
errorAbsolute={true}
popperPlacement='top'
typeErrText='underAbsolute'
isMonthPicker
/>
</div>
</div>
<div className='d-flex' style={{marginTop: '1.2rem', flexDirection: 'column', flex: 0.62}}>
{!!titleLine && <p className='box-chart-title'>{titleLine}</p>}
{!!subTitleLine && <p className='box-chart-subtitle'>{subTitleLine}</p>}
<div className='flex-1' style={{padding: '1.6rem 0'}}>
<VerticalBarChart data={dataBarChart} labels={labelsBarChart}/>
</div>
</div>
<div className='d-flex justify-content-center align-item-center' style={{width: 140, alignSelf: 'center'}}>
{/* {renderArrowLeft()}
<span style={{padding: '0 2rem', fontSize: '1.8rem'}}>Tháng 11</span>
{renderArrowRight()} */}
<InputDate
maxDate={new Date()}
label={''}
styleContainer={{flex: 1}}
value={month}
setValue={changeMonth}
name="month"
renderLabelIcon={renderIconDate}
errorText={monthError}
errorAbsolute={true}
// placeholder={"Chọn ngày sinh"}
popperPlacement='top'
typeErrText='underAbsolute'
isMonthPicker
/>
<Btn icon={renderIconButton()} isDisabled={isDisabledNext} onClick={handleClickNext} />
</div>
</div>
)

@ -3,15 +3,28 @@ import RDoughnutChart from '../chart/RDoughnutChart'
import './boxChart.scss'
export default function BoxDoughnutChart({data = [], title, propsContainer}) {
const _data = (data?.[0] / data?.[1] === 1 || data?.[0] > data?.[1]) ? [data?.[0]] : [data?.[0], data?.[1] - data?.[0]]
const [current, target] = data
const chartData = (current / target === 1 || current > target) ? [current] : [current, target - current];
const renderStats = () => {
return (
<>
<p style={{color: PRIMARY_COLOR, fontSize: '2.2rem', fontWeight: 800}}>{current}</p>
<p style={{color: '#01AEF0', fontSize: '2.2rem', fontWeight: 800}}>{target > 0 ? target : '...'}</p>
</>
)
}
return (
<div className="box-chart-container" {...propsContainer}>
<p className='box-chart-subtitle'>{title}</p>
<div className='d-flex flex-1'>
<div className='doughnut-chart-content flex-1'><RDoughnutChart data={_data} /></div>
<div className='doughnut-chart-content flex-1'>
<RDoughnutChart data={chartData} />
</div>
<div className='origin-vertical justify-content-center align-item-center flex-1'>
<p style={{fontSize: '1.8rem'}}>{data?.[0] + '/' + data?.[1]}</p>
<p style={{color: PRIMARY_COLOR, fontSize: '2.2rem', fontWeight: 800}}>{Math.round(data?.[0]/data?.[1] * 100) + '%'}</p>
{renderStats()}
</div>
</div>
</div>

@ -20,4 +20,32 @@
}
.doughnut-chart-content {}
.custom-button {
padding: 8px;
height: auto;
border-radius: 20px;
border: none;
font-size: 16px;
color: #fff;
font-family: 'Myriadpro-SemiBold';
background-color: var(--button-bg-color);
display: flex;
justify-content: center;
align-items: center;
white-space: nowrap;
width: max-content;
margin: 0 10px;
}
.custom-button:hover {
background-color: #c07a05;
}
.button-disable {
cursor: not-allowed;
/* background: #70707070 !important; */
background: #c1c1c1 !important;
pointer-events: none;
}
}

@ -10,6 +10,14 @@ export default function RDoughnutChart({data = [], ...other}) {
return null
}
// data = data.map((item, index) => {
// if (index === 1 && item === 0) {
// return 1
// }
// return item
// })
// console.log('data',data);
return <Chart
type='doughnut'
data={{

@ -15,6 +15,7 @@ export function VerticalBarChart({data = [], labels = []}) {
maxValue = item;
}
})
// labels = labels?.map((item) => item.replace('Tuần', 'W'));
const options = {
responsive: true,
@ -28,7 +29,7 @@ export function VerticalBarChart({data = [], labels = []}) {
align: 'end', // Alignment of the labels (start, end, center, etc.)
color: 'blue', // Color of the labels
font: {
weight: 'bold',
weight: 'bold',
},
formatter: function (value, context) {
return value; // Display the actual data value
@ -36,6 +37,12 @@ export function VerticalBarChart({data = [], labels = []}) {
}
},
scales: {
x: {
ticks: {
maxRotation: 0,
// scrollX : true,
},
},
y: {
max: maxValue < 5 ? 5 : maxValue < 20 ? maxValue + 5 : maxValue + 10,
ticks: {

@ -139,3 +139,38 @@ export const renderIconSearchInput = () => {
</svg>
);
};
export const renderIconButton = (width = 24, height = 24) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width={width} height={height} viewBox="0 0 24 24"><g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path stroke-dasharray="20" stroke-dashoffset="20" d="M3 12h17.5"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.2s" values="20;0"/></path><path stroke-dasharray="12" stroke-dashoffset="12" d="M21 12l-7 7M21 12l-7 -7"><animate fill="freeze" attributeName="stroke-dashoffset" begin="0.2s" dur="0.2s" values="12;0"/></path></g></svg>
)
}
export const renderIconButtonLeft = (width = 24, height = 24) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
viewBox="0 0 24 24"
preserveAspectRatio="xMidYMid meet"
>
<g fill="none" stroke="#fff" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" transform="rotate(180 12 12)">
<path strokeDasharray="20" strokeDashoffset="20" d="M3 12h17.5">
<animate fill="freeze" attributeName="stroke-dashoffset" dur="0.2s" values="20;0" />
</path>
<path strokeDasharray="12" strokeDashoffset="12" d="M21 12l-7 7M21 12l-7 -7">
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.2s" dur="0.2s" values="12;0" />
</path>
</g>
</svg>
);
};
export const renderIconDate = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 20 20">
<path
fill="#00bbb5"
d="M5.673 0a.7.7 0 0 1 .7.7v1.309h7.517v-1.3a.7.7 0 0 1 1.4 0v1.3H18a2 2 0 0 1 2 1.999v13.993A2 2 0 0 1 18 20H2a2 2 0 0 1-2-1.999V4.008a2 2 0 0 1 2-1.999h2.973V.699a.7.7 0 0 1 .7-.699M1.4 7.742v10.259a.6.6 0 0 0 .6.6h16a.6.6 0 0 0 .6-.6V7.756zm5.267 6.877v1.666H5v-1.666zm4.166 0v1.666H9.167v-1.666zm4.167 0v1.666h-1.667v-1.666zm-8.333-3.977v1.666H5v-1.666zm4.166 0v1.666H9.167v-1.666zm4.167 0v1.666h-1.667v-1.666zM4.973 3.408H2a.6.6 0 0 0-.6.6v2.335l17.2.014V4.008a.6.6 0 0 0-.6-.6h-2.71v.929a.7.7 0 0 1-1.4 0v-.929H6.373v.92a.7.7 0 0 1-1.4 0z"
/>
</svg>
)

@ -40,6 +40,7 @@ export default function CriteriaManage() {
const [limitOnline] = useState(10);
const [offsetOnline, setOffsetOnline] = useState(0);
const [isEndOnlineClasses, setIsEndOnlineClasses] = useState(false);
const [isCheckAll,setIsCheckAll] = useState(false)
const listRef = useRef(null);
const dispatch = useDispatch();
const changeProvince = (item) => {
@ -64,6 +65,12 @@ export default function CriteriaManage() {
});
}
};
useEffect(() => {
if(isCheckAll){
handleSelectAll()
}
}, [listData])
const handleSelectAll = () => {
if (listData?.every((item) => listIdSelected?.includes(item?.school_id))) {
@ -80,6 +87,7 @@ export default function CriteriaManage() {
newListIdSelected.push(item?.school_id);
});
setListIdSelected(newListIdSelected);
setIsCheckAll(true)
};
const handleSelectItem = (item) => {
@ -305,6 +313,7 @@ export default function CriteriaManage() {
isLoadMoreOnline &&
!isLoading
) {
if(listData.length < limitOnline) return
onLoadMoreClasses();
}
};

@ -26,6 +26,7 @@ export default function CriteriaSetting() {
const [criteriaTarget, setCriteriaTarget] = useState(DEFAULT_SETTING_CRITERIA);
const [showAlert, setShowAlert] = useState(false);
const [alertMessage, setAlertMessage] = useState("");
const [isApply, setIsApply] = useState(false)
const schoolList = isJsonString(decodeURIComponent(_schoolList))
? JSON.parse(decodeURIComponent(_schoolList))
@ -60,30 +61,93 @@ export default function CriteriaSetting() {
}
}
const saveCriteriaSetting = async () => {
try {
const dataSave = {
school_list: schoolList,
assign_number_active: criteriaTarget.assign.enable ? 1 : 0,
assign_number_target_1: criteriaTarget.assign.target1,
assign_number_target_2: criteriaTarget.assign.target2,
student_done_active: criteriaTarget.studentDone.enable ? 1 : 0,
student_done_target_1: criteriaTarget.studentDone.target1,
student_done_target_2: criteriaTarget.studentDone.target2,
}
const res = await apiCaller(
"/report/api_report/assignCriteriaToOrganization",
"PUT",
dataSave,
);
if (res?.status) {
setAlertMessage(res?.msg);
const isValid = () => {
if (criteriaTarget.assign.enable) {
if (!criteriaTarget.studentDone.enable) {
return (
!(criteriaTarget.assign.target1 &&
criteriaTarget.assign.target2 &&
criteriaTarget.assign.target1 < criteriaTarget.assign.target2)
);
} else {
return (
!(criteriaTarget.assign.target1 &&
criteriaTarget.assign.target2 &&
criteriaTarget.assign.target1 < criteriaTarget.assign.target2 &&
criteriaTarget.studentDone.target1 != null &&
criteriaTarget.studentDone.target2 != null &&
criteriaTarget.studentDone.target1 < criteriaTarget.studentDone.target2)
);
}
} else if (criteriaTarget.studentDone.enable) {
return (
!(criteriaTarget.studentDone.target1 != null &&
criteriaTarget.studentDone.target2 != null &&
criteriaTarget.studentDone.target1 < criteriaTarget.studentDone.target2)
);
} else {
return true;
}
};
const saveCriteriaSetting = async () => {
try {
const dataSave = {
school_list: schoolList,
assign_number_active: criteriaTarget.assign.enable ? 1 : 0,
assign_number_target_1: criteriaTarget.assign.target1,
assign_number_target_2: criteriaTarget.assign.target2,
student_done_active: criteriaTarget.studentDone.enable ? 1 : 0,
student_done_target_1: criteriaTarget.studentDone.target1,
student_done_target_2: criteriaTarget.studentDone.target2,
};
let res;
if (isApply) {
res = await handleApply(dataSave);
} else {
res = await handleNonApply(dataSave);
}
if (res?.status) {
setAlertMessage(res?.msg);
setShowAlert(true);
}
} catch (err) {
console.error("Error: ", err);
}
};
const handleApply = async (dataSave) => {
if (isValid()) {
return await apiCaller(
"/report/api_report/removeCriteriaFromOrganization",
"PUT",
{ school_list: schoolList }
);
} else {
return await apiCaller(
"/report/api_report/assignCriteriaToOrganization",
"PUT",
dataSave
);
}
};
const handleNonApply = async (dataSave) => {
if (!isValid()) {
return await apiCaller(
"/report/api_report/assignCriteriaToOrganization",
"PUT",
dataSave
);
} else {
setAlertMessage("Bạn chưa thực hiện bất kỳ thay đổi nào");
setShowAlert(true);
return null;
}
} catch (err) {
console.log("err: ", err);
}
}
};
const getData = async () => {
try {
@ -93,6 +157,7 @@ export default function CriteriaSetting() {
"GET",
);
if (res?.status) {
setIsApply(res?.data?.status_criteria ==='1')
setCriteriaTarget({
assign: {
target1: res?.data?.assign_number_target_1 || DEFAULT_SETTING_CRITERIA.assign.target1,
@ -122,6 +187,7 @@ export default function CriteriaSetting() {
"GET",
);
if (res?.status) {
setIsApply(res?.data?.status_criteria ==='1')
setCriteriaTarget({
assign: {
target1: res?.data?.assign_number_target_1 || DEFAULT_SETTING_CRITERIA.assign.target1,
@ -160,34 +226,6 @@ export default function CriteriaSetting() {
return !isRead
}
const isValid = () => {
if (criteriaTarget.assign.enable) {
if (!criteriaTarget.studentDone.enable) {
return (
!(criteriaTarget.assign.target1 &&
criteriaTarget.assign.target2 &&
criteriaTarget.assign.target1 < criteriaTarget.assign.target2)
);
} else {
return (
!(criteriaTarget.assign.target1 &&
criteriaTarget.assign.target2 &&
criteriaTarget.assign.target1 < criteriaTarget.assign.target2 &&
criteriaTarget.studentDone.target1 != null &&
criteriaTarget.studentDone.target2 != null &&
criteriaTarget.studentDone.target1 < criteriaTarget.studentDone.target2)
);
}
} else if (criteriaTarget.studentDone.enable) {
return (
!(criteriaTarget.studentDone.target1 != null &&
criteriaTarget.studentDone.target2 != null &&
criteriaTarget.studentDone.target1 < criteriaTarget.studentDone.target2)
);
} else {
return true;
}
};
const renderRightItem = ({ title, desc, valueName, unit, key, maxLengthInput }) => {
return (
<div
@ -339,8 +377,8 @@ export default function CriteriaSetting() {
>
Quay lại
</PrimaryButton>
{authentication?.user?.role === USER_ROLE.ADMIN && (
<PrimaryButton onClick={saveCriteriaSetting} isDisabled={isValid()}>Lưu</PrimaryButton>
{(authentication?.user?.role === USER_ROLE.ADMIN || authentication?.user?.role === USER_ROLE.HEADMASTER) && (
<PrimaryButton onClick={saveCriteriaSetting}>Lưu</PrimaryButton>
)}
</div>
</div>

@ -232,7 +232,8 @@ export default function AdminHome() {
if (savedState) {
const state = JSON.parse(savedState);
setSearchTextSchool(state.searchTextSchool);
setProvinceSelect(state.provinceSelect);
// setProvinceSelect(state.provinceSelect);
changeProvince(state.provinceSelect)
setDistrictSelect(state.districtSelect);
setendPointSave(state.endPointSave)
if(state.endPointSave){
@ -261,6 +262,7 @@ export default function AdminHome() {
isLoadMoreOnline &&
!isLoading
) {
if(listSchool.length < limitOnline) return
onLoadMoreClasses();
}
};
@ -288,7 +290,7 @@ export default function AdminHome() {
queryParams.length ? `&${queryParams.join("&")}` : ""
}`;
const res = await apiCaller(endPoint, "GET");
if(!res.data){
if(!res?.data){
setIsEndOnlineClasses(true);
} else {
concatListSide = res?.data;

@ -55,7 +55,6 @@ export default function DetailGrade() {
setOffsetOnline(0)
setLoadMoreOnline(true)
setIsEndOnlineClasses(false)
setIsFiltered(true);
scrollToTop()
getData();
};
@ -143,6 +142,7 @@ export default function DetailGrade() {
isLoadMoreOnline &&
!isLoading
) {
if(listClass.length < limitOnline) return
onLoadMoreClasses();
}
};

@ -4,7 +4,7 @@
display: flex;
.detail-room-education-left-side {
width: 40%;
width: 48%;
border-right: 1px solid #c7c7c7;
display: flex;
flex-direction: column;

@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import Header from "../../../_components/Header";
import { renderIconHome } from "../../../_components/renderIcon";
import { renderIconButton, renderIconHome } from "../../../_components/renderIcon";
import RateStar from "../../../_components/RateStar";
import { defaultMonthYearSemester, getListMonthBySemester, LIST_SCHOOL_YEAR, LIST_SEMESTER, PRIMARY_COLOR } from "../../../_constants/common";
import { configConstants, PATH } from "../../../_constants";
@ -34,6 +34,11 @@ export default function DetailRoomEducation() {
const [isLoadingStatisticCircle, setIsLoadingStatisticCircle] = useState(false);
const [isLoadingStudentChart, setIsLoadingStudentChart] = useState(false);
const [isLoadingTeacherChart, setIsLoadingTeacherChart] = useState(false);
const [isLoadMoreOnline, setLoadMoreOnline] = useState(true);
const [limitOnline] = useState(10);
const [offsetOnline, setOffsetOnline] = useState(0);
const [isEndOnlineClasses, setIsEndOnlineClasses] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const getListOrganization = async () => {
try {
@ -211,49 +216,114 @@ export default function DetailRoomEducation() {
}
}, [isLoadingListOrganization, isLoadingStatisticCircle, isLoadingStudentChart, isLoadingTeacherChart])
const renderIconButton = () => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path stroke-dasharray="20" stroke-dashoffset="20" d="M3 12h17.5"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.2s" values="20;0"/></path><path stroke-dasharray="12" stroke-dashoffset="12" d="M21 12l-7 7M21 12l-7 -7"><animate fill="freeze" attributeName="stroke-dashoffset" begin="0.2s" dur="0.2s" values="12;0"/></path></g></svg>
)
}
const handleScroll = (e) => {
if (
e.target.scrollHeight - e.target.scrollTop < e.target.clientHeight + 5 &&
isLoadMoreOnline &&
!isLoading
) {
if(listOrganization.length < limitOnline) return
onLoadMoreClasses();
}
};
// Load More Classes for Teacher
const onLoadMoreClasses = async () => {
let offsetOnlineMore = offsetOnline + limitOnline;
let listNext = [];
setIsLoading(true);
try {
if (!isEndOnlineClasses) {
const endPoint = `/report/api_report/getListOrganizationFromDistrictOrganization?organization_id=${
idRoom
}${
!!schoolYear?.value ? `&year=${schoolYear?.value}` : ""
}${
!!semester?.value ? `&semester=${semester?.value}` : ""
}${
!!month?.value ? `&month=${month?.value}` : ""
}&limit=${limitOnline}&offset=${offsetOnlineMore}`;
const res = await apiCaller(
endPoint,
"GET",
{},
null,
true,
configConstants.API_URL_SETEST,
false
)
// const endPoint = `/report/api_report/getOrganizationAndCriteria?limit=${limitOnline}&offset=${offsetOnlineMore}${
// queryParams.length ? `&${queryParams.join("&")}` : ""
// }`;
// const res = await apiCaller(endPoint, "GET");
if (!res.data){
setIsEndOnlineClasses(true);
} else {
listNext = res?.data;
setOffsetOnline(offsetOnline + limitOnline);
if (res?.data?.length < limitOnline) {
setLoadMoreOnline(false);
if (res?.data?.length == 0) setLoadMoreOnline(false);
setIsEndOnlineClasses(true);
}
}
} else {
setIsEndOnlineClasses(true);
}
let listPrev = listOrganization;
setListOrganization(listPrev?.concat(listNext));
} catch (e) {
} finally {
setIsLoading(false);
}
};
return (
<div className="flex-1">
<Header
icon={renderIconHome({ color: "#4D4D4D" })}
title={"Phòng giáo dục huyện Giao Thủy"}
title={"Phòng giáo dục " + authentication.user.province}
manager={true}
/>
<div className="container-page-header container-page-sidebar">
<div className="detail-room-education-container bg-sub-main-green-img">
<div className="detail-room-education-left-side">
<div className="detail-room-education-statistic-container">
<div className="d-flex flex-1 gap-16" style={{ flexDirection: 'column'}}>
<div className="flex flex-m gap-16" style={{height: '23%'}}>
{data ?
(<>
<div className="flex flex-m gap-16" style={{height: '23%'}}>
<BoxDoughnutChart data={[data?.school_join, data?.total_school]} title={'Số trường đã tham gia'} />
<BoxDoughnutChart data={[data?.class_join, data?.total_class]} title={'Số lớp đã tham gia'} />
</div>
<div className="flex flex-m flex-1 gap-16">
<BoxDoughnutBarChart
dateBarChat={dateTeacherChart}
onSetDateBarChart={setDateTeacherChart}
dataBarChart={dataTeacherChart?.map(item => Number(item?.total_teacher_assignment))}
labelsBarChart={dataTeacherChart?.map(item => item?.label)}
dataDoughnut={[data?.teacher_join, data?.total_teacher]}
titleDoughnut={'Giáo viên'}
subtitleDoughnut={'Số giáo viên đã tham gia'}
subTitleLine={"Số giáo viên giao bài trong tuần"}
/>
<BoxDoughnutBarChart
dateBarChat={dateStudentChart}
onSetDateBarChart={setDateStudentChart}
dataBarChart={dataStudentChart?.map(item => Number(item?.total_student_learn))}
labelsBarChart={dataStudentChart?.map(item => item?.label)}
dataDoughnut={[data?.student_join, data?.total_student]}
titleDoughnut={'Học sinh'}
subtitleDoughnut={'Số học sinh đã tham gia'}
subTitleLine={"Số học sinh làm bài trong tuần"}
/>
</div>
</div>
<div className="flex flex-m flex-1 gap-16">
<BoxDoughnutBarChart
dateBarChat={dateTeacherChart}
onSetDateBarChart={setDateTeacherChart}
dataBarChart={dataTeacherChart?.map(item => Number(item?.total_teacher_assignment))}
labelsBarChart={dataTeacherChart?.map(item => item?.label)}
dataDoughnut={[data?.teacher_join, data?.total_teacher]}
titleDoughnut={'Giáo viên'}
subtitleDoughnut={'Số giáo viên đã tham gia'}
subTitleLine={"Số giáo viên giao bài trong tuần"}
/>
<BoxDoughnutBarChart
dateBarChat={dateStudentChart}
onSetDateBarChart={setDateStudentChart}
dataBarChart={dataStudentChart?.map(item => Number(item?.total_student_learn))}
labelsBarChart={dataStudentChart?.map(item => item?.label)}
dataDoughnut={[data?.student_join, data?.total_student]}
titleDoughnut={'Học sinh'}
subtitleDoughnut={'Số học sinh đã tham gia'}
subTitleLine={"Số học sinh làm bài trong tuần"}
/>
</div>
</>)
:
"Không có dữ liệu để hiển thị"
}
</div>
<div className="d-flex justify-content-center" style={{marginTop: '3rem'}}>
<PrimaryButton className="d-flex" style={{textDecoration: 'underline'}} onClick={goToOutstandingTeacher}>
@ -271,25 +341,25 @@ export default function DetailRoomEducation() {
data={LIST_SCHOOL_YEAR}
value={schoolYear}
setValue={setSchoolYear}
style={{flex: 1}}
style={{flex: 1,maxWidth: '260px'}}
/>
<RootSelect
data={LIST_SEMESTER}
value={semester}
setValue={changeSemester}
style={{flex: 0.5}}
style={{flex: 0.5,maxWidth: '200px'}}
/>
<RootSelect
data={getListMonthBySemester(semester.value)}
value={month}
setValue={setMonth}
style={{flex: 0.5}}
style={{flex: 0.5,maxWidth: '160px'}}
/>
<PrimaryButton onClick={handleFilter}>
Áp dụng
</PrimaryButton>
</div>
<div className="detail-room-education-list detail-room-education-right-p-h scrollbar-custom">
<div onScroll={handleScroll} className="detail-room-education-list detail-room-education-right-p-h scrollbar-custom">
{!isLoadingListOrganization && !listOrganization?.length && (
<p style={{fontSize: '1.8rem', fontWeight: 700}}>
Không trường nào

@ -26,6 +26,16 @@
flex-direction: column;
background-color: #fff;
// .search-input {
// border-radius: 40px;
// height: 46px;
// width: 50%;
// @include screen_pc_sm {
// height: 34px;
// }
// }
.education-department-home-note {
font-size: 2rem;
font-weight: 700;
@ -38,6 +48,7 @@
display: flex;
flex-direction: column;
padding: 0 3.2rem;
max-height: 740px;
.education-department-home-list-room {
flex: 1;

@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import InputText from "../../../_components/Auth/InputText";
import Header from "../../../_components/Header";
import {
renderIconButton,
renderIconHome,
renderIconSearchInput,
} from "../../../_components/renderIcon";
@ -24,6 +25,12 @@ export default function EducationDepartmentHome() {
const [dataStudentChart, setDataStudentChart] = useState([])
const [dataTeacherChart, setDataTeacherChart] = useState([])
const [listOrganization, setListOrganization] = useState([])
const [searchText, setSearchText] = useState('')
const [isLoadMoreOnline, setLoadMoreOnline] = useState(true);
const [limitOnline] = useState(10);
const [offsetOnline, setOffsetOnline] = useState(0);
const [isEndOnlineClasses, setIsEndOnlineClasses] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const getDataOrganization = async () => {
try {
@ -109,22 +116,66 @@ export default function EducationDepartmentHome() {
);
};
const renderIconButton = () => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path stroke-dasharray="20" stroke-dashoffset="20" d="M3 12h17.5"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.2s" values="20;0"/></path><path stroke-dasharray="12" stroke-dashoffset="12" d="M21 12l-7 7M21 12l-7 -7"><animate fill="freeze" attributeName="stroke-dashoffset" begin="0.2s" dur="0.2s" values="12;0"/></path></g></svg>
)
const handleSubmit = ()=>{
}
const handleScroll = (e) => {
if (
e.target.scrollHeight - e.target.scrollTop < e.target.clientHeight + 5 &&
isLoadMoreOnline &&
!isLoading
) {
if(listOrganization.length < limitOnline) return
onLoadMoreClasses();
}
};
// Load More
const onLoadMoreClasses = async () => {
let offsetOnlineMore = offsetOnline + limitOnline;
let listNext = [];
setIsLoading(true);
try {
if (!isEndOnlineClasses) {
const endPoint = `/report/api_report/listChildOrganization?organization_id=${authentication?.user?.organization_id}?limit=${limitOnline}&offset=${offsetOnlineMore}`;
const res = await apiCaller(endPoint, "GET");
if (!res.data){
setIsEndOnlineClasses(true);
} else {
listNext = res?.data;
setOffsetOnline(offsetOnline + limitOnline);
if (res?.data?.length < limitOnline) {
setLoadMoreOnline(false);
if (res?.data?.length == 0) setLoadMoreOnline(false);
setIsEndOnlineClasses(true);
}
}
} else {
setIsEndOnlineClasses(true);
}
let listPrev = listOrganization;
setListOrganization(listPrev?.concat(listNext));
} catch (e) {
} finally {
setIsLoading(false);
}
};
return (
<div className="flex-1">
<Header
icon={renderIconHome({ color: "#4D4D4D" })}
title={"Sở giáo dục tỉnh Nam Định"}
title={"Sở giáo dục " + authentication.user.province}
manager={true}
/>
<div className="container-page-header container-page-sidebar">
<div className="education-department-home-container bg-sub-main-green-img">
<div className="education-department-statistic-container">
{!!data && <div className="d-flex gap-16" style={{height: '80%'}}>
{!!data ? <div className="d-flex gap-16" style={{height: '80%'}}>
<div className="education-department-statistic-col gap-16">
<BoxDoughnutChart data={[data?.district_join, data?.total_district]} title={'Số huyện đã tham gia'} />
<BoxDoughnutChart data={[data?.school_join, data?.total_school]} title={'Số trường đã tham gia'} />
@ -154,7 +205,12 @@ export default function EducationDepartmentHome() {
subTitleLine={"Số học sinh làm bài trong tuần"}
/>
</div>
</div>}
</div>
:
<div className="d-flex justify-content-center">
Không dữ liệu để hiển thị
</div>
}
<div className="d-flex justify-content-center" style={{marginTop: '3rem'}}>
<PrimaryButton className="d-flex" style={{textDecoration: 'underline'}} onClick={goToOutstandingTeacher}>
Top 10 giáo viên tiêu biểu
@ -166,9 +222,23 @@ export default function EducationDepartmentHome() {
<p className="education-department-home-note">
Danh sách phòng giáo dục
</p>
<div className="education-department-home-list-content scrollbar-custom">
{/* <InputText
className="search-input"
value={searchText}
setValue={setSearchText}
type="text"
name="searchText"
placeholder={"Nhập tên giáo viên"}
renderLabelIcon={renderIconSearchInput}
onKeyUp={(e) => {
if (e.which == 13 && !isFilterSchool) {
handleSubmit();
}
}}
/> */}
<div onScroll={handleScroll} className="education-department-home-list-content scrollbar-custom">
<div className="education-department-home-list-room">
{listOrganization.map((item, index) => (
{listOrganization.length > 0 ? listOrganization.map((item, index) => (
<div
key={index}
className="education-department-home-item-room"
@ -181,7 +251,12 @@ export default function EducationDepartmentHome() {
{item?.name}
</p>
</div>
))}
))
:
<div className="d-flex justify-content-center">
Không phòng giáo dục nào
</div>
}
</div>
</div>
</div>

@ -66,7 +66,6 @@ export default function HeadmasterHome() {
setOffsetOnline(0)
setLoadMoreOnline(true)
setIsEndOnlineClasses(false)
setIsFiltered(true);
scrollToTop()
setSemester(item);
setMonth(getListMonthBySemester(item?.value)?.[0]);
@ -170,6 +169,7 @@ export default function HeadmasterHome() {
isLoadMoreOnline &&
!isLoading
) {
if(listTeacher.length < limitOnline) return
onLoadMoreClasses();
}
};
@ -254,19 +254,11 @@ export default function HeadmasterHome() {
<Header
icon={renderIconHome({ color: "#4D4D4D" })}
title={schoolName}
isBack={true}
/>
<div className="container-page-header container-page-sidebar">
<div className="headmaster-home-container bg-main-img">
<div className="headmaster-home-left-side">
{authentication.user.role ==='supper_admin' &&(
<div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}>
<PrimaryButton style={{ alignSelf: "center", justifyContent:'flex end', marginRight:30 }} onClick={()=>{
history.goBack()
}}>
Quay lại
</PrimaryButton>
</div>
)}
<div className="headmaster-home-overview">
<p className="headmaster-home-overview-title">
Mức độ hoàn thành của trường

@ -59,7 +59,7 @@ export default function OutstandingTeacher() {
<div className="flex-1">
<Header
icon={renderIconHome({ color: "#4D4D4D" })}
title={"Top 10 giáo viên tiêu biểu - Tỉnh Nam Định"}
title={"Top 10 giáo viên tiêu biểu - " + authentication.user.province}
/>
<div className="container-page-header container-page-sidebar">
<div className="outstanding-teacher-container bg-outstanding-img">
@ -130,7 +130,7 @@ export default function OutstandingTeacher() {
<div className="outstanding-teacher-list">
{!isLoading && !listTeacher?.length && (
<p style={{fontSize: '1.8rem', fontWeight: 700}}>
Không có giáo viên nào
Không còn GV nào để hiển thị
</p>
)}
{listTeacher.map((item, index) => {
@ -147,10 +147,10 @@ export default function OutstandingTeacher() {
{(index + 2) + '. ' + item?.teacher_name}
</p>
<p className="outstanding-teacher-info-text">
{item?.email}
{item?.district_name}
</p>
<p className="outstanding-teacher-info-text">
{item?.phone}
{item?.school_name}
</p>
</div>
<div className="outstanding-teacher-criteria">

@ -39,6 +39,7 @@
display: flex;
align-items: center;
justify-content: center;
max-height: 290px;
.outstanding-teacher-best-avatar-box {
height: 90%;

@ -175,6 +175,7 @@ export default function TeacherHome() {
isLoadMoreOnline &&
!isLoading
) {
if(listClass.length < limitOnline) return
onLoadMoreClasses();
}
};

Loading…
Cancel
Save