From f9a8da2d0e48049d6184e952f135403a28d8c1e4 Mon Sep 17 00:00:00 2001 From: Huyz109 Date: Fri, 7 Jun 2024 21:46:51 +0700 Subject: [PATCH 01/75] improve(front-end): update chart connect api --- .../src/components/LineChart/LineChart.js | 132 ++++++------------ .../src/components/Modal/ModalChart.js | 23 +-- frontend_web/src/constants/index.js | 2 + frontend_web/src/redux/reducer/recordSlice.js | 31 +++- 4 files changed, 85 insertions(+), 103 deletions(-) diff --git a/frontend_web/src/components/LineChart/LineChart.js b/frontend_web/src/components/LineChart/LineChart.js index 6eccb210..c296eb2e 100644 --- a/frontend_web/src/components/LineChart/LineChart.js +++ b/frontend_web/src/components/LineChart/LineChart.js @@ -3,11 +3,14 @@ import { CategoryScale, Chart, LinearScale, PointElement, LineElement, Title, To import { dummyArray, dummyIncreaseArray } from '../../utils/arrayUtils'; import zoomPlugin from 'chartjs-plugin-zoom'; import { Button } from "antd"; -import { useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { colorChart } from '../../constants'; -const LineChart = () => { +const LineChart = ({ rawData }) => { Chart.register(CategoryScale, LinearScale, PointElement, LineElement, Title, zoomPlugin, Tooltip); const chartRef = useRef(null); + const [yScale, setYScale] = useState({}); + const [datasets, setDataSets] = useState([]); const handleResetZoom = () => { if (chartRef && chartRef.current) { @@ -20,61 +23,49 @@ const LineChart = () => { } const handleDataValue = (data) => { - return data.map(item => item.value) + return data.map(item => item.value); } - const dummyData = { - y: dummyArray(1001), - y2: dummyArray(1001), - y3: dummyArray(1001), - y4: dummyArray(1001) - } + useEffect(() => { + if (rawData) { + const label = Object.keys(rawData).map((key, index) => { + const pointColorChart = pointColor(rawData?.[key], colorChart[index]); + return { + label: key, + data: handleDataValue(rawData?.[key]), + borderColor: colorChart[index], + pointBorderColor: pointColorChart, + backgroundColor: pointColorChart, + yAxisID: key, + stepped: false + } + }); + setDataSets(label); + + let scale = {}; + Object.keys(rawData).forEach((key) => + scale[key] = { + title: { + display: true, + text: key + }, + position: 'left', + stack: 'demo', + stackWeight: 1, + } + ); + setYScale(scale); + } + else { + setDataSets([]); + setYScale({}); + } + }, [rawData]) - const colorData = { - y: pointColor(dummyData.y, 'rgb(54, 162, 235)'), - y2: pointColor(dummyData.y2, 'rgb(255, 99, 132)'), - y3: pointColor(dummyData.y3, 'rgb(75, 192, 192)'), - y4: pointColor(dummyData.y4, 'rgb(255, 205, 86)') - } const dataChart = { - labels: dummyIncreaseArray(1001), - datasets: [ - { - label: "Trục Y", - data: handleDataValue(dummyData.y), - borderColor: 'rgb(54, 162, 235)', - pointBorderColor: colorData.y, - backgroundColor: colorData.y - }, - { - label: "Trục Y2", - data: handleDataValue(dummyData.y2), - borderColor: 'rgb(255, 99, 132)', - pointBorderColor: colorData.y2, - backgroundColor: colorData.y2, - yAxisID: 'y2', - stepped: false - }, - { - label: "Trục Y3", - data: handleDataValue(dummyData.y3), - borderColor: 'rgb(75, 192, 192)', - pointBorderColor: colorData.y3, - backgroundColor: colorData.y3, - yAxisID: 'y3', - stepped: false - }, - { - label: "Trục Y4", - data: handleDataValue(dummyData.y4), - borderColor: 'rgb(255, 205, 86)', - pointBorderColor: colorData.y4, - backgroundColor: colorData.y4, - yAxisID: 'y4', - stepped: false - } - ] + labels: dummyIncreaseArray(74), + datasets: datasets } const optionChart = { @@ -113,42 +104,7 @@ const LineChart = () => { }, max: 150 }, - y: { - title: { - display: true, - text: "Trục Y" - }, - position: 'left', - stack: 'demo', - stackWeight: 1, - }, - y2: { - title: { - display: true, - text: "Trục Y2" - }, - position: 'left', - stack: 'demo', - stackWeight: 1, - }, - y3: { - title: { - display: true, - text: "Trục Y3" - }, - position: 'left', - stack: 'demo', - stackWeight: 1, - }, - y4: { - title: { - display: true, - text: "Trục Y4" - }, - position: 'left', - stack: 'demo', - stackWeight: 1, - } + ...yScale } } diff --git a/frontend_web/src/components/Modal/ModalChart.js b/frontend_web/src/components/Modal/ModalChart.js index 41803276..e69d33c6 100644 --- a/frontend_web/src/components/Modal/ModalChart.js +++ b/frontend_web/src/components/Modal/ModalChart.js @@ -1,23 +1,27 @@ import { Modal } from 'antd'; import { useEffect, useState } from 'react'; -import { httpGetData } from '../../api/common.api'; import LineChart from '../LineChart/LineChart'; +import { useDispatch, useSelector } from 'react-redux'; +import { getDataRecordById, loadStatus, resetChartRecordDataStatus } from '../../redux/reducer/recordSlice'; const ModalChart = ({isOpen, setIsOpen, selectedDevice}) => { const [data, setData] = useState(); + const dispatch = useDispatch(); + const dataState = useSelector((state) => state.record); useEffect(()=>{ - async function fetchData (){ - const data = await httpGetData(`/record/data/100`); - setData(data.metadata); - } - if(selectedDevice.length > 0) { - fetchData(); + if (selectedDevice.length > 0) { + dispatch(getDataRecordById(selectedDevice?.[0])); + }; + + if(dataState.loadChartRecordDataStatus === loadStatus.Success){ + setData(dataState.recordChartData.metadata); } - },[selectedDevice]) + }, [selectedDevice]); const handleCancel = () => { setIsOpen(false); + dispatch(resetChartRecordDataStatus()); } return ( @@ -29,8 +33,9 @@ const ModalChart = ({isOpen, setIsOpen, selectedDevice}) => { onCancel={handleCancel} width={1000} centered + loading={dataState.loadChartRecordDataStatus === loadStatus.Loading} > - + ); diff --git a/frontend_web/src/constants/index.js b/frontend_web/src/constants/index.js index 0fc68391..37e90218 100644 --- a/frontend_web/src/constants/index.js +++ b/frontend_web/src/constants/index.js @@ -138,3 +138,5 @@ export const convertDeviceStatusColor = (status) => { return "red"; } } + +export const colorChart = ['rgb(54, 162, 235)', 'rgb(255, 99, 132)', 'rgb(75, 192, 192)', 'rgb(255, 205, 86)', 'rgba(153, 102, 255)'] \ No newline at end of file diff --git a/frontend_web/src/redux/reducer/recordSlice.js b/frontend_web/src/redux/reducer/recordSlice.js index 1cef66e2..cc45521e 100644 --- a/frontend_web/src/redux/reducer/recordSlice.js +++ b/frontend_web/src/redux/reducer/recordSlice.js @@ -127,12 +127,12 @@ export const getRecordByDoctorId = createAsyncThunk( } } ); + export const getDataRecordById = createAsyncThunk( "/record/getData", async (params, {rejectWithValue }) => { try { - const record_id = params; - const response = await httpGetData(`/record/getData/${record_id}`); + const response = await httpGetData(`/record/getData/${params}`); return response; } catch (error) { return rejectWithValue( @@ -141,17 +141,20 @@ export const getDataRecordById = createAsyncThunk( } } ); + const recordSlice = createSlice({ name: "record", initialState: { data: [], recordData: {}, + recordChartData: {}, loadDataStatus: loadStatus.None, loadCreateDataStatus: loadStatus.None, loadUpdateDataStatus: loadStatus.None, loadDeleteDataStatus: loadStatus.None, loadCheckRecordStatus: loadStatus.None, loadRecordDataStatus: loadStatus.None, + loadChartRecordDataStatus: loadStatus.None, }, reducers: { resetLoadDataStatus: (state, action) => { @@ -170,9 +173,12 @@ const recordSlice = createSlice({ resetCheckRecordStatus: (state, action) => { state.loadCheckRecordStatus = loadStatus.None; }, - // resetRecordDataStatus: (state, action) => { - // state.loadDeviceDataStatus = loadStatus.None; - // }, + resetRecordDataStatus: (state, action) => { + state.loadRecordDataStatus = loadStatus.None; + }, + resetChartRecordDataStatus: (state, action) => { + state.loadChartRecordDataStatus = loadStatus.None; + }, }, extraReducers: (builder) => { builder @@ -256,7 +262,18 @@ const recordSlice = createSlice({ .addCase(getRecordById.rejected, (state, action) => { state.loadRecordDataStatus = loadStatus.Failed; state.recordData = {}; - }); + }) + .addCase(getDataRecordById.pending, (state, action) => { + state.loadChartRecordDataStatus = loadStatus.Loading; + }) + .addCase(getDataRecordById.fulfilled, (state, action) => { + state.loadChartRecordDataStatus = loadStatus.Success; + state.recordChartData = action.payload; + }) + .addCase(getDataRecordById.rejected, (state, action) => { + state.loadChartRecordDataStatus = loadStatus.Failed; + state.recordChartData = {}; + }) }, }); @@ -267,5 +284,7 @@ export const { resetUpdateDataStatus, resetDeleteDataStatus, resetCheckRecordStatus, + resetRecordDataStatus, + resetChartRecordDataStatus } = recordSlice.actions; export default recordReducer; From f11397c6b0c4a6ac6743df5d5c9708f057a128f2 Mon Sep 17 00:00:00 2001 From: hwt75 Date: Sat, 8 Jun 2024 17:10:09 +0700 Subject: [PATCH 02/75] bug: fix bug upload file --- server-web/public/upload/test.csv | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 server-web/public/upload/test.csv diff --git a/server-web/public/upload/test.csv b/server-web/public/upload/test.csv new file mode 100644 index 00000000..e69de29b From 4a171bbc0e47e5cd89f8814a2e2bae680de03335 Mon Sep 17 00:00:00 2001 From: Huyz109 Date: Sat, 8 Jun 2024 21:06:37 +0700 Subject: [PATCH 03/75] improve(front-end): update chart title --- frontend_web/src/components/LineChart/LineChart.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend_web/src/components/LineChart/LineChart.js b/frontend_web/src/components/LineChart/LineChart.js index c296eb2e..cf9c625a 100644 --- a/frontend_web/src/components/LineChart/LineChart.js +++ b/frontend_web/src/components/LineChart/LineChart.js @@ -60,8 +60,7 @@ const LineChart = ({ rawData }) => { setDataSets([]); setYScale({}); } - }, [rawData]) - + }, [rawData]); const dataChart = { labels: dummyIncreaseArray(74), @@ -72,7 +71,7 @@ const LineChart = ({ rawData }) => { plugins: { title: { display: true, - text: 'ChartJS', + text: 'Đồ thị chỉ số sức khỏe', font: { size: 14 } @@ -100,7 +99,7 @@ const LineChart = ({ rawData }) => { x: { title: { display: true, - text: "Trục X" + text: "Thời gian" }, max: 150 }, From bed683b1e1d90f14caff3ed8aa68688fdeba00b0 Mon Sep 17 00:00:00 2001 From: Duong Date: Sun, 9 Jun 2024 09:42:38 +0700 Subject: [PATCH 04/75] improve(front-end): convert device type to text in device table, add color tag for device type --- frontend_web/src/constants/index.js | 64 ++++++++++++++------ frontend_web/src/pages/Device/deviceTable.js | 33 +++++++--- 2 files changed, 69 insertions(+), 28 deletions(-) diff --git a/frontend_web/src/constants/index.js b/frontend_web/src/constants/index.js index 0fc68391..ea4333fc 100644 --- a/frontend_web/src/constants/index.js +++ b/frontend_web/src/constants/index.js @@ -25,35 +25,35 @@ export const ROLE = [ ]; export const userRole = { - admin: '0', - doctor: '1', - patient: '2' + admin: "0", + doctor: "1", + patient: "2", }; export const UserStatus = [ { value: 0, - label: "Đang hoạt động" + label: "Đang hoạt động", }, { value: 1, - label: "Đã nghỉ" - } + label: "Đã nghỉ", + }, ]; export const DeviceStatus = [ { value: 0, - label: "Đang hoạt động" + label: "Đang hoạt động", }, { value: 1, - label: "Đang trống" + label: "Đang trống", }, { value: 2, - label: "Đang bảo trì" - } + label: "Đang bảo trì", + }, ]; export const convertGenderToString = (gender) => { @@ -102,8 +102,8 @@ export const convertStringToRole = (gender) => { } }; -export const convertStatusToString = (status) => { - switch (status){ +export const convertStatusToString = (status) => { + switch (status) { case 0: return "Đang hoạt đông"; case 1: @@ -111,10 +111,10 @@ export const convertStatusToString = (status) => { default: return "Không xác định"; } -} +}; -export const convertDeviceStatusToString = (status) => { - switch (status){ +export const convertDeviceStatusToString = (status) => { + switch (status) { case 0: return "Đang hoạt đông"; case 1: @@ -124,10 +124,10 @@ export const convertDeviceStatusToString = (status) => { default: return "Không xác định"; } -} +}; -export const convertDeviceStatusColor = (status) => { - switch (status){ +export const convertDeviceStatusColor = (status) => { + switch (status) { case 0: return "geekblue"; case 1: @@ -137,4 +137,30 @@ export const convertDeviceStatusColor = (status) => { default: return "red"; } -} +}; + +export const convertDeviceTypeToString = (status) => { + switch (status) { + case 1: + return "Đo điện tim"; + case 2: + return "Đo chất lượng không khí"; + case 3: + return "Đo âm thanh"; + default: + return "Không xác định"; + } +}; + +export const convertDeviceTypeColor = (status) => { + switch (status) { + case 1: + return "volcano"; + case 2: + return "green"; + case 3: + return "geekblue"; + default: + return "red"; + } +}; diff --git a/frontend_web/src/pages/Device/deviceTable.js b/frontend_web/src/pages/Device/deviceTable.js index 55d9644e..fa76f783 100644 --- a/frontend_web/src/pages/Device/deviceTable.js +++ b/frontend_web/src/pages/Device/deviceTable.js @@ -21,7 +21,14 @@ import { httpGetData } from "../../api/common.api"; import dayjs from "dayjs"; import { DeviceDetail } from "./deviceDetail"; import { context } from "../../utils/context"; -import { DeviceStatus, convertDeviceStatusColor, convertDeviceStatusToString, userRole } from "../../constants"; +import { + DeviceStatus, + convertDeviceStatusColor, + convertDeviceStatusToString, + convertDeviceTypeColor, + convertDeviceTypeToString, + userRole, +} from "../../constants"; import { Tag } from "antd"; const DeviceTable = () => { @@ -48,6 +55,14 @@ const DeviceTable = () => { key: "device_type", type: "text", isEdit: true, + render: (status) => { + let color = convertDeviceTypeColor(status); + return ( + + {convertDeviceTypeToString(status)} + + ); + }, }, { title: "Tên người dùng", @@ -64,14 +79,14 @@ const DeviceTable = () => { type: "select", dataSelect: DeviceStatus, isEdit: true, - render: (status)=>{ + render: (status) => { let color = convertDeviceStatusColor(status); - return ( + return ( {convertDeviceStatusToString(status)} - ) - } + ); + }, }, { title: "Thông tin thiết bị", @@ -86,14 +101,14 @@ const DeviceTable = () => { key: "start_date", type: "date", isEdit: true, - } + }, ]; useEffect(() => { if (context.role === userRole.doctor) { dispatch(getDeviceByDoctorId(context.user_id)); } else if (context.role === userRole.patient) { - dispatch(getDevicesById(context.user_id)) + dispatch(getDevicesById(context.user_id)); } else { dispatch(getDevice()); } @@ -143,12 +158,12 @@ const DeviceTable = () => { const handleEditFunction = () => { const rowData = findElementById(dataTable, selectedData[0]); - const dataModal = handleData(rowData, 'form'); + const dataModal = handleData(rowData, "form"); modalUpdateRef.current?.open(dataModal, columns); }; const handleSubmitEditUser = (data) => { - const {doctor_id, frequency, ...payload} = {...data}; + const { doctor_id, frequency, ...payload } = { ...data }; return dispatch(updateDevice(payload)); }; From a17ef3568c669fb8b4d02eb69bd61adf28ba60d9 Mon Sep 17 00:00:00 2001 From: Huyz109 Date: Sun, 9 Jun 2024 22:20:14 +0700 Subject: [PATCH 05/75] feature(front-end): add bubble chat component --- .../src/components/BubbleChat/BubbleChat.js | 115 +++++++++ .../src/components/BubbleChat/BubbleChat.scss | 233 ++++++++++++++++++ .../src/components/Message/Message.js | 19 ++ .../layouts/DefaultLayout/DefaultLayout.js | 2 + 4 files changed, 369 insertions(+) create mode 100644 frontend_web/src/components/BubbleChat/BubbleChat.js create mode 100644 frontend_web/src/components/BubbleChat/BubbleChat.scss create mode 100644 frontend_web/src/components/Message/Message.js diff --git a/frontend_web/src/components/BubbleChat/BubbleChat.js b/frontend_web/src/components/BubbleChat/BubbleChat.js new file mode 100644 index 00000000..ca482b34 --- /dev/null +++ b/frontend_web/src/components/BubbleChat/BubbleChat.js @@ -0,0 +1,115 @@ +import { useEffect, useState } from 'react'; +import './BubbleChat.scss' +import { MessageOutlined, FileImageOutlined, CloseOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; +import Message from '../Message/Message'; + +const BubbleChat = () => { + const [messages, setMessages] = useState([ + { + id: 1, + actor: 'user', + message: "Hi, konichiwa" + }, + { + id: 2, + actor: 'visitor', + message: "Chào bạn" + }, + { + id: 3, + actor: 'user', + img: 'logo192.png' + } + ]); + const [text, setText] = useState(""); + const [img, setImg] = useState(""); + + useEffect(() => { + sentMessage(); + }, [img]) + + const toogleChat = () => { + const chatBox = document.querySelector('.chatbox__support').classList; + chatBox.toggle('chatbox--active'); + } + + const getImgChange = async(e) => { + if (e.target.files.length > 0) { + const src = URL.createObjectURL(e.target.files[0]); + setImg(src); + } + } + + const sentMessage = () => { + const state = messages; + if (text) { + state.push({ + id: state.length + 1, + actor: 'user', + message: text + }); + setMessages(state); + setText(""); + } + if (img) { + state.push({ + id: state.length + 1, + actor: 'user', + img: img + }); + setMessages(state); + setImg(""); + } + } + + const handleOnKey = (e) => { + if(e.key === "Enter"){ + sentMessage(); + } + } + + return ( + <> +
+
+
+
+
+

Chat support

+

Hi. My name is Sam. How can I help you?

+
+
toogleChat()}> + +
+
+
+ {messages.map(item => )} +
+
+ setText(e.target.value)} + value={text} + onKeyDown={handleOnKey} + /> + getImgChange(e)}/> + + +
+
+
+ +
+
+
+ + ) +} + +export default BubbleChat; \ No newline at end of file diff --git a/frontend_web/src/components/BubbleChat/BubbleChat.scss b/frontend_web/src/components/BubbleChat/BubbleChat.scss new file mode 100644 index 00000000..059fd1f3 --- /dev/null +++ b/frontend_web/src/components/BubbleChat/BubbleChat.scss @@ -0,0 +1,233 @@ +$primaryGradient: linear-gradient(93.12deg, #1677ff 0.52%, #589dfd 100%); +$secondaryGradient: linear-gradient(268.91deg, #1677ff -2.14%, #408df8 99.69%); +$primaryBoxShadow: 0px 10px 15px rgba(0, 0, 0, 0.1); +$secondaryBoxShadow: 0px -10px 15px rgba(0, 0, 0, 0.1); +$primary: #1677ff; + +/* CHATBOX +=============== */ +.chatbox { + position: absolute; + bottom: 30px; + right: 30px; +} + +/* CONTENT IS CLOSE */ +.chatbox__support { + display: flex; + flex-direction: column; + background: #eee; + width: 300px; + height: 350px; + z-index: -123456; + opacity: 0; + transition: all .5s ease-in-out; +} + +/* CONTENT ISOPEN */ +.chatbox--active { + transform: translateY(-10px); + z-index: 123456; + opacity: 1; + +} + +/* BUTTON */ +.chatbox__button { + text-align: right; + + svg { + height: 20px; + width: 20px; + } + + svg:hover { + color: $primary; + } +} + +.send__button { + padding: 6px; + background: transparent; + border: none; + outline: none; + cursor: pointer; + height: auto; +} + + +/* HEADER */ +.chatbox__header { + position: sticky; + top: 0; +} + +/* MESSAGES */ +.chatbox__messages { + margin-top: auto; + display: flex; + overflow-y: scroll; + flex-direction: column; +} + +.messages__item { + max-width: 60.6%; + width: fit-content; +} + +.messages__item--operator { + margin-left: auto; +} + +.messages__item--visitor { + margin-right: auto; +} + +/* FOOTER */ +.chatbox__footer { + position: sticky; + bottom: 0; + + #file { + display: none; + } +} + +.chatbox__support { + background: #f9f9f9; + height: 500px; + width: 350px; + box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1); + border-radius: 20px; +} + +/* HEADER */ +.chatbox__header { + background: $primaryGradient; + display: flex; + flex-direction: row; + align-items: center; + padding: 15px 25px; + border-top-left-radius: 20px; + border-top-right-radius: 20px; + box-shadow: $primaryBoxShadow; +} + +.chatbox__image--header { + margin-right: 10px; +} + +.chatbox__heading--header { + font-size: 1.2rem; + color: white; + margin: 0.5rem 0; +} + +.chatbox__description--header { + font-size: .9rem; + color: white; + margin: 8px 0; +} + +.chatbox__header--close { + position: absolute; + right: 20px; + top: 20px; + color: white; + cursor: pointer; + font-size: 16px; +} + +/* Messages */ +.chatbox__messages { + padding: 0 10px 0 20px; +} + +.messages__item { + margin-top: 10px; + background: #E0E0E0; + padding: 8px 12px; + max-width: 70%; + + img { + max-width: 100%; + } +} + +.messages__item--visitor, +.messages__item--typing { + border-top-left-radius: 20px; + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; +} + +.messages__item--operator { + border-top-left-radius: 20px; + border-top-right-radius: 20px; + border-bottom-left-radius: 20px; + background: $primary; + color: white; +} + +/* FOOTER */ +.chatbox__footer { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 20px 20px; + background: $secondaryGradient; + box-shadow: $secondaryBoxShadow; + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; + margin-top: 20px; + + .img-btn { + background-color: white; + width: 32px; + height: 32px; + border-radius: 8px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + margin: 0 6px; + + svg { + font-size: 20px; + } + } + + .img-btn:hover { + color: $primary; + border-color: $primary; + } +} + +.chatbox__footer input { + width: 80%; + border: none; + padding: 10px 10px; + border-radius: 30px; + text-align: left; + outline: none; + font-family: inherit; +} + +.chatbox__send--footer { + color: white; +} + +.chatbox__button button, +.chatbox__button button:focus, +.chatbox__button button:visited { + padding: 10px; + background: white; + border: none; + outline: none; + border-top-left-radius: 50px; + border-top-right-radius: 50px; + border-bottom-left-radius: 50px; + box-shadow: 0px 10px 15px rgba(0, 0, 0, 0.1); + cursor: pointer; +} \ No newline at end of file diff --git a/frontend_web/src/components/Message/Message.js b/frontend_web/src/components/Message/Message.js new file mode 100644 index 00000000..db031354 --- /dev/null +++ b/frontend_web/src/components/Message/Message.js @@ -0,0 +1,19 @@ +import { useEffect, useRef } from "react"; + +const Message = ({data}) => { + const className = `messages__item messages__item--${data.actor === 'user' ? 'operator': 'visitor'}`; + const ref = useRef(); + + useEffect(() => { + ref.current?.scrollIntoView({ behavior: "smooth" }); + }, [data]); + + return ( +
+ {data.message} + {data.img && } +
+ ) +} + +export default Message; diff --git a/frontend_web/src/layouts/DefaultLayout/DefaultLayout.js b/frontend_web/src/layouts/DefaultLayout/DefaultLayout.js index 5f53321e..2d91fd35 100644 --- a/frontend_web/src/layouts/DefaultLayout/DefaultLayout.js +++ b/frontend_web/src/layouts/DefaultLayout/DefaultLayout.js @@ -5,6 +5,7 @@ import "./DefaultLayout.css"; import { getLocalStorage } from "../../utils/storageUtils"; import { Routes } from "../route"; import { HashRouter } from "react-router-dom"; +import BubbleChat from "../../components/BubbleChat/BubbleChat"; const { Header, Content } = Layout; @@ -26,6 +27,7 @@ const DefaultLayout = ({ children }) => { + From 008f954acb53a5269d610b84ffc1255f5a0433fb Mon Sep 17 00:00:00 2001 From: hwt75 Date: Mon, 10 Jun 2024 14:17:55 +0700 Subject: [PATCH 06/75] feature: improve home ui issue #111 --- frontend_web/package.json | 1 + frontend_web/public/index.html | 18 +- frontend_web/src/components/chart/EChart.js | 63 ++++++ .../src/components/chart/LineChart.js | 39 ++++ frontend_web/src/components/chart/chart.scss | 49 +++++ .../src/components/chart/configs/eChart.js | 106 ++++++++++ .../src/components/chart/configs/lineChart.js | 87 ++++++++ frontend_web/src/pages/Home/home.js | 194 ++++++++++++------ frontend_web/src/pages/Home/home.scss | 116 +++++++---- 9 files changed, 573 insertions(+), 100 deletions(-) create mode 100644 frontend_web/src/components/chart/EChart.js create mode 100644 frontend_web/src/components/chart/LineChart.js create mode 100644 frontend_web/src/components/chart/chart.scss create mode 100644 frontend_web/src/components/chart/configs/eChart.js create mode 100644 frontend_web/src/components/chart/configs/lineChart.js diff --git a/frontend_web/package.json b/frontend_web/package.json index e86cff48..28b02e06 100644 --- a/frontend_web/package.json +++ b/frontend_web/package.json @@ -14,6 +14,7 @@ "chartjs-plugin-zoom": "^2.0.1", "dayjs": "^1.11.10", "react": "^18.2.0", + "react-apexcharts": "^1.4.1", "react-chartjs-2": "^5.2.0", "react-cookie": "^7.1.0", "react-dom": "^18.2.0", diff --git a/frontend_web/public/index.html b/frontend_web/public/index.html index b699a994..073ad426 100644 --- a/frontend_web/public/index.html +++ b/frontend_web/public/index.html @@ -5,10 +5,7 @@ - +