add code 1803

master
HOANGLAOTA 4 months ago
parent 0181865128
commit 391738c4e1
  1. 5
      package.json
  2. 65
      public/assets/css/all.css
  3. BIN
      public/assets/imgs/bg/bg_outstanding.png
  4. BIN
      public/assets/imgs/bg/bg_sub_main_green.png
  5. BIN
      public/assets/imgs/copper_medal.png
  6. BIN
      public/assets/imgs/crown_warning.png
  7. BIN
      public/assets/imgs/silver_medal.png
  8. 16
      src/App.js
  9. 90
      src/_components/Auth/InputDate/index.js
  10. 10
      src/_components/Auth/InputDate/index.scss
  11. 494
      src/_components/Calendar/CalendaSchedule.js
  12. 68
      src/_components/Calendar/PickDay.js
  13. 67
      src/_components/Calendar/SelectDate.js
  14. 3
      src/_components/Calendar/index.js
  15. 203
      src/_components/Calendar/style.scss
  16. 5
      src/_components/Header/header.style.scss
  17. 5
      src/_components/RootSelect/index.js
  18. 14
      src/_components/RootSelect/rootSelect.style.scss
  19. 2
      src/_components/Router/RouteRedirectToAdmin.js
  20. 4
      src/_components/Router/RouteRedirectToLogin.js
  21. 4
      src/_components/Sidebar/sidebar.style.scss
  22. 76
      src/_components/boxChart/BoxDoughnutBarChart.js
  23. 19
      src/_components/boxChart/BoxDoughnutChar.js
  24. 23
      src/_components/boxChart/boxChart.scss
  25. 67
      src/_components/chart/RDoughnutChart.js
  26. 36
      src/_components/chart/RPieChart.js
  27. 83
      src/_components/chart/VerticalBarChart.js
  28. 2
      src/_constants/common.js
  29. 1
      src/_constants/path.js
  30. 29
      src/_constants/user.js
  31. 17
      src/_helpers/utils.js
  32. 97
      src/_screens/home/detail-room-education/detailRoomEducation.style.scss
  33. 355
      src/_screens/home/detail-room-education/index.js
  34. 76
      src/_screens/home/education-department/educationDepartmentHome.style.scss
  35. 155
      src/_screens/home/education-department/index.js
  36. 187
      src/_screens/home/outstanding-teacher/index.js
  37. 243
      src/_screens/home/outstanding-teacher/outstandingTeacher.style.scss
  38. 2
      src/_screens/login/index.js

@ -9,7 +9,7 @@
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"axios": "^0.21.1",
"chart.js": "^2.9.4",
"chart.js": "^3.6.0",
"chartjs-plugin-datalabels": "1.0.0",
"classnames": "^2.3.1",
"date-fns": "^2.22.1",
@ -19,13 +19,14 @@
"jquery": "^3.6.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"moment": "^2.30.1",
"moment-duration-format": "2.2.2",
"node-sass": "9.0.0",
"qs": "^6.9.6",
"query-string": "^7.1.1",
"react": "^17.0.1",
"react-apple-login": "^1.1.3",
"react-chartjs-2": "^2.11.1",
"react-chartjs-2": "^4.0.0",
"react-datepicker": "^4.1.1",
"react-dom": "^17.0.1",
"react-facebook-login": "^4.1.1",

