Feat : chart style

uat
Quy_FE 3 months ago
parent d8cb4ae59b
commit 239e0734b3
  1. 19
      src/_components/Header/index.js
  2. 89
      src/_components/boxChart/BoxDoughnutBarChart.js
  3. 21
      src/_components/boxChart/BoxDoughnutChar.js
  4. 8
      src/_components/chart/RDoughnutChart.js
  5. 9
      src/_components/chart/VerticalBarChart.js
  6. 14
      src/_components/renderIcon/index.js
  7. 1
      src/_screens/criteria/criteria-manage/index.js
  8. 1
      src/_screens/home/admin/index.js
  9. 1
      src/_screens/home/detail-grade/index.js
  10. 64
      src/_screens/home/detail-room-education/index.js
  11. 10
      src/_screens/home/education-department/educationDepartmentHome.style.scss
  12. 34
      src/_screens/home/education-department/index.js
  13. 1
      src/_screens/home/headmaster/index.js
  14. 2
      src/_screens/home/outstanding-teacher/index.js
  15. 1
      src/_screens/home/teacher/index.js

@ -2,14 +2,23 @@ import { useSelector } from "react-redux";
import "./header.style.scss";
import { configConstants } from "../../_constants";
export default function Header({ icon, title, subtitles = [] }) {
export default function Header({ icon, title, subtitles = [],manager=false }) {
const authentication = useSelector((state) => state.authentication);
const { fullname, organization_name, role } = authentication?.user || {};
const hasFullName = fullname || organization_name;
const fullName = role === "organization_admin"
? `${organization_name && !organization_name.toLowerCase().includes('giáo dục') ? '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">
@ -27,7 +36,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,71 +1,80 @@
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'
import { renderIconDate } from '../renderIcon'
export default function BoxDoughnutBarChart({dataDoughnut = [], dataBarChart = [], labelsBarChart = [], dateBarChat, onSetDateBarChart, titleDoughnut, subtitleDoughnut, titleLine, subTitleLine}) {
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)
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>
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 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>
)
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>
) : (
<div className="d-flex justify-content-center align-items-center flex-1">
Không dữ liệu để hiển thị
</div>
)}
</div>
)
return (
<div className="box-chart-container">
{renderDoughnutSection()}
{renderBarSection()}
<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}
setValue={handleMonthChange}
name="month"
renderLabelIcon={renderIconDate}
errorText={monthError}
errorAbsolute={true}
// placeholder={"Chọn ngày sinh"}
popperPlacement='top'
typeErrText='underAbsolute'
isMonthPicker

@ -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>

@ -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,17 @@ export const renderIconSearchInput = () => {
</svg>
);
};
export 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>
)
}
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>
)

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

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

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

@ -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";
@ -211,49 +211,51 @@ 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>
)
}
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}>

@ -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;

@ -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,7 @@ export default function EducationDepartmentHome() {
const [dataStudentChart, setDataStudentChart] = useState([])
const [dataTeacherChart, setDataTeacherChart] = useState([])
const [listOrganization, setListOrganization] = useState([])
const [searchText, setSearchText] = useState('')
const getDataOrganization = async () => {
try {
@ -109,22 +111,21 @@ 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 = ()=>{
}
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 +155,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,6 +172,20 @@ export default function EducationDepartmentHome() {
<p className="education-department-home-note">
Danh sách phòng giáo dục
</p>
{/* <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 className="education-department-home-list-content scrollbar-custom">
<div className="education-department-home-list-room">
{listOrganization.map((item, index) => (

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

@ -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">

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

Loading…
Cancel
Save