Skip to content

Commit

Permalink
logs+pagination+stats - done
Browse files Browse the repository at this point in the history
  • Loading branch information
vozmitelvlas committed Dec 22, 2024
1 parent a1e70cc commit 8ac8b5f
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 28 deletions.
40 changes: 40 additions & 0 deletions backend/src/main/java/ru/yandex/forms/utils/DataLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,45 @@ private void formsCreation(){
form3.setDate(Instant.parse("2024-12-01T15:36:50.357Z"));
formRepository.save(form3);
}
if (formRepository.findByOwnerEmailAndName(SASHA_MAIL, "Домашние животные/4").isEmpty()){
Form form3 = new Form();
form3.setName("Домашние животные/4");
form3.setTableName("form4");
form3.setOwnerEmail(SASHA_MAIL);
form3.setRedactors(List.of(SENYA_MAIL, VLAS_MAIL));
form3.setPath("./backend/uploads/tables/" + form3.getTableName() + ".xlsx");
form3.setDate(Instant.parse("2024-12-01T15:36:50.357Z"));
formRepository.save(form3);
}
if (formRepository.findByOwnerEmailAndName(SASHA_MAIL, "Домашние животные/5").isEmpty()){
Form form3 = new Form();
form3.setName("Домашние животные/5");
form3.setTableName("form5");
form3.setOwnerEmail(SASHA_MAIL);
form3.setRedactors(List.of(SENYA_MAIL, VLAS_MAIL));
form3.setPath("./backend/uploads/tables/" + form3.getTableName() + ".xlsx");
form3.setDate(Instant.parse("2024-12-01T15:36:50.357Z"));
formRepository.save(form3);
}
if (formRepository.findByOwnerEmailAndName(SASHA_MAIL, "Домашние животные/6").isEmpty()){
Form form3 = new Form();
form3.setName("Домашние животные/6");
form3.setTableName("form6");
form3.setOwnerEmail(SASHA_MAIL);
form3.setRedactors(List.of(SENYA_MAIL, VLAS_MAIL));
form3.setPath("./backend/uploads/tables/" + form3.getTableName() + ".xlsx");
form3.setDate(Instant.parse("2024-12-01T15:36:50.357Z"));
formRepository.save(form3);
}
if (formRepository.findByOwnerEmailAndName(SASHA_MAIL, "Домашние животные/7").isEmpty()){
Form form3 = new Form();
form3.setName("Домашние животные/7");
form3.setTableName("form7");
form3.setOwnerEmail(SASHA_MAIL);
form3.setRedactors(List.of(SENYA_MAIL, VLAS_MAIL));
form3.setPath("./backend/uploads/tables/" + form3.getTableName() + ".xlsx");
form3.setDate(Instant.parse("2024-12-01T15:36:50.357Z"));
formRepository.save(form3);
}
}
}
4 changes: 2 additions & 2 deletions backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
spring.application.name=forms


#spring.data.mongodb.uri=${DB_URL}
spring.data.mongodb.uri=mongodb://localhost:27017/test
spring.data.mongodb.uri=${DB_URL}
#spring.data.mongodb.uri=mongodb://localhost:27017/test

spring.jpa.hibernate.ddl-auto=create

Expand Down
Binary file added backend/uploads/tables/form4.xlsx
Binary file not shown.
Binary file added backend/uploads/tables/form5.xlsx
Binary file not shown.
Binary file added backend/uploads/tables/form6.xlsx
Binary file not shown.
Binary file added backend/uploads/tables/form7.xlsx
Binary file not shown.
175 changes: 154 additions & 21 deletions frontend/src/components/Tables/Tables.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import Header from "./Header.jsx";
import MainHeader from "./MainHeader.jsx";
import { useState } from "react";
import axios, { all } from "axios";
import React, { useState } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";

import { useDispatch, useSelector } from "react-redux";
import React, { useRef } from "react";
import { useRef } from "react";
import { setBrokers } from "../store/broker-slice.jsx";
import { setUsers } from "../store/user-slice.jsx";
import { setXlsx, setName } from "../store/xlsx-slice.jsx";
import "./modalCss.css";
import Button from "./Button.jsx";
import "./Table.css";
import close from "/close.svg";
import edit from "/edit.svg"
import "./dropMenu.css"
import addRedactor from "/addRedactor.svg"
import { getCurrentDate } from "./utils.jsx";
import { useEffect } from "react";
import { setLogs } from "../store/log-slice.jsx";

import { setLogSearchValues } from "../store/logSearch-slice.jsx"

import { Bar } from "react-chartjs-2";
import Chart from "chart.js/auto";

