Commit abaf3077 by asranov0003

feat: render call history

parent c66190ac
......@@ -118,6 +118,10 @@
"website": "Website",
"date": "Date"
},
"callHistory": {
"title": "Call History",
"empty": "No call history found."
},
"pincode": {
"enterTitle": "Enter your PIN Code",
"createTitle": "Create your PIN Code",
......
......@@ -118,6 +118,10 @@
"website": "Вебсайт",
"date": "Дата"
},
"callHistory": {
"title": "История звонков",
"empty": "История звонков не найдена."
},
"pincode": {
"enterTitle": "Введите PIN-код",
"createTitle": "Создайте PIN-код",
......
......@@ -45,7 +45,7 @@
display: flex;
flex-direction: column;
gap: 0.5rem;
max-height: 70vh;
max-height: 67vh;
overflow-y: auto;
margin: 0.5rem 0;
}
......
......@@ -103,7 +103,11 @@ const BrowserHistory: React.FC = () => {
</div>
</div>
<CButton title={t("button.search")} onClick={filterBrowserHistory} />
<CButton
title={t("button.search")}
onClick={filterBrowserHistory}
isLoading={loading}
/>
</div>
</div>
</div>
......
......@@ -39,4 +39,33 @@
.calls__filter__date label {
margin-right: 0.5rem;
font-weight: bold;
}
.callhistory__list {
display: flex;
flex-direction: column;
gap: 0.5rem;
max-height: 67vh;
overflow-y: auto;
margin: 0.5rem 0;
}
.callhistory__item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-radius: 10px;
background: var(--content-bg-color);
font-size: 0.875rem;
}
.callhistory__item div {
display: flex;
align-items: center;
gap: 0.5rem;
}
.callhistory__item__title {
font-weight: bold;
}
\ No newline at end of file
import React from "react";
import React, { useEffect, useState } from "react";
import "./Calls.css";
import SectionHeader from "../../layouts/SectionHeader";
import { MdInfoOutline } from "react-icons/md";
import CButton from "../../components/CButton";
import { useTranslation } from "react-i18next";
import {
useAppDispatch,
useAppSelector,
type RootState,
} from "../../stores/store";
import { fetchCallHistory } from "../../stores/slices/callHistorySlice";
import { HiOutlinePhoneMissedCall } from "react-icons/hi";
import { VscCallOutgoing } from "react-icons/vsc";
import { IoCallOutline } from "react-icons/io5";
import CLoading from "../../components/CLoading";
const Calls: React.FC = () => {
const today = new Date().toISOString().split("T")[0];
const [fromDate, setFromDate] = useState(today);
const [toDate, setToDate] = useState(today);
const { t } = useTranslation();
const { selectedDevice } = useAppSelector((state: RootState) => state.device);
const { callHistories, loading, error } = useAppSelector(
(state: RootState) => state.callHistory
);
const dispatch = useAppDispatch();
const getCallIcon = (callType: string) => {
if (callType == "3") {
return (
<HiOutlinePhoneMissedCall
style={{ color: "#F44336", fontSize: "18px" }}
/>
);
} else if (callType == "2") {
return <VscCallOutgoing style={{ color: "#4CAF50", fontSize: "18px" }} />;
} else {
return <IoCallOutline style={{ color: "#448AFF", fontSize: "18px" }} />;
}
};
useEffect(() => {
if (selectedDevice?.id) {
filterBrowserHistory();
}
}, [dispatch, selectedDevice]);
const filterBrowserHistory = () => {
if (selectedDevice?.id) {
dispatch(
fetchCallHistory({
deviceId: selectedDevice.id,
dateFrom: new Date(fromDate),
dateTo: new Date(toDate),
recStart: 0,
})
);
}
};
return (
<div className="calls wrapper">
<SectionHeader to="/home" />
......@@ -12,29 +66,68 @@ const Calls: React.FC = () => {
<div className="calls__content">
<div>
<div className="calls__content__header">
<h3>Calls</h3>
<h3>{t("callHistory.title")}</h3>
<MdInfoOutline className="calls__content__header__icon" />
</div>
<p className="calls__content__empty">No data</p>
{loading && (
<div className="cloading__center">
<CLoading />
</div>
)}
{error && <p className="text-danger">{error}</p>}
{callHistories.length === 0 && !loading && (
<p>{t("callHistory.empty")}</p>
)}
<div className="callhistory__list">
{callHistories.map((history, index) => {
return (
<div className="callhistory__item" key={index}>
<div>
{getCallIcon(history.call_type)}
<p className="callhistory__item__title">{history.name}</p>
</div>
<p>{history.date}</p>
</div>
);
})}
</div>
</div>
<div className="calls__filter">
<p className="calls__filter__title">Select Dates</p>
<p className="calls__filter__title">{t("common.selectDates")}</p>
<div className="calls__filter__dates">
<div className="calls__filter__date">
<label htmlFor="from">From</label>
<input type="date" id="from" />
<label htmlFor="from">{t("common.from")}</label>
<input
type="date"
id="from"
value={fromDate}
onChange={(e) => setFromDate(e.target.value)}
/>
</div>
<div className="calls__filter__date">
<label htmlFor="to">To</label>
<input type="date" id="to" />
<label htmlFor="to">{t("common.to")}</label>
<input
type="date"
id="to"
value={toDate}
onChange={(e) => setToDate(e.target.value)}
/>
</div>
</div>
<CButton title="Search" />
<CButton
title={t("button.search")}
onClick={filterBrowserHistory}
isLoading={loading}
/>
</div>
</div>
</div>
......
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { sendRpcRequest } from "../../services/apiClient";
interface ICallHistory {
name: string;
call_type: string;
date: string;
}
interface CallHistoryState {
callHistories: ICallHistory[];
loading: boolean;
error: string | null;
}
const initialState: CallHistoryState = {
callHistories: [],
loading: false,
error: null,
};
export const fetchCallHistory = createAsyncThunk(
"callHistory/fetchCallHistory",
async (
{
deviceId,
dateFrom,
dateTo,
recStart,
}: { deviceId: string; dateFrom: Date; dateTo: Date; recStart: number },
{ rejectWithValue }
) => {
try {
const response = await sendRpcRequest<{ list: ICallHistory[] }>(
"data.getcontent",
{
deviceId,
type: 3,
dateFrom,
dateTo,
recStart,
recLimit: 50,
}
);
return response.list;
} catch (error: unknown) {
if (typeof error === "object" && error !== null && "message" in error) {
return rejectWithValue(error.message);
}
return rejectWithValue("Unknown error occurred");
}
}
);
const callHistorySlice = createSlice({
name: "callHistory",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchCallHistory.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchCallHistory.fulfilled, (state, action) => {
state.callHistories = action.payload;
state.loading = false;
})
.addCase(fetchCallHistory.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string;
});
},
});
export default callHistorySlice.reducer;
......@@ -6,6 +6,7 @@ import deviceSlice from "./slices/deviceSlice";
import commandSlice from "./slices/commandSlice";
import appsSlice from "./slices/appsSlice";
import browserHistorySlice from "./slices/browserHistorySlice";
import callHistorySlice from "./slices/callHistorySlice";
import {
useDispatch,
useSelector,
......@@ -21,6 +22,7 @@ export const store = configureStore({
command: commandSlice,
apps: appsSlice,
browserHistory: browserHistorySlice,
callHistory: callHistorySlice,
},
});
......
......@@ -70,4 +70,9 @@ html[data-theme='dark'] {
.modal__box__actions {
display: flex;
gap: 0.5rem;
}
.cloading__center {
display: flex;
justify-content: center;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment