Commit 728fd061 by asranov0003

feat: render browser histories

parent a055ae8e
......@@ -3,7 +3,11 @@
"yes": "Yes",
"no": "No",
"granted": "Granted",
"denied": "Denied"
"denied": "Denied",
"loading": "Loading...",
"selectDates": "Select dates",
"from": "From",
"to": "To"
},
"auth": {
"entrance": "Sign In",
......@@ -108,6 +112,12 @@
"phoneState": "Phone State",
"phoneStateDescription": "This allows you to know the state of the child's phone, e.g., battery charge"
},
"browserHistory": {
"title": "Browser History",
"empty": "No browser history found.",
"website": "Website",
"date": "Date"
},
"pincode": {
"enterTitle": "Enter your PIN Code",
"createTitle": "Create your PIN Code",
......@@ -129,7 +139,8 @@
"resetPassword": "Reset Password",
"logout": "Logout",
"cancel": "Cancel",
"delete": "Delete"
"delete": "Delete",
"search": "Search"
},
"notFound": {
"code": "404",
......
......@@ -3,7 +3,11 @@
"yes": "Да",
"no": "Нет",
"granted": "Вкл",
"denied": "Выкл"
"denied": "Выкл",
"loading": "Загрузка...",
"selectDates": "Выбрать даты",
"from": "От",
"to": "До"
},
"auth": {
"entrance": "Вход",
......@@ -108,6 +112,12 @@
"phoneState": "Состояние телефона",
"phoneStateDescription": "Позволяет узнать состояние телефона ребенка, например уровень заряда батареи."
},
"browserHistory": {
"title": "История браузера",
"empty": "История браузера не найдена.",
"website": "Вебсайт",
"date": "Дата"
},
"pincode": {
"enterTitle": "Введите PIN-код",
"createTitle": "Создайте PIN-код",
......@@ -124,7 +134,8 @@
"continue": "Продолжить",
"resetPassword": "Сбросить пароль",
"logout": "Выйти",
"cancel": "Отмена"
"cancel": "Отмена",
"search": "Поиск"
},
"notFound": {
"code": "404",
......
......@@ -39,4 +39,25 @@
.browserhistory__filter__date label {
margin-right: 0.5rem;
font-weight: bold;
}
.browserhistory__list {
display: flex;
flex-direction: column;
gap: 0.5rem;
max-height: 70vh;
overflow-y: auto;
margin: 0.5rem 0;
}
.browserhistory__item {
padding: 1rem;
border-radius: 10px;
background: var(--content-bg-color);
font-size: 0.875rem;
}
.browserhistory__item__title {
font-weight: bold;
margin-bottom: 0.5rem;
}
\ No newline at end of file
import React from "react";
import React, { useEffect, useState } from "react";
import "./BrowserHistory.css";
import SectionHeader from "../../layouts/SectionHeader";
import { MdInfoOutline } from "react-icons/md";
import CButton from "../../components/CButton";
import {
useAppDispatch,
useAppSelector,
type RootState,
} from "../../stores/store";
import { fetchBrowserHistory } from "../../stores/slices/browserHistorySlice";
import { useTranslation } from "react-i18next";
const BrowserHistory: 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 { browserHistories, loading, error } = useAppSelector(
(state: RootState) => state.browserHistory
);
const dispatch = useAppDispatch();
useEffect(() => {
if (selectedDevice?.id) {
filterBrowserHistory();
}
}, [dispatch, selectedDevice]);
const filterBrowserHistory = () => {
if (selectedDevice?.id) {
dispatch(
fetchBrowserHistory({
deviceId: selectedDevice.id,
dateFrom: new Date(fromDate),
dateTo: new Date(toDate),
recStart: 0,
})
);
}
};
return (
<div className="browserhistory wrapper">
<SectionHeader to="/home" />
......@@ -12,29 +48,62 @@ const BrowserHistory: React.FC = () => {
<div className="browserhistory__content">
<div>
<div className="browserhistory__content__header">
<h3>Browser History</h3>
<h3>{t("browserHistory.title")}</h3>
<MdInfoOutline className="browserhistory__content__header__icon" />
</div>
<p className="browserhistory__content__empty">No data</p>
{loading && <p>{t("common.loading")}</p>}
{error && <p className="text-danger">{error}</p>}
{browserHistories.length > 0 ? (
<div className="browserhistory__list">
{browserHistories.map((history, index) => {
return (
<div className="browserhistory__item" key={index}>
<p className="browserhistory__item__title">
{t("browserHistory.website")}: {history.name}
</p>
<p>
{t("browserHistory.date")}: {history.date}
</p>
</div>
);
})}
</div>
) : (
<p>{t("browserHistory.empty")}</p>
)}
</div>
<div className="browserhistory__filter">
<p className="browserhistory__filter__title">Select Dates</p>
<p className="browserhistory__filter__title">
{t("common.selectDates")}
</p>
<div className="browserhistory__filter__dates">
<div className="browserhistory__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="browserhistory__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} />
</div>
</div>
</div>
......
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { sendRpcRequest } from "../../services/apiClient";
interface IBrowserHistory {
name: string;
duration: string;
date: string;
}
interface IBrowserHistoryState {
browserHistories: IBrowserHistory[];
loading: boolean;
error: string | null;
}
const initialState: IBrowserHistoryState = {
browserHistories: [],
loading: false,
error: null,
};
export const fetchBrowserHistory = createAsyncThunk(
"browserHistory/fetchBrowserHistory",
async (
{
deviceId,
dateFrom,
dateTo,
recStart,
}: { deviceId: string; dateFrom: Date; dateTo: Date; recStart: number },
{ rejectWithValue }
) => {
try {
const response = await sendRpcRequest<{ list: IBrowserHistory[] }>(
"data.getcontent",
{
deviceId,
type: 38,
dateFrom,
dateTo,
recStart: 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 browserHistorySlice = createSlice({
name: "browserHistory",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchBrowserHistory.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchBrowserHistory.fulfilled, (state, action) => {
state.loading = false;
state.browserHistories = action.payload;
})
.addCase(fetchBrowserHistory.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string;
});
},
});
export default browserHistorySlice.reducer;
......@@ -5,6 +5,7 @@ import notificationSlice from "./slices/notificationSlice";
import deviceSlice from "./slices/deviceSlice";
import commandSlice from "./slices/commandSlice";
import appsSlice from "./slices/appsSlice";
import browserHistorySlice from "./slices/browserHistorySlice";
import {
useDispatch,
useSelector,
......@@ -19,6 +20,7 @@ export const store = configureStore({
device: deviceSlice,
command: commandSlice,
apps: appsSlice,
browserHistory: browserHistorySlice,
},
});
......
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