export default function Tables() {
const dispatch = useDispatch();
const navigate = useNavigate();
Expand Down Expand Up @@ -56,10 +57,15 @@ export default function Tables() {
input.value = ''
})
}
else if (event.target.closest(".stats-modal")) {
let statsModal = document.querySelector(".stats-modal-overlay");
statsModal.classList.add("stats-modal-overlay_hidden");
}
else {
let modalWindow = document.querySelector(".add-modal-overlay");
modalWindow.classList.add("add-modal-overlay_hidden");
}

};

const handleChange = (e) => {
Expand All @@ -73,13 +79,36 @@ export default function Tables() {

try {
await axios.post("http://localhost:8080/forms/create-form", newValues);
const res = await axios.get(`http://localhost:8080/forms/${mail}?page=${serverInfo.page}&size=${size}`);
dispatch(setBrokers(res.data));
modalWindow.classList.add("add-modal-overlay_hidden");
errorSpan.innerText = ""
} catch (error) {
errorSpan.innerText = `${error.response.data}`
}
if (hasAllEmptyProperties(searchValues)) {
const res = await axios.get(`http://localhost:8080/forms/${mail}?page=${serverInfo.page}&size=${size}`);
dispatch(setBrokers(res.data));
} else {
let url = new URL(`http://localhost:8080/forms/table`);

const params = new URLSearchParams({ ...searchValues, page: serverInfo.page, size: size });
url.search = params.toString();

try {
const response = await fetch(url.href, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const data = await response.json();
console.log('Ответ от сервера:', data);
dispatch(setBrokers(data));

} catch (error) {
console.error('Ошибка при выполнении запроса:', error);
}
}


};
const handleSubmit = () => {
Expand Down Expand Up @@ -133,13 +162,39 @@ export default function Tables() {
const deleteForm = async () => {

if (activeForm) {
let check = (forms.length - 1) % 3 === 0 && serverInfo.page !== 0 ? 1 : 0
await axios.delete(`http://localhost:8080/forms/${activeForm.id}`);
const res = await axios.get(`http://localhost:8080/forms/${mail}?page=${serverInfo.page - check}&size=${size}`);
dispatch(setBrokers(res.data));
let redactorsModalWindow = document.querySelector(".delete-modal-overlay")
redactorsModalWindow.classList.add("delete-modal-overlay_hidden");

if (hasAllEmptyProperties(searchValues)) {
let check = (forms.length - 1) % 3 === 0 && serverInfo.page !== 0 ? 1 : 0
const res = await axios.get(`http://localhost:8080/forms/${mail}?page=${serverInfo.page - check}&size=${size}`);
dispatch(setBrokers(res.data));
}
else {
let check = (forms.length - 1) % 3 === 0 && serverInfo.page !== 0 ? 1 : 0
let url = new URL(`http://localhost:8080/forms/table`);

const params = new URLSearchParams({ ...searchValues, page: serverInfo.page - check, size: size });
url.search = params.toString();

try {
const response = await fetch(url.href, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const data = await response.json();
console.log('Ответ от сервера:', data);
dispatch(setBrokers(data));

} catch (error) {
console.error('Ошибка при выполнении запроса:', error);
}
}

}
let redactorsModalWindow = document.querySelector(".delete-modal-overlay")
redactorsModalWindow.classList.add("delete-modal-overlay_hidden");
};

const clicklOnTbody = async (event) => {
Expand Down Expand Up @@ -300,6 +355,7 @@ export default function Tables() {
if (paginationButton.name === "forward") {
if (hasAllEmptyProperties(searchValues)) {
let page = serverInfo.page !== serverInfo.totalPage - 1 ? serverInfo.page + 1 : serverInfo.page
if (serverInfo.totalPage === 0) page = 0

const url = `http://localhost:8080/forms/${mail}?page=${page}&size=${size}`;

Expand All @@ -309,9 +365,9 @@ export default function Tables() {
else {
let url = new URL(`http://localhost:8080/forms/table`);
let page = serverInfo.page !== serverInfo.totalPage - 1 ? serverInfo.page + 1 : serverInfo.page
if (serverInfo.totalPage === 0) page = 0

const params = new URLSearchParams({ ...searchValues, page: page, size: size });
console.log(params);
url.search = params.toString();

try {
Expand Down Expand Up @@ -367,12 +423,60 @@ export default function Tables() {

}
}
const toHistory = () => {
axios.get(`http://localhost:8080/logs`).then((res) => {
console.log(res.data);
dispatch(setLogs(res.data.logs))
})
navigate("/logs")
const toHistory = async () => {
try {
const response = await fetch(`http://localhost:8080/logs`);
const json = await response.json();
dispatch(setLogs(json))

dispatch(setLogSearchValues({}))
navigate("/logs")
} catch (error) {
console.error("Ошибка", error);
}
}

const [chart_data, setChart] = React.useState({
labels: [],
datasets: [
{}
]
});
function groupByDate(data) {
const dates = [];
const counts = [];

data.forEach(item => {
const dateStr = item.date.substring(0, 10); // Берем первые 10 символов строки даты
if (!dates.includes(dateStr)) {
dates.push(dateStr);
counts.push(1);
} else {
const index = dates.indexOf(dateStr);
counts[index]++;
}
});

return [dates, counts];
}

const showStatsAllForms = async () => {
const res = await axios.get(`http://localhost:8080/forms/${mail}?page=${0}&size=${1000}`);
let [dates, counts] = groupByDate(res.data.forms)

setChart({
labels: dates,
datasets: [{
label: 'Количество форм',
backgroundColor: '#F8604A',
borderColor: '#F8604A',
data: counts, // Здесь нужно заменить значения данными из вашего примера

}]
})

let statsModal = document.querySelector(".stats-modal-overlay");
statsModal.classList.remove("stats-modal-overlay_hidden");
}

return (
Expand All @@ -392,7 +496,11 @@ export default function Tables() {
<strong >Экспорт</strong>
</button>
<button className="search_button" onClick={toHistory}><strong>История</strong></button>
<button className="search_button" onClick={showStatsAllForms} >
<strong >Общая статистика</strong>
</button>
</div>

<input
ref={fileInputRef}
type="file"
Expand Down Expand Up @@ -577,6 +685,31 @@ export default function Tables() {
</form>
</div>
</div>

<div className="stats-modal-overlay stats-modal-overlay_hidden">
<div className="stats-modal">
<h3 className="stats-modal__question">
Общая статистика форм по датам
</h3>

<div className="diagram">
<Bar data={chart_data} options={{
scales: {
y: {
ticks: {
beginAtZero: true,
stepSize: 1
}
}
}
}} />
</div>

<div className="stats-modal__buttons">
<Button type="button" text="Закрыть" click={closeModal}></Button>
</div>
</div>
</div>
</div>
);
}
1 change: 0 additions & 1 deletion frontend/src/components/XLSX/TablesXlsx.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ export function TablesXlsx() {
};

useEffect(() => {
console.log("12334");
if (xlsx) {
const workbook = XLSX.read(xlsx, { type: "array" });
const firstSheetName = workbook.SheetNames[0];
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/components/store/log-slice.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createSlice } from '@reduxjs/toolkit';

export const logSlice = createSlice({
name: 'log',
initialState: {
logs: {}
},
reducers: {
setLogs: (state, action) => {
state.logs = action.payload;
}
}
});

export const { setLogs } = logSlice.actions;
export default logSlice.reducer;
16 changes: 16 additions & 0 deletions frontend/src/components/store/logSearch-slice.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createSlice } from '@reduxjs/toolkit';

export const logSearchSlice = createSlice({
name: 'logSearch',
initialState: {
logSearchValues: []
},
reducers: {
setLogSearchValues: (state, action) => {
state.logSearchValues = action.payload;
}
}
});

export const { setLogSearchValues } = logSearchSlice.actions;
export default logSearchSlice.reducer;
16 changes: 16 additions & 0 deletions frontend/src/components/store/search-slice.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createSlice } from '@reduxjs/toolkit';

export const searchSlice = createSlice({
name: 'search',
initialState: {
searchValues: []
},
reducers: {
setSearchValues: (state, action) => {
state.searchValues = action.payload;
}
}
});

export const { setSearchValues } = searchSlice.actions;
export default searchSlice.reducer;
8 changes: 7 additions & 1 deletion frontend/src/components/store/store.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ import { configureStore } from '@reduxjs/toolkit';
import brokerReducer from './broker-slice.jsx';
import xlsxReducer from './xlsx-slice.jsx';
import userReducer from './user-slice.jsx';
import logReducer from './log-slice.jsx'
import searchReducer from './search-slice.jsx'
import logSearchReducer from './logSearch-slice.jsx';

export const store = configureStore({
reducer: {
broker: brokerReducer,
xlsx: xlsxReducer,
user: userReducer
user: userReducer,
log: logReducer,
search: searchReducer,
logSearch: logSearchReducer
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
Expand Down
Loading

0 comments on commit 8ac8b5f

Please sign in to comment.