front #8

Merged
mfnefd merged 14 commits from front into dev 2024-12-25 23:49:21 +04:00
9 changed files with 12874 additions and 177 deletions
Showing only changes of commit 2febb000ba - Show all commits

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,9 @@
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"antd": "^5.21.6", "antd": "^5.21.6",
"axios": "^1.7.7", "axios": "^1.7.7",
"chart.js": "^4.4.7",
"react": "^18.3.1", "react": "^18.3.1",
"react-avatar": "^5.0.3", "react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.3.5", "react-dropzone": "^14.3.5",
"react-router-dom": "^6.27.0", "react-router-dom": "^6.27.0",

View File

@ -4,7 +4,7 @@ import IFarm from '../models/IFarm';
import LoginRequest from '../Requests/LoginRequest'; import LoginRequest from '../Requests/LoginRequest';
import IRegisterRequest from '../Requests/RegisterRequest'; import IRegisterRequest from '../Requests/RegisterRequest';
const API_BASE_URL = 'https://localhost:5124/api'; const API_BASE_URL = 'http://localhost:5124/api';
const getHeaders = (): { [key: string]: string } => { const getHeaders = (): { [key: string]: string } => {
return { return {

View File

@ -8,6 +8,7 @@ import { ProfilePage } from './pages/Profile';
import { GreenHouseListPage } from './pages/GreenHouseListPage'; import { GreenHouseListPage } from './pages/GreenHouseListPage';
import { ReportPage } from './pages/ReportPage'; import { ReportPage } from './pages/ReportPage';
import GreenHousePage from './pages/GreenHousePage'; import GreenHousePage from './pages/GreenHousePage';
import {GreenHouseStats} from './components/greenhouse/GreenHouseStats';
function App() { function App() {
return ( return (
@ -20,6 +21,7 @@ function App() {
<Route path="/profile" element={<ProfilePage />} /> <Route path="/profile" element={<ProfilePage />} />
<Route path="/greenhouses" element={<GreenHouseListPage />} /> <Route path="/greenhouses" element={<GreenHouseListPage />} />
<Route path="/greenhouses/:id" element={<GreenHousePage />} /> <Route path="/greenhouses/:id" element={<GreenHousePage />} />
<Route path="/greenhouses/:id/statistics" element={<GreenHouseStats />} />
<Route path="/report" element={<ReportPage />} /> <Route path="/report" element={<ReportPage />} />
</Route> </Route>

View File

@ -0,0 +1,187 @@
import React, { useState, useEffect } from 'react';
import { DatePicker, Select, Button, Form } from 'antd';
import { useParams } from 'react-router-dom';
import { Line } from 'react-chartjs-2';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
ArcElement,
PointElement,
LineElement,
} from 'chart.js';
import GreenhouseService from '../../core/services/greenhouse-service';
import { setLabels } from 'react-chartjs-2/dist/utils';
export function GreenHouseStats() {
const { id } = useParams<{ id: string }>();
const [from, setFrom] = useState<Date | null>(null);
const [to, setTo] = useState<Date | null>(null);
const [type, setType] = useState<'humidity' | 'temperature'>('humidity');
const service = new GreenhouseService();
const temperatureHistory = service.getTemperatureHistory(1);
const humidityHistory = service.getHumidityHistory(1);
const isButtonDisabled = !from || !to || (to.valueOf() === undefined || from.valueOf() === undefined)
//const isButtonDisabled = !from || !to || (to.getTime() - from.getTime()) / (1000 * 60 * 60 * 24) > 30;
ChartJS.register(ArcElement, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);
const [chartData, setChartData] = useState({
labels: [],
datasets: [
{
label: '',
data: [],
borderColor: '',
fill: false,
tension: 0.4,
},
],
});
const [data, setData] = useState({
labels: ["Jan", "Feb", "Mar", "Apr", "May"],
datasets: [
{
label: "Up Line",
data: [10, 20, 15, 40, 30],
borderColor: "rgb(28, 44, 44)", // Customize line color
fill: false,
tension: 0.4, // Adjust line tension for smoother curves
},
],
});
useEffect(() => {
}, [from, to, type]);
const createDateRange = (startDate: string, endDate: string) => {
const start = new Date(startDate);
const end = new Date(endDate);
const dates = [];
let currentDate = start;
while (currentDate <= end) {
dates.push(new Date(currentDate));
currentDate.setDate(currentDate.getDate() + 1);
}
return dates;
};
const handleGenerateReport = () => {
console.log(from, to, type);
if (from && to) {
const startDate = '2024-12-14';
const endDate = '2024-12-24';
const itemdata = type === 'humidity' ? humidityHistory : temperatureHistory;
const startDateObj = new Date(Date.parse(startDate));
const endDateObj = new Date(Date.parse(endDate));
const filteredData = itemdata.filter(item => {
const itemDate = new Date(Date.parse(item.date));
return itemDate >= startDateObj && itemDate <= endDateObj;
});
filteredData.forEach(element => {
console.log('wff');
console.log(element.value);
});
const dateRange = createDateRange(startDate, endDate);
const labels: string[] = dateRange.map(date => date.getDate() % 5 === 0 ? date.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long' }) : '');
setChartData({
labels,
datasets: [
{
label: type === 'humidity' ? 'Влажность' : 'Температура',
data: itemdata.map(item => item.value),
backgroundColor: [
'rgb(153, 102, 255)'
],
borderColor: [
'rgb(153, 102, 255)'
],
borderWidth: 1
}
]
} as any);
// setData({
// labels,
// datasets: [
// {
// label: 'Expenses by Month',
// data: filteredData.map(item => item.value),
// backgroundColor: [
// 'rgb(153, 102, 255)'
// ],
// borderColor: [
// 'rgb(153, 102, 255)'
// ],
// borderWidth: 1
// }
// ]
// } as any);
}
};
const options = {
scales: {
x: {
display: true,
title: {
display: true,
text: "Дата",
},
min: 0,
max: 100,
},
y: {
display: true,
title: {
display: true,
text: "Значение",
},
},
},
};
return (
<Form onFinish={handleGenerateReport} className="p-8" style={{ marginTop: '100px', marginLeft: '650px', height: '800px', width: '600px' }}>
<h1 className="text-2xl font-bold mb-4">Статистика по выбранной теплице</h1>
<div className="grid grid-cols-2 gap-4">
<DatePicker value={from} onChange={(date) => setFrom(date)} format="DD.MM.YYYY" />
<DatePicker value={to} onChange={(date) => setTo(date)} format="DD.MM.YYYY" />
</div>
<div className="m-4">
<Select value={type} onChange={(value) => setType(value)}>
<Select.Option value="humidity">Влажность</Select.Option>
<Select.Option value="temperature">Температура</Select.Option>
</Select>
</div>
<Button type="primary" htmlType="submit" disabled={isButtonDisabled}>
Сформировать отчёт
</Button>
<div className="m-4">
<Line data={chartData} options={options} />
</div>
</Form>
);
};

View File

@ -7,6 +7,18 @@ export type HistoryData = {
endAt: string endAt: string
} }
export type TemperatureHistoryData = {
id: number,
value: number,
date: string
}
export type HumidityHistoryData = {
id: number,
value: number,
date: string
}
export default class GreenhouseService { export default class GreenhouseService {
private static _info: GreenhouseInfo = {} as GreenhouseInfo; private static _info: GreenhouseInfo = {} as GreenhouseInfo;
public getData(id: number): GreenhouseInfo { public getData(id: number): GreenhouseInfo {
@ -39,10 +51,40 @@ export default class GreenhouseService {
public getCommandsHistory(id: number): HistoryData[] { public getCommandsHistory(id: number): HistoryData[] {
return [ return [
{ id: 1, action: "Открыт вентиль", startAt: new Date().toLocaleDateString(), endAt: new Date().toLocaleDateString() }, { id: 1, action: "Открыт вентиль", startAt: "10.12.2024", endAt: "11.12.2024" },
{ id: 2, action: "Закрыт вентиль", startAt: new Date().toLocaleDateString(), endAt: new Date().toLocaleDateString()}, { id: 2, action: "Закрыт вентиль", startAt: "11.12.2024", endAt: "14.12.2024" },
{ id: 3, action: "Включен нагреватель", startAt: new Date().toLocaleDateString(), endAt: new Date().toLocaleDateString()}, { id: 3, action: "Открыт вентиль", startAt: "14.12.2024", endAt: "15.12.2024" },
{ id: 3, action: "выключен нагреватель", startAt: new Date().toLocaleDateString(), endAt: new Date().toLocaleDateString()}, { id: 4, action: "Закрыт вентиль", startAt: "15.12.2024", endAt: "17.12.2024" },
{ id: 5, action: "Открыт вентиль", startAt: "17.12.2024", endAt: "18.12.2024" },
{ id: 6, action: "Включен нагреватель", startAt: "18.12.2024", endAt: "22.12.2024"},
{ id: 7, action: "Выключен нагреватель", startAt: "22.12.2024", endAt: "24.12.2024"},
]
}
public getTemperatureHistory(id: number): TemperatureHistoryData[] {
return [
{ id: 1, value: 30, date: "10.12.2024" },
{ id: 2, value: 28, date: "12.12.2024" },
{ id: 3, value: 29, date: "14.12.2024" },
{ id: 4, value: 31, date: "16.12.2024" },
{ id: 5, value: 27, date: "18.12.2024" },
{ id: 6, value: 26, date: "20.12.2024" },
{ id: 7, value: 32, date: "22.12.2024" },
{ id: 8, value: 33, date: "24.12.2024" },
]
}
public getHumidityHistory(id: number): HumidityHistoryData[] {
return [
{ id: 1, value: 78, date: "10.12.2024" },
{ id: 2, value: 80, date: "12.12.2024" },
{ id: 3, value: 81, date: "14.12.2024" },
{ id: 4, value: 82, date: "16.12.2024" },
{ id: 5, value: 83, date: "18.12.2024" },
{ id: 6, value: 79, date: "20.12.2024" },
{ id: 7, value: 84, date: "22.12.2024" },
{ id: 8, value: 85, date: "24.12.2024" },
] ]
} }
} }

View File

@ -1,18 +1,12 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Modal, Button, Table, Form, Input, Select, Space } from 'antd'; import { Modal, Button, Table, Form, Input, Select, Space } from 'antd';
import { Greenhouse, GreenhouseInfo, HeatingMode, WateringMode } from '../core/api/data-contracts';
interface IGreenhouse { import { Link } from 'react-router-dom';
id: number;
name: string;
recommendedTemperature: number;
wateringMode: 'Manual' | 'Auto';
heatingMode: 'Manual' | 'Auto';
}
export function GreenHouseListPage() { export function GreenHouseListPage() {
const [greenhouses, setGreenhouses] = useState<IGreenhouse[]>([]); const [greenhouses, setGreenhouses] = useState<GreenhouseInfo[]>([]);
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const [editingGreenhouse, setEditingGreenhouse] = useState<IGreenhouse | null>(null); const [editingGreenhouse, setEditingGreenhouse] = useState<GreenhouseInfo | null>(null);
const [form] = Form.useForm(); const [form] = Form.useForm();
@ -21,7 +15,24 @@ export function GreenHouseListPage() {
}, []); }, []);
const fetchGreenhouses = async () => { const fetchGreenhouses = async () => {
// Fetch data from API setGreenhouses([
{
id: 1735006258520,
soilTemperature: 32,
percentWater: 80,
autoWateringStatus: Boolean(WateringMode.Value1),
pumpStatus: Boolean(WateringMode.Value1),
heatingStatus: Boolean(HeatingMode.Value1)
},
{
id: 1735006258521,
soilTemperature: 30,
percentWater: 82,
autoWateringStatus: Boolean(WateringMode.Value1),
pumpStatus: Boolean(WateringMode.Value1),
heatingStatus: Boolean(HeatingMode.Value1)
},
]);
}; };
const handleAddOrEditSubmit = () => { const handleAddOrEditSubmit = () => {
@ -37,11 +48,11 @@ export function GreenHouseListPage() {
}); });
}; };
const handleDelete = (id: number) => { const handleDelete = (id?: number) => {
setGreenhouses(greenhouses.filter(g => g.id !== id)); setGreenhouses(greenhouses.filter(g => g.id !== id));
}; };
const handleModalOpen = (greenhouse?: IGreenhouse) => { const handleModalOpen = (greenhouse?: GreenhouseInfo) => {
setEditingGreenhouse(greenhouse || null); setEditingGreenhouse(greenhouse || null);
setIsModalVisible(true); setIsModalVisible(true);
if (greenhouse) { if (greenhouse) {
@ -50,53 +61,48 @@ export function GreenHouseListPage() {
}; };
const columns = [ const columns = [
{ {
title: 'Название', title: 'ID',
dataIndex: 'name', dataIndex: 'id',
key: 'name', key: 'id',
}, },
{ {
title: 'Температура', title: 'Температура',
dataIndex: 'Temperature', dataIndex: 'soilTemperature',
key: 'Temperature', key: 'soilTemperature',
}, },
{ {
title: 'Процент влажности', title: 'Процент влажности',
dataIndex: 'Humidity', dataIndex: 'percentWater',
key: 'Humidity', key: 'percentWater',
}, },
{ {
title: 'Режим полива', title: 'Режим полива',
dataIndex: 'wateringMode', dataIndex: 'autoWateringStatus',
key: 'wateringMode', key: 'autoWateringStatus',
render: (text: string) => text === 'Manual' ? 'Вручную' : 'Автоматически', render: (status: boolean) => status ? 'Автоматически' : 'Вручную',
},
{
title: 'Режим отопления',
dataIndex: 'heatingMode',
key: 'heatingMode',
render: (text: string) => text === 'Manual' ? 'Вручную' : 'Автоматически',
}, },
{ {
title: 'Статус вентиля', title: 'Статус вентиля',
dataIndex: 'ventilationStatus', dataIndex: 'pumpStatus',
key: 'ventilationStatus', key: 'pumpStatus',
render: (text: string) => text === 'Manual' ? 'Вручную' : 'Автоматически', render: (status: boolean) => status ? 'Открыт' : 'Закрыт',
}, },
{ {
title: 'Статус нагревателя', title: 'Статус нагревателя',
dataIndex: 'heatingStatus', dataIndex: 'heatingStatus',
key: 'heatingStatus', key: 'heatingStatus',
render: (text: string) => text === 'Manual' ? 'Вручную' : 'Автоматически', render: (status: boolean) => status ? 'Включен' : 'Выключен',
}, },
{ {
title: 'Действия', title: 'Действия',
key: 'action', key: 'action',
render: (_: any, record: IGreenhouse) => ( render: (_: any, record: GreenhouseInfo) => (
<Space> <Space>
<Button>Начать полив</Button> <Button onClick={() => startWatering(record.id)}>Начать полив</Button>
<Button>Начать нагрев</Button> <Button onClick={() => startHeating(record.id)}>Начать нагрев</Button>
<Button onClick={() => handleDelete(record.id)}>Удалить</Button> <Button onClick={() => handleDelete(record.id)}>Удалить</Button>
<Button onClick={() => handleModalOpen(record)}>Редактировать</Button> <Button onClick={() => handleModalOpen(record)}>Редактировать</Button>
<Link to={`/greenhouses/${record.id}`}>Перейти</Link>
</Space> </Space>
), ),
}, },
@ -122,37 +128,28 @@ export function GreenHouseListPage() {
}} }}
> >
<Form form={form} layout="vertical"> <Form form={form} layout="vertical">
<Form.Item name="name" label="Название" rules={[{ required: true }]}> <Form.Item name="soilTemperature" label="Температура">
<Input />
</Form.Item>
<Form.Item name="Temperature" label="Температура">
<Input type="number" /> <Input type="number" />
</Form.Item> </Form.Item>
<Form.Item name="Humidity" label="Процент влажности"> <Form.Item name="percentWater" label="Процент влажности">
<Input type="number" /> <Input type="number" />
</Form.Item> </Form.Item>
<Form.Item name="wateringMode" label="Режим полива"> <Form.Item name="autoWateringStatus" label="Режим автополива">
<Select> <Select>
<Select.Option value="Manual">Вручную</Select.Option> <Select.Option value={WateringMode.Value0}>Отключен</Select.Option>
<Select.Option value="Auto">Автоматически</Select.Option> <Select.Option value={WateringMode.Value1}>Включен</Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item name="heatingMode" label="Режим отопления"> <Form.Item name="pumpStatus" label="Статус вентиля">
<Select> <Select>
<Select.Option value="Manual">Вручную</Select.Option> <Select.Option value={false}>Закрыт</Select.Option>
<Select.Option value="Auto">Автоматически</Select.Option> <Select.Option value={true}>Открыт</Select.Option>
</Select>
</Form.Item>
<Form.Item name="ventilationStatus" label="Статус вентиля">
<Select>
<Select.Option value="Manual">Вручную</Select.Option>
<Select.Option value="Auto">Автоматически</Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item name="heatingStatus" label="Статус нагревателя"> <Form.Item name="heatingStatus" label="Статус нагревателя">
<Select> <Select>
<Select.Option value="Manual">Вручную</Select.Option> <Select.Option value={false}>Отключен</Select.Option>
<Select.Option value="Auto">Автоматически</Select.Option> <Select.Option value={true}>Включен</Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
</Form> </Form>
@ -160,3 +157,11 @@ export function GreenHouseListPage() {
</div> </div>
); );
}; };
function startWatering(id: number | undefined): void {
return;
}
function startHeating(id: number | undefined): void {
return;
}

View File

@ -1,9 +1,10 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { Button, Form, Input } from 'antd'; import { Button, Form, Input } from 'antd';
import { createUser } from '../API/api'; import { } from '../core/api/Api'
import IRegisterRequest from '../Requests/RegisterRequest'; import IRegisterRequest from '../Requests/RegisterRequest';
import Text from 'antd/es/typography/Text'; import Text from 'antd/es/typography/Text';
import { createUser } from '../API/api';
export function RegisterPage () { export function RegisterPage () {
const [name, setName] = useState(''); const [name, setName] = useState('');
@ -33,7 +34,7 @@ export function RegisterPage () {
console.error('Error registering:'); console.error('Error registering:');
} }
} catch (error) { } catch (error) {
console.error('Error registering:', error); console.error('Error registering:', error, userData);
alert('Error registering'); alert('Error registering');
} }
}; };