@ -88,6 +88,7 @@
--primary-color: #00CC83;
--second-color: #FF9F00;
--button-bg-color: #FF9F00;
--text-color: #4D4D4D;
--height-header: 64px;
--width-sidebar: 78px;
}
@ -96,9 +97,10 @@ html {
font-size: 62.5%;
}
@media screen and (max-height: 700px) {
@media screen and (max-height: 800px) {
:root {
--height-header: 54px
--height-header: 42px;
--width-sidebar: 68px;
}
html {
@ -1139,6 +1141,30 @@ a:hover {
background-size: cover;
}
.bg-sub-main-green-img {
width: 100%;
height: 100%;
background-image: url("./../imgs/bg/bg_sub_main_green.png");
background-position: bottom;
background-repeat: no-repeat;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
.bg-outstanding-img {
width: 100%;
height: 100%;
background-image: url("./../imgs/bg/bg_outstanding.png");
background-position: bottom;
background-repeat: no-repeat;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
.bg-main-color {
background-image: linear-gradient(to top, #fafffd, #e1fef0);
}
@ -8491,12 +8517,41 @@ p.text-center.f-18 {
.react-datepicker__month-text--keyboard-selected,
.react-datepicker__quarter-text--keyboard-selected,
.react-datepicker__year-text--keyboard-selected {
border-radius: 50% !important;
border-radius: 8px !important;
background-image: linear-gradient(to right, #00e1a0, #00b9b7);
border: none;
color: #fff !important;
}
.react-datepicker__month .react-datepicker__month-text:hover, .react-datepicker__month .react-datepicker__quarter-text:hover {
border-radius: 8px !important;
border: none !important;
}
.react-datepicker__month .react-datepicker__month-text, .react-datepicker__month .react-datepicker__quarter-text {
width: auto !important;
}
.react-datepicker__current-month, .react-datepicker-time__header, .react-datepicker-year-header {
font-size: 1.6rem !important;
}
.react-datepicker__navigation {
top: .8rem !important;
}
.react-datepicker__month {
width: 180px !important;
}
.react-datepicker__month-wrapper {
display: flex !important;
}
.react-datepicker__day--today, .react-datepicker__month-text--today, .react-datepicker__quarter-text--today, .react-datepicker__year-text--today {
border-radius: 8px !important;
}
.react-datepicker__day--keyboard-selected {
border-radius: 0;
background-color: #fff !important;
@ -11243,3 +11298,7 @@ strong {
align-items: center;
gap: 2.4rem;
}
.gap-16 {
gap: 16px
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@ -9,7 +9,7 @@ import {
import { alertActions } from "./_actions";
import { history } from "./_helpers";
import Login from "./_screens/login";
import { PATH, USER_ROLE } from "./_constants";
import { PATH, USER_ROLE, USER_TYPE } from "./_constants";
import HomePage from "./_screens/home";
import ForgetPassword from "./_screens/forget-password";
import TeacherHome from "./_screens/home/teacher";
@ -21,6 +21,7 @@ import DetailGrade from "./_screens/home/detail-grade";
import DetailRoomEducation from "./_screens/home/detail-room-education";
import EducationDepartmentHome from "./_screens/home/education-department";
import { Alert } from "./_components/Alert";
import OutstandingTeacher from "./_screens/home/outstanding-teacher";
function App() {
const dispatch = useDispatch();
@ -83,7 +84,7 @@ function App() {
/>
<RouteRedirectToLogin
path={PATH.home.headmaster}
prefix={[USER_ROLE.HEADMASTER]}
// prefix={[USER_ROLE.HEADMASTER]}
exact={true}
component={HeadmasterHome}
authentication={authentication}
@ -104,15 +105,22 @@ function App() {
/>
<RouteRedirectToLogin
path={PATH.home.educationDepartment}
// prefix={USER_ROLE.TEACHER}
prefix={[USER_ROLE.ADMIN, USER_TYPE.PROVINCE]}
exact={true}
component={EducationDepartmentHome}
authentication={authentication}
/>
<RouteRedirectToLogin
path={PATH.home.detailRoomEducation}
path={PATH.home.outstandingTeacherByEducation}
// prefix={USER_ROLE.TEACHER}
exact={true}
component={OutstandingTeacher}
authentication={authentication}
/>
<RouteRedirectToLogin
path={PATH.home.detailRoomEducation}
prefix={[USER_ROLE.ADMIN, USER_TYPE.PROVINCE, USER_TYPE.DISTRICT]}
exact={true}
component={DetailRoomEducation}
authentication={authentication}
/>

@ -3,9 +3,33 @@ import React, { useRef, useState } from "react";
// import { SelectDate } from "../../Calendar";
import "./index.scss";
import DatePicker, { registerLocale } from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import vi from "date-fns/locale/vi";
import { isEmpty } from "lodash";
registerLocale("vi", vi);
const months = [
"Tháng 1",
"Tháng 2",
"Tháng 3",
"Tháng 4",
"Tháng 5",
"Tháng 6",
"Tháng 7",
"Tháng 8",
"Tháng 9",
"Tháng 10",
"Tháng 11",
"Tháng 12",
];
const days = ["CN", "T2", "T3", "T4", "T5", "T6", "T7"];
registerLocale("vi", {
...vi,
options: { ...vi.options, weekStartsOn: 1 },
localize: {
month: (n) => months[n],
day: (n) => days[n],
},
});
const InputDate = (props) => {
const $inputRef = useRef(null);
@ -23,15 +47,9 @@ const InputDate = (props) => {
};
return (
<div className="input_date_base_container">
{props.errorText ? (
<div
className={
props.errorAbsolute
? "error_text_absolute error_input"
: "error_text error_input"
}
>
<div className="input_date_base_container" style={props?.styleContainer}>
{props.errorText && props?.typeErrText != "underAbsolute" ? (
<div className={props.errorAbsolute ? "error_text_absolute error_input" : "error_text error_input"}>
<span>{props.errorText}</span>
</div>
) : null}
@ -42,44 +60,54 @@ const InputDate = (props) => {
focus: isFocus,
warning: props.isWarning,
},
`${props.className ? props.className : ""} input_date_base`,
`${props.className ? props.className : ""} input_date_base ${props.readOnly && " bg_readonly"}`
)}
style={props.style}
>
<div className="icon_label">
{props.renderLabelIcon ? props.renderLabelIcon() : null}
</div>
<div className="icon_label">{props.renderLabelIcon ? props.renderLabelIcon() : null}</div>
<DatePicker
maxDate={props?.maxDate}
className="date_picker"
className={`date_picker ${
props.readOnly && " notallowed_cursor"
}`}
selected={props.value}
onChange={(date) => changeValue(date)}
showMonthDropdown
// showMonthDropdown
showYearDropdown
showMonthYearPicker={!!props?.isMonthPicker}
showFullMonthYearPicker={!!props?.isMonthPicker}
fixedHeight
dropdownMode="select"
dateFormat="P"
dateFormat={!!props?.isMonthPicker ? "MM/yyyy" : "P"}
locale="vi"
placeholderText={
!isEmpty(props?.placeholder)
? `${props?.placeholder}`
: `DD/MM/YYYY`
}
placeholderText={!isEmpty(props?.placeholder) ? `${props?.placeholder}`: !!props?.isMonthPicker ? 'MM/YYYY' : `DD/MM/YYYY`}
name={props.name}
popperPlacement="bottom"
popperModifiers={[
{
name: "flip",
options: {
fallbackPlacements: ["bottom"],
},
},
]}
popperPlacement={props?.popperPlacement ? props.popperPlacement : "bottom"}
// popperModifiers={[
// {
// name: "flip",
// options: {
// fallbackPlacements: ["bottom"],
// },
// },
// ]}
onKeyDown={(e) => {
e.preventDefault();
}}
readOnly={props?.readOnly}
/>
</div>
{props.errorText && props?.typeErrText == "underAbsolute" ? (
<div
className={
props.errorAbsolute
? "error_text_absolute_new error_input"
: "error_text error_input"
}
>
<span>{props.errorText}</span>
</div>
) : null}
</div>
);
};

@ -25,23 +25,19 @@ $border-color: #4a4848;
.input_date_base {
display: flex;
align-items: center;
height: 65px;
height: 40px;
padding: 0px 22px;
border: 1px solid $border-color;
border-radius: 10px;
margin-bottom: 22px;
@media screen and (max-width: 768px) {
height: 44px;
// margin-bottom: 22px;
svg {
width: 18px;
}
}
.icon_label {
width: 31px;
margin-right: 26px;
// margin-right: 26px;
flex: none;
@include screen_mobile {

@ -0,0 +1,494 @@
import React, { useCallback, useEffect, useState } from "react";
import dayjs from "dayjs";
import range from "lodash-es/range";
import { scheduleActions } from "./../../_actions";
import moment from "moment";
import "./style.scss";
import { useSelector, useDispatch } from "react-redux";
import { useParams } from "react-router-dom";
import { scheduleConstants } from "../../_constants";
import { isEmpty } from "lodash";
const weekDays = ["T2", "T3", "T4", "T5", "T6", "T7", "CN"];
const todayObj = dayjs();
function CalendaSchedule(props) {
const dispatch = useDispatch();
const schedules = useSelector((state) => state.schedules);
const status = schedules.status;
const date = new Date();
let {
changeDateCalendaSchedule,
collapse,
showAll,
showMonth,
role,
student_id,
hideEvent,
class_id,
homePageTeacherCalendaSchedule,
} = props;
role = role || "teacher";
const [fullHeight, setFullHeight] = useState(false);
const [dayObj, setDayObj] = useState(
dayjs(
date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate()
)
);
const thisYearSelected = schedules.selectDate
? parseInt(moment(schedules.selectDate)?.format("YYYY"))
: dayObj.year();
const thisMonthSelected = schedules.selectDate
? parseInt(moment(schedules.selectDate)?.format("MM") - 1)
: dayObj.month();
const thisDateSelected = schedules.selectDate
? parseInt(moment(schedules.selectDate)?.format("DD"))
: dayObj.date();
const thisWeekSelected = weekCount(
thisYearSelected,
thisMonthSelected,
thisDateSelected
);
const thisYear = dayObj.year();
const thisMonth = dayObj.month();
const thisDate = dayObj.date();
const thisWeek = weekCount(thisYear, thisMonth, thisDate);
const daysInMonth = dayObj.daysInMonth();
const dayObjOf1 = dayjs(`${thisYear}-${thisMonth + 1}-1`);
let weekDayOf1 = dayObjOf1.day() - 1;
if (weekDayOf1 < 0) {
weekDayOf1 = 6;
}
const dayObjOfLast = dayjs(`${thisYear}-${thisMonth + 1}-${daysInMonth}`);
let weekDayOfLast = dayObjOfLast.day() - 1;
if (weekDayOfLast < 0) {
weekDayOfLast = 6;
}
const handlePrev = () => {
let dayObjTmp = dayObj.subtract(1, "month");
dispatch(
scheduleActions.getStatus(
dayObjTmp.month() + 1,
dayObjTmp.year(),
role,
student_id,
class_id
)
);
setDayObj(dayObjTmp);
};
const handleNext = () => {
let dayObjTmp = dayObj.add(1, "month");
dispatch(
scheduleActions.getStatus(
dayObjTmp.month() + 1,
dayObjTmp.year(),
role,
student_id,
class_id
)
);
setDayObj(dayObjTmp);
};
const [selectDate, setSelectDate] = useState({
year: schedules.dateSelectedCalendar
? parseInt(moment(schedules.dateSelectedCalendar).format("YYYY"))
: localStorage.getItem("date_selected")
? parseInt(
moment(JSON.parse(localStorage.getItem("date_selected"))).format(
"YYYY"
)
)
: thisYear,
month: schedules.dateSelectedCalendar
? parseInt(
parseInt(moment(schedules.dateSelectedCalendar).format("MM")) - 1
)
: localStorage.getItem("date_selected")
? parseInt(
parseInt(
moment(JSON.parse(localStorage.getItem("date_selected"))).format(
"MM"
)
) - 1
)
: thisMonth,
day: schedules.dateSelectedCalendar
? parseInt(moment(schedules.dateSelectedCalendar).format("DD"))
: localStorage.getItem("date_selected")
? parseInt(
moment(JSON.parse(localStorage.getItem("date_selected"))).format("DD")
)
: todayObj.date(),
});
function selectDay(year, month, day) {
localStorage.setItem(
"date_selected",
JSON.stringify(`${year}-${parseInt(month) + 1}-${day}`)
);
setSelectDate({
year,
month,
day,
});
dispatch({
type: scheduleConstants.SET_DATE_SELECTED_CALENDAR,
time: `${year}-${parseInt(month) + 1}-${day}`,
});
changeDateCalendaSchedule({
year,
month,
day,
});
}
useEffect(() => {
let currentDate = new Date();
dispatch(
scheduleActions.getStatus(
parseInt(currentDate.getMonth()) + 1,
currentDate.getFullYear(),
role,
student_id,
class_id
)
);
return () => {
dispatch({
type: scheduleConstants.GET_STATUS,
data: {
in_complete: [],
complete: [],
has_log: [],
},
});
};
}, []);
const renderClass = useCallback(
(day, month, year) => {
if (hideEvent) {
return "";
}
let date = year + "-" + parseInt(month) + "-" + day;
if (status.in_complete.indexOf(moment(date).format("YYYY-MM-DD")) != -1) {
return " missing";
} else if (
status.complete.indexOf(moment(date).format("YYYY-MM-DD")) != -1
) {
return " complete";
} else if (
status?.has_log?.length > 0 &&
status.has_log?.indexOf(moment(date).format("YYYY-MM-DD")) != -1
) {
return " active_date";
} else {
return "";
}
},
[status]
);
function showFullHeight() {
setFullHeight(!fullHeight);
}
function renderClassHideCollapse(day, month, year) {
let dateFormat =
selectDate.year +
"-" +
(parseInt(selectDate.month) + 1) +
"-" +
selectDate.day;
let dateWeekToday = moment(dateFormat);
let dowDateWeekToday = parseInt(dateWeekToday.day()) + 1;
let listDateShowCollapse = [moment(dateFormat).format("YYYY-MM-DD")];
if (dowDateWeekToday == 2) {
for (let i = 1; i <= 6; i++) {
listDateShowCollapse.push(
moment(dateFormat, "YYYY-MM-DD").add("days", i).format("YYYY-MM-DD")
);
}
} else if (dowDateWeekToday == 1) {
for (let i = 1; i <= 6; i++) {
listDateShowCollapse.push(
moment(dateFormat, "YYYY-MM-DD")
.subtract("days", i)
.format("YYYY-MM-DD")
);
}
} else {
for (let i = 2; i < dowDateWeekToday; i++) {
listDateShowCollapse.push(
moment(dateFormat, "YYYY-MM-DD")
.subtract("days", i - 1)
.format("YYYY-MM-DD")
);
}
for (let i = 1; i <= 8 - dowDateWeekToday; i++) {
listDateShowCollapse.push(
moment(dateFormat, "YYYY-MM-DD").add("days", i).format("YYYY-MM-DD")
);
}
}
let date = year + "-" + month + "-" + day;
if (listDateShowCollapse.indexOf(moment(date).format("YYYY-MM-DD")) == -1) {
// return ' hide_collapse';
} else {
// return '';
}
return "";
}
function dequyWeek(days, week, total) {
days = parseInt(days) + 7;
week++;
if (total > days) {
return dequyWeek(days, week, total);
} else {
return week;
}
}
function weekCount(year, month_number, total) {
var firstOfMonth = new Date(year, month_number, 1);
let week = 1;
let days = firstOfMonth.getDay() == 0 ? 1 : 8 - firstOfMonth.getDay();
if (total > days) {
week = dequyWeek(days, week, total);
}
return week;
}
const renderClassShot = (date) => {
if (fullHeight || !collapse) {
return "";
} else {
let className = "";
if (
thisMonthSelected == dayObj.month() &&
thisYearSelected == dayObj.year()
) {
let week = weekCount(thisYearSelected, thisMonthSelected, date);
className = " hide-date-custom";
if (thisWeekSelected == week) {
className = "";
}
}
return className;
}
};
const showWeekDayOf1 = () => {
if (fullHeight || !collapse) {
return true;
} else {
let isHide = true;
if (
thisMonthSelected == dayObj.month() &&
thisYearSelected == dayObj.year()
) {
var firstOfMonth = new Date(thisYear, thisMonth, 1);
if (
(firstOfMonth.getDay() == 1 && thisWeekSelected == 1) ||
thisWeekSelected != 1
) {
isHide = false;
}
}
// console.log('isHide ====', isHide)
return isHide;
}
};
return (
<div
className={
(fullHeight || showAll ? "fullHeight " : "") +
(collapse ? " collapse" : "") +
(showMonth ? " showMonth" : "")
}
>
<div className="calendar">
<div
className={
"header day-calendar-custom" +
(collapse && !showMonth ? "hide" : "")
}
>
<button
type="button"
className={"nav nav--prev"}
onClick={handlePrev}
>
<img
alt="ico_left_calender"
src="/assets/images/icon/ico_left_calender.png"
/>
</button>
<div className="datetime">{"Tháng " + dayObj.format("M/YYYY")}</div>
<button
type="button"
className={"nav nav--prev"}
onClick={handleNext}
>
<img
alt="ico_right_calender"
src="/assets/images/icon/ico_right_calender.png"
/>
</button>
</div>
<div className="week-container fix-nowap-row-calendar">
{weekDays.map((d) => (
<div
className={`${
homePageTeacherCalendaSchedule
? "homePageTeacherCalendaSchedule"
: ""
} week-cell flex-1`}
key={d}
>
{d}
</div>
))}
</div>
<div className="day-container">
{showWeekDayOf1() &&
range(weekDayOf1).map((i) => (
<div className={"day-box"} key={i}>
<div
className={
`${
homePageTeacherCalendaSchedule
? "homePageTeacherCalendaSchedule"
: ""
} day-cell day-cell--faded` +
renderClassHideCollapse(
dayObjOf1.subtract(weekDayOf1 - i, "day").date() + 1,
todayObj.month(),
todayObj.year()
) +
renderClass(
dayObjOf1.subtract(weekDayOf1 - i, "day").date() + 1,
todayObj.month(),
todayObj.year()
)
}
key={i}
>
{dayObjOf1.subtract(weekDayOf1 - i, "day").date()}
</div>
</div>
))}
{range(daysInMonth).map((i) => (
<div className={"day-box" + renderClassShot(i + 1)} key={i}>
<div
className={
`${
homePageTeacherCalendaSchedule
? "homePageTeacherCalendaSchedule"
: ""
} rel day-cell day-cell--in-month${
i + 1 == todayObj.date() &&
thisMonth == todayObj.month() &&
thisYear == todayObj.year()
? " day-cell--today"
: ""
}${
i + 1 == selectDate.day &&
selectDate.month == thisMonth &&
selectDate.year == thisYear
? " active"
: ""
}` +
renderClass(i + 1, thisMonth + 1, thisYear) +
renderClassHideCollapse(i + 1, thisMonth + 1, thisYear) +
renderClassShot(i + 1)
}
key={i}
onClick={() => selectDay(thisYear, thisMonth, i + 1)}
>
{i + 1}
<img
alt="ico_check_calendar"
src="/assets/images/icon/ico_check_calendar.png"
className="img-check-calendar"
/>
<img
alt="ico_missing_calendar"
src="/assets/images/icon/ico_missing_calendar.png"
className="img-missing-calendar"
/>
<img
alt="ico_active_calendar_green"
src="/assets/images/icon/ico_green_circle.png"
className="img-active_dot-calendar"
/>
</div>
</div>
))}
{range(6 - weekDayOfLast).map((i) => (
<div className={"day-box"} key={i}>
<div
className={
`${
homePageTeacherCalendaSchedule
? "homePageTeacherCalendaSchedule"
: ""
} day-cell day-cell--faded` +
renderClassHideCollapse(
dayObjOfLast.add(i + 1, "day").date(),
todayObj.month() + 2,
todayObj.year()
) +
renderClass(
dayObjOfLast.add(i + 1, "day").date(),
todayObj.month() + 2,
todayObj.year()
)
}
key={i}
>
{dayObjOfLast.add(i + 1, "day").date()}
</div>
</div>
))}
</div>
</div>
{collapse && (
<div className="btn-collapse" onClick={() => showFullHeight()}>
{fullHeight ? (
<img
src="/assets/images/student/ico_dropup_blue.png"
alt="ico_dropup_blue"
className="ico-calender-collapse"
/>
) : (
<img
src="/assets/images/student/ico_dropdown_blue.png"
alt="ico_dropdown_blue"
className="ico-calender-collapse"
/>
)}
</div>
)}
</div>
);
}
export { CalendaSchedule };

@ -0,0 +1,68 @@
import React, { useState } from 'react';
import { isEmpty } from 'lodash';
function PickDay(props) {
let { time, handleChangeDate, name_time } = props;
const [inputs, setInputs] = useState({
day: !isEmpty(time) ? time.substr(0, 2) : new Date().getDate(),
month: !isEmpty(time) ? time.substr(3, 2) : new Date().getMonth() + 1,
year: !isEmpty(time) ? time.substr(6, 4) : new Date().getFullYear(),
});
function createElementsOption(start, end) {
var elements = [];
for (let i = start; i <= end; i++) {
elements.push(<option key={i} >{i}</option>);
}
return elements;
}
let defaultValueDate = new Date().getDate();
let defaultValueMonth = new Date().getMonth() + 1;
let defaultValueYear = new Date().getFullYear();
if (!isEmpty(time)) {
defaultValueDate = time.substr(0, 2);
defaultValueMonth = time.substr(3, 2)
defaultValueYear = time.substr(6, 4)
}
function handleChange(e) {
try{
const { name, value } = e.target;
let day = defaultValueDate;
let month = defaultValueMonth;
let year = defaultValueYear;
if(_.isEqual(name, 'day')){
day = value;
}else if(_.isEqual(name, 'month')){
month = value;
if ((new Date(defaultValueYear, month, 0)).getDate() < (new Date(defaultValueYear, defaultValueMonth, 0)).getDate() && day > (new Date(defaultValueYear, month, 0)).getDate()){
day = (new Date(defaultValueYear, month, 0)).getDate()
}
}else if(_.isEqual(name, 'year')){
year = value;
}
handleChangeDate({
[name_time]: year + '-' + month + '-' + day,
})
}catch(e){
console.log('handleChange', e)
}
}
return (
<div className="select-gr">
<select onChange={handleChange} name={'day'} value={parseInt(defaultValueDate)}>
{createElementsOption(1, (new Date(defaultValueYear, defaultValueMonth, 0)).getDate()) || 31}
</select>
<select onChange={handleChange} name={'month'} value={parseInt(defaultValueMonth)}>
{createElementsOption(1, 12)}
</select>
<select onChange={handleChange} name={'year'} value={parseInt(defaultValueYear)}>
{createElementsOption(2000, 2099)}
</select>
</div>
);
}
export { PickDay };

@ -0,0 +1,67 @@
import React, { Fragment, useEffect, useRef } from "react";
// import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import DatePicker, { registerLocale } from "react-datepicker";
import es from "date-fns/locale/es";
const months = [
"Tháng 1 / ",
"Tháng 2 / ",
"Tháng 3 / ",
"Tháng 4 / ",
"Tháng 5 / ",
"Tháng 6 / ",
"Tháng 7 / ",
"Tháng 8 / ",
"Tháng 9 / ",
"Tháng 10 / ",
"Tháng 11 / ",
"Tháng 12 / ",
];
const days = ["CN", "T2", "T3", "T4", "T5", "T6", "T7"];
registerLocale("es", {
...es,
options: { ...es.options, weekStartsOn: 1 },
localize: {
month: (n) => months[n],
day: (n) => days[n],
},
});
function SelectDate(props) {
const { id, name, selected, onChange, dateFormat, disableMouseFocus, minDate } = props;
const showTimeSelect = props.showTimeSelect || false;
const showTimeSelectOnly = props.showTimeSelectOnly || false;
const dateRef = useRef(null);
useEffect(() => {
let isFirefox = window.navigator.userAgent.indexOf("Firefox") != -1;
if (dateRef.current && disableMouseFocus && !isFirefox) {
dateRef.current?.input?.setAttribute("disabled", true);
}
}, [dateRef, disableMouseFocus]);
return (
<Fragment>
<DatePicker
minDate={minDate ? new Date() : {}}
onChangeRaw={(e) => e.preventDefault()}
showTimeSelect={showTimeSelect}
showTimeSelectOnly={showTimeSelectOnly}
// timeFormat="HH:mm"
locale="es"
id={id}
timeIntervals={props?.timeIntervals || 15}
dateFormat={dateFormat}
name={name}
selected={selected}
placeholderText="DD/MM/YYYY"
onChange={(time) => onChange(time)}
ref={dateRef}
/>
</Fragment>
);
}
export { SelectDate };

@ -0,0 +1,3 @@
export * from './CalendaSchedule';
export * from './PickDay';
export * from './SelectDate';

@ -0,0 +1,203 @@
@import url('https://fonts.googleapis.com/css?family=Manjari:400,700&display=swap');
body {
font-family: Manjari, sans-serif;
font-size: 14px;
}
.calendar {
width: 350px;
margin: 0 auto;
box-shadow: 0 2px 4px 0 rgba(21, 27, 38, .15);
border-radius: 6px;
}
.header {
display: flex;
align-items: center;
padding: 12px 12px 0 12px;
border-radius: 6px 6px 0 0;
background-color: #fff;
color: #fff;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
}
.nav {
border: 1px solid #fff;
border-radius: 25px;
outline: 0;
background-color: transparent;
cursor: pointer;
}
.datetime {
margin: 0 auto;
font-size: 18px;
font-weight: bold;
color: #00B9B7;
}
.week-container,
.day-container {
display: flex;
flex-wrap: wrap;
}
.week-cell{
font-family: 'Myriadpro-SemiBold';
}
.week-cell,
.day-cell {
flex: 0 0 calc(100% / 7 - 10px);
display: flex;
justify-content: center;
align-items: center;
height: 40px;
width: 40px;
cursor: pointer;
margin: 5px;
}
.day-cell.active{
background: #fff;
border: 2px solid #00A79D!important;
border-radius: 25px!important;
color: #404041!important;
}
.img-check-calendar{
display: none;
position: absolute;
top: 0;
right: -4px;
}
.day-cell.complete .img-check-calendar{
display: block;
}
.img-missing-calendar{
display: none;
position: absolute;
top: 9px;
right: 1px;
}
.day-cell.missing .img-missing-calendar{
display: block;
}
.day-cell.complete .img-missing-calendar{
display: none;
}
.day-cell.active_date .img-missing-calendar{
display: none;
}
.day-cell.today .img-missing-calendar{
display: none;
}
.img-active_dot-calendar{
display: none;
position: absolute;
top: 0px;
right: -5px;
}
.day-cell.missing .img-active_dot-calendar{
display: none;
}
.day-cell.complete .img-active_dot-calendar{
display: none;
}
.day-cell.active_date .img-active_dot-calendar{
display: block;
}
.day-cell.today .img-active_dot-calendar{
display: none;
}
.day-cell.today{
border: none!important;
}
.day-cell {
&--faded {
opacity: .4;
}
&--today {
border-radius: 25px;
background-image: linear-gradient(to right, #00e1a0 , #00b9b7)!important;
color: #fff!important;
border: none;
}
}
.lichngay-teacher .week-cell,.lichngay-teacher .day-cell {
flex-basis: calc(100% / 7 - 36px);
margin: 5px 0px;
}
.hide_collapse{
display : none;
}
.fullHeight .hide_collapse{
display : flex;
}
@media screen and (max-height: 800px) {
.img-check-calendar {
top: -4px;
right: -2px;
}
.box-40-40.lichngay-teacher .day-cell {
flex-basis: calc(100% / 7 - 24px);
margin: 5px 0px;
}
.hide_collapse {
display: none;
}
}
@media screen and (max-height: 700px) {
.calendar {
// width: 258px;
margin: 0 auto;
box-shadow: 2px 3px 4px 0 rgba(21, 27, 38, .15);
border-radius: 6px;
}
.week-cell,
.day-cell {
font-size: 14px;
flex: 0 0 calc(100% / 7 - 10px);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
margin: 5px 0px;
}
.day-cell.missing .img-missing-calendar {
display: block;
width: 7px;
}
.img-missing-calendar {
display: none;
position: absolute;
top: 2px;
right: -3px;
}
.img-check-calendar {
top: -4px;
right: -4px;
}
.box-40-40.lichngay-teacher .day-cell {
flex-basis: calc(100% / 7 - 27px);
margin: 5px 0px;
}
.hide_collapse {
display: none;
}
.homePageTeacherCalendaSchedule {
margin: 0 !important
}
}

@ -36,6 +36,11 @@
box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px,
rgba(0, 0, 0, 0.24) 0px 1px 2px;
border-radius: 50%;
@media screen and (max-height: 800px) {
width: 32px;
height: 32px;
}
}
}
}

@ -9,6 +9,8 @@ export default function RootSelect({
setValue,
className,
isLoading = false,
bgWhite = false,
...other
}) {
const ref = useRef(null);
const [isOpen, setIsOpen] = useState(false);
@ -66,8 +68,9 @@ export default function RootSelect({
return (
<div
ref={ref}
className={"box-select " + (className ? className : "")}
className={"box-select" + (bgWhite ? ' box-select-bg-white' : ' ') + (className ? className : "")}
onClick={onToggle}
{...other}
>
{isLoading ? (
<RootLoading height={24} width={24} strokeWidth={4} />

@ -22,7 +22,7 @@
.item-selected-label {
color: #fff;
font-size: 2rem;
font-size: 1.6rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -71,3 +71,15 @@
}
}
}
.box-select.box-select-bg-white {
background-color: #fff;
.item-selected-label {
color: var(--text-color);
}
svg {
color: var(--text-color);
}
}

@ -18,7 +18,7 @@ export const RouteRedirectToAdmin = ({
return (
<Redirect
to={{
pathname: routeHome(rest?.authentication?.user?.role),
pathname: routeHome(rest?.authentication?.user),
state: { from: props.location },
}}
/>

@ -27,11 +27,11 @@ export const RouteRedirectToLogin = ({
/>
);
}
if (!!prefix?.length && !prefix?.includes(rest?.authentication?.user?.role)) {
if (!!prefix?.length && !prefix?.includes(rest?.authentication?.user?.role) && !prefix?.includes(rest?.authentication?.user?.type)) {
return (
<Redirect
to={{
pathname: routeHome(rest?.authentication?.user?.role),
pathname: routeHome(rest?.authentication?.user),
state: { from: props.location },
}}
/>

@ -14,6 +14,10 @@
justify-content: center;
align-items: center;
@media screen and (max-height: 800px) {
width: 60px;
}
.sidebar-logo-img {
width: 100%;
height: 100%;

@ -0,0 +1,76 @@
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 InputDate from '../Auth/InputDate'
export default function BoxDoughnutBarChart({dataDoughnut = [], dataBarChart = [], labelsBarChart = [], dateBarChat, onSetDateBarChart, titleDoughnut, subtitleDoughnut, titleLine, subTitleLine}) {
const [month, setMonth] = useState(dateBarChat)
const [monthError, setMonthError] = useState('')
const changeMonth = (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 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>
)
}
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>
</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
/>
</div>
</div>
)
}

@ -0,0 +1,19 @@
import { PRIMARY_COLOR } from '../../_constants/common'
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]]
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='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>
</div>
</div>
</div>
)
}

@ -0,0 +1,23 @@
.box-chart-container {
padding: 1.6rem;
border-radius: 8px;
box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
background-color: #fff;
flex: 1;
display: flex;
flex-direction: column;
.box-chart-title {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
padding-bottom: 0.5rem;
}
.box-chart-subtitle {
font-size: 1.8rem;
font-weight: 700;
}
.doughnut-chart-content {}
}

@ -0,0 +1,67 @@
import React from 'react';
import { Chart } from 'react-chartjs-2';
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
import { PRIMARY_COLOR } from '../../_constants/common';
ChartJS.register(ArcElement, Tooltip, Legend);
// const data = {
// labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
// datasets: [
// {
// label: '# of Votes',
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: [
// 'rgba(255, 99, 132, 0.2)',
// 'rgba(54, 162, 235, 0.2)',
// 'rgba(255, 206, 86, 0.2)',
// 'rgba(75, 192, 192, 0.2)',
// 'rgba(153, 102, 255, 0.2)',
// 'rgba(255, 159, 64, 0.2)',
// ],
// borderColor: [
// 'rgba(255, 99, 132, 1)',
// 'rgba(54, 162, 235, 1)',
// 'rgba(255, 206, 86, 1)',
// 'rgba(75, 192, 192, 1)',
// 'rgba(153, 102, 255, 1)',
// 'rgba(255, 159, 64, 1)',
// ],
// borderWidth: 1,
// },
// ],
// };
export default function RDoughnutChart({data = [], ...other}) {
if (!data?.length) {
return null
}
return <Chart
type='doughnut'
data={{
labels: [],
datasets: [
{
label: '',
data,
backgroundColor: [
PRIMARY_COLOR, '#01AEF0'
],
borderWidth: 0
},
],
}}
options={{
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: {
enabled: false
}
}
}}
{...other}
/>;
}

@ -0,0 +1,36 @@
import React from 'react';
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
import { Pie } from 'react-chartjs-2';
ChartJS.register(ArcElement, Tooltip, Legend);
// const data = {
// labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
// datasets: [
// {
// label: '# of Votes',
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: [
// 'rgba(255, 99, 132, 0.2)',
// 'rgba(54, 162, 235, 0.2)',
// 'rgba(255, 206, 86, 0.2)',
// 'rgba(75, 192, 192, 0.2)',
// 'rgba(153, 102, 255, 0.2)',
// 'rgba(255, 159, 64, 0.2)',
// ],
// borderColor: [
// 'rgba(255, 99, 132, 1)',
// 'rgba(54, 162, 235, 1)',
// 'rgba(255, 206, 86, 1)',
// 'rgba(75, 192, 192, 1)',
// 'rgba(153, 102, 255, 1)',
// 'rgba(255, 159, 64, 1)',
// ],
// borderWidth: 1,
// },
// ],
// };
export default function RPieChart({data, ...other}) {
return <Pie data={data} {...other} />;
}

@ -0,0 +1,83 @@
import React from 'react';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import { Bar } from 'react-chartjs-2';
import { PRIMARY_COLOR } from '../../_constants/common';
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
export function VerticalBarChart({data = [], labels = []}) {
if (!data?.length) {
return null
}
let maxValue = 0;
data?.map(item => {
if (item > maxValue) {
maxValue = item;
}
})
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
datalabels: {
anchor: 'end', // Position of the labels (start, end, center, etc.)
align: 'end', // Alignment of the labels (start, end, center, etc.)
color: 'blue', // Color of the labels
font: {
weight: 'bold',
},
formatter: function (value, context) {
return value; // Display the actual data value
}
}
},
scales: {
y: {
max: maxValue < 5 ? 5 : maxValue < 20 ? maxValue + 5 : maxValue + 10,
ticks: {
callback: function(value) {if (value % 1 === 0) {return value;}},
stepSize: 1,
precision: 0,
}
},
yAxes: [{
ticks: {
beginAtZero: true,
callback: function(value) {if (value % 1 === 0) {return value;}},
stepSize: 1,
precision: 0,
}
}]
},
};
return <Bar options={options} data={{
labels,
datasets: [
{
data,
backgroundColor: PRIMARY_COLOR,
barPercentage: 0.4,
},
],
}} />;
}

@ -175,3 +175,5 @@ export const DEFAULT_SETTING_CRITERIA = {
enable: false,
},
}
export const PRIMARY_COLOR = '#00CC83'

@ -11,6 +11,7 @@ export const PATH = {
detailGrade: "/home/detail-grade/:schoolId/:gradeId",
detailRoomEducation: "/home/detail-room-education/:idRoom",
educationDepartment: "/home/education-department",
outstandingTeacherByEducation: '/home/education-department/outstanding-teacher/:id'
},
criteria: {
root: "/criteria",

@ -3,3 +3,32 @@ export const USER_ROLE = {
ADMIN: "supper_admin",
HEADMASTER: "organization_admin"
};
export const USER_TYPE = {
PROVINCE: 'province',
DISTRICT: 'district',
SCHOOLMASTER: 'schoolmaster'
}
export const USER_TYPE_ROLE = {
admin: {
role: USER_ROLE.ADMIN,
type: null
},
PROVINCE: {
role: USER_ROLE.HEADMASTER,
type: USER_TYPE.PROVINCE
},
DISTRICT: {
role: USER_ROLE.HEADMASTER,
type: USER_TYPE.DISTRICT
},
headMaster: {
role: USER_ROLE.HEADMASTER,
type: USER_TYPE.SCHOOLMASTER
},
teacher: {
role: USER_ROLE.TEACHER,
type: null
}
}

@ -1,4 +1,4 @@
import { PATH, USER_ROLE } from "../_constants";
import { PATH, USER_ROLE, USER_TYPE } from "../_constants";
import XLSX from "sheetjs-style";
import { listAlphabet } from "../_constants/common";
@ -29,14 +29,23 @@ export const convertSkillVN = (skill) => {
}
};
export const routeHome = (role) => {
switch (role) {
export const routeHome = (user) => {
switch (user?.role) {
case USER_ROLE.TEACHER:
return PATH.home.teacher;
case USER_ROLE.ADMIN:
return PATH.home.admin;
case USER_ROLE.HEADMASTER:
return PATH.home.headmaster
switch (user?.type) {
case USER_TYPE.PROVINCE:
return PATH.home.educationDepartment;
case USER_TYPE.DISTRICT:
return replacePathParams(PATH.home.detailRoomEducation, {idRoom: user?.organization_id});
case USER_TYPE.SCHOOLMASTER:
return PATH.home.headmaster;
default:
return "";
}
default:
return "";
}

@ -1,62 +1,71 @@
@import "/src/_styles/mixin";
.detail-room-education-container {
padding: 24px 0 48px;
display: flex;
flex-direction: column;
overflow: auto;
.detail-room-education-action {
.detail-room-education-left-side {
width: 40%;
border-right: 1px solid #c7c7c7;
display: flex;
gap: 16px;
justify-content: space-between;
align-items: center;
padding: 0 24px 0 20rem;
flex-direction: column;
padding: 16px 0;
.detail-room-education-note {
.detail-room-education-statistic-container {
flex: 1;
text-align: center;
font-weight: 700;
font-size: 2.2rem;
max-height: 95%;
padding: 0 2rem 2rem;
display: flex;
flex-direction: column;
}
}
.detail-room-education-list-container {
padding: 4rem 24rem 0;
.detail-room-education-right-side {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding-right: 8px;
background-color: #fff;
.detail-room-education-list-school {
// margin-top: 16px;
padding-bottom: 16px;
padding-right: 24px;
margin-right: -24px;
.detail-room-education-right-p-h {
padding-right: 32px;
}
.detail-room-education-list-container {
padding: 2rem 0;
display: flex;
flex-direction: column;
height: 100%;
.detail-room-education-list {
flex: 1;
gap: 20px;
display: flex;
flex-direction: column;
padding-bottom: 16px;
padding-left: 32px;
@include screen_pc_sm {
gap: 16px;
}
.detail-room-education-item-school {
.detail-room-education-item {
padding: 8px;
border: 1px solid #e5e5e5;
border-radius: 6px;
display: flex;
gap: 16px;
border: 1.25px solid var(--primary-color);
border-radius: 4px;
background-color: #fff;
flex: 1;
transition: all 0.5s linear;
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
&:hover {
opacity: 0.8;
cursor: pointer;
}
.detail-room-education-avatar-school {
.detail-room-education-item-content {
display: flex;
gap: 16px;
.detail-room-education-avatar {
width: 88px;
height: 88px;
border-radius: 5px;
@ -76,48 +85,57 @@
}
}
.detail-room-education-detail-school {
.detail-room-education-detail {
display: flex;
flex: 1;
justify-content: space-between;
gap: 24px;
.detail-room-education-info-school {
.detail-room-education-info {
display: flex;
align-items: center;
flex-direction: column;
.criteria-info-school-name {
.detail-room-education-info-name {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
}
.criteria-info-school-text {
}
}
}
.detail-room-education-info-text {
font-size: 1.8rem;
}
}
}
}
}
.detail-room-education-criteria-class {
.detail-room-education-criteria {
display: flex;
flex-direction: column;
.criteria-class-title {
.detail-room-education-criteria-title {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
}
.criteria-class-row {
.detail-room-education-criteria-row {
display: flex;
justify-content: space-between;
gap: 8px;
.criteria-class-title {
.criteria-teacher-title {
font-size: 2rem;
font-weight: 700;
}
.criteria-class-criteria {
.detail-room-education-criteria-ratio {
font-size: 1.8rem;
font-weight: 400;
position: relative;
@ -136,9 +154,6 @@
}
}
}
}
}
}
}
}
}

@ -1,11 +1,222 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import Header from "../../../_components/Header";
import { renderIconHome } from "../../../_components/renderIcon";
import PrimaryButton from "../../../_components/Button/PrimaryButton";
import "./detailRoomEducation.style.scss";
import RateStar from "../../../_components/RateStar";
import { defaultMonthYearSemester, getListMonthBySemester, LIST_SCHOOL_YEAR, LIST_SEMESTER, PRIMARY_COLOR } from "../../../_constants/common";
import { configConstants, PATH } from "../../../_constants";
import RootSelect from "../../../_components/RootSelect";
import PrimaryButton from "../../../_components/Button/PrimaryButton";
import { useSelector } from "react-redux";
import './detailRoomEducation.style.scss'
import BoxDoughnutChart from "../../../_components/boxChart/BoxDoughnutChar";
import BoxDoughnutBarChart from "../../../_components/boxChart/BoxDoughnutBarChart";
import { apiCaller, history } from "../../../_helpers";
import { replacePathParams } from "../../../_helpers/utils";
import $ from "jquery";
import { useParams } from "react-router-dom";
export default function DetailRoomEducation() {
const {idRoom} = useParams()
const grade = useSelector((state) => state.grade);
const authentication = useSelector((state) => state.authentication);
const [dateStudentChart, setDateStudentChart] = useState(new Date())
const [dateTeacherChart, setDateTeacherChart] = useState(new Date())
const [data, setData] = useState()
const [dataStudentChart, setDataStudentChart] = useState([])
const [dataTeacherChart, setDataTeacherChart] = useState([])
const [schoolYear, setSchoolYear] = useState(
grade?.filterGrade?.schoolYear,
);
const [semester, setSemester] = useState(grade?.filterGrade?.semester);
const [month, setMonth] = useState(grade?.filterGrade?.month);
const [listOrganization, setListOrganization] = useState([]);
const [isLoadingListOrganization, setIsLoadingListOrganization] = useState(false);
const [isLoadingStatisticCircle, setIsLoadingStatisticCircle] = useState(false);
const [isLoadingStudentChart, setIsLoadingStudentChart] = useState(false);
const [isLoadingTeacherChart, setIsLoadingTeacherChart] = useState(false);
const getListOrganization = async () => {
try {
setIsLoadingListOrganization(true);
const endPoint = `/report/api_report/getListOrganizationFromDistrictOrganization?organization_id=${
idRoom
}${
!!schoolYear?.value ? `&year=${schoolYear?.value}` : ""
}${
!!semester?.value ? `&semester=${semester?.value}` : ""
}${
!!month?.value ? `&month=${month?.value}` : ""
}`;
const res = await apiCaller(
endPoint,
"GET",
{},
null,
true,
configConstants.API_URL_SETEST,
false
)
if (res?.status) {
const endPointStatistic = `/report/api_report/getOrganizationStatisticsByOrganizationId?organization_id=${
idRoom
}&is_no_cache=1${
!!schoolYear?.value ? `&year=${schoolYear?.value}` : ""
}${
!!semester?.value ? `&semester=${semester?.value}` : ""
}${
!!month?.value ? `&month=${month?.value}` : ""
}`;
apiCaller(
endPointStatistic,
"GET",
{},
null,
true,
configConstants.API_URL_SETEST,
false
)
.then(resStatistic => {
if (resStatistic?.status) {
const listData = res?.data?.map(item => {
const statistic = resStatistic?.data?.find(statisticItem => statisticItem?.organization_id === item?.organization_id);
return {
...item,
...statistic
}
})
setListOrganization(listData)
} else {
setListOrganization(res?.data);
}
setIsLoadingListOrganization(false);
}).catch(e => {
setListOrganization(res?.data);
setIsLoadingListOrganization(false);
})
} else {
setIsLoadingListOrganization(false);
}
} catch (err) {
setIsLoadingListOrganization(false)
console.log(err)
}
}
const getDataStatisticCircle = async () => {
try {
setIsLoadingStatisticCircle(true);
const res = await apiCaller(
"/report/api_report/statisticalInfoOrganization?organization_id=" + idRoom,
"GET",
{},
null,
true,
configConstants.API_URL_SETEST,
false
)
if (res?.status) {
setData(res?.data)
}
setIsLoadingStatisticCircle(false);
} catch (e) {
setIsLoadingStatisticCircle(false);
}
}
const getDataStudentChart = async () => {
try {
setIsLoadingStudentChart(true);
const endPoint = '/report/api_report/homeworkChartInfo?organization_id=' + idRoom +
'&month='+ (dateStudentChart.getMonth() + 1) + '&year=' + dateStudentChart.getFullYear()
const res = await apiCaller(
endPoint,
"GET",
{},
null,
true,
configConstants.API_URL_SETEST,
false
)
if (res?.status) {
setDataStudentChart(res?.data)
}
setIsLoadingStudentChart(false);
} catch (e) {
setIsLoadingStudentChart(false);
}
}
const getDataTeacherChart = async () => {
try {
setIsLoadingTeacherChart(true);
const endPoint = '/report/api_report/assignmentChartInfo?organization_id=' + idRoom +
'&month='+ (dateTeacherChart.getMonth() + 1) + '&year=' + dateTeacherChart.getFullYear()
const res = await apiCaller(
endPoint,
"GET",
{},
null,
true,
configConstants.API_URL_SETEST,
false
)
if (res?.status) {
setDataTeacherChart(res?.data)
}
setIsLoadingTeacherChart(false)
} catch (e) {
setIsLoadingTeacherChart(false);
}
}
const changeSemester = (item) => {
setSemester(item);
setMonth(getListMonthBySemester(item?.value)?.[0]);
};
const handleFilter = () => {
getListOrganization();
};
const goToOutstandingTeacher = () => {
history.push(replacePathParams(PATH.home.outstandingTeacherByEducation, {id: idRoom}))
}
const goToDetailSchool = (item) => {
history.push(replacePathParams(PATH.home.detailSchool, {schoolId: item?.organization_id}) +
"?school_name=" +
encodeURIComponent(item?.organization_name));
}
useEffect(() => {
getDataStatisticCircle()
getListOrganization()
}, [])
useEffect(() => {
getDataStudentChart()
}, [dateStudentChart])
useEffect(() => {
getDataTeacherChart()
}, [dateTeacherChart])
useEffect(() => {
if (isLoadingListOrganization || isLoadingStatisticCircle || isLoadingStudentChart || isLoadingTeacherChart) {
$(".loading").removeClass("hide");
} else {
$(".loading").addClass("hide");
}
}, [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>
)
}
return (
<div className="flex-1">
<Header
@ -13,55 +224,126 @@ export default function DetailRoomEducation() {
title={"Phòng giáo dục huyện Giao Thủy"}
/>
<div className="container-page-header container-page-sidebar">
<div className="detail-room-education-container bg-main-img">
<div className="detail-room-education-action">
<p className="detail-room-education-note">
Vui lòng chọn 1 trường trong danh sách sau
</p>
<PrimaryButton>Xuất excel</PrimaryButton>
<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%'}}>
<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="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
<div style={{marginLeft: 8, paddingBottom: 4}}>{renderIconButton()}</div>
</PrimaryButton>
</div>
</div>
</div>
<div className="detail-room-education-right-side">
<div className="detail-room-education-list-container">
<div className="detail-room-education-list-school scrollbar-custom">
{Array(12)
.fill(0)
.map((item, index) => {
<span style={{fontSize: '2rem', fontWeight: 700, padding: '0 3.2rem'}}>Danh sách trường</span>
<div className="flex gap-16 align-item-center" style={{padding: '1rem 3.2rem'}}>
<RootSelect
data={LIST_SCHOOL_YEAR}
value={schoolYear}
setValue={setSchoolYear}
style={{flex: 1}}
/>
<RootSelect
data={LIST_SEMESTER}
value={semester}
setValue={changeSemester}
style={{flex: 0.5}}
/>
<RootSelect
data={getListMonthBySemester(semester.value)}
value={month}
setValue={setMonth}
style={{flex: 0.5}}
/>
<PrimaryButton onClick={handleFilter}>
Áp dụng
</PrimaryButton>
</div>
<div 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
</p>
)}
{listOrganization.map((item, index) => {
return (
<div
className="detail-room-education-item-school"
key={index}
onClick={() => handleSelectItem(item)}
>
<div className="detail-room-education-avatar-school">
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcThSrNdeft8MqmfOGz-QLn5vzMZ35iCfLaOsA&s" />
</div>
<div className="detail-room-education-detail-school">
<div className="detail-room-education-info-school">
<p className="criteria-info-school-name">
Trường THCS Giao An
<div className="detail-room-education-item" key={index} onClick={() => goToDetailSchool(item)}>
<div className="detail-room-education-item-content">
<div className="detail-room-education-avatar">
<img src={configConstants.BASE_URL + item?.avatar} />
</div>
<div className="detail-room-education-detail">
<div className="detail-room-education-info">
<p className="detail-room-education-info-name">
{item?.organization_name}
</p>
<p className="detail-room-education-info-text">
{'Giáo viên tham gia: ' + (item?.total_join_teacher || 0) + '/' + (item?.total_real_teacher || 0)}
</p>
<p className="detail-room-education-info-text">
{'Học sinh tham gia: ' + (item?.total_join_student || 0) + '/' + (item?.total_real_student || 0)}
</p>
</div>
<div className="detail-room-education-criteria-class">
<div className="criteria-class-row">
<p className="criteria-class-title font_news">
<div className="detail-room-education-criteria">
<div className="detail-room-education-criteria-row">
<p className="detail-room-education-criteria-title">
Mức độ CĐS Sunday English
</p>
<RateStar rate={1} />
<RateStar rate={item?.criteria_level} />
</div>
<div className="criteria-class-row">
<p className="criteria-class-criteria font_news">
<div className="detail-room-education-criteria-row">
<p className="detail-room-education-criteria-ratio">
Tiêu chí giao bài
</p>
<RateStar rate={2} />
<RateStar rate={item?.assign_number_level} />
</div>
<div className="criteria-class-row">
<p className="criteria-class-criteria font_news">
<div className="detail-room-education-criteria-row">
<p className="detail-room-education-criteria-ratio">
Tiêu chí tỷ lệ học sinh làm bài
</p>
<RateStar rate={3} />
<RateStar rate={item?.student_done_per_level} />
</div>
</div>
</div>
</div>
<p className="detail-room-education-info-text">
Số lượng active:{' '}
<span style={{fontWeight: 700}}>{item?.total_active_teacher || 0}</span>
{' '}giáo viên |{' '}
<span style={{fontWeight: 700}}>{item?.total_active_student || 0}</span>
{' '}học sinh
</p>
</div>
);
})}
</div>
@ -69,5 +351,6 @@ export default function DetailRoomEducation() {
</div>
</div>
</div>
);
</div>
)
}

@ -1,76 +1,53 @@
@import "/src/_styles/mixin";
.education-department-home-container {
overflow: auto;
display: flex;
flex-direction: column;
overflow: hidden;
.education-department-home-banner {
width: 100%;
height: 30%;
background-image: url("../../../_assets/banner_education_department.png");
background-position: bottom;
background-repeat: no-repeat;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
display: flex;
justify-content: center;
align-items: flex-end;
.education-department-statistic-container {
flex: 0.7;
height: calc(100vh - var(--height-header));
padding: 2rem;
@include screen_pc_sm {
height: 35%;
.education-department-statistic-col {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
}
.input_text_base_container {
width: unset;
}
.education-department-home-search {
.education-department-home-list-container {
flex: 0.3;
height: calc(100vh - var(--height-header));
overflow: hidden;
padding: 2rem 0;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 40px;
margin-bottom: 16px;
height: 46px;
width: 392px;
@include screen_pc_sm {
height: 36px;
}
}
}
.education-department-home-note {
font-size: 2.2rem;
font-size: 2rem;
font-weight: 700;
text-align: center;
margin-top: 24px;
@include screen_pc_sm {
margin-top: 16px;
}
padding-left: 3.2rem;
padding-bottom: 1.2rem;
}
.education-department-home-list-container {
.education-department-home-list-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 0 10% 2.4rem;
padding: 0 3.2rem;
.education-department-home-list-room {
flex: 1;
margin-top: 12px;
display: flex;
flex-wrap: wrap;
justify-content: center;
// align-items: center;
column-gap: 40px;
row-gap: 2.4rem;
flex-direction: column;
gap: 1.6rem;
.education-department-home-item-room {
max-width: 544px;
min-width: 444px;
width: 100%;
height: fit-content;
border-radius: 10px;
border: 1px solid var(--primary-color);
@ -101,7 +78,8 @@
}
.education-department-home-item-text {
font-size: 2rem;
font-size: 1.6rem;
}
}
}
}

@ -1,4 +1,4 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import InputText from "../../../_components/Auth/InputText";
import Header from "../../../_components/Header";
import {
@ -6,9 +6,91 @@ import {
renderIconSearchInput,
} from "../../../_components/renderIcon";
import "./educationDepartmentHome.style.scss";
import { PRIMARY_COLOR } from "../../../_constants/common";
import RDoughnutChart from "../../../_components/chart/RDoughnutChart";
import BoxDoughnutChart from "../../../_components/boxChart/BoxDoughnutChar";
import BoxDoughnutBarChart from "../../../_components/boxChart/BoxDoughnutBarChart";
import PrimaryButton from "../../../_components/Button/PrimaryButton";
import { apiCaller, history } from "../../../_helpers";
import { PATH } from "../../../_constants";
import { replacePathParams } from "../../../_helpers/utils";
import { useSelector } from "react-redux";
export default function EducationDepartmentHome() {
const [searchText, setSearchText] = useState("");
const authentication = useSelector((state) => state.authentication);
const [dateStudentChart, setDateStudentChart] = useState(new Date())
const [dateTeacherChart, setDateTeacherChart] = useState(new Date())
const [data, setData] = useState()
const [dataStudentChart, setDataStudentChart] = useState([])
const [dataTeacherChart, setDataTeacherChart] = useState([])
const [listOrganization, setListOrganization] = useState([])
const getDataOrganization = async () => {
try {
const res = await apiCaller("/report/api_report/listChildOrganization?organization_id=" + authentication?.user?.organization_id, "GET")
if (res?.status) {
setListOrganization(res?.data)
}
} catch (e) {
}
}
const getDataStatisticCircle = async () => {
try {
const res = await apiCaller("/report/api_report/statisticalInfoOrganization?organization_id=" + authentication?.user?.organization_id, "GET")
if (res?.status) {
setData(res?.data)
}
} catch (e) {
}
}
const getDataStudentChart = async () => {
try {
const endPoint = '/report/api_report/homeworkChartInfo?organization_id=' + authentication?.user?.organization_id +
'&month='+ (dateStudentChart.getMonth() + 1) + '&year=' + dateStudentChart.getFullYear()
const res = await apiCaller(endPoint, "GET")
if (res?.status) {
setDataStudentChart(res?.data)
}
} catch (e) { }
}
const getDataTeacherChart = async () => {
try {
const endPoint = '/report/api_report/assignmentChartInfo?organization_id=' + authentication?.user?.organization_id +
'&month='+ (dateTeacherChart.getMonth() + 1) + '&year=' + dateTeacherChart.getFullYear()
const res = await apiCaller(endPoint, "GET")
if (res?.status) {
setDataTeacherChart(res?.data)
}
} catch (e) { }
}
const goToOutstandingTeacher = () => {
history.push(replacePathParams(PATH.home.outstandingTeacherByEducation, {id: authentication?.user?.organization_id}))
}
const goToDetailRoomEducation = (item) => {
history.push(replacePathParams(PATH.home.detailRoomEducation, {idRoom: item?.id}))
}
useEffect(() => {
getDataOrganization()
getDataStatisticCircle()
}, [])
useEffect(() => {
getDataStudentChart()
}, [dateStudentChart])
useEffect(() => {
getDataTeacherChart()
}, [dateTeacherChart])
const renderIconBook = () => {
return (
@ -27,6 +109,12 @@ 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>
)
}
return (
<div className="flex-1">
<Header
@ -34,35 +122,63 @@ export default function EducationDepartmentHome() {
title={"Sở giáo dục tỉnh Nam Định"}
/>
<div className="container-page-header container-page-sidebar">
<div className="education-department-home-container bg-sub-main-img">
<div className="education-department-home-banner">
<InputText
className="education-department-home-search"
value={searchText}
setValue={setSearchText}
type="text"
name="searchText"
placeholder={"Nhập tên giáo viên để tìm kiếm"}
renderLabelIcon={renderIconSearchInput}
<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%'}}>
<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'} />
<BoxDoughnutChart data={[data?.class_join, data?.total_class]} title={'Số lớp đã tham gia'} />
</div>
<div className="education-department-statistic-col" style={{height: '100%', display: 'contents'}}>
<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"}
/>
</div>
<div className="education-department-statistic-col" style={{height: '100%', display: 'contents'}}>
<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="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
<div style={{marginLeft: 8, paddingBottom: 4}}>{renderIconButton()}</div>
</PrimaryButton>
</div>
</div>
<div className="education-department-home-list-container">
<p className="education-department-home-note">
Vui lòng chọn Phòng Giáo Dục trong danh sách sau
Danh sách phòng giáo dục
</p>
<div className="education-department-home-list-container">
<div className="education-department-home-list-room scrollbar-custom">
{Array(12)
.fill(0)
.map((item, index) => (
<div className="education-department-home-list-content scrollbar-custom">
<div className="education-department-home-list-room">
{listOrganization.map((item, index) => (
<div
key={index}
className="education-department-home-item-room"
onClick={() => goToDetailRoomEducation(item)}
>
<div className="education-department-home-item-img">
{renderIconBook()}
</div>
<p className="education-department-home-item-text">
Phòng giáo dục huyện Giao Thủy
{item?.name}
</p>
</div>
))}
@ -71,5 +187,6 @@ export default function EducationDepartmentHome() {
</div>
</div>
</div>
</div>
);
}

@ -0,0 +1,187 @@
import { useEffect, useState } from "react";
import Header from "../../../_components/Header";
import { renderIconHome } from "../../../_components/renderIcon";
import RateStar from "../../../_components/RateStar";
import { defaultMonthYearSemester, getListMonthBySemester, LIST_SCHOOL_YEAR, LIST_SEMESTER } from "../../../_constants/common";
import { configConstants } from "../../../_constants";
import RootSelect from "../../../_components/RootSelect";
import PrimaryButton from "../../../_components/Button/PrimaryButton";
import { useSelector } from "react-redux";
import './outstandingTeacher.style.scss'
import { apiCaller } from "../../../_helpers";
import { useParams } from "react-router-dom";
export default function OutstandingTeacher() {
const {id} = useParams()
const authentication = useSelector((state) => state.authentication);
const grade = useSelector((state) => state.grade);
const [schoolYear, setSchoolYear] = useState(
grade?.filterGrade?.schoolYear,
);
const [semester, setSemester] = useState(grade?.filterGrade?.semester);
const [month, setMonth] = useState(grade?.filterGrade?.month);
const [firstTeacher, setFirstTeacher] = useState();
const [listTeacher, setListTeacher] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const getData = async () => {
setIsLoading(true)
try {
const endPoint = `/report/api_report/outStandingTeachers?organization_id=${id}&year=${schoolYear?.value}${!!semester?.value ? `&semester=${semester?.value}` : ""}${!!month?.value ? `&month=${month?.value}` : ""}`
const res = await apiCaller(endPoint, "GET")
if (res.status) {
const firstTeacher = res?.data?.[0];
const listTeacher = res?.data?.slice(1);
setFirstTeacher(firstTeacher);
setListTeacher(listTeacher);
setIsLoading(false)
}
} catch (err) {
setIsLoading(false)
}
}
const changeSemester = (item) => {
setSemester(item);
setMonth(getListMonthBySemester(item?.value)?.[0]);
};
const handleFilter = () => {
getData();
};
useEffect(() => {
getData()
}, [])
return (
<div className="flex-1">
<Header
icon={renderIconHome({ color: "#4D4D4D" })}
title={"Top 10 giáo viên tiêu biểu - Tỉnh Nam Định"}
/>
<div className="container-page-header container-page-sidebar">
<div className="outstanding-teacher-container bg-outstanding-img">
<div className="outstanding-teacher-left-side">
<div className="outstanding-teacher-form-select">
<RootSelect
data={LIST_SCHOOL_YEAR}
value={schoolYear}
setValue={setSchoolYear}
bgWhite
/>
<RootSelect
data={LIST_SEMESTER}
value={semester}
setValue={changeSemester}
bgWhite
/>
<RootSelect
data={getListMonthBySemester(semester.value)}
value={month}
setValue={setMonth}
bgWhite
/>
<PrimaryButton style={{ alignSelf: "center", marginTop: 8 }} onClick={handleFilter}>
Áp dụng
</PrimaryButton>
</div>
{!!firstTeacher && <div className="outstanding-teacher-best-container">
<div className="outstanding-teacher-best-avatar-content">
<div className="outstanding-teacher-best-avatar-box">
<img className="outstanding-teacher-best-crown" src="/assets/imgs/crown_warning.png" />
<img className="outstanding-teacher-best-avatar" src={configConstants.BASE_URL + firstTeacher?.avatar} />
</div>
</div>
<div className="outstanding-teacher-best-info">
<span className="outstanding-teacher-best-name">{firstTeacher?.teacher_name}</span>
<div className="flex flex-m" style={{gap: '1.6rem', width: '100%', marginTop: '1rem'}}>
<div className="flex-1 outstanding-teacher-best-address">{firstTeacher?.district_name}</div>
<div className="flex-1 outstanding-teacher-best-address">{firstTeacher?.school_name}</div>
</div>
<div className="outstanding-teacher-best-criteria">
<div className="outstanding-teacher-criteria">
<div className="outstanding-teacher-criteria-row">
<p className="outstanding-teacher-criteria-title">
Mức độ CĐS Sunday English
</p>
<RateStar rate={firstTeacher?.criteria_teacher?.criteria_level} />
</div>
<div className="outstanding-teacher-criteria-row">
<p className="outstanding-teacher-criteria-ratio">
Tiêu chí giao bài
</p>
<RateStar rate={firstTeacher?.criteria_teacher?.assign_number_level} />
</div>
<div className="outstanding-teacher-criteria-row">
<p className="outstanding-teacher-criteria-ratio">
Tiêu chí tỷ lệ học sinh làm bài
</p>
<RateStar rate={firstTeacher?.criteria_teacher?.student_done_per_level} />
</div>
</div>
</div>
</div>
</div>}
</div>
<div className="outstanding-teacher-right-side">
<div className="outstanding-teacher-list-container outstanding-teacher-right-p-h scrollbar-custom">
<div className="outstanding-teacher-list">
{!isLoading && !listTeacher?.length && (
<p style={{fontSize: '1.8rem', fontWeight: 700}}>
Không giáo viên nào
</p>
)}
{listTeacher.map((item, index) => {
return (
<div className="outstanding-teacher-item" key={index} onClick={() => handleClickTeacherItem(item)}>
{index === 0 && <img src="/assets/imgs/silver_medal.png" className="outstanding-teacher-silver-medal" />}
{index === 1 && <img src="/assets/imgs/copper_medal.png" className="outstanding-teacher-copper-medal" />}
<div className="outstanding-teacher-avatar">
<img src={configConstants.BASE_URL + item?.avatar} />
</div>
<div className="outstanding-teacher-detail">
<div className="outstanding-teacher-info">
<p className="outstanding-teacher-info-name">
{(index + 2) + '. ' + item?.teacher_name}
</p>
<p className="outstanding-teacher-info-text">
{item?.email}
</p>
<p className="outstanding-teacher-info-text">
{item?.phone}
</p>
</div>
<div className="outstanding-teacher-criteria">
<div className="outstanding-teacher-criteria-row">
<p className="outstanding-teacher-criteria-title">
Mức độ CĐS Sunday English
</p>
<RateStar rate={item?.criteria_teacher?.criteria_level} />
</div>
<div className="outstanding-teacher-criteria-row">
<p className="outstanding-teacher-criteria-ratio">
Tiêu chí giao bài
</p>
<RateStar rate={item?.criteria_teacher?.assign_number_level} />
</div>
<div className="outstanding-teacher-criteria-row">
<p className="outstanding-teacher-criteria-ratio">
Tiêu chí tỷ lệ học sinh làm bài
</p>
<RateStar rate={item?.criteria_teacher?.student_done_per_level} />
</div>
</div>
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
</div>
</div>
)
}

@ -0,0 +1,243 @@
@import "/src/_styles/mixin";
.outstanding-teacher-container {
display: flex;
padding: 24px 0 48px;
// overflow: hidden;
@include screen_pc_sm {
padding: 16px 0;
}
.outstanding-teacher-left-side {
width: 30%;
border-right: 1px solid #4d4d4d;
display: flex;
flex-direction: column;
.outstanding-teacher-form-select {
padding: 0 32px;
display: flex;
flex-direction: column;
gap: 16px;
}
.outstanding-teacher-best-container {
flex: 1;
display: flex;
flex-direction: column;
padding: 0 32px;
margin-top: 3.2rem;
@include screen_pc_sm {
margin-top: 2rem;
}
.outstanding-teacher-best-avatar-content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
.outstanding-teacher-best-avatar-box {
height: 90%;
aspect-ratio: 1;
padding: 0.8rem;
border-radius: 50%;
border: 1px solid #fff;
position: relative;
.outstanding-teacher-best-crown {
position: absolute;
top: -30%;
left: -16%;
height: 70%;
width: 70%;
}
.outstanding-teacher-best-avatar {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px;
}
}
}
.outstanding-teacher-best-info {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
.outstanding-teacher-best-name {
color: #fff;
font-size: 2.4rem;
font-weight: 700;
align-self: center;
}
.outstanding-teacher-best-address {
padding: .4rem;
border-radius: 2.4rem;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(17, 17, 17, 0.3);
color: #fff;
}
.outstanding-teacher-best-criteria {
background-color: #fff;
width: 100%;
padding: 1.6rem;
border-radius: .8rem;
margin-top: 1.6rem;
}
}
}
}
.outstanding-teacher-right-side {
flex: 1;
display: flex;
flex-direction: column;
padding-right: 8px;
.outstanding-teacher-right-p-h {
padding-right: 32px;
}
.outstanding-teacher-list-container {
.outstanding-teacher-list {
flex: 1;
gap: 20px;
display: flex;
flex-direction: column;
margin-top: 16px;
padding-bottom: 16px;
padding-left: 32px;
@include screen_pc_sm {
gap: 16px;
}
.outstanding-teacher-item {
padding: 8px;
border: 1.25px solid #e5e5e5;
border-radius: 4px;
display: flex;
gap: 16px;
background-color: #fff;
transition: all 0.5s linear;
position: relative;
&:hover {
opacity: 0.8;
cursor: pointer;
}
.outstanding-teacher-silver-medal,
.outstanding-teacher-copper-medal {
width: 3.2rem;
height: 4.9rem;
object-fit: cover;
position: absolute;
top: -1rem;
left: -1.6rem;
}
.outstanding-teacher-avatar {
width: 88px;
height: 88px;
border-radius: 5px;
box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px,
rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;
@include screen_pc_sm {
height: 70px;
width: 70px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 5px;
}
}
.outstanding-teacher-detail {
display: flex;
flex: 1;
justify-content: space-between;
gap: 24px;
.outstanding-teacher-info {
display: flex;
flex-direction: column;
.outstanding-teacher-info-name {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
}
.outstanding-teacher-info-text {
font-size: 1.8rem;
}
}
}
}
}
}
}
.outstanding-teacher-criteria {
display: flex;
flex-direction: column;
.outstanding-teacher-criteria-title {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
}
.outstanding-teacher-criteria-row {
display: flex;
justify-content: space-between;
gap: 8px;
.criteria-teacher-title {
font-size: 2rem;
font-weight: 700;
}
.outstanding-teacher-criteria-ratio {
font-size: 1.8rem;
font-weight: 400;
position: relative;
padding-left: 12px;
&::before {
content: "";
display: block;
position: absolute;
left: 0;
top: 12px;
width: 5px;
height: 5px;
background-color: var(--primary-color);
border-radius: 50%;
}
}
}
}
}

@ -96,7 +96,7 @@ export default function Login() {
},
});
// if (user?.user_role === USER_ROLE.TEACHER) {
history.push(routeHome(user?.role));
history.push(routeHome(user));
// }
};

Loading…
Cancel
Save