diff --git a/lake-catalog/Dockerfile b/Dockerfile similarity index 100% rename from lake-catalog/Dockerfile rename to Dockerfile diff --git a/lake-catalog/db-init/neo4j.dump b/db-init/neo4j.dump similarity index 100% rename from lake-catalog/db-init/neo4j.dump rename to db-init/neo4j.dump diff --git a/lake-catalog/docker-compose.yml b/docker-compose.yml similarity index 100% rename from lake-catalog/docker-compose.yml rename to docker-compose.yml diff --git a/lake-catalog/import.json b/import.json similarity index 100% rename from lake-catalog/import.json rename to import.json diff --git a/lake-catalog/.env b/lake-catalog/.env deleted file mode 100644 index 3ec579b..0000000 --- a/lake-catalog/.env +++ /dev/null @@ -1,3 +0,0 @@ -SPRING_NEO4J_URI=bolt://db:7687 -SPRING_NEO4J_AUTHENTICATION_USERNAME=neo4j -SPRING_NEO4J_AUTHENTICATION_PASSWORD=neo4jneo4j \ No newline at end of file diff --git a/lake-catalog/.gitattributes b/lake-catalog/.gitattributes deleted file mode 100644 index 0ac0d33..0000000 --- a/lake-catalog/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -/mvnw text eol=lf -*.cmd text eol=crlf diff --git a/lake-catalog/.gitignore b/lake-catalog/.gitignore deleted file mode 100644 index 7bc4bf7..0000000 --- a/lake-catalog/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/lake-catalog/.mvn/wrapper/MavenWrapperDownloader.java b/lake-catalog/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index 519f798..0000000 --- a/lake-catalog/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/lake-catalog/.mvn/wrapper/maven-wrapper.jar b/lake-catalog/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a..0000000 Binary files a/lake-catalog/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/lake-catalog/.mvn/wrapper/maven-wrapper.properties b/lake-catalog/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 6b02994..0000000 --- a/lake-catalog/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar \ No newline at end of file diff --git a/lake-catalog/lakes.json b/lakes.json similarity index 100% rename from lake-catalog/lakes.json rename to lakes.json diff --git a/lake-catalog/mvnw b/mvnw similarity index 100% rename from lake-catalog/mvnw rename to mvnw diff --git a/lake-catalog/mvnw.cmd b/mvnw.cmd similarity index 100% rename from lake-catalog/mvnw.cmd rename to mvnw.cmd diff --git a/lake-catalog/neo4j_dumps/neo4j.dump b/neo4j_dumps/neo4j.dump similarity index 100% rename from lake-catalog/neo4j_dumps/neo4j.dump rename to neo4j_dumps/neo4j.dump diff --git a/lake-catalog/pom.xml b/pom.xml similarity index 97% rename from lake-catalog/pom.xml rename to pom.xml index ba381f4..807b2cf 100644 --- a/lake-catalog/pom.xml +++ b/pom.xml @@ -1,81 +1,81 @@ - - - 4.0.0 - - - org.springframework.boot - spring-boot-starter-parent - 3.3.5 - - - - com.example - lake-catalog - 0.0.1-SNAPSHOT - lake-catalog - Demo project for Spring Boot - - - 17 - - - - - - org.springframework.boot - spring-boot-starter-data-neo4j - - - - - org.springframework.boot - spring-boot-starter-web - - - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - - - org.springframework.boot - spring-boot-devtools - runtime - true - - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - org.springframework.security - spring-security-test - test - - - - - org.springframework.security - spring-security-crypto - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.3.5 + + + + com.example + lake-catalog + 0.0.1-SNAPSHOT + lake-catalog + Demo project for Spring Boot + + + 17 + + + + + + org.springframework.boot + spring-boot-starter-data-neo4j + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.security + spring-security-test + test + + + + + org.springframework.security + spring-security-crypto + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/lake-catalog/records.json b/records.json similarity index 100% rename from lake-catalog/records.json rename to records.json diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/LakeCatalogApplication.java b/src/main/java/com/example/lake_catalog/LakeCatalogApplication.java similarity index 96% rename from lake-catalog/src/main/java/com/example/lake_catalog/LakeCatalogApplication.java rename to src/main/java/com/example/lake_catalog/LakeCatalogApplication.java index a2981f6..572956f 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/LakeCatalogApplication.java +++ b/src/main/java/com/example/lake_catalog/LakeCatalogApplication.java @@ -1,16 +1,16 @@ -package com.example.lake_catalog; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; - -@SpringBootApplication(exclude = { SecurityAutoConfiguration.class }) - -public class LakeCatalogApplication { - - public static void main(String[] args) { - SpringApplication.run(LakeCatalogApplication.class, args); - } - -} +package com.example.lake_catalog; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; + +@SpringBootApplication(exclude = { SecurityAutoConfiguration.class }) + +public class LakeCatalogApplication { + + public static void main(String[] args) { + SpringApplication.run(LakeCatalogApplication.class, args); + } + +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/controller/AuthController.java b/src/main/java/com/example/lake_catalog/controller/AuthController.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/controller/AuthController.java rename to src/main/java/com/example/lake_catalog/controller/AuthController.java index 27e1d6c..f18a19e 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/controller/AuthController.java +++ b/src/main/java/com/example/lake_catalog/controller/AuthController.java @@ -1,118 +1,118 @@ -// AuthController.java -package com.example.lake_catalog.controller; - -import com.example.lake_catalog.service.AuthService; - -import jakarta.servlet.http.HttpSession; -import com.example.lake_catalog.model.*; - -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.ui.Model; - -@Controller -public class AuthController { - - private final AuthService authService; - - @Autowired - public AuthController(AuthService authService) { - this.authService = authService; - } - - // Первая страница - @GetMapping("/") - public String homePage() { - return "home/home"; - } - - // Страница входа - @GetMapping("/login") - public String loginPage() { - return "login/login"; - } - - @GetMapping("/filtration") - public String filterPage() { - return "filtration/filtration"; - } - - // Страница регистрации - @GetMapping("/register") - public String registerPage() { - return "register/register"; - } - - // Перенаправление на страницу /lakes/main после входа - @GetMapping("/main") - public String mainPage() { - return "redirect:/lakes/main"; - } - - // Обработка формы входа - @PostMapping("/perform_login") - public String performLogin(String email, String password, HttpSession session, Model model) { - if (email == null || email.isEmpty() || password == null || password.isEmpty()) { - model.addAttribute("error", "Введите email и пароль."); - return "login/login"; // Вернуть страницу входа с сообщением об ошибке - } - - try { - User user = authService.loginUser(email, password); - session.setAttribute("currentUser", user); - System.out.println("Пользователь вошел: " + user.getEmail()); - return "redirect:/lakes/main"; // Редирект при успешном входе - } catch (RuntimeException e) { - model.addAttribute("error", e.getMessage()); // Передача текста ошибки в шаблон - return "login/login"; // Вернуть страницу входа - } - } - - - // Обработка формы регистрации - @PostMapping("/perform_register") - public String performRegister(String username, String email, String password, String confirm, HttpSession session, Model model) { - if (username == null || username.isEmpty() || email == null || email.isEmpty() || - password == null || password.isEmpty() || confirm == null || confirm.isEmpty()) { - model.addAttribute("error", "Заполните все поля."); - return "register/register"; // Вернуть страницу регистрации с сообщением об ошибке - } - - if (!password.equals(confirm)) { - model.addAttribute("error", "Пароли не совпадают."); - return "register/register"; - } - - try { - authService.registerUser(username, email, password); - User user = authService.loginUser(email, password); // Автоматический вход - session.setAttribute("currentUser", user); - System.out.println("Новый пользователь зарегистрирован: " + user.getEmail()); - return "redirect:/lakes/main"; - } catch (RuntimeException e) { - model.addAttribute("error", e.getMessage()); // Передача текста ошибки в шаблон - return "register/register"; - } - } - - - // Выход из профиля - @GetMapping("/logout") - public String logout(HttpSession session) { - session.invalidate(); // Уничтожение текущей сессии - return "redirect:/"; // Перенаправление на главную страницу после выхода - } - - @GetMapping("/check-auth") - @ResponseBody - public Map checkAuth(HttpSession session) { - User currentUser = (User) session.getAttribute("currentUser"); - return Map.of("authenticated", currentUser != null); - } - -} +// AuthController.java +package com.example.lake_catalog.controller; + +import com.example.lake_catalog.service.AuthService; + +import jakarta.servlet.http.HttpSession; +import com.example.lake_catalog.model.*; + +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.ui.Model; + +@Controller +public class AuthController { + + private final AuthService authService; + + @Autowired + public AuthController(AuthService authService) { + this.authService = authService; + } + + // Первая страница + @GetMapping("/") + public String homePage() { + return "home/home"; + } + + // Страница входа + @GetMapping("/login") + public String loginPage() { + return "login/login"; + } + + @GetMapping("/filtration") + public String filterPage() { + return "filtration/filtration"; + } + + // Страница регистрации + @GetMapping("/register") + public String registerPage() { + return "register/register"; + } + + // Перенаправление на страницу /lakes/main после входа + @GetMapping("/main") + public String mainPage() { + return "redirect:/lakes/main"; + } + + // Обработка формы входа + @PostMapping("/perform_login") + public String performLogin(String email, String password, HttpSession session, Model model) { + if (email == null || email.isEmpty() || password == null || password.isEmpty()) { + model.addAttribute("error", "Введите email и пароль."); + return "login/login"; // Вернуть страницу входа с сообщением об ошибке + } + + try { + User user = authService.loginUser(email, password); + session.setAttribute("currentUser", user); + System.out.println("Пользователь вошел: " + user.getEmail()); + return "redirect:/lakes/main"; // Редирект при успешном входе + } catch (RuntimeException e) { + model.addAttribute("error", e.getMessage()); // Передача текста ошибки в шаблон + return "login/login"; // Вернуть страницу входа + } + } + + + // Обработка формы регистрации + @PostMapping("/perform_register") + public String performRegister(String username, String email, String password, String confirm, HttpSession session, Model model) { + if (username == null || username.isEmpty() || email == null || email.isEmpty() || + password == null || password.isEmpty() || confirm == null || confirm.isEmpty()) { + model.addAttribute("error", "Заполните все поля."); + return "register/register"; // Вернуть страницу регистрации с сообщением об ошибке + } + + if (!password.equals(confirm)) { + model.addAttribute("error", "Пароли не совпадают."); + return "register/register"; + } + + try { + authService.registerUser(username, email, password); + User user = authService.loginUser(email, password); // Автоматический вход + session.setAttribute("currentUser", user); + System.out.println("Новый пользователь зарегистрирован: " + user.getEmail()); + return "redirect:/lakes/main"; + } catch (RuntimeException e) { + model.addAttribute("error", e.getMessage()); // Передача текста ошибки в шаблон + return "register/register"; + } + } + + + // Выход из профиля + @GetMapping("/logout") + public String logout(HttpSession session) { + session.invalidate(); // Уничтожение текущей сессии + return "redirect:/"; // Перенаправление на главную страницу после выхода + } + + @GetMapping("/check-auth") + @ResponseBody + public Map checkAuth(HttpSession session) { + User currentUser = (User) session.getAttribute("currentUser"); + return Map.of("authenticated", currentUser != null); + } + +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/controller/LakeController.java b/src/main/java/com/example/lake_catalog/controller/LakeController.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/controller/LakeController.java rename to src/main/java/com/example/lake_catalog/controller/LakeController.java index 128b515..e95f716 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/controller/LakeController.java +++ b/src/main/java/com/example/lake_catalog/controller/LakeController.java @@ -1,194 +1,194 @@ -// LakeController.java -package com.example.lake_catalog.controller; - -import com.example.lake_catalog.service.LakeService; -import com.example.lake_catalog.service.UserService; - -import jakarta.servlet.http.HttpSession; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import java.util.Optional; -import com.example.lake_catalog.model.*; -import com.example.lake_catalog.repository.LakeRepository; - -import java.net.URI; -import java.util.List; -import java.util.Map; - -@Controller -@RequestMapping("/lakes") -public class LakeController { - - private final LakeService lakeService; - - @Autowired - private UserService userService; - - @Autowired - private LakeRepository lakeRepository; - - @Autowired - public LakeController(LakeService lakeService) { - this.lakeService = lakeService; - } - - @GetMapping("/main") - public String showMainPage(Model model, - @RequestParam(defaultValue = "0") int page, - @RequestParam(required = false) String name, - @RequestParam(required = false) String depth, - @RequestParam(required = false) String square, - @RequestParam(required = false) String city, - @RequestParam(required = false) String region, - @RequestParam(required = false) String rating, - HttpSession session) { - int pageSize = 8; // Количество озер на одной странице - - // Вызываем метод фильтрации - Page lakePage = lakeService.filterLakes(name, depth, square, city, region, rating, page, pageSize); - - // Параметры пагинации - int totalPages = lakePage.getTotalPages(); - int startPage = Math.max(0, page - 1); - int endPage = Math.min(totalPages - 1, page + 1); - User currentUser = (User) session.getAttribute("currentUser"); - if(currentUser!=null){ - model.addAttribute("userPhoto", currentUser.getPhoto()); - } else { - model.addAttribute("userPhoto", "https://www.hydropower.ru/en/auth/inside.png"); - } - // Передаем данные в модель - model.addAttribute("lakePage", lakePage); - model.addAttribute("currentPage", page); - model.addAttribute("totalPages", totalPages); - model.addAttribute("startPage", startPage); - model.addAttribute("endPage", endPage); - model.addAttribute("name", name); - model.addAttribute("depth", depth); - model.addAttribute("square", square); - model.addAttribute("city", city); - model.addAttribute("region", region); - model.addAttribute("rating", rating); - return "main/main"; // Возвращаем имя представления - } - - @GetMapping("/lake_page/{id}") - public String showLakeDetails(@PathVariable("id") Long id, Model model, HttpSession session) { - // Загружаем озеро по id - Optional lakeOptional = lakeService.findLakeById(id); - - if (lakeOptional.isPresent()) { - Lake lake = lakeOptional.get(); - model.addAttribute("lake", lake); - User currentUser = (User) session.getAttribute("currentUser"); - - boolean isInWantVisit = false; - boolean isInVisited = false; - if (currentUser != null) { - isInWantVisit = userService.isLakeInWantVisit(currentUser.getId(), lake.getId()); - isInVisited = userService.isLakeInVisited(currentUser.getId(), lake.getId()); - } - model.addAttribute("isInWantVisit", isInWantVisit); - model.addAttribute("isInVisited", isInVisited); - - return "card/card"; // Имя представления для отображения информации об озере - } else { - return "error"; // Если озеро не найдено - } - } - - @PostMapping("/{lakeId}/action") - public ResponseEntity handleLakeAction(@PathVariable Long lakeId, @RequestBody Map request, HttpSession session) { - User currentUser = (User) session.getAttribute("currentUser"); - if (currentUser == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Вы не авторизованы.")); - } - - String action = request.get("action"); - Optional optionalLake = lakeService.findLakeById(lakeId); - - if (optionalLake.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("message", "Озеро не найдено.")); - } - - Lake lake = optionalLake.get(); - - switch (action) { - case "want_visit": - userService.addWantVisitLake(currentUser, lake); - return ResponseEntity.ok(Map.of("message", "Добавлено в 'Хочу посетить'.")); - case "remove_want_visit": - userService.removeWantVisitLake(currentUser.getId(), lake.getId()); - return ResponseEntity.ok(Map.of("message", "Удалено из 'Хочу посетить'.")); - case "visited": - userService.addVisitedLake(currentUser, lake); - return ResponseEntity.ok(Map.of("message", "Добавлено в 'Уже посетил'.")); - case "remove_visited": - userService.removeVisitedLake(currentUser.getId(), lake.getId()); - return ResponseEntity.ok(Map.of("message", "Удалено из 'Уже посетил'.")); - default: - return ResponseEntity.badRequest().body(Map.of("message", "Неизвестное действие.")); - } - } - - @GetMapping("/{lakeId}/status") - public ResponseEntity> getLakeStatus(@PathVariable Long lakeId, HttpSession session) { - User currentUser = (User) session.getAttribute("currentUser"); - - if (currentUser == null) { - // Пользователь не авторизован, возвращаем ответ с сообщением в виде Map - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("isWantVisit", false, "isVisited", false)); - } - - Optional optionalLake = lakeService.findLakeById(lakeId); - if (optionalLake.isEmpty()) { - // Озеро не найдено, возвращаем ответ с сообщением в виде Map - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("isWantVisit", false, "isVisited", false)); - } - - Lake lake = optionalLake.get(); - boolean isInWantVisit = userService.isLakeInWantVisit(currentUser.getId(), lake.getId()); - boolean isInVisited = userService.isLakeInVisited(currentUser.getId(), lake.getId()); - - // Возвращаем информацию о статусе озера в виде Map - return ResponseEntity.ok(Map.of( - "isWantVisit", isInWantVisit, - "isVisited", isInVisited - )); - } - - - - @GetMapping() - public List getAllLakes() { - return lakeService.findAll(); - } - - @GetMapping("/initialize") - public ResponseEntity initialize() { - lakeService.initialize(); - return ResponseEntity.status(HttpStatus.FOUND).location(URI.create("/lakes")).build(); - } - - @GetMapping("/regions") - public List getAllRegions() { - return lakeRepository.findAllRegions(); - } - - // Получение всех городов для выпадающего списка - @GetMapping("/cities") - public List getAllCities() { - return lakeRepository.findAllCities(); - } -} +// LakeController.java +package com.example.lake_catalog.controller; + +import com.example.lake_catalog.service.LakeService; +import com.example.lake_catalog.service.UserService; + +import jakarta.servlet.http.HttpSession; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import java.util.Optional; +import com.example.lake_catalog.model.*; +import com.example.lake_catalog.repository.LakeRepository; + +import java.net.URI; +import java.util.List; +import java.util.Map; + +@Controller +@RequestMapping("/lakes") +public class LakeController { + + private final LakeService lakeService; + + @Autowired + private UserService userService; + + @Autowired + private LakeRepository lakeRepository; + + @Autowired + public LakeController(LakeService lakeService) { + this.lakeService = lakeService; + } + + @GetMapping("/main") + public String showMainPage(Model model, + @RequestParam(defaultValue = "0") int page, + @RequestParam(required = false) String name, + @RequestParam(required = false) String depth, + @RequestParam(required = false) String square, + @RequestParam(required = false) String city, + @RequestParam(required = false) String region, + @RequestParam(required = false) String rating, + HttpSession session) { + int pageSize = 8; // Количество озер на одной странице + + // Вызываем метод фильтрации + Page lakePage = lakeService.filterLakes(name, depth, square, city, region, rating, page, pageSize); + + // Параметры пагинации + int totalPages = lakePage.getTotalPages(); + int startPage = Math.max(0, page - 1); + int endPage = Math.min(totalPages - 1, page + 1); + User currentUser = (User) session.getAttribute("currentUser"); + if(currentUser!=null){ + model.addAttribute("userPhoto", currentUser.getPhoto()); + } else { + model.addAttribute("userPhoto", "https://www.hydropower.ru/en/auth/inside.png"); + } + // Передаем данные в модель + model.addAttribute("lakePage", lakePage); + model.addAttribute("currentPage", page); + model.addAttribute("totalPages", totalPages); + model.addAttribute("startPage", startPage); + model.addAttribute("endPage", endPage); + model.addAttribute("name", name); + model.addAttribute("depth", depth); + model.addAttribute("square", square); + model.addAttribute("city", city); + model.addAttribute("region", region); + model.addAttribute("rating", rating); + return "main/main"; // Возвращаем имя представления + } + + @GetMapping("/lake_page/{id}") + public String showLakeDetails(@PathVariable("id") Long id, Model model, HttpSession session) { + // Загружаем озеро по id + Optional lakeOptional = lakeService.findLakeById(id); + + if (lakeOptional.isPresent()) { + Lake lake = lakeOptional.get(); + model.addAttribute("lake", lake); + User currentUser = (User) session.getAttribute("currentUser"); + + boolean isInWantVisit = false; + boolean isInVisited = false; + if (currentUser != null) { + isInWantVisit = userService.isLakeInWantVisit(currentUser.getId(), lake.getId()); + isInVisited = userService.isLakeInVisited(currentUser.getId(), lake.getId()); + } + model.addAttribute("isInWantVisit", isInWantVisit); + model.addAttribute("isInVisited", isInVisited); + + return "card/card"; // Имя представления для отображения информации об озере + } else { + return "error"; // Если озеро не найдено + } + } + + @PostMapping("/{lakeId}/action") + public ResponseEntity handleLakeAction(@PathVariable Long lakeId, @RequestBody Map request, HttpSession session) { + User currentUser = (User) session.getAttribute("currentUser"); + if (currentUser == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Вы не авторизованы.")); + } + + String action = request.get("action"); + Optional optionalLake = lakeService.findLakeById(lakeId); + + if (optionalLake.isEmpty()) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("message", "Озеро не найдено.")); + } + + Lake lake = optionalLake.get(); + + switch (action) { + case "want_visit": + userService.addWantVisitLake(currentUser, lake); + return ResponseEntity.ok(Map.of("message", "Добавлено в 'Хочу посетить'.")); + case "remove_want_visit": + userService.removeWantVisitLake(currentUser.getId(), lake.getId()); + return ResponseEntity.ok(Map.of("message", "Удалено из 'Хочу посетить'.")); + case "visited": + userService.addVisitedLake(currentUser, lake); + return ResponseEntity.ok(Map.of("message", "Добавлено в 'Уже посетил'.")); + case "remove_visited": + userService.removeVisitedLake(currentUser.getId(), lake.getId()); + return ResponseEntity.ok(Map.of("message", "Удалено из 'Уже посетил'.")); + default: + return ResponseEntity.badRequest().body(Map.of("message", "Неизвестное действие.")); + } + } + + @GetMapping("/{lakeId}/status") + public ResponseEntity> getLakeStatus(@PathVariable Long lakeId, HttpSession session) { + User currentUser = (User) session.getAttribute("currentUser"); + + if (currentUser == null) { + // Пользователь не авторизован, возвращаем ответ с сообщением в виде Map + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("isWantVisit", false, "isVisited", false)); + } + + Optional optionalLake = lakeService.findLakeById(lakeId); + if (optionalLake.isEmpty()) { + // Озеро не найдено, возвращаем ответ с сообщением в виде Map + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("isWantVisit", false, "isVisited", false)); + } + + Lake lake = optionalLake.get(); + boolean isInWantVisit = userService.isLakeInWantVisit(currentUser.getId(), lake.getId()); + boolean isInVisited = userService.isLakeInVisited(currentUser.getId(), lake.getId()); + + // Возвращаем информацию о статусе озера в виде Map + return ResponseEntity.ok(Map.of( + "isWantVisit", isInWantVisit, + "isVisited", isInVisited + )); + } + + + + @GetMapping() + public List getAllLakes() { + return lakeService.findAll(); + } + + @GetMapping("/initialize") + public ResponseEntity initialize() { + lakeService.initialize(); + return ResponseEntity.status(HttpStatus.FOUND).location(URI.create("/lakes")).build(); + } + + @GetMapping("/regions") + public List getAllRegions() { + return lakeRepository.findAllRegions(); + } + + // Получение всех городов для выпадающего списка + @GetMapping("/cities") + public List getAllCities() { + return lakeRepository.findAllCities(); + } +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/controller/ReviewController.java b/src/main/java/com/example/lake_catalog/controller/ReviewController.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/controller/ReviewController.java rename to src/main/java/com/example/lake_catalog/controller/ReviewController.java index 0127a54..b325b58 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/controller/ReviewController.java +++ b/src/main/java/com/example/lake_catalog/controller/ReviewController.java @@ -1,89 +1,89 @@ -package com.example.lake_catalog.controller; - -import com.example.lake_catalog.model.*; -import com.example.lake_catalog.service.*; - -import jakarta.servlet.http.HttpSession; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.time.LocalDate; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -@RestController -@RequestMapping("lakes/lake_page/{lakeId}") -public class ReviewController { - - private static final Logger logger = LoggerFactory.getLogger(ReviewController.class); - - @Autowired - private ReviewService reviewService; - - @Autowired - private LakeService lakeService; - - @Autowired - private UserService userService; - - // Метод для получения списка отзывов для озера (API для получения отзывов) - @GetMapping("/reviews") - public ResponseEntity getLakeReviews(@PathVariable Long lakeId) { - try { - List reviews = reviewService.getReviewsForLake(lakeId); - return ResponseEntity.ok(reviews); - } catch (Exception e) { - logger.error("error ID {}: {}", lakeId, e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); - } - } - - - // Метод для добавления отзыва - @PostMapping("/reviews") - public ResponseEntity addReview( - @PathVariable Long lakeId, - @RequestBody Review reviewRequest, - HttpSession session) { - User currentUser = (User) session.getAttribute("currentUser"); - if (currentUser == null) { - return ResponseEntity.status(HttpStatus.FORBIDDEN).body("User is not logged in"); - } - Optional lake = lakeService.findLakeById(lakeId); - if (lake.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Lake not found"); - } - System.out.println("Current user Email: " + currentUser.getEmail()); - - Optional userOptional = userService.findUserByEmail(currentUser.getEmail()); - if (userOptional.isEmpty()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND).body("User with the given email not found"); - } - - User user = userOptional.get(); - System.out.println("Curr user: " + user); - Review review = new Review(); - review.setLake(lake.get()); - review.setUser(user); - review.setMessage(reviewRequest.getMessage()); - review.setStars(reviewRequest.getStars()); - review.setDate(LocalDate.now()); - - - try { - Review savedReview = reviewService.addReview(lakeId, user.getId(), review.getMessage(), review.getStars()); - Map response = new HashMap<>(); - response.put("review", savedReview); // Добавляем отзыв в ответ - response.put("lake", lake.get()); - return ResponseEntity.ok(response); - } catch (RuntimeException e) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); // Ответ 400 в случае ошибки - } - } -} +package com.example.lake_catalog.controller; + +import com.example.lake_catalog.model.*; +import com.example.lake_catalog.service.*; + +import jakarta.servlet.http.HttpSession; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@RestController +@RequestMapping("lakes/lake_page/{lakeId}") +public class ReviewController { + + private static final Logger logger = LoggerFactory.getLogger(ReviewController.class); + + @Autowired + private ReviewService reviewService; + + @Autowired + private LakeService lakeService; + + @Autowired + private UserService userService; + + // Метод для получения списка отзывов для озера (API для получения отзывов) + @GetMapping("/reviews") + public ResponseEntity getLakeReviews(@PathVariable Long lakeId) { + try { + List reviews = reviewService.getReviewsForLake(lakeId); + return ResponseEntity.ok(reviews); + } catch (Exception e) { + logger.error("error ID {}: {}", lakeId, e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); + } + } + + + // Метод для добавления отзыва + @PostMapping("/reviews") + public ResponseEntity addReview( + @PathVariable Long lakeId, + @RequestBody Review reviewRequest, + HttpSession session) { + User currentUser = (User) session.getAttribute("currentUser"); + if (currentUser == null) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).body("User is not logged in"); + } + Optional lake = lakeService.findLakeById(lakeId); + if (lake.isEmpty()) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Lake not found"); + } + System.out.println("Current user Email: " + currentUser.getEmail()); + + Optional userOptional = userService.findUserByEmail(currentUser.getEmail()); + if (userOptional.isEmpty()) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("User with the given email not found"); + } + + User user = userOptional.get(); + System.out.println("Curr user: " + user); + Review review = new Review(); + review.setLake(lake.get()); + review.setUser(user); + review.setMessage(reviewRequest.getMessage()); + review.setStars(reviewRequest.getStars()); + review.setDate(LocalDate.now()); + + + try { + Review savedReview = reviewService.addReview(lakeId, user.getId(), review.getMessage(), review.getStars()); + Map response = new HashMap<>(); + response.put("review", savedReview); // Добавляем отзыв в ответ + response.put("lake", lake.get()); + return ResponseEntity.ok(response); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); // Ответ 400 в случае ошибки + } + } +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/controller/UserController.java b/src/main/java/com/example/lake_catalog/controller/UserController.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/controller/UserController.java rename to src/main/java/com/example/lake_catalog/controller/UserController.java index d48a73f..9cbb733 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/controller/UserController.java +++ b/src/main/java/com/example/lake_catalog/controller/UserController.java @@ -1,209 +1,209 @@ -package com.example.lake_catalog.controller; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import java.util.Optional; -import com.example.lake_catalog.model.*; -import com.example.lake_catalog.repository.LakeRepository; -import com.example.lake_catalog.service.*; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.http.HttpHeaders; - -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; - -import java.io.IOException; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.security.Principal; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -@Controller -@RequestMapping("/users") -public class UserController { - - @Autowired - private UserService userService; - - @Autowired - private LakeService lakeService; - - @Autowired - private LakeRepository lakeRepository; - - @GetMapping("/profile/current") - public String currentUserProfile(HttpSession session) { - User currentUser = (User) session.getAttribute("currentUser"); - if (currentUser == null) { - return "redirect:/"; // Если пользователь не авторизован, перенаправляем на страницу входа - } - return "redirect:/users/profile/" + currentUser.getId(); // Перенаправление на страницу профиля текущего пользователя - } - - @GetMapping("/profile/{userId}") - public String getUserProfile(@PathVariable Long userId, Model model) { - Optional userOptional = userService.findUserById(userId); - if (userOptional.isPresent()) { - User user = userOptional.get(); - List wantVisitLakes = userService.getWantVisitLakes(userId); - List visitedLakes = userService.getVisitedLakes(userId); - model.addAttribute("user", user); - model.addAttribute("wantVisitLakes", wantVisitLakes); - model.addAttribute("visitedLakes", visitedLakes); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy", Locale.forLanguageTag("ru")); - LocalDate creationDate = user.getCreationDate(); - String formattedDate = (creationDate != null) ? creationDate.format(DateTimeFormatter.ofPattern("dd-MM-yyyy")) : "Дата не указана"; - - model.addAttribute("formattedDate", formattedDate); - - LocalDateTime editDate = user.getEditDate(); - String formatted_editDate = (editDate != null) ? editDate.format(DateTimeFormatter.ofPattern("dd-MM-yyyy")) : "Дата не указана"; - - model.addAttribute("formatted_editDate", formatted_editDate); - - //Lake lakePage = lakeService.getLakePageByUserId(userId); // Например, метод для получения lakePage - //model.addAttribute("lakePage", lakePage); // Передаем в шаблон - return "profile/profile"; - } else { - return "error/404"; // Возвращаем страницу ошибки - } - } - - @PostMapping("/check-email-unique") - public ResponseEntity> checkEmailUnique(@RequestBody Map request) { - String email = request.get("email"); - boolean isUnique = userService.isEmailUnique(email); - return ResponseEntity.ok(Collections.singletonMap("isUnique", isUnique)); - } - - @PutMapping("/profile/{userId}/update-profile") - public ResponseEntity> updateUserProfile( - @PathVariable Long userId, // Заменили @RequestParam на @PathVariable для userId - @RequestParam String newName, - @RequestParam String newEmail) { - try { - userService.updateUserProfile(userId, newName, newEmail); - Map response = new HashMap<>(); - response.put("message", "Профиль обновлен успешно!"); - response.put("editDate", LocalDateTime.now().toString()); - return ResponseEntity.ok(response); - } catch (IllegalArgumentException e) { - Map response = new HashMap<>(); - response.put("message", e.getMessage()); // Возвращаем ошибку как часть ответа - return ResponseEntity.badRequest().body(response); - } - } - - @GetMapping("/profile/{userId}/imp_exp/export") - public void exportLakes(HttpServletResponse response) throws IOException { - List lakes = lakeRepository.findAll(); - - // Преобразуем каждый объект в нужный формат - List> exportData = new ArrayList<>(); - for (Lake lake : lakes) { - Map lakeData = new HashMap<>(); - Map properties = new HashMap<>(); - properties.put("square", lake.getSquare()); - properties.put("depth", lake.getDepth()); - properties.put("city", lake.getCity()); - properties.put("name", lake.getName()); - properties.put("rating", lake.getRating()); - properties.put("description", lake.getDescription()); - properties.put("region", lake.getRegion()); - properties.put("photos", lake.getPhotos()); - - Map lakeItem = new HashMap<>(); - lakeItem.put("identity", lake.getId()); // предполагаем, что getId() возвращает идентификатор - lakeItem.put("labels", List.of("Lake")); - lakeItem.put("properties", properties); - - Map wrappedLake = new HashMap<>(); - wrappedLake.put("l", lakeItem); - - exportData.add(wrappedLake); - } - - // Конвертируем данные в JSON - ObjectMapper objectMapper = new ObjectMapper(); - String json = objectMapper.writeValueAsString(exportData); - - // Устанавливаем заголовки ответа - response.setContentType("application/json; charset=UTF-8"); - response.setHeader("Content-Disposition", "attachment; filename=lakes.json"); - response.getWriter().write(json); - } - - @GetMapping("/imp_exp") - public String loginPage() { - return "import-export/import-export"; - } - - @PostMapping("/profile/{userId}/imp_exp/import") - public ResponseEntity importLakes(@RequestBody List> lakesData) { - try { - for (Map lakeData : lakesData) { - Map lData = (Map) lakeData.get("l"); - - // Из "l" извлекаем "properties" - Map properties = (Map) lData.get("properties"); - - String name = (String) properties.get("name"); - - // Проверяем, существует ли озеро с таким названием - Optional existingLake = lakeRepository.findByName(name); - - if (existingLake.isPresent()) { - // Обновляем свойства существующего озера - Lake lake = existingLake.get(); - lake.setRegion((String) properties.get("region")); - lake.setCity((String) properties.get("city")); - lake.setRating(((Number) properties.get("rating")).doubleValue()); - lake.setDepth(((Number) properties.get("depth")).doubleValue()); - lake.setSquare(((Number) properties.get("square")).doubleValue()); - lake.setDescription((String) properties.get("description")); - lake.setPhotos((List) properties.get("photos")); - - lakeRepository.save(lake); - } else { - // Создаем новое озеро - Lake lake = new Lake(); - lake.setName(name); - lake.setRegion((String) properties.get("region")); - lake.setCity((String) properties.get("city")); - lake.setRating(((Number) properties.get("rating")).doubleValue()); - lake.setDepth(((Number) properties.get("depth")).doubleValue()); - lake.setSquare(((Number) properties.get("square")).doubleValue()); - lake.setDescription((String) properties.get("description")); - lake.setPhotos((List) properties.get("photos")); - - lakeRepository.save(lake); - } - } - - return ResponseEntity.ok("Озера успешно импортированы."); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Ошибка импорта данных."); - } - } - +package com.example.lake_catalog.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import java.util.Optional; +import com.example.lake_catalog.model.*; +import com.example.lake_catalog.repository.LakeRepository; +import com.example.lake_catalog.service.*; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.HttpHeaders; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.Principal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +@Controller +@RequestMapping("/users") +public class UserController { + + @Autowired + private UserService userService; + + @Autowired + private LakeService lakeService; + + @Autowired + private LakeRepository lakeRepository; + + @GetMapping("/profile/current") + public String currentUserProfile(HttpSession session) { + User currentUser = (User) session.getAttribute("currentUser"); + if (currentUser == null) { + return "redirect:/"; // Если пользователь не авторизован, перенаправляем на страницу входа + } + return "redirect:/users/profile/" + currentUser.getId(); // Перенаправление на страницу профиля текущего пользователя + } + + @GetMapping("/profile/{userId}") + public String getUserProfile(@PathVariable Long userId, Model model) { + Optional userOptional = userService.findUserById(userId); + if (userOptional.isPresent()) { + User user = userOptional.get(); + List wantVisitLakes = userService.getWantVisitLakes(userId); + List visitedLakes = userService.getVisitedLakes(userId); + model.addAttribute("user", user); + model.addAttribute("wantVisitLakes", wantVisitLakes); + model.addAttribute("visitedLakes", visitedLakes); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy", Locale.forLanguageTag("ru")); + LocalDate creationDate = user.getCreationDate(); + String formattedDate = (creationDate != null) ? creationDate.format(DateTimeFormatter.ofPattern("dd-MM-yyyy")) : "Дата не указана"; + + model.addAttribute("formattedDate", formattedDate); + + LocalDateTime editDate = user.getEditDate(); + String formatted_editDate = (editDate != null) ? editDate.format(DateTimeFormatter.ofPattern("dd-MM-yyyy")) : "Дата не указана"; + + model.addAttribute("formatted_editDate", formatted_editDate); + + //Lake lakePage = lakeService.getLakePageByUserId(userId); // Например, метод для получения lakePage + //model.addAttribute("lakePage", lakePage); // Передаем в шаблон + return "profile/profile"; + } else { + return "error/404"; // Возвращаем страницу ошибки + } + } + + @PostMapping("/check-email-unique") + public ResponseEntity> checkEmailUnique(@RequestBody Map request) { + String email = request.get("email"); + boolean isUnique = userService.isEmailUnique(email); + return ResponseEntity.ok(Collections.singletonMap("isUnique", isUnique)); + } + + @PutMapping("/profile/{userId}/update-profile") + public ResponseEntity> updateUserProfile( + @PathVariable Long userId, // Заменили @RequestParam на @PathVariable для userId + @RequestParam String newName, + @RequestParam String newEmail) { + try { + userService.updateUserProfile(userId, newName, newEmail); + Map response = new HashMap<>(); + response.put("message", "Профиль обновлен успешно!"); + response.put("editDate", LocalDateTime.now().toString()); + return ResponseEntity.ok(response); + } catch (IllegalArgumentException e) { + Map response = new HashMap<>(); + response.put("message", e.getMessage()); // Возвращаем ошибку как часть ответа + return ResponseEntity.badRequest().body(response); + } + } + + @GetMapping("/profile/{userId}/imp_exp/export") + public void exportLakes(HttpServletResponse response) throws IOException { + List lakes = lakeRepository.findAll(); + + // Преобразуем каждый объект в нужный формат + List> exportData = new ArrayList<>(); + for (Lake lake : lakes) { + Map lakeData = new HashMap<>(); + Map properties = new HashMap<>(); + properties.put("square", lake.getSquare()); + properties.put("depth", lake.getDepth()); + properties.put("city", lake.getCity()); + properties.put("name", lake.getName()); + properties.put("rating", lake.getRating()); + properties.put("description", lake.getDescription()); + properties.put("region", lake.getRegion()); + properties.put("photos", lake.getPhotos()); + + Map lakeItem = new HashMap<>(); + lakeItem.put("identity", lake.getId()); // предполагаем, что getId() возвращает идентификатор + lakeItem.put("labels", List.of("Lake")); + lakeItem.put("properties", properties); + + Map wrappedLake = new HashMap<>(); + wrappedLake.put("l", lakeItem); + + exportData.add(wrappedLake); + } + + // Конвертируем данные в JSON + ObjectMapper objectMapper = new ObjectMapper(); + String json = objectMapper.writeValueAsString(exportData); + + // Устанавливаем заголовки ответа + response.setContentType("application/json; charset=UTF-8"); + response.setHeader("Content-Disposition", "attachment; filename=lakes.json"); + response.getWriter().write(json); + } + + @GetMapping("/imp_exp") + public String loginPage() { + return "import-export/import-export"; + } + + @PostMapping("/profile/{userId}/imp_exp/import") + public ResponseEntity importLakes(@RequestBody List> lakesData) { + try { + for (Map lakeData : lakesData) { + Map lData = (Map) lakeData.get("l"); + + // Из "l" извлекаем "properties" + Map properties = (Map) lData.get("properties"); + + String name = (String) properties.get("name"); + + // Проверяем, существует ли озеро с таким названием + Optional existingLake = lakeRepository.findByName(name); + + if (existingLake.isPresent()) { + // Обновляем свойства существующего озера + Lake lake = existingLake.get(); + lake.setRegion((String) properties.get("region")); + lake.setCity((String) properties.get("city")); + lake.setRating(((Number) properties.get("rating")).doubleValue()); + lake.setDepth(((Number) properties.get("depth")).doubleValue()); + lake.setSquare(((Number) properties.get("square")).doubleValue()); + lake.setDescription((String) properties.get("description")); + lake.setPhotos((List) properties.get("photos")); + + lakeRepository.save(lake); + } else { + // Создаем новое озеро + Lake lake = new Lake(); + lake.setName(name); + lake.setRegion((String) properties.get("region")); + lake.setCity((String) properties.get("city")); + lake.setRating(((Number) properties.get("rating")).doubleValue()); + lake.setDepth(((Number) properties.get("depth")).doubleValue()); + lake.setSquare(((Number) properties.get("square")).doubleValue()); + lake.setDescription((String) properties.get("description")); + lake.setPhotos((List) properties.get("photos")); + + lakeRepository.save(lake); + } + } + + return ResponseEntity.ok("Озера успешно импортированы."); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Ошибка импорта данных."); + } + } + } \ No newline at end of file diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/model/Lake.java b/src/main/java/com/example/lake_catalog/model/Lake.java similarity index 95% rename from lake-catalog/src/main/java/com/example/lake_catalog/model/Lake.java rename to src/main/java/com/example/lake_catalog/model/Lake.java index 3dbedd4..4e2d845 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/model/Lake.java +++ b/src/main/java/com/example/lake_catalog/model/Lake.java @@ -1,136 +1,136 @@ -package com.example.lake_catalog.model; - -import org.springframework.data.annotation.Id; -import org.springframework.data.neo4j.core.schema.GeneratedValue; -import org.springframework.data.neo4j.core.schema.Node; -import org.springframework.data.neo4j.core.schema.Relationship; - -import java.util.ArrayList; -import java.util.List; - -@Node -public class Lake { - @Id - @GeneratedValue - private Long id; - private String name; - private String region; - private String city; - private Double rating; - private Double depth; - private Double square; - private List photos; - private String description; - - // Конструктор - public Lake(Long id, String name, String region, String city, Double rating, Double depth, Double square, List photos, String description) { - this.id = id; - this.name = name; - this.region = region; - this.city = city; - this.rating = rating; - this.depth = depth; - this.square = square; - this.photos = photos != null ? photos : new ArrayList<>(); - this.description = description; - } - - public Lake(){ - - } - - // Геттеры и сеттеры - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getPrimaryPhoto() { - return (photos != null && !photos.isEmpty()) ? photos.get(0) : "https://avatars.mds.yandex.net/i?id=5b8ada33bb7301c22680812d7cd9feab_l-11548596-images-thumbs&n=13"; - } - - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getRegion() { - return region; - } - - public void setRegion(String region) { - this.region = region; - } - - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - public Double getRating() { - return rating; - } - - public void setRating(Double rating) { - this.rating = rating; - } - - public Double getDepth() { - return depth; - } - - public void setDepth(Double depth) { - this.depth = depth; - } - - public Double getSquare() { - return square; - } - - public void setSquare(Double square) { - this.square = square; - } - - public List getPhotos() { - return photos; - } - - public void setPhotos(List photos) { - this.photos = (photos != null) ? photos : new ArrayList<>(); - } - - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - // Метод для вывода информации об озере - @Override - public String toString() { - return "Lake{" + - "id=" + id + - ", name='" + name + '\'' + - ", region='" + region + '\'' + - ", city='" + city + '\'' + - ", rating=" + rating + - ", depth=" + depth + - ", square=" + square + - ", photos=" + photos + - ", description='" + description + '\'' + - '}'; - } -} +package com.example.lake_catalog.model; + +import org.springframework.data.annotation.Id; +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +import java.util.ArrayList; +import java.util.List; + +@Node +public class Lake { + @Id + @GeneratedValue + private Long id; + private String name; + private String region; + private String city; + private Double rating; + private Double depth; + private Double square; + private List photos; + private String description; + + // Конструктор + public Lake(Long id, String name, String region, String city, Double rating, Double depth, Double square, List photos, String description) { + this.id = id; + this.name = name; + this.region = region; + this.city = city; + this.rating = rating; + this.depth = depth; + this.square = square; + this.photos = photos != null ? photos : new ArrayList<>(); + this.description = description; + } + + public Lake(){ + + } + + // Геттеры и сеттеры + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getPrimaryPhoto() { + return (photos != null && !photos.isEmpty()) ? photos.get(0) : "https://avatars.mds.yandex.net/i?id=5b8ada33bb7301c22680812d7cd9feab_l-11548596-images-thumbs&n=13"; + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public Double getRating() { + return rating; + } + + public void setRating(Double rating) { + this.rating = rating; + } + + public Double getDepth() { + return depth; + } + + public void setDepth(Double depth) { + this.depth = depth; + } + + public Double getSquare() { + return square; + } + + public void setSquare(Double square) { + this.square = square; + } + + public List getPhotos() { + return photos; + } + + public void setPhotos(List photos) { + this.photos = (photos != null) ? photos : new ArrayList<>(); + } + + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + // Метод для вывода информации об озере + @Override + public String toString() { + return "Lake{" + + "id=" + id + + ", name='" + name + '\'' + + ", region='" + region + '\'' + + ", city='" + city + '\'' + + ", rating=" + rating + + ", depth=" + depth + + ", square=" + square + + ", photos=" + photos + + ", description='" + description + '\'' + + '}'; + } +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/model/Review.java b/src/main/java/com/example/lake_catalog/model/Review.java similarity index 95% rename from lake-catalog/src/main/java/com/example/lake_catalog/model/Review.java rename to src/main/java/com/example/lake_catalog/model/Review.java index bfae662..686a8b5 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/model/Review.java +++ b/src/main/java/com/example/lake_catalog/model/Review.java @@ -1,109 +1,109 @@ -package com.example.lake_catalog.model; -import org.springframework.data.annotation.Id; -import org.springframework.data.neo4j.core.schema.GeneratedValue; -import org.springframework.data.neo4j.core.schema.Node; -import org.springframework.data.neo4j.core.schema.Relationship; - -import com.fasterxml.jackson.annotation.JsonFormat; - -import java.time.LocalDate; -import java.util.List; - -@Node -public class Review { - @Id - @GeneratedValue - private Long id; - private Integer stars; - private String message; - private List photos; - private LocalDate date; - @Relationship(type = "ABOUT", direction = Relationship.Direction.OUTGOING) - private Lake lake; - @Relationship(type = "POSTED_BY", direction = Relationship.Direction.OUTGOING) - private User user; - - // Конструктор - public Review(Long id, int stars, String message, List photos, LocalDate date) { - this.id = id; - this.stars = stars; - this.message = message; - this.photos = photos; - this.date = date; - } - - public Review(){ - - } - - // Геттеры и сеттеры - public Lake getLake() { - return lake; - } - - public void setLake(Lake lake) { - this.lake = lake; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - // Геттеры и сеттеры - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public int getStars() { - return stars; - } - - public void setStars(int stars) { - this.stars = stars; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public List getPhotos() { - return photos; - } - - public void setPhotos(List photos) { - this.photos = photos; - } - - public LocalDate getDate() { - return date; - } - - public void setDate(LocalDate date) { - this.date = date; - } - - // Метод для вывода информации о обзоре - @Override - public String toString() { - return "Review{" + - "id=" + id + - ", stars=" + stars + - ", message='" + message + '\'' + - ", photos=" + photos + - ", date=" + date + - '}'; - } -} - +package com.example.lake_catalog.model; +import org.springframework.data.annotation.Id; +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.time.LocalDate; +import java.util.List; + +@Node +public class Review { + @Id + @GeneratedValue + private Long id; + private Integer stars; + private String message; + private List photos; + private LocalDate date; + @Relationship(type = "ABOUT", direction = Relationship.Direction.OUTGOING) + private Lake lake; + @Relationship(type = "POSTED_BY", direction = Relationship.Direction.OUTGOING) + private User user; + + // Конструктор + public Review(Long id, int stars, String message, List photos, LocalDate date) { + this.id = id; + this.stars = stars; + this.message = message; + this.photos = photos; + this.date = date; + } + + public Review(){ + + } + + // Геттеры и сеттеры + public Lake getLake() { + return lake; + } + + public void setLake(Lake lake) { + this.lake = lake; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + // Геттеры и сеттеры + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public int getStars() { + return stars; + } + + public void setStars(int stars) { + this.stars = stars; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public List getPhotos() { + return photos; + } + + public void setPhotos(List photos) { + this.photos = photos; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + // Метод для вывода информации о обзоре + @Override + public String toString() { + return "Review{" + + "id=" + id + + ", stars=" + stars + + ", message='" + message + '\'' + + ", photos=" + photos + + ", date=" + date + + '}'; + } +} + diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/model/User.java b/src/main/java/com/example/lake_catalog/model/User.java similarity index 96% rename from lake-catalog/src/main/java/com/example/lake_catalog/model/User.java rename to src/main/java/com/example/lake_catalog/model/User.java index f7fc91b..8680ab6 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/model/User.java +++ b/src/main/java/com/example/lake_catalog/model/User.java @@ -1,141 +1,141 @@ -package com.example.lake_catalog.model; - -import org.springframework.data.annotation.Id; -import org.springframework.data.neo4j.core.schema.GeneratedValue; -import org.springframework.data.neo4j.core.schema.Node; -import org.springframework.data.neo4j.core.schema.Relationship; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; - -@Node -public class User { - @Id - @GeneratedValue - private Long id; - private String email; - private String password; - private String nickname; - private String photo; - private LocalDate creationDate; - private LocalDateTime editDate; - - @Relationship(type = "WANT_VISIT", direction = Relationship.Direction.OUTGOING) - private List wantVisitLakes; - - @Relationship(type = "VISITED", direction = Relationship.Direction.OUTGOING) - private List visitedLakes; - - public User(){ - - } - // Конструктор - public User(Long id, String email, String password, String nickname, String photo, LocalDate creationDate, LocalDateTime editDate) { - this.id = id; - this.email = email; - this.password = password; - this.nickname = nickname; - this.photo = photo; - this.creationDate = creationDate; - this.editDate = editDate; - } - - // Геттеры и сеттеры - public List getWantVisitLakes() { - return wantVisitLakes; - } - - public void setWantVisitLakes(List wantVisitLakes) { - this.wantVisitLakes = wantVisitLakes; - } - - public List getVisitedLakes() { - return visitedLakes; - } - - public void setVisitedLakes(List visitedLakes) { - this.visitedLakes = visitedLakes; - } - - public boolean isLakeInWantVisit(User user, Lake lake) { - return user.getWantVisitLakes().contains(lake); - } - - public boolean isLakeInVisited(User user, Lake lake) { - return user.getVisitedLakes().contains(lake); - } - - - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getNickname() { - return nickname; - } - - public void setNickname(String nickname) { - this.nickname = nickname; - } - - public String getPhoto() { - return photo; - } - - public void setPhoto(String photo) { - this.photo = photo; - } - - public LocalDate getCreationDate() { - return creationDate; - } - - public void setCreationDate(LocalDate creationDate) { - this.creationDate = creationDate; - } - - public LocalDateTime getEditDate() { - return editDate; - } - - public void setEditDate(LocalDateTime editDate) { - this.editDate = editDate; - } - - // Метод для вывода информации о пользователе - @Override - public String toString() { - return "User{" + - "id=" + id + - ", email='" + email + '\'' + - ", password='" + password + '\'' + - ", nickname='" + nickname + '\'' + - ", photo='" + photo + '\'' + - ", creationDate=" + creationDate + - ", editDate=" + editDate + - '}'; - } -} - +package com.example.lake_catalog.model; + +import org.springframework.data.annotation.Id; +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +@Node +public class User { + @Id + @GeneratedValue + private Long id; + private String email; + private String password; + private String nickname; + private String photo; + private LocalDate creationDate; + private LocalDateTime editDate; + + @Relationship(type = "WANT_VISIT", direction = Relationship.Direction.OUTGOING) + private List wantVisitLakes; + + @Relationship(type = "VISITED", direction = Relationship.Direction.OUTGOING) + private List visitedLakes; + + public User(){ + + } + // Конструктор + public User(Long id, String email, String password, String nickname, String photo, LocalDate creationDate, LocalDateTime editDate) { + this.id = id; + this.email = email; + this.password = password; + this.nickname = nickname; + this.photo = photo; + this.creationDate = creationDate; + this.editDate = editDate; + } + + // Геттеры и сеттеры + public List getWantVisitLakes() { + return wantVisitLakes; + } + + public void setWantVisitLakes(List wantVisitLakes) { + this.wantVisitLakes = wantVisitLakes; + } + + public List getVisitedLakes() { + return visitedLakes; + } + + public void setVisitedLakes(List visitedLakes) { + this.visitedLakes = visitedLakes; + } + + public boolean isLakeInWantVisit(User user, Lake lake) { + return user.getWantVisitLakes().contains(lake); + } + + public boolean isLakeInVisited(User user, Lake lake) { + return user.getVisitedLakes().contains(lake); + } + + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getPhoto() { + return photo; + } + + public void setPhoto(String photo) { + this.photo = photo; + } + + public LocalDate getCreationDate() { + return creationDate; + } + + public void setCreationDate(LocalDate creationDate) { + this.creationDate = creationDate; + } + + public LocalDateTime getEditDate() { + return editDate; + } + + public void setEditDate(LocalDateTime editDate) { + this.editDate = editDate; + } + + // Метод для вывода информации о пользователе + @Override + public String toString() { + return "User{" + + "id=" + id + + ", email='" + email + '\'' + + ", password='" + password + '\'' + + ", nickname='" + nickname + '\'' + + ", photo='" + photo + '\'' + + ", creationDate=" + creationDate + + ", editDate=" + editDate + + '}'; + } +} + diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/repository/LakeRepository.java b/src/main/java/com/example/lake_catalog/repository/LakeRepository.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/repository/LakeRepository.java rename to src/main/java/com/example/lake_catalog/repository/LakeRepository.java index 6f7c54b..30fe52f 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/repository/LakeRepository.java +++ b/src/main/java/com/example/lake_catalog/repository/LakeRepository.java @@ -1,46 +1,46 @@ -// LakeRepository.java -package com.example.lake_catalog.repository; - -import com.example.lake_catalog.model.Lake; - -import org.springframework.data.domain.Page; -import org.springframework.data.neo4j.repository.Neo4jRepository; -import org.springframework.data.neo4j.repository.query.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.data.domain.Pageable; - - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public interface LakeRepository extends Neo4jRepository { - List findByRegionAndCityAndNameAndRating(String region, String city, String name, Integer rating); - Page findByNameContainingIgnoreCase(String name, Pageable pageable); - Optional findByName(String name); - Optional findById(Long id); - - @Query("MATCH (l:Lake) RETURN DISTINCT l.region AS region") - List findAllRegions(); - - // Получение всех уникальных городов - @Query("MATCH (l:Lake) RETURN DISTINCT l.city AS city") - List findAllCities(); - - @Query("MATCH (l:Lake) WHERE (l.region IN $regions OR $regions IS NULL) AND (l.city IN $cities OR $cities IS NULL) " + - "AND (l.depth >= $minDepth AND l.depth <= $maxDepth) " + - "AND (l.square >= $minArea AND l.square <= $maxArea) RETURN l") - List filterLakes(@Param("regions") List regions, - @Param("cities") List cities, - @Param("minDepth") double minDepth, - @Param("maxDepth") double maxDepth, - @Param("minArea") double minArea, - @Param("maxArea") double maxArea); - - - @Query("MATCH (l:Lake) RETURN l") - List getAllLakes(); - -} - +// LakeRepository.java +package com.example.lake_catalog.repository; + +import com.example.lake_catalog.model.Lake; + +import org.springframework.data.domain.Page; +import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.data.domain.Pageable; + + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public interface LakeRepository extends Neo4jRepository { + List findByRegionAndCityAndNameAndRating(String region, String city, String name, Integer rating); + Page findByNameContainingIgnoreCase(String name, Pageable pageable); + Optional findByName(String name); + Optional findById(Long id); + + @Query("MATCH (l:Lake) RETURN DISTINCT l.region AS region") + List findAllRegions(); + + // Получение всех уникальных городов + @Query("MATCH (l:Lake) RETURN DISTINCT l.city AS city") + List findAllCities(); + + @Query("MATCH (l:Lake) WHERE (l.region IN $regions OR $regions IS NULL) AND (l.city IN $cities OR $cities IS NULL) " + + "AND (l.depth >= $minDepth AND l.depth <= $maxDepth) " + + "AND (l.square >= $minArea AND l.square <= $maxArea) RETURN l") + List filterLakes(@Param("regions") List regions, + @Param("cities") List cities, + @Param("minDepth") double minDepth, + @Param("maxDepth") double maxDepth, + @Param("minArea") double minArea, + @Param("maxArea") double maxArea); + + + @Query("MATCH (l:Lake) RETURN l") + List getAllLakes(); + +} + diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/repository/ReviewRepository.java b/src/main/java/com/example/lake_catalog/repository/ReviewRepository.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/repository/ReviewRepository.java rename to src/main/java/com/example/lake_catalog/repository/ReviewRepository.java index 37f5b21..54f2dd2 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/repository/ReviewRepository.java +++ b/src/main/java/com/example/lake_catalog/repository/ReviewRepository.java @@ -1,23 +1,23 @@ -// ReviewRepository.java -package com.example.lake_catalog.repository; - -import com.example.lake_catalog.model.Review; - -import org.springframework.data.neo4j.repository.Neo4jRepository; -import org.springframework.data.neo4j.repository.query.Query; -import org.springframework.data.repository.query.Param; - -import java.util.List; -import java.util.Map; - -public interface ReviewRepository extends Neo4jRepository { - @Query("MATCH (r:Review)-[:ABOUT]->(l:Lake) WHERE id(l) = $lakeId RETURN r") - List findByLakeId(Long lakeId); - - @Query("MATCH (r:Review)-[:ABOUT]->(l:Lake), (r)-[:POSTED_BY]->(u:User) " + - "WHERE id(l) = $lakeId " + - "RETURN r, u") - List> findReviewsWithUserByLakeId(Long lakeId); - - -} +// ReviewRepository.java +package com.example.lake_catalog.repository; + +import com.example.lake_catalog.model.Review; + +import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.Map; + +public interface ReviewRepository extends Neo4jRepository { + @Query("MATCH (r:Review)-[:ABOUT]->(l:Lake) WHERE id(l) = $lakeId RETURN r") + List findByLakeId(Long lakeId); + + @Query("MATCH (r:Review)-[:ABOUT]->(l:Lake), (r)-[:POSTED_BY]->(u:User) " + + "WHERE id(l) = $lakeId " + + "RETURN r, u") + List> findReviewsWithUserByLakeId(Long lakeId); + + +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/repository/UserRepository.java b/src/main/java/com/example/lake_catalog/repository/UserRepository.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/repository/UserRepository.java rename to src/main/java/com/example/lake_catalog/repository/UserRepository.java index 2cec09d..3076ce6 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/repository/UserRepository.java +++ b/src/main/java/com/example/lake_catalog/repository/UserRepository.java @@ -1,42 +1,42 @@ -// UserRepository.java -package com.example.lake_catalog.repository; - -import com.example.lake_catalog.model.Lake; -import com.example.lake_catalog.model.User; -import org.springframework.data.neo4j.repository.Neo4jRepository; -import org.springframework.data.neo4j.repository.query.Query; - -import java.util.List; -import java.util.Optional; - -public interface UserRepository extends Neo4jRepository { - Optional findByEmail(String email); // для поиска пользователя по email - @Query("MATCH (r:Review)-[:POSTED_BY]->(u:User) WHERE id(r) = $reviewId RETURN u") - User findUserByReviewId(Long reviewId); - Optional findById(Long id); - // Проверка, есть ли озеро в списке "хочу посетить" - @Query("MATCH (u:User)-[:WANT_VISIT]->(l:Lake) WHERE ID(u) = $userId AND ID(l) = $lakeId RETURN COUNT(l) > 0") - boolean existsByIdAndWantVisitLakesContains(Long userId, Long lakeId); - - // Проверка, есть ли озеро в списке "уже посетил" - @Query("MATCH (u:User)-[:VISITED]->(l:Lake) WHERE ID(u) = $userId AND ID(l) = $lakeId RETURN COUNT(l) > 0") - boolean existsByIdAndVisitedLakesContains(Long userId, Long lakeId); - - // Удаление связи "хочу посетить" - @Query("MATCH (u:User)-[r:WANT_VISIT]->(l:Lake) WHERE ID(u) = $userId AND ID(l) = $lakeId DELETE r") - void removeWantVisitLake(Long userId, Long lakeId); - - // Удаление связи "уже посетил" - @Query("MATCH (u:User)-[r:VISITED]->(l:Lake) WHERE ID(u) = $userId AND ID(l) = $lakeId DELETE r") - void removeVisitedLake(Long userId, Long lakeId); - - @Query("MATCH (u:User)-[:WANT_VISIT]->(l:Lake) WHERE ID(u) = $userId RETURN l") - List findWantVisitLakes(Long userId); - - @Query("MATCH (u:User)-[:VISITED]->(l:Lake) WHERE ID(u) = $userId RETURN l") - List findVisitedLakes(Long userId); - - - - -} +// UserRepository.java +package com.example.lake_catalog.repository; + +import com.example.lake_catalog.model.Lake; +import com.example.lake_catalog.model.User; +import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.springframework.data.neo4j.repository.query.Query; + +import java.util.List; +import java.util.Optional; + +public interface UserRepository extends Neo4jRepository { + Optional findByEmail(String email); // для поиска пользователя по email + @Query("MATCH (r:Review)-[:POSTED_BY]->(u:User) WHERE id(r) = $reviewId RETURN u") + User findUserByReviewId(Long reviewId); + Optional findById(Long id); + // Проверка, есть ли озеро в списке "хочу посетить" + @Query("MATCH (u:User)-[:WANT_VISIT]->(l:Lake) WHERE ID(u) = $userId AND ID(l) = $lakeId RETURN COUNT(l) > 0") + boolean existsByIdAndWantVisitLakesContains(Long userId, Long lakeId); + + // Проверка, есть ли озеро в списке "уже посетил" + @Query("MATCH (u:User)-[:VISITED]->(l:Lake) WHERE ID(u) = $userId AND ID(l) = $lakeId RETURN COUNT(l) > 0") + boolean existsByIdAndVisitedLakesContains(Long userId, Long lakeId); + + // Удаление связи "хочу посетить" + @Query("MATCH (u:User)-[r:WANT_VISIT]->(l:Lake) WHERE ID(u) = $userId AND ID(l) = $lakeId DELETE r") + void removeWantVisitLake(Long userId, Long lakeId); + + // Удаление связи "уже посетил" + @Query("MATCH (u:User)-[r:VISITED]->(l:Lake) WHERE ID(u) = $userId AND ID(l) = $lakeId DELETE r") + void removeVisitedLake(Long userId, Long lakeId); + + @Query("MATCH (u:User)-[:WANT_VISIT]->(l:Lake) WHERE ID(u) = $userId RETURN l") + List findWantVisitLakes(Long userId); + + @Query("MATCH (u:User)-[:VISITED]->(l:Lake) WHERE ID(u) = $userId RETURN l") + List findVisitedLakes(Long userId); + + + + +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/service/AuthService.java b/src/main/java/com/example/lake_catalog/service/AuthService.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/service/AuthService.java rename to src/main/java/com/example/lake_catalog/service/AuthService.java index 65c4747..1b0c2e3 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/service/AuthService.java +++ b/src/main/java/com/example/lake_catalog/service/AuthService.java @@ -1,54 +1,54 @@ -package com.example.lake_catalog.service; - -import com.example.lake_catalog.model.User; -import com.example.lake_catalog.repository.LakeRepository; -import com.example.lake_catalog.repository.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.time.LocalDate; -import java.util.Date; -import java.util.Optional; - -@Service -public class AuthService { - private final UserRepository userRepository; - private final UserService userService; - - - @Autowired - public AuthService(UserRepository userRepository, UserService userService) { - this.userRepository = userRepository; - this.userService = userService; - } - - public void registerUser(String nickname, String email, String password) { - if (userService.findUserByEmail(email).isPresent()) - throw new RuntimeException("Пользователь с такой почтой уже существует!"); - User user = new User(); - user.setEmail(email); - user.setNickname(nickname); - user.setPassword(password); - user.setPhoto("https://gimnaziya23saransk-r13.gosweb.gosuslugi.ru/netcat_files/8/168/1663871865_44_top_fon_com_p_serii_fon_tik_tok_foto_50.jpg"); - user.setCreationDate(LocalDate.now()); - userRepository.save(user); - //setCurrentUser(userService.findUserByEmail(email).get()); - } - - public User loginUser(String email, String password){ - System.out.println(email); - System.out.println(password); - if (email == null || email.isEmpty() || password == null || password.isEmpty()) { - throw new RuntimeException("Введите email и пароль."); - } - Optional optionalUser = userService.findUserByEmail(email); - if (optionalUser.isEmpty()) - throw new RuntimeException("Пользователя с такой почтой не существует!"); - User user = optionalUser.get(); - if (!user.getPassword().equals(password)){ - throw new RuntimeException("Неверный пароль!"); - } - //setCurrentUser(user); - return user; - } -} +package com.example.lake_catalog.service; + +import com.example.lake_catalog.model.User; +import com.example.lake_catalog.repository.LakeRepository; +import com.example.lake_catalog.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.Date; +import java.util.Optional; + +@Service +public class AuthService { + private final UserRepository userRepository; + private final UserService userService; + + + @Autowired + public AuthService(UserRepository userRepository, UserService userService) { + this.userRepository = userRepository; + this.userService = userService; + } + + public void registerUser(String nickname, String email, String password) { + if (userService.findUserByEmail(email).isPresent()) + throw new RuntimeException("Пользователь с такой почтой уже существует!"); + User user = new User(); + user.setEmail(email); + user.setNickname(nickname); + user.setPassword(password); + user.setPhoto("https://gimnaziya23saransk-r13.gosweb.gosuslugi.ru/netcat_files/8/168/1663871865_44_top_fon_com_p_serii_fon_tik_tok_foto_50.jpg"); + user.setCreationDate(LocalDate.now()); + userRepository.save(user); + //setCurrentUser(userService.findUserByEmail(email).get()); + } + + public User loginUser(String email, String password){ + System.out.println(email); + System.out.println(password); + if (email == null || email.isEmpty() || password == null || password.isEmpty()) { + throw new RuntimeException("Введите email и пароль."); + } + Optional optionalUser = userService.findUserByEmail(email); + if (optionalUser.isEmpty()) + throw new RuntimeException("Пользователя с такой почтой не существует!"); + User user = optionalUser.get(); + if (!user.getPassword().equals(password)){ + throw new RuntimeException("Неверный пароль!"); + } + //setCurrentUser(user); + return user; + } +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/service/LakeService.java b/src/main/java/com/example/lake_catalog/service/LakeService.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/service/LakeService.java rename to src/main/java/com/example/lake_catalog/service/LakeService.java index ad6ce61..85d3047 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/service/LakeService.java +++ b/src/main/java/com/example/lake_catalog/service/LakeService.java @@ -1,214 +1,214 @@ -// LakeService.java -package com.example.lake_catalog.service; - -import com.example.lake_catalog.model.Lake; -import com.example.lake_catalog.repository.LakeRepository; -import com.example.lake_catalog.repository.UserRepository; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.stereotype.Service; -import com.example.lake_catalog.model.User; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.Arrays; - - -@Service -public class LakeService { - - private final LakeRepository lakeRepository; - - @Autowired - public LakeService(LakeRepository lakeRepository) { - this.lakeRepository = lakeRepository; - } - - - public List findByRegionAndCityAndNameAndRating(String region, String city, String name, Integer rating) { - return lakeRepository.findByRegionAndCityAndNameAndRating(region, city, name, rating); - } - - public List findAll() { - return lakeRepository.findAll(); - } - - public Page findLakesByNameWithPagination(String name, int page, int pageSize) { - return lakeRepository.findByNameContainingIgnoreCase(name, PageRequest.of(page, pageSize)); - } - - - public Page findLakesWithPagination(int page, int pageSize) { - return lakeRepository.findAll(PageRequest.of(page, pageSize)); - } - - public void initialize() { - ObjectMapper objectMapper = new ObjectMapper(); - try { - // Чтение JSON файла - JsonNode lakesArray = objectMapper.readTree(new File("/app/data/records.json")); - for (JsonNode lakeJson : lakesArray) { - // Извлечение основных данных - JsonNode tagsNode = lakeJson.path("l").path("properties"); - String name = tagsNode.path("name").asText(null); - if (name == null || name.isEmpty()) { - System.out.println("Пропуск объекта без имени."); - continue; // Пропускаем объекты без имени - } - - // Установка значений с учётом значений по умолчанию - String region = tagsNode.path("region").asText("Не указан"); - String city = tagsNode.path("city").asText("Не указан"); - Double rating = tagsNode.path("rating").asDouble(0.0); - Double depth = tagsNode.path("depth").asDouble(0.0); - Double square = tagsNode.path("square").asDouble(0.0); - - // Извлечение списка фотографий с проверкой - List photos = new ArrayList<>(); - JsonNode photosNode = tagsNode.path("photos"); - if (photosNode.isArray()) { - for (JsonNode photo : photosNode) { - photos.add(photo.asText()); - } - } else { - photos.add("https://cdn1.ozone.ru/s3/multimedia-1-z/6980409107.jpg"); // Значение по умолчанию - } - - String description = tagsNode.path("description").asText("Описание отсутствует"); - - // Проверка существования озера в базе - Optional optionalLake = lakeRepository.findByName(name); - if (optionalLake.isPresent()) { - Lake lake = optionalLake.get(); - lake.setRegion(region); - lake.setCity(city); - lake.setRating(rating); - lake.setDepth(depth); - lake.setSquare(square); - lake.setPhotos(photos); - lake.setDescription(description); - lakeRepository.save(lake); // Обновление существующего озера - System.out.println("Обновлено озеро: " + name); - } else { - Lake newLake = new Lake(); - newLake.setName(name); - newLake.setRegion(region); - newLake.setCity(city); - newLake.setRating(rating); - newLake.setDepth(depth); - newLake.setSquare(square); - newLake.setPhotos(photos); - newLake.setDescription(description); - lakeRepository.save(newLake); // Добавление нового озера - System.out.println("Добавлено озеро: " + name); - } - } - } catch (IOException e) { - throw new RuntimeException("Ошибка чтения файла records.json: " + e.getMessage(), e); - } - } - - List getAllLakes() { - return lakeRepository.getAllLakes(); - } - - - public Optional findLakeById(Long id) { - return lakeRepository.findById(id); - } - - - public void addLake(Lake lake) { - lakeRepository.save(lake); - } - - public Page filterLakes(String name, String depth, String square, String city, String region, String rating, int page, int pageSize) { - List lakes = lakeRepository.findAll(); // Загружаем все озера - - // Фильтруем по имени, если указано - if (name != null && !name.isEmpty()) { - lakes = lakes.stream() - .filter(lake -> lake.getName().toLowerCase().contains(name.toLowerCase())) - .collect(Collectors.toList()); - } - - // Фильтруем по глубине, если указано - if (depth != null && !depth.isEmpty()) { - double depthValue = Double.parseDouble(depth); // Преобразуем глубину в число - lakes = lakes.stream() - .filter(lake -> lake.getDepth() <= depthValue) - .collect(Collectors.toList()); - } - - // Фильтруем по площади, если указано - if (square != null && !square.isEmpty()) { - double squareValue = Double.parseDouble(square); // Преобразуем площадь в число - lakes = lakes.stream() - .filter(lake -> lake.getSquare() <= squareValue) - .collect(Collectors.toList()); - } - - if (city != null && !city.isEmpty()) { - List cities = Arrays.asList(city.split(",")); - lakes = lakes.stream() - .filter(lake -> lake.getCity() != null && - cities.stream().anyMatch(c -> lake.getCity().contains(c))) - .collect(Collectors.toList()); - } - - if (region != null && !region.isEmpty()) { - List regions = Arrays.asList(region.split(",")); - lakes = lakes.stream() - .filter(lake -> lake.getRegion() != null && - regions.stream().anyMatch(r -> lake.getRegion().contains(r))) - .collect(Collectors.toList()); - } - - if (rating != null && !rating.isEmpty()) { - List ratings = Arrays.stream(rating.split(",")) - .map(r -> { - switch (r) { - case "withoutRating": - return 0; - case "rating1": - return 1; - case "rating2": - return 2; - case "rating3": - return 3; - case "rating4": - return 4; - case "rating5": - return 5; - default: - throw new IllegalArgumentException("Invalid rating: " + r); - } - }) - .collect(Collectors.toList()); - lakes = lakes.stream() - .filter(lake -> - ratings.contains((int)Math.round(lake.getRating()))) - .collect(Collectors.toList()); - } - - // Пагинация - int start = page * pageSize; - int end = Math.min(start + pageSize, lakes.size()); - - // Проверяем на выход за пределы - if (start > lakes.size()) { - return Page.empty(); // Возвращаем пустую страницу - } - - return new PageImpl<>(lakes.subList(start, end), PageRequest.of(page, pageSize), lakes.size()); - } - -} +// LakeService.java +package com.example.lake_catalog.service; + +import com.example.lake_catalog.model.Lake; +import com.example.lake_catalog.repository.LakeRepository; +import com.example.lake_catalog.repository.UserRepository; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import com.example.lake_catalog.model.User; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.Arrays; + + +@Service +public class LakeService { + + private final LakeRepository lakeRepository; + + @Autowired + public LakeService(LakeRepository lakeRepository) { + this.lakeRepository = lakeRepository; + } + + + public List findByRegionAndCityAndNameAndRating(String region, String city, String name, Integer rating) { + return lakeRepository.findByRegionAndCityAndNameAndRating(region, city, name, rating); + } + + public List findAll() { + return lakeRepository.findAll(); + } + + public Page findLakesByNameWithPagination(String name, int page, int pageSize) { + return lakeRepository.findByNameContainingIgnoreCase(name, PageRequest.of(page, pageSize)); + } + + + public Page findLakesWithPagination(int page, int pageSize) { + return lakeRepository.findAll(PageRequest.of(page, pageSize)); + } + + public void initialize() { + ObjectMapper objectMapper = new ObjectMapper(); + try { + // Чтение JSON файла + JsonNode lakesArray = objectMapper.readTree(new File("/app/data/records.json")); + for (JsonNode lakeJson : lakesArray) { + // Извлечение основных данных + JsonNode tagsNode = lakeJson.path("l").path("properties"); + String name = tagsNode.path("name").asText(null); + if (name == null || name.isEmpty()) { + System.out.println("Пропуск объекта без имени."); + continue; // Пропускаем объекты без имени + } + + // Установка значений с учётом значений по умолчанию + String region = tagsNode.path("region").asText("Не указан"); + String city = tagsNode.path("city").asText("Не указан"); + Double rating = tagsNode.path("rating").asDouble(0.0); + Double depth = tagsNode.path("depth").asDouble(0.0); + Double square = tagsNode.path("square").asDouble(0.0); + + // Извлечение списка фотографий с проверкой + List photos = new ArrayList<>(); + JsonNode photosNode = tagsNode.path("photos"); + if (photosNode.isArray()) { + for (JsonNode photo : photosNode) { + photos.add(photo.asText()); + } + } else { + photos.add("https://cdn1.ozone.ru/s3/multimedia-1-z/6980409107.jpg"); // Значение по умолчанию + } + + String description = tagsNode.path("description").asText("Описание отсутствует"); + + // Проверка существования озера в базе + Optional optionalLake = lakeRepository.findByName(name); + if (optionalLake.isPresent()) { + Lake lake = optionalLake.get(); + lake.setRegion(region); + lake.setCity(city); + lake.setRating(rating); + lake.setDepth(depth); + lake.setSquare(square); + lake.setPhotos(photos); + lake.setDescription(description); + lakeRepository.save(lake); // Обновление существующего озера + System.out.println("Обновлено озеро: " + name); + } else { + Lake newLake = new Lake(); + newLake.setName(name); + newLake.setRegion(region); + newLake.setCity(city); + newLake.setRating(rating); + newLake.setDepth(depth); + newLake.setSquare(square); + newLake.setPhotos(photos); + newLake.setDescription(description); + lakeRepository.save(newLake); // Добавление нового озера + System.out.println("Добавлено озеро: " + name); + } + } + } catch (IOException e) { + throw new RuntimeException("Ошибка чтения файла records.json: " + e.getMessage(), e); + } + } + + List getAllLakes() { + return lakeRepository.getAllLakes(); + } + + + public Optional findLakeById(Long id) { + return lakeRepository.findById(id); + } + + + public void addLake(Lake lake) { + lakeRepository.save(lake); + } + + public Page filterLakes(String name, String depth, String square, String city, String region, String rating, int page, int pageSize) { + List lakes = lakeRepository.findAll(); // Загружаем все озера + + // Фильтруем по имени, если указано + if (name != null && !name.isEmpty()) { + lakes = lakes.stream() + .filter(lake -> lake.getName().toLowerCase().contains(name.toLowerCase())) + .collect(Collectors.toList()); + } + + // Фильтруем по глубине, если указано + if (depth != null && !depth.isEmpty()) { + double depthValue = Double.parseDouble(depth); // Преобразуем глубину в число + lakes = lakes.stream() + .filter(lake -> lake.getDepth() <= depthValue) + .collect(Collectors.toList()); + } + + // Фильтруем по площади, если указано + if (square != null && !square.isEmpty()) { + double squareValue = Double.parseDouble(square); // Преобразуем площадь в число + lakes = lakes.stream() + .filter(lake -> lake.getSquare() <= squareValue) + .collect(Collectors.toList()); + } + + if (city != null && !city.isEmpty()) { + List cities = Arrays.asList(city.split(",")); + lakes = lakes.stream() + .filter(lake -> lake.getCity() != null && + cities.stream().anyMatch(c -> lake.getCity().contains(c))) + .collect(Collectors.toList()); + } + + if (region != null && !region.isEmpty()) { + List regions = Arrays.asList(region.split(",")); + lakes = lakes.stream() + .filter(lake -> lake.getRegion() != null && + regions.stream().anyMatch(r -> lake.getRegion().contains(r))) + .collect(Collectors.toList()); + } + + if (rating != null && !rating.isEmpty()) { + List ratings = Arrays.stream(rating.split(",")) + .map(r -> { + switch (r) { + case "withoutRating": + return 0; + case "rating1": + return 1; + case "rating2": + return 2; + case "rating3": + return 3; + case "rating4": + return 4; + case "rating5": + return 5; + default: + throw new IllegalArgumentException("Invalid rating: " + r); + } + }) + .collect(Collectors.toList()); + lakes = lakes.stream() + .filter(lake -> + ratings.contains((int)Math.round(lake.getRating()))) + .collect(Collectors.toList()); + } + + // Пагинация + int start = page * pageSize; + int end = Math.min(start + pageSize, lakes.size()); + + // Проверяем на выход за пределы + if (start > lakes.size()) { + return Page.empty(); // Возвращаем пустую страницу + } + + return new PageImpl<>(lakes.subList(start, end), PageRequest.of(page, pageSize), lakes.size()); + } + +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/service/ReviewService.java b/src/main/java/com/example/lake_catalog/service/ReviewService.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/service/ReviewService.java rename to src/main/java/com/example/lake_catalog/service/ReviewService.java index bf2bb22..cb8d120 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/service/ReviewService.java +++ b/src/main/java/com/example/lake_catalog/service/ReviewService.java @@ -1,79 +1,79 @@ -package com.example.lake_catalog.service; -import com.example.lake_catalog.repository.*; -import com.example.lake_catalog.model.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import java.time.LocalDate; - -@Service -public class ReviewService { - - @Autowired - private ReviewRepository reviewRepository; - - @Autowired - private LakeRepository lakeRepository; - - @Autowired - private UserRepository userRepository; - - private static final Logger logger = LoggerFactory.getLogger(ReviewService.class); - - - public Review addReview(Long lakeId, Long userId, String text, Integer rating) { - // Получаем озеро и пользователя - Lake lake = lakeRepository.findById(lakeId).orElseThrow(() -> new RuntimeException("Lake not found")); - User user = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found")); - logger.info("user", user.getEmail()); - logger.info("lake", lake.getName()); - // Создаем новый отзыв - Review review = new Review(); - review.setLake(lake); - review.setUser(user); - review.setStars(rating); - review.setMessage(text); - review.setDate(LocalDate.now()); - Review save_review = reviewRepository.save(review); - updateLakeRating(lake); - return save_review; - - } - - public List getReviewsForLake(Long lakeId) { - // Получаем все отзывы для озера - List reviews = reviewRepository.findByLakeId(lakeId); - - // Для каждого отзыва получаем пользователя и связываем с отзывом - for (Review review : reviews) { - User user = userRepository.findUserByReviewId(review.getId()); - review.setUser(user); - } - - return reviews; - } - - private void updateLakeRating(Lake lake) { - // Получаем все отзывы для озера - List reviews = reviewRepository.findByLakeId(lake.getId()); - - // Считаем средний рейтинг - Double averageRating = reviews.stream() - .mapToInt(Review::getStars) - .average() - .orElse(0.0); - - // Обновляем рейтинг озера - lake.setRating(averageRating); - lakeRepository.save(lake); - } - -} +package com.example.lake_catalog.service; +import com.example.lake_catalog.repository.*; +import com.example.lake_catalog.model.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.time.LocalDate; + +@Service +public class ReviewService { + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private LakeRepository lakeRepository; + + @Autowired + private UserRepository userRepository; + + private static final Logger logger = LoggerFactory.getLogger(ReviewService.class); + + + public Review addReview(Long lakeId, Long userId, String text, Integer rating) { + // Получаем озеро и пользователя + Lake lake = lakeRepository.findById(lakeId).orElseThrow(() -> new RuntimeException("Lake not found")); + User user = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found")); + logger.info("user", user.getEmail()); + logger.info("lake", lake.getName()); + // Создаем новый отзыв + Review review = new Review(); + review.setLake(lake); + review.setUser(user); + review.setStars(rating); + review.setMessage(text); + review.setDate(LocalDate.now()); + Review save_review = reviewRepository.save(review); + updateLakeRating(lake); + return save_review; + + } + + public List getReviewsForLake(Long lakeId) { + // Получаем все отзывы для озера + List reviews = reviewRepository.findByLakeId(lakeId); + + // Для каждого отзыва получаем пользователя и связываем с отзывом + for (Review review : reviews) { + User user = userRepository.findUserByReviewId(review.getId()); + review.setUser(user); + } + + return reviews; + } + + private void updateLakeRating(Lake lake) { + // Получаем все отзывы для озера + List reviews = reviewRepository.findByLakeId(lake.getId()); + + // Считаем средний рейтинг + Double averageRating = reviews.stream() + .mapToInt(Review::getStars) + .average() + .orElse(0.0); + + // Обновляем рейтинг озера + lake.setRating(averageRating); + lakeRepository.save(lake); + } + +} diff --git a/lake-catalog/src/main/java/com/example/lake_catalog/service/UserService.java b/src/main/java/com/example/lake_catalog/service/UserService.java similarity index 97% rename from lake-catalog/src/main/java/com/example/lake_catalog/service/UserService.java rename to src/main/java/com/example/lake_catalog/service/UserService.java index 789f681..2de493f 100644 --- a/lake-catalog/src/main/java/com/example/lake_catalog/service/UserService.java +++ b/src/main/java/com/example/lake_catalog/service/UserService.java @@ -1,124 +1,124 @@ -package com.example.lake_catalog.service; - -import com.example.lake_catalog.model.Lake; -import com.example.lake_catalog.model.User; -import com.example.lake_catalog.repository.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.web.bind.annotation.GetMapping; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; - -@Service -public class UserService { - private final UserRepository userRepository; - - @Autowired - public UserService(UserRepository userRepository) { - this.userRepository = userRepository; - } - public Optional findUserByEmail(String email){ - return userRepository.findByEmail(email); - } - - public Optional findUserById(Long id) { - return userRepository.findById(id); - } - - public void addWantVisitLake(Long userId, Lake lake) { - Optional userOptional = userRepository.findById(userId); - if (userOptional.isPresent()) { - User user = userOptional.get(); - user.getWantVisitLakes().add(lake); - userRepository.save(user); - } - } - - public void addVisitedLake(Long userId, Lake lake) { - Optional userOptional = userRepository.findById(userId); - if (userOptional.isPresent()) { - User user = userOptional.get(); - user.getVisitedLakes().add(lake); - userRepository.save(user); - } - } - - public void addWantVisitLake(User user, Lake lake) { - user.getWantVisitLakes().add(lake); - userRepository.save(user); - } - - // public void removeWantVisitLake(User user, Lake lake) { - // user.getWantVisitLakes().remove(lake); - // userRepository.save(user); - // } - - public void addVisitedLake(User user, Lake lake) { - user.getVisitedLakes().add(lake); - userRepository.save(user); - } - - // public void removeVisitedLake(User user, Lake lake) { - // user.getVisitedLakes().remove(lake); - // userRepository.save(user); - // } - - public boolean isEmailUnique(String email) { - Optional user = userRepository.findByEmail(email); - return !user.isPresent(); - } - - public void updateUserProfile(Long userId, String newName, String newEmail) { - - // Ищем пользователя по id - User user = userRepository.findById(userId) - .orElseThrow(() -> new IllegalArgumentException("Пользователь не найден")); - - // Проверяем уникальность email - if (!newEmail.equals(user.getEmail())){ - if (!isEmailUnique(newEmail)) { - throw new IllegalArgumentException("Этот email уже занят."); - } - } - - // Обновляем имя и email - user.setNickname(newName); - user.setEmail(newEmail); - user.setEditDate(LocalDateTime.now()); - - // Сохраняем изменения - userRepository.save(user); - } - - // Проверка, есть ли озеро в списке "хочу посетить" - public boolean isLakeInWantVisit(Long userId, Long lakeId) { - return userRepository.existsByIdAndWantVisitLakesContains(userId, lakeId); - } - - // Проверка, есть ли озеро в списке "уже посетил" - public boolean isLakeInVisited(Long userId, Long lakeId) { - return userRepository.existsByIdAndVisitedLakesContains(userId, lakeId); - } - - public void removeWantVisitLake(Long userId, Long lakeId) { - userRepository.removeWantVisitLake(userId, lakeId); - } - - public void removeVisitedLake(Long userId, Long lakeId) { - userRepository.removeVisitedLake(userId, lakeId); - } - - public List getWantVisitLakes(Long userId) { - List lakes = userRepository.findWantVisitLakes(userId); - return lakes != null ? lakes : List.of(); // Возвращаем пустой список вместо null - } - - public List getVisitedLakes(Long userId) { - List lakes = userRepository.findVisitedLakes(userId); - return lakes != null ? lakes : List.of(); // Возвращаем пустой список вместо null - } - - -} +package com.example.lake_catalog.service; + +import com.example.lake_catalog.model.Lake; +import com.example.lake_catalog.model.User; +import com.example.lake_catalog.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.GetMapping; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Service +public class UserService { + private final UserRepository userRepository; + + @Autowired + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + public Optional findUserByEmail(String email){ + return userRepository.findByEmail(email); + } + + public Optional findUserById(Long id) { + return userRepository.findById(id); + } + + public void addWantVisitLake(Long userId, Lake lake) { + Optional userOptional = userRepository.findById(userId); + if (userOptional.isPresent()) { + User user = userOptional.get(); + user.getWantVisitLakes().add(lake); + userRepository.save(user); + } + } + + public void addVisitedLake(Long userId, Lake lake) { + Optional userOptional = userRepository.findById(userId); + if (userOptional.isPresent()) { + User user = userOptional.get(); + user.getVisitedLakes().add(lake); + userRepository.save(user); + } + } + + public void addWantVisitLake(User user, Lake lake) { + user.getWantVisitLakes().add(lake); + userRepository.save(user); + } + + // public void removeWantVisitLake(User user, Lake lake) { + // user.getWantVisitLakes().remove(lake); + // userRepository.save(user); + // } + + public void addVisitedLake(User user, Lake lake) { + user.getVisitedLakes().add(lake); + userRepository.save(user); + } + + // public void removeVisitedLake(User user, Lake lake) { + // user.getVisitedLakes().remove(lake); + // userRepository.save(user); + // } + + public boolean isEmailUnique(String email) { + Optional user = userRepository.findByEmail(email); + return !user.isPresent(); + } + + public void updateUserProfile(Long userId, String newName, String newEmail) { + + // Ищем пользователя по id + User user = userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("Пользователь не найден")); + + // Проверяем уникальность email + if (!newEmail.equals(user.getEmail())){ + if (!isEmailUnique(newEmail)) { + throw new IllegalArgumentException("Этот email уже занят."); + } + } + + // Обновляем имя и email + user.setNickname(newName); + user.setEmail(newEmail); + user.setEditDate(LocalDateTime.now()); + + // Сохраняем изменения + userRepository.save(user); + } + + // Проверка, есть ли озеро в списке "хочу посетить" + public boolean isLakeInWantVisit(Long userId, Long lakeId) { + return userRepository.existsByIdAndWantVisitLakesContains(userId, lakeId); + } + + // Проверка, есть ли озеро в списке "уже посетил" + public boolean isLakeInVisited(Long userId, Long lakeId) { + return userRepository.existsByIdAndVisitedLakesContains(userId, lakeId); + } + + public void removeWantVisitLake(Long userId, Long lakeId) { + userRepository.removeWantVisitLake(userId, lakeId); + } + + public void removeVisitedLake(Long userId, Long lakeId) { + userRepository.removeVisitedLake(userId, lakeId); + } + + public List getWantVisitLakes(Long userId) { + List lakes = userRepository.findWantVisitLakes(userId); + return lakes != null ? lakes : List.of(); // Возвращаем пустой список вместо null + } + + public List getVisitedLakes(Long userId) { + List lakes = userRepository.findVisitedLakes(userId); + return lakes != null ? lakes : List.of(); // Возвращаем пустой список вместо null + } + + +} diff --git a/lake-catalog/src/main/resources/application.properties b/src/main/resources/application.properties similarity index 97% rename from lake-catalog/src/main/resources/application.properties rename to src/main/resources/application.properties index df4823f..ad53c1a 100644 --- a/lake-catalog/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,6 @@ -spring.application.name=lake-catalog - -spring.neo4j.uri=neo4j://127.0.0.1::7687 -spring.neo4j.authentication.username=neo4j -spring.neo4j.authentication.password=neo4jneo4j +spring.application.name=lake-catalog + +spring.neo4j.uri=neo4j://127.0.0.1::7687 +spring.neo4j.authentication.username=neo4j +spring.neo4j.authentication.password=neo4jneo4j server.servlet.encoding.charset=UTF-8 \ No newline at end of file diff --git a/lake-catalog/src/main/resources/static/JavaScript/card_script.js b/src/main/resources/static/JavaScript/card_script.js similarity index 97% rename from lake-catalog/src/main/resources/static/JavaScript/card_script.js rename to src/main/resources/static/JavaScript/card_script.js index 19949ae..877f658 100644 --- a/lake-catalog/src/main/resources/static/JavaScript/card_script.js +++ b/src/main/resources/static/JavaScript/card_script.js @@ -1,368 +1,368 @@ -// Простой слайдер изображений с использованием стрелок -const images = document.querySelectorAll('.slider-image'); -const leftArrow = document.querySelector('.left-arrow'); -const rightArrow = document.querySelector('.right-arrow'); -let currentIndex = 0; - -function showImage(index) { - images.forEach((img, i) => { - img.style.display = i === index ? 'block' : 'none'; - }); -} - -leftArrow.addEventListener('click', () => { - currentIndex = (currentIndex - 1 + images.length) % images.length; - showImage(currentIndex); -}); - -rightArrow.addEventListener('click', () => { - currentIndex = (currentIndex + 1) % images.length; - showImage(currentIndex); -}); - -showImage(currentIndex); - -// Обработчик кнопки "На главную" -const returnButton = document.getElementById("back-button"); -returnButton.addEventListener("click", () => { - window.history.back(); -}); - -// Обработчики для кнопок "хочу посетить" и "уже посетил" -const heartButton = document.getElementById('heart-button'); -const heartIcon = document.getElementById('heart-icon'); -let isHeartFilled = false; - -// document.addEventListener('DOMContentLoaded', () => { -// const pathParts = window.location.pathname.split('/'); -// const lakeId = pathParts[pathParts.length - 1]; - -// fetch(`/lakes/${lakeId}/status`) // На сервере сделайте API для получения текущего состояния -// .then(response => response.json()) -// .then(data => { -// isHeartFilled = data.isWantVisit; // Примерный формат данных от сервера -// heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; -// }) -// .catch(error => console.error('Ошибка при получении состояния:', error)); -// }); - -// heartButton.addEventListener('click', () => { -// const pathParts = window.location.pathname.split('/'); -// const lakeId = pathParts[pathParts.length - 1]; - -// fetch('/check-auth', { method: 'GET' }) -// .then(response => response.json()) -// .then(data => { -// if (!data.authenticated) { -// alert('Вы должны войти в систему, чтобы добавить в "Хочу посетить".'); -// return; -// } - -// isHeartFilled = !isHeartFilled; -// heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; - -// fetch(`/lakes/${lakeId}/action`, { -// method: 'POST', -// headers: { 'Content-Type': 'application/json' }, -// body: JSON.stringify({ action: isHeartFilled ? 'want_visit' : 'remove_want_visit' }), -// }) -// .then(response => response.json()) -// .then(data => alert(data.message)) -// .catch(error => console.error('Ошибка:', error)); -// }); -// }); - -const eyeButton = document.getElementById('eye-button'); -const eyeIcon = document.getElementById('eye-icon'); -let isEyeFilled = false; - -// eyeButton.addEventListener('click', () => { -// const pathParts = window.location.pathname.split('/'); -// const lakeId = pathParts[pathParts.length - 1]; - -// fetch('/check-auth', { method: 'GET' }) -// .then(response => response.json()) -// .then(data => { -// if (!data.authenticated) { -// alert('Вы должны войти в систему, чтобы добавить в "Уже посетил".'); -// return; -// } - -// isEyeFilled = !isEyeFilled; -// eyeIcon.src = isEyeFilled ? '/assets/eye_filled.png' : '/assets/eye.png'; - -// fetch(`/lakes/${lakeId}/action`, { -// method: 'POST', -// headers: { 'Content-Type': 'application/json' }, -// body: JSON.stringify({ action: isEyeFilled ? 'visited' : 'remove_visited' }), -// }) -// .then(response => response.json()) -// .then(data => alert(data.message)) -// .catch(error => console.error('Ошибка:', error)); -// }); -// }); - -document.addEventListener('DOMContentLoaded', () => { - const pathParts = window.location.pathname.split('/'); - const lakeId = pathParts[pathParts.length - 1]; - - // Запрос состояния озера (хочет ли посетить и уже посетил) - fetch(`/lakes/${lakeId}/status`) - .then(response => response.json()) - .then(data => { - isHeartFilled = data.isWantVisit; // Статус кнопки "хочу посетить" - isEyeFilled = data.isVisited; // Статус кнопки "уже посетил" - - // Обновляем иконки на странице в зависимости от состояния - heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; - eyeIcon.src = isEyeFilled ? '/assets/eye_filled.png' : '/assets/eye.png'; - }) - .catch(error => console.error('Ошибка при получении состояния:', error)); -}); - -heartButton.addEventListener('click', () => { - const pathParts = window.location.pathname.split('/'); - const lakeId = pathParts[pathParts.length - 1]; - - fetch('/check-auth', { method: 'GET' }) - .then(response => response.json()) - .then(data => { - if (!data.authenticated) { - alert('Вы должны войти в систему, чтобы добавить в "Хочу посетить".'); - return; - } - - isHeartFilled = !isHeartFilled; - heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; - - fetch(`/lakes/${lakeId}/action`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ action: isHeartFilled ? 'want_visit' : 'remove_want_visit' }), - }) - .then(response => response.json()) - .then(data => { - alert(data.message); - // После выполнения действия обновляем состояние на сервере - fetch(`/lakes/${lakeId}/status`) - .then(response => response.json()) - .then(data => { - // Обновляем иконки после успешного действия - heartIcon.src = data.isWantVisit ? '/assets/heart_filled.png' : '/assets/heart.png'; - }); - }) - .catch(error => console.error('Ошибка:', error)); - }); -}); - -eyeButton.addEventListener('click', () => { - const pathParts = window.location.pathname.split('/'); - const lakeId = pathParts[pathParts.length - 1]; - - fetch('/check-auth', { method: 'GET' }) - .then(response => response.json()) - .then(data => { - if (!data.authenticated) { - alert('Вы должны войти в систему, чтобы добавить в "Уже посетил".'); - return; - } - - isEyeFilled = !isEyeFilled; - eyeIcon.src = isEyeFilled ? '/assets/eye_filled.png' : '/assets/eye.png'; - - fetch(`/lakes/${lakeId}/action`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ action: isEyeFilled ? 'visited' : 'remove_visited' }), - }) - .then(response => response.json()) - .then(data => { - alert(data.message); - // После выполнения действия обновляем состояние на сервере - fetch(`/lakes/${lakeId}/status`) - .then(response => response.json()) - .then(data => { - // Обновляем иконки после успешного действия - eyeIcon.src = data.isVisited ? '/assets/eye_filled.png' : '/assets/eye.png'; - }); - }) - .catch(error => console.error('Ошибка:', error)); - }); -}); - - -// Обработчик отправки отзыва -const submitButton = document.getElementById('submit-review-btn'); -const reviewInput = document.getElementById('review-input'); -const starRating = document.getElementById('star-rating'); -const pathParts = window.location.pathname.split('/'); -const lakeId = pathParts[pathParts.length - 1]; - -let selectedValue = 0; - -document.addEventListener('DOMContentLoaded', () => { - const pathParts = window.location.pathname.split('/'); - const lakeId = pathParts[pathParts.length - 1]; - //const lakeId = window.location.pathname.split('/').pop(); // Извлекаем lakeId из URL - fetch(`/lakes/lake_page/${lakeId}/reviews`) - .then(response => { - if (!response.ok) { - throw new Error('Ошибка при загрузке отзывов'); - } - return response.json(); - }) - .then(reviews => { - console.log(reviews); - // После получения отзывов, добавляем их на страницу - const reviewsList = document.getElementById('reviews-list'); - reviews.forEach(review => { - const reviewElement = createReviewElement(review); - reviewsList.appendChild(reviewElement); - }); - }) - .catch(error => { - console.error('Ошибка:', error); - }); -}); - -// Функция для создания элемента отзыва -function createReviewElement(review) { - const nickname = review.user?.nickname || 'Анонимный пользователь'; - const reviewElement = document.createElement('div'); - reviewElement.classList.add('review'); - reviewElement.innerHTML = ` -
- Фото пользователя - -
-
-
- ${'★'.repeat(review.stars)} - ${'☆'.repeat(5 - review.stars)} -
-
-

${review.message}

-
-
- `; - return reviewElement; -} - -const ratingElement = document.getElementById('average-rating'); - -submitButton.addEventListener('click', () => { - // Проверяем, выбрал ли пользователь количество звезд и написал ли отзыв - if (selectedValue === 0 || reviewInput.value.trim() === '') { - alert('Пожалуйста, оцените озеро и напишите отзыв!'); - return; - } - console.log(selectedValue); - - const reviewText = reviewInput.value.trim(); - const reviewData = { - message: reviewText, - stars: selectedValue // Сохраняем выбранное значение - }; - - // Отправляем отзыв на сервер через fetch - fetch(`/lakes/lake_page/${lakeId}/reviews`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(reviewData), - }) - .then(response => { - if (!response.ok) { - return response.text().then(errorText => { - throw new Error(errorText || 'Ошибка при добавлении отзыва'); - }); - } - return response.json(); - }) - .then(data => { - const review = data.review; - console.log(review); - console.log(data.lake.rating); - const reviewElement = document.createElement('div'); - reviewElement.classList.add('review'); - reviewElement.innerHTML = ` -
- Фото пользователя - -
-
-
- ${'★'.repeat(review.stars)} - ${'☆'.repeat(5 - review.stars)} -
-
-

${review.message}

-
-
- `; - document.getElementById('reviews-list').appendChild(reviewElement); - const oldRating = data.lake.rating; - const oldCount = document.getElementById('reviews-list').children.length - 1; - const newRating = ((oldRating * oldCount) + review.stars) / (oldCount + 1); - ratingElement.textContent = newRating.toFixed(2); - reviewInput.value = ''; - // Сброс активных звезд после публикации отзыва - starRating.querySelectorAll('span').forEach(span => span.classList.remove('active')); - selectedValue = 0; // Сброс значения звезд - }) - .catch(error => { - alert(error.message); - }); -}); - - -// Обработчик для клика на звезды -starRating.addEventListener('click', (e) => { - const clickedSpan = e.target; - if (clickedSpan.dataset.value) { - selectedValue = parseInt(clickedSpan.dataset.value, 10); // Сохраняем выбранное количество звезд - // Обновляем все звезды в соответствии с выбранной - starRating.querySelectorAll('span').forEach(span => { - if (parseInt(span.dataset.value, 10) <= selectedValue) { - span.classList.add('active'); - } else { - span.classList.remove('active'); - } - }); - } -}); - -// Обработчик для наведения -starRating.addEventListener('mouseover', (e) => { - const hoveredSpan = e.target; - if (hoveredSpan.dataset.value) { - const value = parseInt(hoveredSpan.dataset.value, 10); - // Подсвечиваем все звезды до текущей при наведении - starRating.querySelectorAll('span').forEach(span => { - if (parseInt(span.dataset.value, 10) <= value) { - span.classList.add('active'); - } else { - span.classList.remove('active'); - } - }); - } -}); - -// Обработчик для отмены подсветки при уходе мыши -starRating.addEventListener('mouseout', () => { - // После ухода мыши, возвращаем закраску в соответствии с выбранной звездой - starRating.querySelectorAll('span').forEach(span => { - if (parseInt(span.dataset.value, 10) <= selectedValue) { - span.classList.add('active'); // Подсвечиваем все выбранные звезды - } else { - span.classList.remove('active'); - } - }); -}); +// Простой слайдер изображений с использованием стрелок +const images = document.querySelectorAll('.slider-image'); +const leftArrow = document.querySelector('.left-arrow'); +const rightArrow = document.querySelector('.right-arrow'); +let currentIndex = 0; + +function showImage(index) { + images.forEach((img, i) => { + img.style.display = i === index ? 'block' : 'none'; + }); +} + +leftArrow.addEventListener('click', () => { + currentIndex = (currentIndex - 1 + images.length) % images.length; + showImage(currentIndex); +}); + +rightArrow.addEventListener('click', () => { + currentIndex = (currentIndex + 1) % images.length; + showImage(currentIndex); +}); + +showImage(currentIndex); + +// Обработчик кнопки "На главную" +const returnButton = document.getElementById("back-button"); +returnButton.addEventListener("click", () => { + window.history.back(); +}); + +// Обработчики для кнопок "хочу посетить" и "уже посетил" +const heartButton = document.getElementById('heart-button'); +const heartIcon = document.getElementById('heart-icon'); +let isHeartFilled = false; + +// document.addEventListener('DOMContentLoaded', () => { +// const pathParts = window.location.pathname.split('/'); +// const lakeId = pathParts[pathParts.length - 1]; + +// fetch(`/lakes/${lakeId}/status`) // На сервере сделайте API для получения текущего состояния +// .then(response => response.json()) +// .then(data => { +// isHeartFilled = data.isWantVisit; // Примерный формат данных от сервера +// heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; +// }) +// .catch(error => console.error('Ошибка при получении состояния:', error)); +// }); + +// heartButton.addEventListener('click', () => { +// const pathParts = window.location.pathname.split('/'); +// const lakeId = pathParts[pathParts.length - 1]; + +// fetch('/check-auth', { method: 'GET' }) +// .then(response => response.json()) +// .then(data => { +// if (!data.authenticated) { +// alert('Вы должны войти в систему, чтобы добавить в "Хочу посетить".'); +// return; +// } + +// isHeartFilled = !isHeartFilled; +// heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; + +// fetch(`/lakes/${lakeId}/action`, { +// method: 'POST', +// headers: { 'Content-Type': 'application/json' }, +// body: JSON.stringify({ action: isHeartFilled ? 'want_visit' : 'remove_want_visit' }), +// }) +// .then(response => response.json()) +// .then(data => alert(data.message)) +// .catch(error => console.error('Ошибка:', error)); +// }); +// }); + +const eyeButton = document.getElementById('eye-button'); +const eyeIcon = document.getElementById('eye-icon'); +let isEyeFilled = false; + +// eyeButton.addEventListener('click', () => { +// const pathParts = window.location.pathname.split('/'); +// const lakeId = pathParts[pathParts.length - 1]; + +// fetch('/check-auth', { method: 'GET' }) +// .then(response => response.json()) +// .then(data => { +// if (!data.authenticated) { +// alert('Вы должны войти в систему, чтобы добавить в "Уже посетил".'); +// return; +// } + +// isEyeFilled = !isEyeFilled; +// eyeIcon.src = isEyeFilled ? '/assets/eye_filled.png' : '/assets/eye.png'; + +// fetch(`/lakes/${lakeId}/action`, { +// method: 'POST', +// headers: { 'Content-Type': 'application/json' }, +// body: JSON.stringify({ action: isEyeFilled ? 'visited' : 'remove_visited' }), +// }) +// .then(response => response.json()) +// .then(data => alert(data.message)) +// .catch(error => console.error('Ошибка:', error)); +// }); +// }); + +document.addEventListener('DOMContentLoaded', () => { + const pathParts = window.location.pathname.split('/'); + const lakeId = pathParts[pathParts.length - 1]; + + // Запрос состояния озера (хочет ли посетить и уже посетил) + fetch(`/lakes/${lakeId}/status`) + .then(response => response.json()) + .then(data => { + isHeartFilled = data.isWantVisit; // Статус кнопки "хочу посетить" + isEyeFilled = data.isVisited; // Статус кнопки "уже посетил" + + // Обновляем иконки на странице в зависимости от состояния + heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; + eyeIcon.src = isEyeFilled ? '/assets/eye_filled.png' : '/assets/eye.png'; + }) + .catch(error => console.error('Ошибка при получении состояния:', error)); +}); + +heartButton.addEventListener('click', () => { + const pathParts = window.location.pathname.split('/'); + const lakeId = pathParts[pathParts.length - 1]; + + fetch('/check-auth', { method: 'GET' }) + .then(response => response.json()) + .then(data => { + if (!data.authenticated) { + alert('Вы должны войти в систему, чтобы добавить в "Хочу посетить".'); + return; + } + + isHeartFilled = !isHeartFilled; + heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; + + fetch(`/lakes/${lakeId}/action`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ action: isHeartFilled ? 'want_visit' : 'remove_want_visit' }), + }) + .then(response => response.json()) + .then(data => { + alert(data.message); + // После выполнения действия обновляем состояние на сервере + fetch(`/lakes/${lakeId}/status`) + .then(response => response.json()) + .then(data => { + // Обновляем иконки после успешного действия + heartIcon.src = data.isWantVisit ? '/assets/heart_filled.png' : '/assets/heart.png'; + }); + }) + .catch(error => console.error('Ошибка:', error)); + }); +}); + +eyeButton.addEventListener('click', () => { + const pathParts = window.location.pathname.split('/'); + const lakeId = pathParts[pathParts.length - 1]; + + fetch('/check-auth', { method: 'GET' }) + .then(response => response.json()) + .then(data => { + if (!data.authenticated) { + alert('Вы должны войти в систему, чтобы добавить в "Уже посетил".'); + return; + } + + isEyeFilled = !isEyeFilled; + eyeIcon.src = isEyeFilled ? '/assets/eye_filled.png' : '/assets/eye.png'; + + fetch(`/lakes/${lakeId}/action`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ action: isEyeFilled ? 'visited' : 'remove_visited' }), + }) + .then(response => response.json()) + .then(data => { + alert(data.message); + // После выполнения действия обновляем состояние на сервере + fetch(`/lakes/${lakeId}/status`) + .then(response => response.json()) + .then(data => { + // Обновляем иконки после успешного действия + eyeIcon.src = data.isVisited ? '/assets/eye_filled.png' : '/assets/eye.png'; + }); + }) + .catch(error => console.error('Ошибка:', error)); + }); +}); + + +// Обработчик отправки отзыва +const submitButton = document.getElementById('submit-review-btn'); +const reviewInput = document.getElementById('review-input'); +const starRating = document.getElementById('star-rating'); +const pathParts = window.location.pathname.split('/'); +const lakeId = pathParts[pathParts.length - 1]; + +let selectedValue = 0; + +document.addEventListener('DOMContentLoaded', () => { + const pathParts = window.location.pathname.split('/'); + const lakeId = pathParts[pathParts.length - 1]; + //const lakeId = window.location.pathname.split('/').pop(); // Извлекаем lakeId из URL + fetch(`/lakes/lake_page/${lakeId}/reviews`) + .then(response => { + if (!response.ok) { + throw new Error('Ошибка при загрузке отзывов'); + } + return response.json(); + }) + .then(reviews => { + console.log(reviews); + // После получения отзывов, добавляем их на страницу + const reviewsList = document.getElementById('reviews-list'); + reviews.forEach(review => { + const reviewElement = createReviewElement(review); + reviewsList.appendChild(reviewElement); + }); + }) + .catch(error => { + console.error('Ошибка:', error); + }); +}); + +// Функция для создания элемента отзыва +function createReviewElement(review) { + const nickname = review.user?.nickname || 'Анонимный пользователь'; + const reviewElement = document.createElement('div'); + reviewElement.classList.add('review'); + reviewElement.innerHTML = ` +
+ Фото пользователя + +
+
+
+ ${'★'.repeat(review.stars)} + ${'☆'.repeat(5 - review.stars)} +
+
+

${review.message}

+
+
+ `; + return reviewElement; +} + +const ratingElement = document.getElementById('average-rating'); + +submitButton.addEventListener('click', () => { + // Проверяем, выбрал ли пользователь количество звезд и написал ли отзыв + if (selectedValue === 0 || reviewInput.value.trim() === '') { + alert('Пожалуйста, оцените озеро и напишите отзыв!'); + return; + } + console.log(selectedValue); + + const reviewText = reviewInput.value.trim(); + const reviewData = { + message: reviewText, + stars: selectedValue // Сохраняем выбранное значение + }; + + // Отправляем отзыв на сервер через fetch + fetch(`/lakes/lake_page/${lakeId}/reviews`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(reviewData), + }) + .then(response => { + if (!response.ok) { + return response.text().then(errorText => { + throw new Error(errorText || 'Ошибка при добавлении отзыва'); + }); + } + return response.json(); + }) + .then(data => { + const review = data.review; + console.log(review); + console.log(data.lake.rating); + const reviewElement = document.createElement('div'); + reviewElement.classList.add('review'); + reviewElement.innerHTML = ` +
+ Фото пользователя + +
+
+
+ ${'★'.repeat(review.stars)} + ${'☆'.repeat(5 - review.stars)} +
+
+

${review.message}

+
+
+ `; + document.getElementById('reviews-list').appendChild(reviewElement); + const oldRating = data.lake.rating; + const oldCount = document.getElementById('reviews-list').children.length - 1; + const newRating = ((oldRating * oldCount) + review.stars) / (oldCount + 1); + ratingElement.textContent = newRating.toFixed(2); + reviewInput.value = ''; + // Сброс активных звезд после публикации отзыва + starRating.querySelectorAll('span').forEach(span => span.classList.remove('active')); + selectedValue = 0; // Сброс значения звезд + }) + .catch(error => { + alert(error.message); + }); +}); + + +// Обработчик для клика на звезды +starRating.addEventListener('click', (e) => { + const clickedSpan = e.target; + if (clickedSpan.dataset.value) { + selectedValue = parseInt(clickedSpan.dataset.value, 10); // Сохраняем выбранное количество звезд + // Обновляем все звезды в соответствии с выбранной + starRating.querySelectorAll('span').forEach(span => { + if (parseInt(span.dataset.value, 10) <= selectedValue) { + span.classList.add('active'); + } else { + span.classList.remove('active'); + } + }); + } +}); + +// Обработчик для наведения +starRating.addEventListener('mouseover', (e) => { + const hoveredSpan = e.target; + if (hoveredSpan.dataset.value) { + const value = parseInt(hoveredSpan.dataset.value, 10); + // Подсвечиваем все звезды до текущей при наведении + starRating.querySelectorAll('span').forEach(span => { + if (parseInt(span.dataset.value, 10) <= value) { + span.classList.add('active'); + } else { + span.classList.remove('active'); + } + }); + } +}); + +// Обработчик для отмены подсветки при уходе мыши +starRating.addEventListener('mouseout', () => { + // После ухода мыши, возвращаем закраску в соответствии с выбранной звездой + starRating.querySelectorAll('span').forEach(span => { + if (parseInt(span.dataset.value, 10) <= selectedValue) { + span.classList.add('active'); // Подсвечиваем все выбранные звезды + } else { + span.classList.remove('active'); + } + }); +}); diff --git a/lake-catalog/src/main/resources/static/JavaScript/filtration_script.js b/src/main/resources/static/JavaScript/filtration_script.js similarity index 97% rename from lake-catalog/src/main/resources/static/JavaScript/filtration_script.js rename to src/main/resources/static/JavaScript/filtration_script.js index 34e7fa6..4d377fb 100644 --- a/lake-catalog/src/main/resources/static/JavaScript/filtration_script.js +++ b/src/main/resources/static/JavaScript/filtration_script.js @@ -1,127 +1,127 @@ - -document.addEventListener("DOMContentLoaded", () => { - const searchButton = document.getElementById("searchButton"); - const returnButton = document.getElementById("returnButton"); - const applyFiltersButton = document.getElementById("applyFilters"); - - const depthRange = document.getElementById("depthRange"); - const depthInput = document.getElementById("depthInput"); - const areaRange = document.getElementById("areaRange"); - const areaInput = document.getElementById("areaInput"); - const regionInput = document.getElementById("regionInput"); - const regionList = document.getElementById("regionList"); - const regionSearch = document.getElementById("regionSearch"); - const cityInput = document.getElementById("cityInput"); - const cityList = document.getElementById("cityList"); - const citySearch = document.getElementById("citySearch"); - - // Функция для открытия и закрытия выпадающего списка - function toggleDropdown(input, list) { - input.parentElement.classList.toggle("open"); - list.classList.toggle("show"); - } - - // Функция для фильтрации списка по поиску - function filterList(searchInput, container) { - const filter = searchInput.value.toLowerCase(); - const labels = container.querySelectorAll("label"); - labels.forEach(label => { - const text = label.textContent.toLowerCase(); - label.style.display = text.includes(filter) ? "block" : "none"; - }); - } - - // Функция для обновления текста в инпуте на основании выбранных чекбоксов - function updateInputText(input, container) { - const selected = Array.from(container.querySelectorAll("input[type='checkbox']:checked")) - .map(checkbox => checkbox.value); - input.value = selected.join(", "); - } - - // Обработчики для региона - regionInput.addEventListener("click", () => toggleDropdown(regionInput, regionList)); - regionSearch.addEventListener("input", () => filterList(regionSearch, regionList)); - regionList.addEventListener("change", () => updateInputText(regionInput, regionList)); - - // Обработчики для города - cityInput.addEventListener("click", () => toggleDropdown(cityInput, cityList)); - citySearch.addEventListener("input", () => filterList(citySearch, cityList)); - cityList.addEventListener("change", () => updateInputText(cityInput, cityList)); - - // Закрытие выпадающих списков при клике вне их области - document.addEventListener("click", (event) => { - if (!regionInput.parentElement.contains(event.target)) { - regionInput.parentElement.classList.remove("open"); - } - if (!cityInput.parentElement.contains(event.target)) { - cityInput.parentElement.classList.remove("open"); - } - }); - // Обработчик для синхронизации значения глубины (range и input) - depthRange.addEventListener("input", () => { - depthInput.value = depthRange.value; - }); - - depthInput.addEventListener("input", () => { - // Проверка, чтобы значение было в допустимых пределах - if (depthInput.value < depthRange.min) { - depthInput.value = depthRange.min; - } else if (depthInput.value > depthRange.max) { - depthInput.value = depthRange.max; - } - depthRange.value = depthInput.value; - }); - - // Обработчик для синхронизации значения площади (range и input) - areaRange.addEventListener("input", () => { - areaInput.value = areaRange.value; - }); - - areaInput.addEventListener("input", () => { - // Проверка, чтобы значение было в допустимых пределах - if (areaInput.value < areaRange.min) { - areaInput.value = areaRange.min; - } else if (areaInput.value > areaRange.max) { - areaInput.value = areaRange.max; - } - areaRange.value = areaInput.value; - }); - - // Обработчик кнопки поиска - // searchButton.addEventListener("click", () => { - // const searchQuery = document.getElementById("searchInput").value; - // alert(`Ищем: ${searchQuery}`); - // }); - - // Обработчик кнопки применения фильтров - searchButton.addEventListener("click", () => { - const form = document.createElement('form'); - form.method = "GET"; - form.action = "/lakes/main"; - - const addInput = (name, value) => { - if (value) { - const input = document.createElement('input'); - input.type = 'hidden'; - input.name = name; - input.value = value; - form.appendChild(input); - } - }; - - addInput('depth', document.getElementById('depthInput').value || document.getElementById('depthRange').value); - addInput('region', Array.from(document.querySelectorAll('#regionList input:checked')).map(input => input.value).join(',')); - addInput('square', document.getElementById('areaInput').value || document.getElementById('areaRange').value); - addInput('rating', Array.from(document.querySelectorAll('.rating input:checked')).map(input => input.id).join(',')); - addInput('city', Array.from(document.querySelectorAll('#cityList input:checked')).map(input => input.value).join(',')); - addInput('name', document.getElementById('searchInput').value.trim()); - - document.body.appendChild(form); - form.submit(); - }); - - returnButton.addEventListener("click", () => { - window.location.href = '/main'; - }); -}); - + +document.addEventListener("DOMContentLoaded", () => { + const searchButton = document.getElementById("searchButton"); + const returnButton = document.getElementById("returnButton"); + const applyFiltersButton = document.getElementById("applyFilters"); + + const depthRange = document.getElementById("depthRange"); + const depthInput = document.getElementById("depthInput"); + const areaRange = document.getElementById("areaRange"); + const areaInput = document.getElementById("areaInput"); + const regionInput = document.getElementById("regionInput"); + const regionList = document.getElementById("regionList"); + const regionSearch = document.getElementById("regionSearch"); + const cityInput = document.getElementById("cityInput"); + const cityList = document.getElementById("cityList"); + const citySearch = document.getElementById("citySearch"); + + // Функция для открытия и закрытия выпадающего списка + function toggleDropdown(input, list) { + input.parentElement.classList.toggle("open"); + list.classList.toggle("show"); + } + + // Функция для фильтрации списка по поиску + function filterList(searchInput, container) { + const filter = searchInput.value.toLowerCase(); + const labels = container.querySelectorAll("label"); + labels.forEach(label => { + const text = label.textContent.toLowerCase(); + label.style.display = text.includes(filter) ? "block" : "none"; + }); + } + + // Функция для обновления текста в инпуте на основании выбранных чекбоксов + function updateInputText(input, container) { + const selected = Array.from(container.querySelectorAll("input[type='checkbox']:checked")) + .map(checkbox => checkbox.value); + input.value = selected.join(", "); + } + + // Обработчики для региона + regionInput.addEventListener("click", () => toggleDropdown(regionInput, regionList)); + regionSearch.addEventListener("input", () => filterList(regionSearch, regionList)); + regionList.addEventListener("change", () => updateInputText(regionInput, regionList)); + + // Обработчики для города + cityInput.addEventListener("click", () => toggleDropdown(cityInput, cityList)); + citySearch.addEventListener("input", () => filterList(citySearch, cityList)); + cityList.addEventListener("change", () => updateInputText(cityInput, cityList)); + + // Закрытие выпадающих списков при клике вне их области + document.addEventListener("click", (event) => { + if (!regionInput.parentElement.contains(event.target)) { + regionInput.parentElement.classList.remove("open"); + } + if (!cityInput.parentElement.contains(event.target)) { + cityInput.parentElement.classList.remove("open"); + } + }); + // Обработчик для синхронизации значения глубины (range и input) + depthRange.addEventListener("input", () => { + depthInput.value = depthRange.value; + }); + + depthInput.addEventListener("input", () => { + // Проверка, чтобы значение было в допустимых пределах + if (depthInput.value < depthRange.min) { + depthInput.value = depthRange.min; + } else if (depthInput.value > depthRange.max) { + depthInput.value = depthRange.max; + } + depthRange.value = depthInput.value; + }); + + // Обработчик для синхронизации значения площади (range и input) + areaRange.addEventListener("input", () => { + areaInput.value = areaRange.value; + }); + + areaInput.addEventListener("input", () => { + // Проверка, чтобы значение было в допустимых пределах + if (areaInput.value < areaRange.min) { + areaInput.value = areaRange.min; + } else if (areaInput.value > areaRange.max) { + areaInput.value = areaRange.max; + } + areaRange.value = areaInput.value; + }); + + // Обработчик кнопки поиска + // searchButton.addEventListener("click", () => { + // const searchQuery = document.getElementById("searchInput").value; + // alert(`Ищем: ${searchQuery}`); + // }); + + // Обработчик кнопки применения фильтров + searchButton.addEventListener("click", () => { + const form = document.createElement('form'); + form.method = "GET"; + form.action = "/lakes/main"; + + const addInput = (name, value) => { + if (value) { + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = name; + input.value = value; + form.appendChild(input); + } + }; + + addInput('depth', document.getElementById('depthInput').value || document.getElementById('depthRange').value); + addInput('region', Array.from(document.querySelectorAll('#regionList input:checked')).map(input => input.value).join(',')); + addInput('square', document.getElementById('areaInput').value || document.getElementById('areaRange').value); + addInput('rating', Array.from(document.querySelectorAll('.rating input:checked')).map(input => input.id).join(',')); + addInput('city', Array.from(document.querySelectorAll('#cityList input:checked')).map(input => input.value).join(',')); + addInput('name', document.getElementById('searchInput').value.trim()); + + document.body.appendChild(form); + form.submit(); + }); + + returnButton.addEventListener("click", () => { + window.location.href = '/main'; + }); +}); + diff --git a/lake-catalog/src/main/resources/static/JavaScript/import-export_script.js b/src/main/resources/static/JavaScript/import-export_script.js similarity index 97% rename from lake-catalog/src/main/resources/static/JavaScript/import-export_script.js rename to src/main/resources/static/JavaScript/import-export_script.js index 14f74ef..791e5e3 100644 --- a/lake-catalog/src/main/resources/static/JavaScript/import-export_script.js +++ b/src/main/resources/static/JavaScript/import-export_script.js @@ -1,81 +1,81 @@ - -// Функция для обработки кнопки "Загрузить" -document.getElementById('upload-button').addEventListener('click', function () { - const fileInput = document.getElementById('file-upload'); - if (fileInput.files.length === 0) { - alert('Пожалуйста, выберите файл для загрузки'); - return; - } - alert(`Файл ${fileInput.files[0].name} будет загружен!`); - // Здесь можно добавить логику для загрузки данных (например, через API) -}); - -document.getElementById('upload-button').addEventListener('click', async function () { - const fileInput = document.getElementById('file-upload'); - const file = fileInput.files[0]; // Получаем выбранный файл - - if (!file) { - alert('Пожалуйста, выберите файл для загрузки.'); - return; - } - - try { - // Читаем содержимое файла как текст - const fileContent = await file.text(); - const lakesData = JSON.parse(fileContent); // Преобразуем текст в JSON - - // Отправляем данные на сервер - const response = await fetch('/users/profile/{userId}/imp_exp/import', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(lakesData), - }); - - if (response.ok) { - const message = await response.text(); - alert(message); - } else { - const errorMessage = await response.text(); - alert(`Ошибка: ${errorMessage}`); - } - } catch (error) { - console.error('Ошибка при обработке файла:', error); - alert('Произошла ошибка при загрузке файла.'); - } -}); - - -function goToProfile(){ - window.location.href = '/users/profile/current'; -} - -document.getElementById('export-button').addEventListener('click', async () => { - alert('База данных будет скачана!'); - - try { - // Отправляем запрос на экспорт данных - const response = await fetch('/users/profile/{userId}/imp_exp/export', { - method: 'GET', - }); - - if (!response.ok) { - throw new Error('Ошибка при получении данных'); - } - - // Создаем Blob с типом json - const blob = await response.blob(); - - // Создаем ссылку для скачивания файла - const link = document.createElement('a'); - link.href = URL.createObjectURL(blob); - link.download = 'lakes.json'; // Имя файла - - // Инициируем скачивание - link.click(); - } catch (error) { - alert('Ошибка при экспорте данных: ' + error.message); - } -}); - + +// Функция для обработки кнопки "Загрузить" +document.getElementById('upload-button').addEventListener('click', function () { + const fileInput = document.getElementById('file-upload'); + if (fileInput.files.length === 0) { + alert('Пожалуйста, выберите файл для загрузки'); + return; + } + alert(`Файл ${fileInput.files[0].name} будет загружен!`); + // Здесь можно добавить логику для загрузки данных (например, через API) +}); + +document.getElementById('upload-button').addEventListener('click', async function () { + const fileInput = document.getElementById('file-upload'); + const file = fileInput.files[0]; // Получаем выбранный файл + + if (!file) { + alert('Пожалуйста, выберите файл для загрузки.'); + return; + } + + try { + // Читаем содержимое файла как текст + const fileContent = await file.text(); + const lakesData = JSON.parse(fileContent); // Преобразуем текст в JSON + + // Отправляем данные на сервер + const response = await fetch('/users/profile/{userId}/imp_exp/import', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(lakesData), + }); + + if (response.ok) { + const message = await response.text(); + alert(message); + } else { + const errorMessage = await response.text(); + alert(`Ошибка: ${errorMessage}`); + } + } catch (error) { + console.error('Ошибка при обработке файла:', error); + alert('Произошла ошибка при загрузке файла.'); + } +}); + + +function goToProfile(){ + window.location.href = '/users/profile/current'; +} + +document.getElementById('export-button').addEventListener('click', async () => { + alert('База данных будет скачана!'); + + try { + // Отправляем запрос на экспорт данных + const response = await fetch('/users/profile/{userId}/imp_exp/export', { + method: 'GET', + }); + + if (!response.ok) { + throw new Error('Ошибка при получении данных'); + } + + // Создаем Blob с типом json + const blob = await response.blob(); + + // Создаем ссылку для скачивания файла + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = 'lakes.json'; // Имя файла + + // Инициируем скачивание + link.click(); + } catch (error) { + alert('Ошибка при экспорте данных: ' + error.message); + } +}); + diff --git a/lake-catalog/src/main/resources/static/JavaScript/main_script.js b/src/main/resources/static/JavaScript/main_script.js similarity index 96% rename from lake-catalog/src/main/resources/static/JavaScript/main_script.js rename to src/main/resources/static/JavaScript/main_script.js index 0491bf5..74b302f 100644 --- a/lake-catalog/src/main/resources/static/JavaScript/main_script.js +++ b/src/main/resources/static/JavaScript/main_script.js @@ -1,7 +1,7 @@ -function goFilter(){ - window.location.href = '/filtration'; -} - -function goToProfile(){ - window.location.href = '/users/profile/current'; +function goFilter(){ + window.location.href = '/filtration'; +} + +function goToProfile(){ + window.location.href = '/users/profile/current'; } \ No newline at end of file diff --git a/lake-catalog/src/main/resources/static/JavaScript/profile_script.js b/src/main/resources/static/JavaScript/profile_script.js similarity index 97% rename from lake-catalog/src/main/resources/static/JavaScript/profile_script.js rename to src/main/resources/static/JavaScript/profile_script.js index 60882fe..682242a 100644 --- a/lake-catalog/src/main/resources/static/JavaScript/profile_script.js +++ b/src/main/resources/static/JavaScript/profile_script.js @@ -1,127 +1,127 @@ -const backButton = document.getElementById("back-button"); -backButton.addEventListener("click", () => { - window.location.href = '/main'; -}); - -const logoutButton = document.getElementById("logout-button"); -logoutButton.addEventListener("click", () => { - window.location.href = '/logout'; -}); - -const imp_expButton = document.getElementById("import-export-button"); -imp_expButton.addEventListener("click", () => { - window.location.href = '/users/imp_exp'; -}); - - -document.addEventListener('DOMContentLoaded', () => { - const tabButtons = document.querySelectorAll('.tab-button'); - const tabContents = document.querySelectorAll('.tab-content'); - - tabButtons.forEach(button => { - button.addEventListener('click', () => { - // Убираем активный класс у всех кнопок и контента - tabButtons.forEach(btn => btn.classList.remove('active')); - tabContents.forEach(content => content.classList.remove('active')); - - // Добавляем активный класс к выбранной кнопке и контенту - button.classList.add('active'); - document.getElementById(button.dataset.tab).classList.add('active'); - }); - }); - - const editProfileButton = document.getElementById('edit-profile'); - const saveProfileButton = document.getElementById('save-profile'); - const nameField = document.getElementById('name-field'); - const emailField = document.getElementById('email-field'); - const nameInput = document.getElementById('name-input'); - const emailInput = document.getElementById('email-input'); - - // Включаем режим редактирования - editProfileButton.addEventListener('click', () => { - nameInput.value = document.getElementById('user-name').textContent; - emailInput.value = document.getElementById('user-email').textContent; - - nameField.style.display = 'none'; - emailField.style.display = 'none'; - nameInput.style.display = 'block'; - emailInput.style.display = 'block'; - saveProfileButton.style.display = 'inline-block'; - editProfileButton.style.display = 'none'; // Скрыть кнопку редактирования - }); - - // Сохраняем изменения и возвращаем режим просмотра - saveProfileButton.addEventListener('click', async () => { - const newName = nameInput.value.trim(); - const newEmail = emailInput.value.trim(); - - // Проверяем заполненность полей - if (!newName || !newEmail) { - alert('Пожалуйста, заполните все поля.'); - return; - } - - const currentEmail = document.getElementById('user-email').textContent; - - // Если email не изменился, пропускаем проверку на уникальность - if (newEmail === currentEmail) { - await updateProfile(newName, newEmail); // Просто обновляем профиль - } else { - // Проверка уникальности email - const emailResponse = await fetch(`/users/profile/${userId}/check-email-unique`, { - method: 'POST', - body: JSON.stringify({ email: newEmail }), - headers: { - 'Content-Type': 'application/json', - }, - }); - - const emailData = await emailResponse.json(); - if (!emailData.isUnique) { - alert('Этот email уже занят.'); - return; - } - - // Отправляем данные на сервер для обновления профиля - await updateProfile(newName, newEmail); - } - - // Обновляем значения в режиме просмотра - document.getElementById('user-name').textContent = newName; - document.getElementById('user-email').textContent = newEmail; - - nameField.style.display = 'block'; - emailField.style.display = 'block'; - nameInput.style.display = 'none'; - emailInput.style.display = 'none'; - saveProfileButton.style.display = 'none'; - editProfileButton.style.display = 'inline-block'; // Вернуть кнопку редактирования - }); -}); - -async function updateProfile(newName, newEmail) { - const pathParts = window.location.pathname.split('/'); - const userId = pathParts[pathParts.length - 1]; - const updateResponse = await fetch(`/users/profile/${userId}/update-profile?newName=${newName}&newEmail=${newEmail}`, { - method: 'PUT', - }); - - if (updateResponse.ok) { - const updateData = await updateResponse.json(); - alert(updateData.message); // Профиль успешно обновлен - if (updateData.editDate) { - const editDateElement = document.getElementById('edit-date'); - const date = new Date(updateData.editDate); - - const day = String(date.getDate()).padStart(2, '0'); - const month = String(date.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed - const year = date.getFullYear(); - - const formattedDate = `${day}-${month}-${year}`; - editDateElement.textContent = formattedDate; - } - } else { - const errorData = await updateResponse.json(); - alert('Ошибка при обновлении профиля: ' + errorData.message); - } -} +const backButton = document.getElementById("back-button"); +backButton.addEventListener("click", () => { + window.location.href = '/main'; +}); + +const logoutButton = document.getElementById("logout-button"); +logoutButton.addEventListener("click", () => { + window.location.href = '/logout'; +}); + +const imp_expButton = document.getElementById("import-export-button"); +imp_expButton.addEventListener("click", () => { + window.location.href = '/users/imp_exp'; +}); + + +document.addEventListener('DOMContentLoaded', () => { + const tabButtons = document.querySelectorAll('.tab-button'); + const tabContents = document.querySelectorAll('.tab-content'); + + tabButtons.forEach(button => { + button.addEventListener('click', () => { + // Убираем активный класс у всех кнопок и контента + tabButtons.forEach(btn => btn.classList.remove('active')); + tabContents.forEach(content => content.classList.remove('active')); + + // Добавляем активный класс к выбранной кнопке и контенту + button.classList.add('active'); + document.getElementById(button.dataset.tab).classList.add('active'); + }); + }); + + const editProfileButton = document.getElementById('edit-profile'); + const saveProfileButton = document.getElementById('save-profile'); + const nameField = document.getElementById('name-field'); + const emailField = document.getElementById('email-field'); + const nameInput = document.getElementById('name-input'); + const emailInput = document.getElementById('email-input'); + + // Включаем режим редактирования + editProfileButton.addEventListener('click', () => { + nameInput.value = document.getElementById('user-name').textContent; + emailInput.value = document.getElementById('user-email').textContent; + + nameField.style.display = 'none'; + emailField.style.display = 'none'; + nameInput.style.display = 'block'; + emailInput.style.display = 'block'; + saveProfileButton.style.display = 'inline-block'; + editProfileButton.style.display = 'none'; // Скрыть кнопку редактирования + }); + + // Сохраняем изменения и возвращаем режим просмотра + saveProfileButton.addEventListener('click', async () => { + const newName = nameInput.value.trim(); + const newEmail = emailInput.value.trim(); + + // Проверяем заполненность полей + if (!newName || !newEmail) { + alert('Пожалуйста, заполните все поля.'); + return; + } + + const currentEmail = document.getElementById('user-email').textContent; + + // Если email не изменился, пропускаем проверку на уникальность + if (newEmail === currentEmail) { + await updateProfile(newName, newEmail); // Просто обновляем профиль + } else { + // Проверка уникальности email + const emailResponse = await fetch(`/users/profile/${userId}/check-email-unique`, { + method: 'POST', + body: JSON.stringify({ email: newEmail }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + const emailData = await emailResponse.json(); + if (!emailData.isUnique) { + alert('Этот email уже занят.'); + return; + } + + // Отправляем данные на сервер для обновления профиля + await updateProfile(newName, newEmail); + } + + // Обновляем значения в режиме просмотра + document.getElementById('user-name').textContent = newName; + document.getElementById('user-email').textContent = newEmail; + + nameField.style.display = 'block'; + emailField.style.display = 'block'; + nameInput.style.display = 'none'; + emailInput.style.display = 'none'; + saveProfileButton.style.display = 'none'; + editProfileButton.style.display = 'inline-block'; // Вернуть кнопку редактирования + }); +}); + +async function updateProfile(newName, newEmail) { + const pathParts = window.location.pathname.split('/'); + const userId = pathParts[pathParts.length - 1]; + const updateResponse = await fetch(`/users/profile/${userId}/update-profile?newName=${newName}&newEmail=${newEmail}`, { + method: 'PUT', + }); + + if (updateResponse.ok) { + const updateData = await updateResponse.json(); + alert(updateData.message); // Профиль успешно обновлен + if (updateData.editDate) { + const editDateElement = document.getElementById('edit-date'); + const date = new Date(updateData.editDate); + + const day = String(date.getDate()).padStart(2, '0'); + const month = String(date.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed + const year = date.getFullYear(); + + const formattedDate = `${day}-${month}-${year}`; + editDateElement.textContent = formattedDate; + } + } else { + const errorData = await updateResponse.json(); + alert('Ошибка при обновлении профиля: ' + errorData.message); + } +} diff --git a/lake-catalog/src/main/resources/static/assets/avatar.jpg b/src/main/resources/static/assets/avatar.jpg similarity index 100% rename from lake-catalog/src/main/resources/static/assets/avatar.jpg rename to src/main/resources/static/assets/avatar.jpg diff --git a/lake-catalog/src/main/resources/static/assets/down.png b/src/main/resources/static/assets/down.png similarity index 100% rename from lake-catalog/src/main/resources/static/assets/down.png rename to src/main/resources/static/assets/down.png diff --git a/lake-catalog/src/main/resources/static/assets/edit.png b/src/main/resources/static/assets/edit.png similarity index 100% rename from lake-catalog/src/main/resources/static/assets/edit.png rename to src/main/resources/static/assets/edit.png diff --git a/lake-catalog/src/main/resources/static/assets/eye.png b/src/main/resources/static/assets/eye.png similarity index 100% rename from lake-catalog/src/main/resources/static/assets/eye.png rename to src/main/resources/static/assets/eye.png diff --git a/lake-catalog/src/main/resources/static/assets/eye_filled.png b/src/main/resources/static/assets/eye_filled.png similarity index 100% rename from lake-catalog/src/main/resources/static/assets/eye_filled.png rename to src/main/resources/static/assets/eye_filled.png diff --git a/lake-catalog/src/main/resources/static/assets/heart.png b/src/main/resources/static/assets/heart.png similarity index 100% rename from lake-catalog/src/main/resources/static/assets/heart.png rename to src/main/resources/static/assets/heart.png diff --git a/lake-catalog/src/main/resources/static/assets/heart_filled.png b/src/main/resources/static/assets/heart_filled.png similarity index 100% rename from lake-catalog/src/main/resources/static/assets/heart_filled.png rename to src/main/resources/static/assets/heart_filled.png diff --git a/lake-catalog/src/main/resources/static/assets/lake.jpg b/src/main/resources/static/assets/lake.jpg similarity index 100% rename from lake-catalog/src/main/resources/static/assets/lake.jpg rename to src/main/resources/static/assets/lake.jpg diff --git a/lake-catalog/src/main/resources/static/assets/lake2.jpg b/src/main/resources/static/assets/lake2.jpg similarity index 100% rename from lake-catalog/src/main/resources/static/assets/lake2.jpg rename to src/main/resources/static/assets/lake2.jpg diff --git a/lake-catalog/src/main/resources/static/assets/left.png b/src/main/resources/static/assets/left.png similarity index 100% rename from lake-catalog/src/main/resources/static/assets/left.png rename to src/main/resources/static/assets/left.png diff --git a/lake-catalog/src/main/resources/static/assets/right.png b/src/main/resources/static/assets/right.png similarity index 100% rename from lake-catalog/src/main/resources/static/assets/right.png rename to src/main/resources/static/assets/right.png diff --git a/lake-catalog/src/main/resources/templates/card/card_styles.css b/src/main/resources/static/css/card_styles.css similarity index 94% rename from lake-catalog/src/main/resources/templates/card/card_styles.css rename to src/main/resources/static/css/card_styles.css index ef41884..4c570d9 100644 --- a/lake-catalog/src/main/resources/templates/card/card_styles.css +++ b/src/main/resources/static/css/card_styles.css @@ -1,241 +1,241 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; - font-family: 'Century Gothic', Arial, sans-serif; -} - -body { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #DEE9FE; - color: #003366; -} - -.container { - max-width: 1000px; - margin: 0 auto; - padding: 20px; -} - -.lake-header { - display: flex; - align-items: center; - justify-content: space-between; - margin: 20px 0; - position: relative; -} - -.back-button { - padding: 10px 20px; - background-color: #003366; - color: white; - border: none; - cursor: pointer; -} - -.lake-title { - font-size: 2em; - color: #003366; -} - -.icon-container { - display: flex; - gap: 15px; -} - -.icon-button { - background: none; - border: none; - cursor: pointer; - padding: 0; -} - -.icon { - width: 30px; - height: 30px; -} - - -.slider { - display: flex; - align-items: center; - justify-content: center; - gap: 10px; - margin: 50px 60px; - position: relative; -} - -.arrow-button { - width: 32px; - height: 32px; - cursor: pointer; - user-select: none; -} - -.left-arrow { - position: absolute; - left: -60px; - top: 50%; - transform: translateY(-50%); -} - -.right-arrow { - position: absolute; - right: -60px; - top: 50%; - transform: translateY(-50%); -} - -.slider-image { - max-width: 100%; - height: auto; -} - - -.description, .info, .map, .reviews { - margin: 40px 0; -} - -.info { - line-height: 2; -} - -h2 { - margin-bottom: 20px; -} - -ul { - list-style: none; - padding-left: 0; -} - -.map iframe { - width: 100%; - height: 300px; - border: 0; - border-radius: 10px; -} - -.star-rating { - display: flex; - gap: 5px; - cursor: pointer; - margin-bottom: 10px; -} - -.star-rating span { - font-size: 24px; - color: #ccc; /* Серый цвет для неактивных звезд */ - transition: color 0.2s; -} - -.star-rating span.active { - color: #f39c12; /* Оранжевый цвет для активных звезд */ -} - -.star-rating span:hover, -.star-rating span:hover ~ span { - color: #ccc; /* Убираем цвет для звезд справа при наведении */ -} - -.star-rating span:hover, -.star-rating span:hover ~ .star-rating span { - color: #f39c12; -} - -textarea { - font-family: 'Century Gothic', Arial, sans-serif; - width: 100%; - padding: 10px; - border-radius: 5px; - border: 1px solid #003366; - margin-bottom: 20px; -} - -/* Стили для отзыва */ -.review { - display: flex; - flex-direction: column; - padding: 16px; - border: 1px solid #e0e0e0; - border-radius: 8px; - background-color: #fff; - margin-top: 16px; -} - -.review-input-container { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 16px; -} - -#review-input { - flex: 1; - padding: 10px; - border: 1px solid #ccc; - border-radius: 8px; - font-size: 16px; -} - -#submit-review-btn { - padding: 10px 16px; - border: none; - border-radius: 8px; - background-color: #003366; - color: #fff; - font-size: 16px; - cursor: pointer; - transition: background-color 0.2s; -} - -#submit-review-btn:hover { - background-color: #012d5a; -} - -.review-user { - display: flex; - align-items: center; - margin-bottom: 8px; -} - -.user-photo { - width: 64px; - height: 64px; - border-radius: 50%; - object-fit: cover; - margin-right: 12px; -} - -.user-info { - display: flex; - flex-direction: column; -} - -.user-info h4 { - font-size: 18px; - margin: 0; -} - -.user-info p { - font-size: 14px; - color: #888; - margin: 4px 0 0; -} - -.review-content { - display: flex; - flex-direction: column; -} - -.review-text { - font-size: 16px; - /* margin-bottom: 8px; */ -} - -.review-rating { - font-size: 20px; - color: #ffc107; /* Желтый цвет для звезд */ - margin-bottom: 10px; -} - +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Century Gothic', Arial, sans-serif; +} + +body { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #DEE9FE; + color: #003366; +} + +.container { + max-width: 1000px; + margin: 0 auto; + padding: 20px; +} + +.lake-header { + display: flex; + align-items: center; + justify-content: space-between; + margin: 20px 0; + position: relative; +} + +.back-button { + padding: 10px 20px; + background-color: #003366; + color: white; + border: none; + cursor: pointer; +} + +.lake-title { + font-size: 2em; + color: #003366; +} + +.icon-container { + display: flex; + gap: 15px; +} + +.icon-button { + background: none; + border: none; + cursor: pointer; + padding: 0; +} + +.icon { + width: 30px; + height: 30px; +} + + +.slider { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + margin: 50px 60px; + position: relative; +} + +.arrow-button { + width: 32px; + height: 32px; + cursor: pointer; + user-select: none; +} + +.left-arrow { + position: absolute; + left: -60px; + top: 50%; + transform: translateY(-50%); +} + +.right-arrow { + position: absolute; + right: -60px; + top: 50%; + transform: translateY(-50%); +} + +.slider-image { + max-width: 100%; + height: auto; +} + + +.description, .info, .map, .reviews { + margin: 40px 0; +} + +.info { + line-height: 2; +} + +h2 { + margin-bottom: 20px; +} + +ul { + list-style: none; + padding-left: 0; +} + +.map iframe { + width: 100%; + height: 300px; + border: 0; + border-radius: 10px; +} + +.star-rating { + display: flex; + gap: 5px; + cursor: pointer; + margin-bottom: 10px; +} + +.star-rating span { + font-size: 24px; + color: #ccc; /* Серый цвет для неактивных звезд */ + transition: color 0.2s; +} + +.star-rating span.active { + color: #f39c12; /* Оранжевый цвет для активных звезд */ +} + +.star-rating span:hover, +.star-rating span:hover ~ span { + color: #ccc; /* Убираем цвет для звезд справа при наведении */ +} + +.star-rating span:hover, +.star-rating span:hover ~ .star-rating span { + color: #f39c12; +} + +textarea { + font-family: 'Century Gothic', Arial, sans-serif; + width: 100%; + padding: 10px; + border-radius: 5px; + border: 1px solid #003366; + margin-bottom: 20px; +} + +/* Стили для отзыва */ +.review { + display: flex; + flex-direction: column; + padding: 16px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background-color: #fff; + margin-top: 16px; +} + +.review-input-container { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; +} + +#review-input { + flex: 1; + padding: 10px; + border: 1px solid #ccc; + border-radius: 8px; + font-size: 16px; +} + +#submit-review-btn { + padding: 10px 16px; + border: none; + border-radius: 8px; + background-color: #003366; + color: #fff; + font-size: 16px; + cursor: pointer; + transition: background-color 0.2s; +} + +#submit-review-btn:hover { + background-color: #012d5a; +} + +.review-user { + display: flex; + align-items: center; + margin-bottom: 8px; +} + +.user-photo { + width: 64px; + height: 64px; + border-radius: 50%; + object-fit: cover; + margin-right: 12px; +} + +.user-info { + display: flex; + flex-direction: column; +} + +.user-info h4 { + font-size: 18px; + margin: 0; +} + +.user-info p { + font-size: 14px; + color: #888; + margin: 4px 0 0; +} + +.review-content { + display: flex; + flex-direction: column; +} + +.review-text { + font-size: 16px; + /* margin-bottom: 8px; */ +} + +.review-rating { + font-size: 20px; + color: #ffc107; /* Желтый цвет для звезд */ + margin-bottom: 10px; +} + diff --git a/lake-catalog/src/main/resources/static/css/filtration_styles.css b/src/main/resources/static/css/filtration_styles.css similarity index 95% rename from lake-catalog/src/main/resources/static/css/filtration_styles.css rename to src/main/resources/static/css/filtration_styles.css index eb4a272..0d277ff 100644 --- a/lake-catalog/src/main/resources/static/css/filtration_styles.css +++ b/src/main/resources/static/css/filtration_styles.css @@ -1,182 +1,182 @@ -body { - background-color: #DEE9FE; - font-family: Arial, sans-serif; - color: #003366; - text-align: center; - font-family: 'Century Gothic', Arial, sans-serif; -} - -header { - margin-top: 20px; -} - -h1 { - font-size: 3rem; - color: #003366; -} - -.search-container { - display: flex; - justify-content: center; - margin: 20px 0; -} - -select { - font-family: 'Century Gothic', Arial, sans-serif; -} - -#searchInput { - font-family: 'Century Gothic', Arial, sans-serif; - padding: 10px; - width: 700px; /* Увеличенная ширина */ - border-radius: 20px; - border: 2px solid #003366; -} - -#searchButton, #returnButton { - font-family: 'Century Gothic', Arial, sans-serif; - margin-left: 10px; - padding: 10px 20px; - background-color: #003366; - color: white; - border: none; - border-radius: 20px; - cursor: pointer; -} - -.filters { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; - padding: 20px; -} - -.filter-group { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #DEE9FE; - padding: 15px; - border-radius: 10px; - /* box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); */ - text-align: left; - border: 2px solid #003366; -} - -.rating { - text-align: left; /* Выравнивание по левому краю */ - margin-top: 15px; -} - -.rating label { - margin-left: 5px; -} - -.range-container { - display: flex; - align-items: center; - gap: 10px; -} -/* Цвет звездочек */ -.star { - color: #003366; - /* font-size: 1.5rem; */ -} - -/* Убедитесь, что цвета не переопределяются другими стилями */ -input[type="checkbox"]:checked + .star { - color: #003366; - font-family: 'Century Gothic', Arial, sans-serif; -} - - -input[type="number"] { - width: 80px; - padding: 5px; - border-radius: 5px; - border: 1px solid #ccc; - font-family: 'Century Gothic', Arial, sans-serif; -} - -input[type="text"] { - font-family: 'Century Gothic', Arial, sans-serif; -} - -#applyFilters { - font-family: 'Century Gothic', Arial, sans-serif; - margin-top: 20px; - padding: 10px 30px; - background-color: #003366; - color: white; - border: none; - border-radius: 20px; - cursor: pointer; -} - -/* Стили для кастомного выпадающего списка */ -.dropdown { - position: relative; - width: 90%; -} - -#regionInput, -#cityInput { - width: 90%; - padding: 10px; - border: 1px solid #003366; - border-radius: 5px; - cursor: pointer; -} - -.dropdown-content { - display: none; - position: absolute; - width: 93%; - max-height: 200px; - overflow-y: auto; - background-color: #fff; - border: 1px solid #003366; - border-radius: 5px; - z-index: 1; -} - -.dropdown-content input[type="text"] { - width: calc(90% - 20px); - padding: 5px; - margin: 10px; - border: 1px solid #ccc; -} - -.checkbox-container { - display: flex; - flex-direction: column; - padding: 10px; -} - -.checkbox-container label { - margin: 5px 0; -} - -/* Показываем выпадающий список при фокусе */ -.dropdown.open .dropdown-content { - display: block; -} - -input[type="range"] { - - width: 300px; - height: 8px; - background: #ddd; /* Цвет трека (линии) */ - border-radius: 5px; - outline: none; -} - - -.downArrow { - position: absolute; - right: 50px; - top: 50%; - transform: translateY(-50%); - width: 24px; - height: 24px; - pointer-events: none; -} +body { + background-color: #DEE9FE; + font-family: Arial, sans-serif; + color: #003366; + text-align: center; + font-family: 'Century Gothic', Arial, sans-serif; +} + +header { + margin-top: 20px; +} + +h1 { + font-size: 3rem; + color: #003366; +} + +.search-container { + display: flex; + justify-content: center; + margin: 20px 0; +} + +select { + font-family: 'Century Gothic', Arial, sans-serif; +} + +#searchInput { + font-family: 'Century Gothic', Arial, sans-serif; + padding: 10px; + width: 700px; /* Увеличенная ширина */ + border-radius: 20px; + border: 2px solid #003366; +} + +#searchButton, #returnButton { + font-family: 'Century Gothic', Arial, sans-serif; + margin-left: 10px; + padding: 10px 20px; + background-color: #003366; + color: white; + border: none; + border-radius: 20px; + cursor: pointer; +} + +.filters { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + padding: 20px; +} + +.filter-group { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #DEE9FE; + padding: 15px; + border-radius: 10px; + /* box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); */ + text-align: left; + border: 2px solid #003366; +} + +.rating { + text-align: left; /* Выравнивание по левому краю */ + margin-top: 15px; +} + +.rating label { + margin-left: 5px; +} + +.range-container { + display: flex; + align-items: center; + gap: 10px; +} +/* Цвет звездочек */ +.star { + color: #003366; + /* font-size: 1.5rem; */ +} + +/* Убедитесь, что цвета не переопределяются другими стилями */ +input[type="checkbox"]:checked + .star { + color: #003366; + font-family: 'Century Gothic', Arial, sans-serif; +} + + +input[type="number"] { + width: 80px; + padding: 5px; + border-radius: 5px; + border: 1px solid #ccc; + font-family: 'Century Gothic', Arial, sans-serif; +} + +input[type="text"] { + font-family: 'Century Gothic', Arial, sans-serif; +} + +#applyFilters { + font-family: 'Century Gothic', Arial, sans-serif; + margin-top: 20px; + padding: 10px 30px; + background-color: #003366; + color: white; + border: none; + border-radius: 20px; + cursor: pointer; +} + +/* Стили для кастомного выпадающего списка */ +.dropdown { + position: relative; + width: 90%; +} + +#regionInput, +#cityInput { + width: 90%; + padding: 10px; + border: 1px solid #003366; + border-radius: 5px; + cursor: pointer; +} + +.dropdown-content { + display: none; + position: absolute; + width: 93%; + max-height: 200px; + overflow-y: auto; + background-color: #fff; + border: 1px solid #003366; + border-radius: 5px; + z-index: 1; +} + +.dropdown-content input[type="text"] { + width: calc(90% - 20px); + padding: 5px; + margin: 10px; + border: 1px solid #ccc; +} + +.checkbox-container { + display: flex; + flex-direction: column; + padding: 10px; +} + +.checkbox-container label { + margin: 5px 0; +} + +/* Показываем выпадающий список при фокусе */ +.dropdown.open .dropdown-content { + display: block; +} + +input[type="range"] { + + width: 300px; + height: 8px; + background: #ddd; /* Цвет трека (линии) */ + border-radius: 5px; + outline: none; +} + + +.downArrow { + position: absolute; + right: 50px; + top: 50%; + transform: translateY(-50%); + width: 24px; + height: 24px; + pointer-events: none; +} \ No newline at end of file diff --git a/lake-catalog/src/main/resources/static/css/home_style.css b/src/main/resources/static/css/home_style.css similarity index 94% rename from lake-catalog/src/main/resources/static/css/home_style.css rename to src/main/resources/static/css/home_style.css index 4057ed1..171fe47 100644 --- a/lake-catalog/src/main/resources/static/css/home_style.css +++ b/src/main/resources/static/css/home_style.css @@ -1,65 +1,65 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: 'Century Gothic', Arial, sans-serif; /* Указание шрифта Century Gothic */ - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - background-color: #e6f0ff; -} - -.container { - text-align: center; - padding: 20px; - border-radius: 8px; -} - -.title { - font-size: 4rem; - color: #003366; - margin-bottom: 50px; -} - -.button-container { - display: flex; - flex-direction: column; - gap: 10px; - align-items: center; -} - -.btn { - width: 350px; - padding: 15px 20px; - font-size: 1rem; - cursor: pointer; - /* border-radius: 4px; */ - border: none; - transition: background-color 0.3s; - font-family: 'Century Gothic', Arial, sans-serif; /* Указываем шрифт для кнопок */ -} - - -.primary-btn { - background-color: #003366; - color: #ffffff; -} - -.primary-btn:hover { - background-color: #002244; -} - -.secondary-btn { - background-color: #e6f0ff; - color: #003366; - border: 2px solid #003366; - margin-top: 40px; -} - -.secondary-btn:hover { - background-color: #f0f8ff; -} +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Century Gothic', Arial, sans-serif; /* Указание шрифта Century Gothic */ + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #e6f0ff; +} + +.container { + text-align: center; + padding: 20px; + border-radius: 8px; +} + +.title { + font-size: 4rem; + color: #003366; + margin-bottom: 50px; +} + +.button-container { + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; +} + +.btn { + width: 350px; + padding: 15px 20px; + font-size: 1rem; + cursor: pointer; + /* border-radius: 4px; */ + border: none; + transition: background-color 0.3s; + font-family: 'Century Gothic', Arial, sans-serif; /* Указываем шрифт для кнопок */ +} + + +.primary-btn { + background-color: #003366; + color: #ffffff; +} + +.primary-btn:hover { + background-color: #002244; +} + +.secondary-btn { + background-color: #e6f0ff; + color: #003366; + border: 2px solid #003366; + margin-top: 40px; +} + +.secondary-btn:hover { + background-color: #f0f8ff; +} diff --git a/lake-catalog/src/main/resources/templates/import-export/import-export_styles.css b/src/main/resources/static/css/import-export_styles.css similarity index 94% rename from lake-catalog/src/main/resources/templates/import-export/import-export_styles.css rename to src/main/resources/static/css/import-export_styles.css index c9db430..3330db1 100644 --- a/lake-catalog/src/main/resources/templates/import-export/import-export_styles.css +++ b/src/main/resources/static/css/import-export_styles.css @@ -1,105 +1,105 @@ -/* Общие стили */ -body { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #e6f0ff; - margin: 0; - padding: 0; -} - -/* Контейнер */ -.container { - max-width: 1200px; - margin: 0 auto; - padding: 20px; -} - -/* Заголовки и кнопки */ -header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 30px; -} - -h1 { - font-size: 2.5rem; - color: #003366; -} - -button { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #003366; - color: white; - border: none; - padding: 10px 20px; - font-size: 1rem; - cursor: pointer; -} - -button:hover { - background-color: #003366; -} - -#back-button { - /* background-color: #ff6347; */ - font-size: 1.2rem; -} - -input { - font-family: 'Century Gothic', Arial, sans-serif; -} - -/* Стиль для аватара */ -.avatar { - width: 50px; - height: 50px; - border-radius: 50%; -} - -/* Секции Экспорт/Импорт */ -.export-import-container { - display: flex; - justify-content: space-between; - text-align: center; - padding: 0px 150px; -} - -.export-section, .import-section { - border: 2px solid #003366; - border-radius: 10px; - padding: 50px 20px; - width: 40%; - box-shadow: 0 4px 8px #0033661a; -} - -h2 { - font-size: 1.8rem; - margin-bottom: 10px; - margin: 0; - color: #003366; -} - - -.export-section p, .import-section p { - font-size: 1rem; - margin-bottom: 20px; - color: #003366; -} - -#file-upload { - display: none; -} - -.custom-file-upload { - background-color: transparent; - border: 2px solid #003366; - color: #003366; - padding: 8px 20px; - font-size: 1rem; - cursor: pointer; - display: inline-block; -} - -#profile-btn { - background-color: transparent; -} +/* Общие стили */ +body { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #e6f0ff; + margin: 0; + padding: 0; +} + +/* Контейнер */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +/* Заголовки и кнопки */ +header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30px; +} + +h1 { + font-size: 2.5rem; + color: #003366; +} + +button { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #003366; + color: white; + border: none; + padding: 10px 20px; + font-size: 1rem; + cursor: pointer; +} + +button:hover { + background-color: #003366; +} + +#back-button { + /* background-color: #ff6347; */ + font-size: 1.2rem; +} + +input { + font-family: 'Century Gothic', Arial, sans-serif; +} + +/* Стиль для аватара */ +.avatar { + width: 50px; + height: 50px; + border-radius: 50%; +} + +/* Секции Экспорт/Импорт */ +.export-import-container { + display: flex; + justify-content: space-between; + text-align: center; + padding: 0px 150px; +} + +.export-section, .import-section { + border: 2px solid #003366; + border-radius: 10px; + padding: 50px 20px; + width: 40%; + box-shadow: 0 4px 8px #0033661a; +} + +h2 { + font-size: 1.8rem; + margin-bottom: 10px; + margin: 0; + color: #003366; +} + + +.export-section p, .import-section p { + font-size: 1rem; + margin-bottom: 20px; + color: #003366; +} + +#file-upload { + display: none; +} + +.custom-file-upload { + background-color: transparent; + border: 2px solid #003366; + color: #003366; + padding: 8px 20px; + font-size: 1rem; + cursor: pointer; + display: inline-block; +} + +#profile-btn { + background-color: transparent; +} diff --git a/lake-catalog/src/main/resources/static/css/login_styles.css b/src/main/resources/static/css/login_styles.css similarity index 95% rename from lake-catalog/src/main/resources/static/css/login_styles.css rename to src/main/resources/static/css/login_styles.css index 01b3549..a66441e 100644 --- a/lake-catalog/src/main/resources/static/css/login_styles.css +++ b/src/main/resources/static/css/login_styles.css @@ -1,93 +1,93 @@ -body { - font-family: 'Century Gothic', Arial, sans-serif; - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - background-color: #e6f0ff; -} - -.login-container { - width: 100%; - padding: 200px; - text-align: center; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.login-title { - font-size: 3.5rem; - color: #003366; - margin-bottom: 50px; -} - -.back-btn { - font-size: 1rem; - padding: 8px 16px; - color: #003366; - background-color: transparent; - border: 2px solid #003366; - cursor: pointer; - margin-bottom: -120px; /* Чтобы кнопка "Назад" была ближе к заголовку */ - margin-left: 0; - align-self: flex-start; - font-family: 'Century Gothic', Arial, sans-serif; -} - -.back-btn:hover { - background-color: #f0f8ff; -} - -.login-form { - display: flex; - flex-direction: column; - width: 300px; - gap: 15px; -} - -.login-form label { - font-size: 1rem; - color: #003366; - text-align: left; -} - -.login-form input { - padding: 8px; - border: 1px solid #cccccc; - font-size: 1rem; -} - -.submit-btn { - background-color: #003366; - color: #ffffff; - padding: 10px; - border: none; - cursor: pointer; - font-size: 1rem; - font-family: 'Century Gothic', Arial, sans-serif; - margin-top: 40px; -} - -.submit-btn:hover { - background-color: #002244; -} - -.d-none { - display: none !important; - } - .alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; - font-size: 1rem; - } - - /* Красный alert для ошибок */ - .alert-danger { - color: #721c24; - background-color: #f8d7da; - border-color: #f5c6cb; +body { + font-family: 'Century Gothic', Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #e6f0ff; +} + +.login-container { + width: 100%; + padding: 200px; + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.login-title { + font-size: 3.5rem; + color: #003366; + margin-bottom: 50px; +} + +.back-btn { + font-size: 1rem; + padding: 8px 16px; + color: #003366; + background-color: transparent; + border: 2px solid #003366; + cursor: pointer; + margin-bottom: -120px; /* Чтобы кнопка "Назад" была ближе к заголовку */ + margin-left: 0; + align-self: flex-start; + font-family: 'Century Gothic', Arial, sans-serif; +} + +.back-btn:hover { + background-color: #f0f8ff; +} + +.login-form { + display: flex; + flex-direction: column; + width: 300px; + gap: 15px; +} + +.login-form label { + font-size: 1rem; + color: #003366; + text-align: left; +} + +.login-form input { + padding: 8px; + border: 1px solid #cccccc; + font-size: 1rem; +} + +.submit-btn { + background-color: #003366; + color: #ffffff; + padding: 10px; + border: none; + cursor: pointer; + font-size: 1rem; + font-family: 'Century Gothic', Arial, sans-serif; + margin-top: 40px; +} + +.submit-btn:hover { + background-color: #002244; +} + +.d-none { + display: none !important; + } + .alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; + font-size: 1rem; + } + + /* Красный alert для ошибок */ + .alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; } \ No newline at end of file diff --git a/lake-catalog/src/main/resources/static/css/main_styles.css b/src/main/resources/static/css/main_styles.css similarity index 95% rename from lake-catalog/src/main/resources/static/css/main_styles.css rename to src/main/resources/static/css/main_styles.css index 0f98494..74263a4 100644 --- a/lake-catalog/src/main/resources/static/css/main_styles.css +++ b/src/main/resources/static/css/main_styles.css @@ -1,162 +1,162 @@ -* { - font-family: 'Century Gothic', Arial, sans-serif; - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #E6F0FF; - display: flex; - flex-direction: column; - align-items: center; - min-height: 100vh; - margin: 0; -} - -header { - display: flex; - align-items: center; - width: 100%; - max-width: 1200px; - padding: 20px; -} - -.profile-btn { - border: none; -} - -.logo { - font-size: 2em; - font-weight: bold; - color: #003366; - flex-grow: 1; - text-align: center; -} - -.profile-link { - margin-left: auto; - text-decoration: none; - display: flex; - align-items: center; -} - -.profile-img { - width: 50px; - height: 50px; - border-radius: 50%; - object-fit: cover; - cursor: pointer; -} - -.search-bar { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - max-width: 1200px; - margin: 20px 0; -} - -.search-left { - display: flex; - align-items: center; - gap: 10px; -} - -.search-input, .map-search { - padding: 10px; - border: 1px solid #cccccc; - border-radius: 20px; - width: 300px; -} - -.filter-btn { - padding: 10px 20px; - background-color: #003366; - color: #FFFFFF; - border: none; - border-radius: 20px; - cursor: pointer; -} - -.stats-btn { - background-color: #003366; - color: #FFFFFF; - padding: 10px 20px; - border: none; - font-size: 1rem; - margin-bottom: 20px; - cursor: pointer; - align-self: flex-start; -} - -.main-content { - display: flex; - flex-direction: column; - align-items: center; - max-width: 1200px; - width: 100%; - flex-grow: 1; /* Заставляем main-content занимать оставшееся пространство */ -} - -.lake-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); - gap: 20px; - width: 100%; -} - -.lake-card { - display: block; /* Чтобы занимала всю карточку */ - background-color: #FFFFFF; - border-radius: 10px; - overflow: hidden; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - text-decoration: none; /* Убираем подчеркивание у ссылок */ -} - -.lake-card:hover { - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); -} - -.lake-image { - width: 100%; - height: 200px; - object-fit: cover; -} - -.lake-title { - padding: 10px; - font-size: 1rem; - color: #003366; - text-align: center; -} - -.pagination { - display: flex; - gap: 10px; - margin-top: auto; /* Прижимает пагинацию к низу, если main-content растягивается */ - padding-bottom: 20px; /* Добавьте немного отступа снизу для визуального пространства */ -} - -.pagination a { - text-decoration: none; - padding: 7px 10px; - background-color: #003366; - color: #FFFFFF; - font-size: 1rem; -} - -.pagination a:hover { - background-color: #002244; -} - -.page-link.active { - font-weight: bold; - color: #5f98d6; /* Синий цвет для выделенной страницы */ - background-color: #b6d7ff; /* Легкий фон для выделенной страницы */ - border-radius: 5px; - padding: 5px 10px; -} +* { + font-family: 'Century Gothic', Arial, sans-serif; + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #E6F0FF; + display: flex; + flex-direction: column; + align-items: center; + min-height: 100vh; + margin: 0; +} + +header { + display: flex; + align-items: center; + width: 100%; + max-width: 1200px; + padding: 20px; +} + +.profile-btn { + border: none; +} + +.logo { + font-size: 2em; + font-weight: bold; + color: #003366; + flex-grow: 1; + text-align: center; +} + +.profile-link { + margin-left: auto; + text-decoration: none; + display: flex; + align-items: center; +} + +.profile-img { + width: 50px; + height: 50px; + border-radius: 50%; + object-fit: cover; + cursor: pointer; +} + +.search-bar { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + max-width: 1200px; + margin: 20px 0; +} + +.search-left { + display: flex; + align-items: center; + gap: 10px; +} + +.search-input, .map-search { + padding: 10px; + border: 1px solid #cccccc; + border-radius: 20px; + width: 300px; +} + +.filter-btn { + padding: 10px 20px; + background-color: #003366; + color: #FFFFFF; + border: none; + border-radius: 20px; + cursor: pointer; +} + +.stats-btn { + background-color: #003366; + color: #FFFFFF; + padding: 10px 20px; + border: none; + font-size: 1rem; + margin-bottom: 20px; + cursor: pointer; + align-self: flex-start; +} + +.main-content { + display: flex; + flex-direction: column; + align-items: center; + max-width: 1200px; + width: 100%; + flex-grow: 1; /* Заставляем main-content занимать оставшееся пространство */ +} + +.lake-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 20px; + width: 100%; +} + +.lake-card { + display: block; /* Чтобы занимала всю карточку */ + background-color: #FFFFFF; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + text-decoration: none; /* Убираем подчеркивание у ссылок */ +} + +.lake-card:hover { + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); +} + +.lake-image { + width: 100%; + height: 200px; + object-fit: cover; +} + +.lake-title { + padding: 10px; + font-size: 1rem; + color: #003366; + text-align: center; +} + +.pagination { + display: flex; + gap: 10px; + margin-top: auto; /* Прижимает пагинацию к низу, если main-content растягивается */ + padding-bottom: 20px; /* Добавьте немного отступа снизу для визуального пространства */ +} + +.pagination a { + text-decoration: none; + padding: 7px 10px; + background-color: #003366; + color: #FFFFFF; + font-size: 1rem; +} + +.pagination a:hover { + background-color: #002244; +} + +.page-link.active { + font-weight: bold; + color: #5f98d6; /* Синий цвет для выделенной страницы */ + background-color: #b6d7ff; /* Легкий фон для выделенной страницы */ + border-radius: 5px; + padding: 5px 10px; +} diff --git a/lake-catalog/src/main/resources/static/css/profile_styles.css b/src/main/resources/static/css/profile_styles.css similarity index 94% rename from lake-catalog/src/main/resources/static/css/profile_styles.css rename to src/main/resources/static/css/profile_styles.css index 555dacb..99d3e5e 100644 --- a/lake-catalog/src/main/resources/static/css/profile_styles.css +++ b/src/main/resources/static/css/profile_styles.css @@ -1,263 +1,263 @@ -* { - font-family: 'Century Gothic', Arial, sans-serif; - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #DEE9FE; - color: #003366; -} - - - -.profile-container { - width: 85%; - max-width: 1000px; - margin: 20px auto; - padding: 20px; -} - -header { - display: flex; - justify-content: space-between; - align-items: center; - -} - -.profile-info { - display: flex; - align-items: center; - gap: 20px; - margin-top: 80px; - margin-bottom: 50px; -} - -.profile-photo { - width: 150px; - height: 150px; - border-radius: 50%; - object-fit: cover; -} - -.user-details { - flex-grow: 1; - margin-top: -60px; -} - -#edit-profile { - background: none; - border: none; - font-size: 24px; - cursor: pointer; -} - -.profile-dates { - text-align: right; - margin-top: 100px; - color: #666; -} - -#import-export-button, #back-button, #logout-button { - margin-top: 10px; - padding: 10px 20px; - background-color: #003366; - font-family: 'Century Gothic', Arial, sans-serif; - color: #fff; - border: none; - cursor: pointer; -} - -.tabs { - display: flex; - justify-content: space-around; - margin-top: 50px; - -} - -.tab-button { - padding: 10px; - font-weight: 600; - border: none; - cursor: pointer; - background-color: #DEE9FE; - color: #003366; - position: relative; -} - -.tab-button.active { - color: #003366; - font-weight: bold; -} - -.tab-button.active::after { - content: ""; - display: block; - width: 100%; - height: 3px; - background-color: #003366; - position: absolute; - bottom: -2px; - left: 0; -} - -.tab-content { - display: none; - margin-top: 20px; -} - -.tab-content.active { - display: block; -} - -.lake-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* Автоматическая адаптация */ - gap: 15px; - justify-items: center; /* Центрирование карточек */ -} - -.lake-card { - padding: 10px; - background-color: #cce5ff; - margin-bottom: 10px; - display: flex; - flex-direction: column; - align-items: center; - max-width: 200px; - border-radius: 8px; -} - -.lake-image { - width: 100%; - height: 150px; - object-fit: cover; - border-radius: 8px; - margin-bottom: 10px; -} - -.lake-title { - font-size: 16px; - text-align: center; - color: #003366; - font-weight: bold; -} - - -/* Стили для отзыва */ -.review { - display: flex; - flex-direction: column; - padding: 16px; - border: 1px solid #e0e0e0; - border-radius: 8px; - background-color: #fff; - margin-top: 16px; -} - -.review-user { - display: flex; - align-items: center; - margin-bottom: 8px; -} - -.user-photo { - width: 64px; - height: 64px; - border-radius: 50%; - object-fit: cover; - margin-right: 12px; -} - -.user-info { - display: flex; - flex-direction: column; -} - -.user-info h4 { - font-size: 18px; - margin: 0; -} - -.user-info p { - font-size: 14px; - color: #888; - margin: 4px 0 0; -} - -.review-content { - display: flex; - flex-direction: column; -} - -.review-text { - font-size: 16px; - /* margin-bottom: 8px; */ -} - -.review-rating { - font-size: 20px; - color: #ffc107; /* Желтый цвет для звезд */ - margin-bottom: 10px; -} - -.icon-button { - margin-left: 120px; - background: none; - border: none; - cursor: pointer; - padding: 0; - width: 32px; - height: 32px; -} - -.icon { - height: 24px; - width: 24px; -} - -#edit-profile { - width: 24px; - height: 24px; - margin-left: 250px; -} - -/* Основные стили */ -.user-details { - display: flex; - flex-direction: column; - align-items: flex-start; -} - -/* Поля ввода */ -#name-input, #email-input { - margin-top: 10px; - padding: 8px; - width: 100%; - font-size: 16px; - border: 1px solid #ccc; - border-radius: 4px; - display: none; /* Скрыты по умолчанию */ -} - - -#edit-profile { - background-color: none; - color: white; -} - -#save-profile { - background-color: #003366; - color: white; - margin-top: 10px; - padding: 10px 15px; - border: none; - cursor: pointer; - font-size: 16px; -} - -button:hover { - opacity: 0.9; -} +* { + font-family: 'Century Gothic', Arial, sans-serif; + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #DEE9FE; + color: #003366; +} + + + +.profile-container { + width: 85%; + max-width: 1000px; + margin: 20px auto; + padding: 20px; +} + +header { + display: flex; + justify-content: space-between; + align-items: center; + +} + +.profile-info { + display: flex; + align-items: center; + gap: 20px; + margin-top: 80px; + margin-bottom: 50px; +} + +.profile-photo { + width: 150px; + height: 150px; + border-radius: 50%; + object-fit: cover; +} + +.user-details { + flex-grow: 1; + margin-top: -60px; +} + +#edit-profile { + background: none; + border: none; + font-size: 24px; + cursor: pointer; +} + +.profile-dates { + text-align: right; + margin-top: 100px; + color: #666; +} + +#import-export-button, #back-button, #logout-button { + margin-top: 10px; + padding: 10px 20px; + background-color: #003366; + font-family: 'Century Gothic', Arial, sans-serif; + color: #fff; + border: none; + cursor: pointer; +} + +.tabs { + display: flex; + justify-content: space-around; + margin-top: 50px; + +} + +.tab-button { + padding: 10px; + font-weight: 600; + border: none; + cursor: pointer; + background-color: #DEE9FE; + color: #003366; + position: relative; +} + +.tab-button.active { + color: #003366; + font-weight: bold; +} + +.tab-button.active::after { + content: ""; + display: block; + width: 100%; + height: 3px; + background-color: #003366; + position: absolute; + bottom: -2px; + left: 0; +} + +.tab-content { + display: none; + margin-top: 20px; +} + +.tab-content.active { + display: block; +} + +.lake-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* Автоматическая адаптация */ + gap: 15px; + justify-items: center; /* Центрирование карточек */ +} + +.lake-card { + padding: 10px; + background-color: #cce5ff; + margin-bottom: 10px; + display: flex; + flex-direction: column; + align-items: center; + max-width: 200px; + border-radius: 8px; +} + +.lake-image { + width: 100%; + height: 150px; + object-fit: cover; + border-radius: 8px; + margin-bottom: 10px; +} + +.lake-title { + font-size: 16px; + text-align: center; + color: #003366; + font-weight: bold; +} + + +/* Стили для отзыва */ +.review { + display: flex; + flex-direction: column; + padding: 16px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background-color: #fff; + margin-top: 16px; +} + +.review-user { + display: flex; + align-items: center; + margin-bottom: 8px; +} + +.user-photo { + width: 64px; + height: 64px; + border-radius: 50%; + object-fit: cover; + margin-right: 12px; +} + +.user-info { + display: flex; + flex-direction: column; +} + +.user-info h4 { + font-size: 18px; + margin: 0; +} + +.user-info p { + font-size: 14px; + color: #888; + margin: 4px 0 0; +} + +.review-content { + display: flex; + flex-direction: column; +} + +.review-text { + font-size: 16px; + /* margin-bottom: 8px; */ +} + +.review-rating { + font-size: 20px; + color: #ffc107; /* Желтый цвет для звезд */ + margin-bottom: 10px; +} + +.icon-button { + margin-left: 120px; + background: none; + border: none; + cursor: pointer; + padding: 0; + width: 32px; + height: 32px; +} + +.icon { + height: 24px; + width: 24px; +} + +#edit-profile { + width: 24px; + height: 24px; + margin-left: 250px; +} + +/* Основные стили */ +.user-details { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +/* Поля ввода */ +#name-input, #email-input { + margin-top: 10px; + padding: 8px; + width: 100%; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 4px; + display: none; /* Скрыты по умолчанию */ +} + + +#edit-profile { + background-color: none; + color: white; +} + +#save-profile { + background-color: #003366; + color: white; + margin-top: 10px; + padding: 10px 15px; + border: none; + cursor: pointer; + font-size: 16px; +} + +button:hover { + opacity: 0.9; +} diff --git a/lake-catalog/src/main/resources/static/css/register_styles.css b/src/main/resources/static/css/register_styles.css similarity index 94% rename from lake-catalog/src/main/resources/static/css/register_styles.css rename to src/main/resources/static/css/register_styles.css index a4b7cce..77b855b 100644 --- a/lake-catalog/src/main/resources/static/css/register_styles.css +++ b/src/main/resources/static/css/register_styles.css @@ -1,97 +1,97 @@ -body { - font-family: 'Century Gothic', Arial, sans-serif; - display: flex; - justify-content: center; - align-items: center; - max-height: 100vh; - background-color: #e6f0ff; -} - -.register-container { - width: 100%; - padding: 200px; - text-align: center; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.register-top { - /* width: 100%; */ - display: flex; -} -.d-none { - display: none !important; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; - font-size: 1rem; -} - -/* Красный alert для ошибок */ -.alert-danger { - color: #721c24; - background-color: #f8d7da; - border-color: #f5c6cb; -} -.register-title { - font-size: 3.5rem; - color: #003366; - margin-bottom: 50px; -} - -.back-btn { - font-size: 1rem; - padding: 8px 16px; - color: #003366; - background-color: transparent; - border: 2px solid #003366; - cursor: pointer; - margin-bottom: -120px; - margin-left: 0; - align-self: flex-start; - font-family: 'Century Gothic', Arial, sans-serif; -} - - -.back-btn:hover { - background-color: #f0f8ff; -} - -.register-form { - display: flex; - flex-direction: column; - width: 300px; - gap: 15px; -} - -.register-form label { - font-size: 1rem; - color: #003366; - text-align: left; -} - -.register-form input { - padding: 8px; - border: 1px solid #cccccc; - font-size: 1rem; -} - -.submit-btn { - background-color: #003366; - color: #ffffff; - padding: 10px; - border: none; - cursor: pointer; - font-size: 1rem; - font-family: 'Century Gothic', Arial, sans-serif; - margin-top: 40px; -} - -.submit-btn:hover { - background-color: #002244; -} +body { + font-family: 'Century Gothic', Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + max-height: 100vh; + background-color: #e6f0ff; +} + +.register-container { + width: 100%; + padding: 200px; + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.register-top { + /* width: 100%; */ + display: flex; +} +.d-none { + display: none !important; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; + font-size: 1rem; +} + +/* Красный alert для ошибок */ +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} +.register-title { + font-size: 3.5rem; + color: #003366; + margin-bottom: 50px; +} + +.back-btn { + font-size: 1rem; + padding: 8px 16px; + color: #003366; + background-color: transparent; + border: 2px solid #003366; + cursor: pointer; + margin-bottom: -120px; + margin-left: 0; + align-self: flex-start; + font-family: 'Century Gothic', Arial, sans-serif; +} + + +.back-btn:hover { + background-color: #f0f8ff; +} + +.register-form { + display: flex; + flex-direction: column; + width: 300px; + gap: 15px; +} + +.register-form label { + font-size: 1rem; + color: #003366; + text-align: left; +} + +.register-form input { + padding: 8px; + border: 1px solid #cccccc; + font-size: 1rem; +} + +.submit-btn { + background-color: #003366; + color: #ffffff; + padding: 10px; + border: none; + cursor: pointer; + font-size: 1rem; + font-family: 'Century Gothic', Arial, sans-serif; + margin-top: 40px; +} + +.submit-btn:hover { + background-color: #002244; +} diff --git a/lake-catalog/src/main/resources/templates/card/card.html b/src/main/resources/templates/card/card.html similarity index 97% rename from lake-catalog/src/main/resources/templates/card/card.html rename to src/main/resources/templates/card/card.html index 1772e5c..1196ba2 100644 --- a/lake-catalog/src/main/resources/templates/card/card.html +++ b/src/main/resources/templates/card/card.html @@ -1,121 +1,121 @@ - - - - - - - - - - - -
- -
- -

-
- - -
-
- - -
- Left Arrow - - Lake Image - - Right Arrow -
- - -
-

Описание

-

-
- - -
-

Справочная информация:

-
    -
  • Максимальная глубина: 0
  • -
  • Площадь: 0
  • -
  • Субъект РФ: Неизвестно
  • -
  • Город: Неизвестно
  • -
  • Рейтинг: 0
  • -
-
- - -
-

Местоположение

-
-
- - -
-

Отзывы

-
- - - - - -
-
- - -
-
-
-
- - - - - - - + + + + + + + + + + + +
+ +
+ +

+
+ + +
+
+ + +
+ Left Arrow + + Lake Image + + Right Arrow +
+ + +
+

Описание

+

+
+ + +
+

Справочная информация:

+
    +
  • Максимальная глубина: 0
  • +
  • Площадь: 0
  • +
  • Субъект РФ: Неизвестно
  • +
  • Город: Неизвестно
  • +
  • Рейтинг: 0
  • +
+
+ + +
+

Местоположение

+
+
+ + +
+

Отзывы

+
+ + + + + +
+
+ + +
+
+
+
+ + + + + + + diff --git a/lake-catalog/src/main/resources/templates/card/card_script.js b/src/main/resources/templates/card/card_script.js similarity index 97% rename from lake-catalog/src/main/resources/templates/card/card_script.js rename to src/main/resources/templates/card/card_script.js index 4c06e48..f6abbb0 100644 --- a/lake-catalog/src/main/resources/templates/card/card_script.js +++ b/src/main/resources/templates/card/card_script.js @@ -1,163 +1,163 @@ -// Простой слайдер изображений с использованием стрелок -const images = document.querySelectorAll('.slider-image'); -const leftArrow = document.querySelector('.left-arrow'); -const rightArrow = document.querySelector('.right-arrow'); -let currentIndex = 0; - -function showImage(index) { - images.forEach((img, i) => { - img.style.display = i === index ? 'block' : 'none'; - }); -} - -leftArrow.addEventListener('click', () => { - currentIndex = (currentIndex - 1 + images.length) % images.length; - showImage(currentIndex); -}); - -rightArrow.addEventListener('click', () => { - currentIndex = (currentIndex + 1) % images.length; - showImage(currentIndex); -}); - -showImage(currentIndex); - -// Обработчик кнопки "На главную" -const returnButton = document.getElementById("back-button"); -returnButton.addEventListener("click", () => { - window.location.href = '/main'; -}); - -// Обработчики для кнопок "хочу посетить" и "уже посетил" -const heartButton = document.getElementById('heart-button'); -const heartIcon = document.getElementById('heart-icon'); -let isHeartFilled = false; -heartButton.addEventListener('click', () => { - isHeartFilled = !isHeartFilled; - heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; - alert(isHeartFilled ? 'Добавлено в "Хочу посетить"' : 'Удалено из "Хочу посетить"'); -}); - -const eyeButton = document.getElementById('eye-button'); -const eyeIcon = document.getElementById('eye-icon'); -let isEyeFilled = false; -eyeButton.addEventListener('click', () => { - isEyeFilled = !isEyeFilled; - eyeIcon.src = isEyeFilled ? '/assets/eye_filled.png' : '/assets/eye.png'; - alert(isEyeFilled ? 'Добавлено в "Уже посетил"' : 'Удалено из "Уже посетил"'); -}); - -const submitButton = document.getElementById('submit-review-btn'); -const reviewInput = document.getElementById('review-input'); -const starRating = document.getElementById('star-rating'); - -const lakeId = document.getElementById('lake-id').value; // Получаем lakeId из HTML - -let selectedValue = 0; - -submitButton.addEventListener('click', () => { - // Проверяем, выбрал ли пользователь количество звезд и написал ли отзыв - if (selectedValue === 0 || reviewInput.value.trim() === '') { - alert('Пожалуйста, оцените озеро и напишите отзыв!'); - return; - } - - const reviewText = reviewInput.value.trim(); - const reviewData = { - text: reviewText, - rating: selectedValue - }; - - // Отправляем отзыв на сервер через fetch - fetch(`/lakes/lake_page/${lakeId}/reviews`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + sessionStorage.getItem('authToken') // Пример использования токена авторизации - }, - body: JSON.stringify(reviewData), // Отправляем данные как JSON - }) - .then(response => { - if (!response.ok) { - return response.text().then(errorText => { - throw new Error(errorText || 'Ошибка при добавлении отзыва'); - }); - } - return response.json(); - }) - .then(review => { - // После успешного добавления отзыва, отобразим его - const reviewElement = document.createElement('div'); - reviewElement.classList.add('review'); - reviewElement.innerHTML = ` -
- Фото пользователя - -
-
-
- ${'★'.repeat(review.rating)} -
-
-

${review.text}

-
-
- `; - document.getElementById('reviews-list').appendChild(reviewElement); - reviewInput.value = ''; - // Сброс активных звезд после публикации отзыва - starRating.querySelectorAll('span').forEach(span => span.classList.remove('active')); - selectedValue = 0; // Сброс значения звезд - }) - .catch(error => { - alert(error.message); - }); -}); - - -// Обработчик для клика на звезды -starRating.addEventListener('click', (e) => { - const clickedSpan = e.target; - if (clickedSpan.dataset.value) { - selectedValue = parseInt(clickedSpan.dataset.value, 10); // Сохраняем выбранное количество звезд - // Обновляем все звезды в соответствии с выбранной - starRating.querySelectorAll('span').forEach(span => { - if (parseInt(span.dataset.value, 10) <= selectedValue) { - span.classList.add('active'); - } else { - span.classList.remove('active'); - } - }); - } -}); - -// Обработчик для наведения -starRating.addEventListener('mouseover', (e) => { - const hoveredSpan = e.target; - if (hoveredSpan.dataset.value) { - const value = parseInt(hoveredSpan.dataset.value, 10); - // Подсвечиваем все звезды до текущей при наведении - starRating.querySelectorAll('span').forEach(span => { - if (parseInt(span.dataset.value, 10) <= value) { - span.classList.add('active'); - } else { - span.classList.remove('active'); - } - }); - } -}); - -// Обработчик для отмены подсветки при уходе мыши -starRating.addEventListener('mouseout', () => { - // После ухода мыши, возвращаем закраску в соответствии с выбранной звездой - starRating.querySelectorAll('span').forEach(span => { - if (parseInt(span.dataset.value, 10) <= selectedValue) { - span.classList.add('active'); // Подсвечиваем все выбранные звезды - } else { - span.classList.remove('active'); - } - }); -}); +// Простой слайдер изображений с использованием стрелок +const images = document.querySelectorAll('.slider-image'); +const leftArrow = document.querySelector('.left-arrow'); +const rightArrow = document.querySelector('.right-arrow'); +let currentIndex = 0; + +function showImage(index) { + images.forEach((img, i) => { + img.style.display = i === index ? 'block' : 'none'; + }); +} + +leftArrow.addEventListener('click', () => { + currentIndex = (currentIndex - 1 + images.length) % images.length; + showImage(currentIndex); +}); + +rightArrow.addEventListener('click', () => { + currentIndex = (currentIndex + 1) % images.length; + showImage(currentIndex); +}); + +showImage(currentIndex); + +// Обработчик кнопки "На главную" +const returnButton = document.getElementById("back-button"); +returnButton.addEventListener("click", () => { + window.location.href = '/main'; +}); + +// Обработчики для кнопок "хочу посетить" и "уже посетил" +const heartButton = document.getElementById('heart-button'); +const heartIcon = document.getElementById('heart-icon'); +let isHeartFilled = false; +heartButton.addEventListener('click', () => { + isHeartFilled = !isHeartFilled; + heartIcon.src = isHeartFilled ? '/assets/heart_filled.png' : '/assets/heart.png'; + alert(isHeartFilled ? 'Добавлено в "Хочу посетить"' : 'Удалено из "Хочу посетить"'); +}); + +const eyeButton = document.getElementById('eye-button'); +const eyeIcon = document.getElementById('eye-icon'); +let isEyeFilled = false; +eyeButton.addEventListener('click', () => { + isEyeFilled = !isEyeFilled; + eyeIcon.src = isEyeFilled ? '/assets/eye_filled.png' : '/assets/eye.png'; + alert(isEyeFilled ? 'Добавлено в "Уже посетил"' : 'Удалено из "Уже посетил"'); +}); + +const submitButton = document.getElementById('submit-review-btn'); +const reviewInput = document.getElementById('review-input'); +const starRating = document.getElementById('star-rating'); + +const lakeId = document.getElementById('lake-id').value; // Получаем lakeId из HTML + +let selectedValue = 0; + +submitButton.addEventListener('click', () => { + // Проверяем, выбрал ли пользователь количество звезд и написал ли отзыв + if (selectedValue === 0 || reviewInput.value.trim() === '') { + alert('Пожалуйста, оцените озеро и напишите отзыв!'); + return; + } + + const reviewText = reviewInput.value.trim(); + const reviewData = { + text: reviewText, + rating: selectedValue + }; + + // Отправляем отзыв на сервер через fetch + fetch(`/lakes/lake_page/${lakeId}/reviews`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + sessionStorage.getItem('authToken') // Пример использования токена авторизации + }, + body: JSON.stringify(reviewData), // Отправляем данные как JSON + }) + .then(response => { + if (!response.ok) { + return response.text().then(errorText => { + throw new Error(errorText || 'Ошибка при добавлении отзыва'); + }); + } + return response.json(); + }) + .then(review => { + // После успешного добавления отзыва, отобразим его + const reviewElement = document.createElement('div'); + reviewElement.classList.add('review'); + reviewElement.innerHTML = ` +
+ Фото пользователя + +
+
+
+ ${'★'.repeat(review.rating)} +
+
+

${review.text}

+
+
+ `; + document.getElementById('reviews-list').appendChild(reviewElement); + reviewInput.value = ''; + // Сброс активных звезд после публикации отзыва + starRating.querySelectorAll('span').forEach(span => span.classList.remove('active')); + selectedValue = 0; // Сброс значения звезд + }) + .catch(error => { + alert(error.message); + }); +}); + + +// Обработчик для клика на звезды +starRating.addEventListener('click', (e) => { + const clickedSpan = e.target; + if (clickedSpan.dataset.value) { + selectedValue = parseInt(clickedSpan.dataset.value, 10); // Сохраняем выбранное количество звезд + // Обновляем все звезды в соответствии с выбранной + starRating.querySelectorAll('span').forEach(span => { + if (parseInt(span.dataset.value, 10) <= selectedValue) { + span.classList.add('active'); + } else { + span.classList.remove('active'); + } + }); + } +}); + +// Обработчик для наведения +starRating.addEventListener('mouseover', (e) => { + const hoveredSpan = e.target; + if (hoveredSpan.dataset.value) { + const value = parseInt(hoveredSpan.dataset.value, 10); + // Подсвечиваем все звезды до текущей при наведении + starRating.querySelectorAll('span').forEach(span => { + if (parseInt(span.dataset.value, 10) <= value) { + span.classList.add('active'); + } else { + span.classList.remove('active'); + } + }); + } +}); + +// Обработчик для отмены подсветки при уходе мыши +starRating.addEventListener('mouseout', () => { + // После ухода мыши, возвращаем закраску в соответствии с выбранной звездой + starRating.querySelectorAll('span').forEach(span => { + if (parseInt(span.dataset.value, 10) <= selectedValue) { + span.classList.add('active'); // Подсвечиваем все выбранные звезды + } else { + span.classList.remove('active'); + } + }); +}); diff --git a/lake-catalog/src/main/resources/static/css/card_styles.css b/src/main/resources/templates/card/card_styles.css similarity index 94% rename from lake-catalog/src/main/resources/static/css/card_styles.css rename to src/main/resources/templates/card/card_styles.css index ef41884..4c570d9 100644 --- a/lake-catalog/src/main/resources/static/css/card_styles.css +++ b/src/main/resources/templates/card/card_styles.css @@ -1,241 +1,241 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; - font-family: 'Century Gothic', Arial, sans-serif; -} - -body { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #DEE9FE; - color: #003366; -} - -.container { - max-width: 1000px; - margin: 0 auto; - padding: 20px; -} - -.lake-header { - display: flex; - align-items: center; - justify-content: space-between; - margin: 20px 0; - position: relative; -} - -.back-button { - padding: 10px 20px; - background-color: #003366; - color: white; - border: none; - cursor: pointer; -} - -.lake-title { - font-size: 2em; - color: #003366; -} - -.icon-container { - display: flex; - gap: 15px; -} - -.icon-button { - background: none; - border: none; - cursor: pointer; - padding: 0; -} - -.icon { - width: 30px; - height: 30px; -} - - -.slider { - display: flex; - align-items: center; - justify-content: center; - gap: 10px; - margin: 50px 60px; - position: relative; -} - -.arrow-button { - width: 32px; - height: 32px; - cursor: pointer; - user-select: none; -} - -.left-arrow { - position: absolute; - left: -60px; - top: 50%; - transform: translateY(-50%); -} - -.right-arrow { - position: absolute; - right: -60px; - top: 50%; - transform: translateY(-50%); -} - -.slider-image { - max-width: 100%; - height: auto; -} - - -.description, .info, .map, .reviews { - margin: 40px 0; -} - -.info { - line-height: 2; -} - -h2 { - margin-bottom: 20px; -} - -ul { - list-style: none; - padding-left: 0; -} - -.map iframe { - width: 100%; - height: 300px; - border: 0; - border-radius: 10px; -} - -.star-rating { - display: flex; - gap: 5px; - cursor: pointer; - margin-bottom: 10px; -} - -.star-rating span { - font-size: 24px; - color: #ccc; /* Серый цвет для неактивных звезд */ - transition: color 0.2s; -} - -.star-rating span.active { - color: #f39c12; /* Оранжевый цвет для активных звезд */ -} - -.star-rating span:hover, -.star-rating span:hover ~ span { - color: #ccc; /* Убираем цвет для звезд справа при наведении */ -} - -.star-rating span:hover, -.star-rating span:hover ~ .star-rating span { - color: #f39c12; -} - -textarea { - font-family: 'Century Gothic', Arial, sans-serif; - width: 100%; - padding: 10px; - border-radius: 5px; - border: 1px solid #003366; - margin-bottom: 20px; -} - -/* Стили для отзыва */ -.review { - display: flex; - flex-direction: column; - padding: 16px; - border: 1px solid #e0e0e0; - border-radius: 8px; - background-color: #fff; - margin-top: 16px; -} - -.review-input-container { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 16px; -} - -#review-input { - flex: 1; - padding: 10px; - border: 1px solid #ccc; - border-radius: 8px; - font-size: 16px; -} - -#submit-review-btn { - padding: 10px 16px; - border: none; - border-radius: 8px; - background-color: #003366; - color: #fff; - font-size: 16px; - cursor: pointer; - transition: background-color 0.2s; -} - -#submit-review-btn:hover { - background-color: #012d5a; -} - -.review-user { - display: flex; - align-items: center; - margin-bottom: 8px; -} - -.user-photo { - width: 64px; - height: 64px; - border-radius: 50%; - object-fit: cover; - margin-right: 12px; -} - -.user-info { - display: flex; - flex-direction: column; -} - -.user-info h4 { - font-size: 18px; - margin: 0; -} - -.user-info p { - font-size: 14px; - color: #888; - margin: 4px 0 0; -} - -.review-content { - display: flex; - flex-direction: column; -} - -.review-text { - font-size: 16px; - /* margin-bottom: 8px; */ -} - -.review-rating { - font-size: 20px; - color: #ffc107; /* Желтый цвет для звезд */ - margin-bottom: 10px; -} - +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Century Gothic', Arial, sans-serif; +} + +body { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #DEE9FE; + color: #003366; +} + +.container { + max-width: 1000px; + margin: 0 auto; + padding: 20px; +} + +.lake-header { + display: flex; + align-items: center; + justify-content: space-between; + margin: 20px 0; + position: relative; +} + +.back-button { + padding: 10px 20px; + background-color: #003366; + color: white; + border: none; + cursor: pointer; +} + +.lake-title { + font-size: 2em; + color: #003366; +} + +.icon-container { + display: flex; + gap: 15px; +} + +.icon-button { + background: none; + border: none; + cursor: pointer; + padding: 0; +} + +.icon { + width: 30px; + height: 30px; +} + + +.slider { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + margin: 50px 60px; + position: relative; +} + +.arrow-button { + width: 32px; + height: 32px; + cursor: pointer; + user-select: none; +} + +.left-arrow { + position: absolute; + left: -60px; + top: 50%; + transform: translateY(-50%); +} + +.right-arrow { + position: absolute; + right: -60px; + top: 50%; + transform: translateY(-50%); +} + +.slider-image { + max-width: 100%; + height: auto; +} + + +.description, .info, .map, .reviews { + margin: 40px 0; +} + +.info { + line-height: 2; +} + +h2 { + margin-bottom: 20px; +} + +ul { + list-style: none; + padding-left: 0; +} + +.map iframe { + width: 100%; + height: 300px; + border: 0; + border-radius: 10px; +} + +.star-rating { + display: flex; + gap: 5px; + cursor: pointer; + margin-bottom: 10px; +} + +.star-rating span { + font-size: 24px; + color: #ccc; /* Серый цвет для неактивных звезд */ + transition: color 0.2s; +} + +.star-rating span.active { + color: #f39c12; /* Оранжевый цвет для активных звезд */ +} + +.star-rating span:hover, +.star-rating span:hover ~ span { + color: #ccc; /* Убираем цвет для звезд справа при наведении */ +} + +.star-rating span:hover, +.star-rating span:hover ~ .star-rating span { + color: #f39c12; +} + +textarea { + font-family: 'Century Gothic', Arial, sans-serif; + width: 100%; + padding: 10px; + border-radius: 5px; + border: 1px solid #003366; + margin-bottom: 20px; +} + +/* Стили для отзыва */ +.review { + display: flex; + flex-direction: column; + padding: 16px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background-color: #fff; + margin-top: 16px; +} + +.review-input-container { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; +} + +#review-input { + flex: 1; + padding: 10px; + border: 1px solid #ccc; + border-radius: 8px; + font-size: 16px; +} + +#submit-review-btn { + padding: 10px 16px; + border: none; + border-radius: 8px; + background-color: #003366; + color: #fff; + font-size: 16px; + cursor: pointer; + transition: background-color 0.2s; +} + +#submit-review-btn:hover { + background-color: #012d5a; +} + +.review-user { + display: flex; + align-items: center; + margin-bottom: 8px; +} + +.user-photo { + width: 64px; + height: 64px; + border-radius: 50%; + object-fit: cover; + margin-right: 12px; +} + +.user-info { + display: flex; + flex-direction: column; +} + +.user-info h4 { + font-size: 18px; + margin: 0; +} + +.user-info p { + font-size: 14px; + color: #888; + margin: 4px 0 0; +} + +.review-content { + display: flex; + flex-direction: column; +} + +.review-text { + font-size: 16px; + /* margin-bottom: 8px; */ +} + +.review-rating { + font-size: 20px; + color: #ffc107; /* Желтый цвет для звезд */ + margin-bottom: 10px; +} + diff --git a/lake-catalog/src/main/resources/templates/filtration/filtration.html b/src/main/resources/templates/filtration/filtration.html similarity index 98% rename from lake-catalog/src/main/resources/templates/filtration/filtration.html rename to src/main/resources/templates/filtration/filtration.html index a46ed82..0b043e5 100644 --- a/lake-catalog/src/main/resources/templates/filtration/filtration.html +++ b/src/main/resources/templates/filtration/filtration.html @@ -1,140 +1,140 @@ - - - - - - Фильтрация озер - - - - -
-

Поиск

-
- -
-
- - - -
- -
-
- - -
- -
- - -
- -
- -
- -
- -
- - -
- - -
- - -
- - - - - -
-
- -
- -
- -
- - -
- 0 - - 100 -
-
- -
- - -
- 0 - - 1000 -
-
- - -
-
- - - - + + + + + + Фильтрация озер + + + + +
+

Поиск

+
+ +
+
+ + + +
+ +
+
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ + +
+ + +
+ + + + + +
+
+ +
+ +
+ +
+ + +
+ 0 + + 100 +
+
+ +
+ + +
+ 0 + + 1000 +
+
+ + +
+
+ + + + diff --git a/lake-catalog/src/main/resources/templates/filtration/filtration_script.js b/src/main/resources/templates/filtration/filtration_script.js similarity index 97% rename from lake-catalog/src/main/resources/templates/filtration/filtration_script.js rename to src/main/resources/templates/filtration/filtration_script.js index 3e23f47..66795a5 100644 --- a/lake-catalog/src/main/resources/templates/filtration/filtration_script.js +++ b/src/main/resources/templates/filtration/filtration_script.js @@ -1,108 +1,108 @@ -document.addEventListener("DOMContentLoaded", () => { - const searchButton = document.getElementById("searchButton"); - const returnButton = document.getElementById("returnButton"); - const applyFiltersButton = document.getElementById("applyFilters"); - - const depthRange = document.getElementById("depthRange"); - const depthInput = document.getElementById("depthInput"); - const areaRange = document.getElementById("areaRange"); - const areaInput = document.getElementById("areaInput"); - const regionInput = document.getElementById("regionInput"); - const regionList = document.getElementById("regionList"); - const regionSearch = document.getElementById("regionSearch"); - const cityInput = document.getElementById("cityInput"); - const cityList = document.getElementById("cityList"); - const citySearch = document.getElementById("citySearch"); - - // Функция для открытия и закрытия выпадающего списка - function toggleDropdown(input, list) { - input.parentElement.classList.toggle("open"); - list.classList.toggle("show"); - } - - // Функция для фильтрации списка по поиску - function filterList(searchInput, container) { - const filter = searchInput.value.toLowerCase(); - const labels = container.querySelectorAll("label"); - labels.forEach(label => { - const text = label.textContent.toLowerCase(); - label.style.display = text.includes(filter) ? "block" : "none"; - }); - } - - // Функция для обновления текста в инпуте на основании выбранных чекбоксов - function updateInputText(input, container) { - const selected = Array.from(container.querySelectorAll("input[type='checkbox']:checked")) - .map(checkbox => checkbox.value); - input.value = selected.join(", "); - } - - // Обработчики для региона - regionInput.addEventListener("click", () => toggleDropdown(regionInput, regionList)); - regionSearch.addEventListener("input", () => filterList(regionSearch, regionList)); - regionList.addEventListener("change", () => updateInputText(regionInput, regionList)); - - // Обработчики для города - cityInput.addEventListener("click", () => toggleDropdown(cityInput, cityList)); - citySearch.addEventListener("input", () => filterList(citySearch, cityList)); - cityList.addEventListener("change", () => updateInputText(cityInput, cityList)); - - // Закрытие выпадающих списков при клике вне их области - document.addEventListener("click", (event) => { - if (!regionInput.parentElement.contains(event.target)) { - regionInput.parentElement.classList.remove("open"); - } - if (!cityInput.parentElement.contains(event.target)) { - cityInput.parentElement.classList.remove("open"); - } - }); - // Обработчик для синхронизации значения глубины (range и input) - depthRange.addEventListener("input", () => { - depthInput.value = depthRange.value; - }); - - depthInput.addEventListener("input", () => { - // Проверка, чтобы значение было в допустимых пределах - if (depthInput.value < depthRange.min) { - depthInput.value = depthRange.min; - } else if (depthInput.value > depthRange.max) { - depthInput.value = depthRange.max; - } - depthRange.value = depthInput.value; - }); - - // Обработчик для синхронизации значения площади (range и input) - areaRange.addEventListener("input", () => { - areaInput.value = areaRange.value; - }); - - areaInput.addEventListener("input", () => { - // Проверка, чтобы значение было в допустимых пределах - if (areaInput.value < areaRange.min) { - areaInput.value = areaRange.min; - } else if (areaInput.value > areaRange.max) { - areaInput.value = areaRange.max; - } - areaRange.value = areaInput.value; - }); - - // Обработчик кнопки поиска - // searchButton.addEventListener("click", () => { - // const searchQuery = document.getElementById("searchInput").value; - // alert(`Ищем: ${searchQuery}`); - // }); - - // Обработчик кнопки применения фильтров - searchButton.addEventListener("click", () => { - const region = document.getElementById("regionSelect").value; - const city = document.getElementById("citySelect").value; - const depth = depthRange.value; - const area = areaRange.value; - - alert(`Фильтры применены:\nРегион: ${region}\nГород: ${city}\nГлубина: ${depth} м\nПлощадь: ${area} км²`); - }); - returnButton.addEventListener("click", () => { - window.location.href = '/main'; - }); -}); - +document.addEventListener("DOMContentLoaded", () => { + const searchButton = document.getElementById("searchButton"); + const returnButton = document.getElementById("returnButton"); + const applyFiltersButton = document.getElementById("applyFilters"); + + const depthRange = document.getElementById("depthRange"); + const depthInput = document.getElementById("depthInput"); + const areaRange = document.getElementById("areaRange"); + const areaInput = document.getElementById("areaInput"); + const regionInput = document.getElementById("regionInput"); + const regionList = document.getElementById("regionList"); + const regionSearch = document.getElementById("regionSearch"); + const cityInput = document.getElementById("cityInput"); + const cityList = document.getElementById("cityList"); + const citySearch = document.getElementById("citySearch"); + + // Функция для открытия и закрытия выпадающего списка + function toggleDropdown(input, list) { + input.parentElement.classList.toggle("open"); + list.classList.toggle("show"); + } + + // Функция для фильтрации списка по поиску + function filterList(searchInput, container) { + const filter = searchInput.value.toLowerCase(); + const labels = container.querySelectorAll("label"); + labels.forEach(label => { + const text = label.textContent.toLowerCase(); + label.style.display = text.includes(filter) ? "block" : "none"; + }); + } + + // Функция для обновления текста в инпуте на основании выбранных чекбоксов + function updateInputText(input, container) { + const selected = Array.from(container.querySelectorAll("input[type='checkbox']:checked")) + .map(checkbox => checkbox.value); + input.value = selected.join(", "); + } + + // Обработчики для региона + regionInput.addEventListener("click", () => toggleDropdown(regionInput, regionList)); + regionSearch.addEventListener("input", () => filterList(regionSearch, regionList)); + regionList.addEventListener("change", () => updateInputText(regionInput, regionList)); + + // Обработчики для города + cityInput.addEventListener("click", () => toggleDropdown(cityInput, cityList)); + citySearch.addEventListener("input", () => filterList(citySearch, cityList)); + cityList.addEventListener("change", () => updateInputText(cityInput, cityList)); + + // Закрытие выпадающих списков при клике вне их области + document.addEventListener("click", (event) => { + if (!regionInput.parentElement.contains(event.target)) { + regionInput.parentElement.classList.remove("open"); + } + if (!cityInput.parentElement.contains(event.target)) { + cityInput.parentElement.classList.remove("open"); + } + }); + // Обработчик для синхронизации значения глубины (range и input) + depthRange.addEventListener("input", () => { + depthInput.value = depthRange.value; + }); + + depthInput.addEventListener("input", () => { + // Проверка, чтобы значение было в допустимых пределах + if (depthInput.value < depthRange.min) { + depthInput.value = depthRange.min; + } else if (depthInput.value > depthRange.max) { + depthInput.value = depthRange.max; + } + depthRange.value = depthInput.value; + }); + + // Обработчик для синхронизации значения площади (range и input) + areaRange.addEventListener("input", () => { + areaInput.value = areaRange.value; + }); + + areaInput.addEventListener("input", () => { + // Проверка, чтобы значение было в допустимых пределах + if (areaInput.value < areaRange.min) { + areaInput.value = areaRange.min; + } else if (areaInput.value > areaRange.max) { + areaInput.value = areaRange.max; + } + areaRange.value = areaInput.value; + }); + + // Обработчик кнопки поиска + // searchButton.addEventListener("click", () => { + // const searchQuery = document.getElementById("searchInput").value; + // alert(`Ищем: ${searchQuery}`); + // }); + + // Обработчик кнопки применения фильтров + searchButton.addEventListener("click", () => { + const region = document.getElementById("regionSelect").value; + const city = document.getElementById("citySelect").value; + const depth = depthRange.value; + const area = areaRange.value; + + alert(`Фильтры применены:\nРегион: ${region}\nГород: ${city}\nГлубина: ${depth} м\nПлощадь: ${area} км²`); + }); + returnButton.addEventListener("click", () => { + window.location.href = '/main'; + }); +}); + diff --git a/lake-catalog/src/main/resources/templates/filtration/filtration_styles.css b/src/main/resources/templates/filtration/filtration_styles.css similarity index 95% rename from lake-catalog/src/main/resources/templates/filtration/filtration_styles.css rename to src/main/resources/templates/filtration/filtration_styles.css index eb4a272..0d277ff 100644 --- a/lake-catalog/src/main/resources/templates/filtration/filtration_styles.css +++ b/src/main/resources/templates/filtration/filtration_styles.css @@ -1,182 +1,182 @@ -body { - background-color: #DEE9FE; - font-family: Arial, sans-serif; - color: #003366; - text-align: center; - font-family: 'Century Gothic', Arial, sans-serif; -} - -header { - margin-top: 20px; -} - -h1 { - font-size: 3rem; - color: #003366; -} - -.search-container { - display: flex; - justify-content: center; - margin: 20px 0; -} - -select { - font-family: 'Century Gothic', Arial, sans-serif; -} - -#searchInput { - font-family: 'Century Gothic', Arial, sans-serif; - padding: 10px; - width: 700px; /* Увеличенная ширина */ - border-radius: 20px; - border: 2px solid #003366; -} - -#searchButton, #returnButton { - font-family: 'Century Gothic', Arial, sans-serif; - margin-left: 10px; - padding: 10px 20px; - background-color: #003366; - color: white; - border: none; - border-radius: 20px; - cursor: pointer; -} - -.filters { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; - padding: 20px; -} - -.filter-group { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #DEE9FE; - padding: 15px; - border-radius: 10px; - /* box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); */ - text-align: left; - border: 2px solid #003366; -} - -.rating { - text-align: left; /* Выравнивание по левому краю */ - margin-top: 15px; -} - -.rating label { - margin-left: 5px; -} - -.range-container { - display: flex; - align-items: center; - gap: 10px; -} -/* Цвет звездочек */ -.star { - color: #003366; - /* font-size: 1.5rem; */ -} - -/* Убедитесь, что цвета не переопределяются другими стилями */ -input[type="checkbox"]:checked + .star { - color: #003366; - font-family: 'Century Gothic', Arial, sans-serif; -} - - -input[type="number"] { - width: 80px; - padding: 5px; - border-radius: 5px; - border: 1px solid #ccc; - font-family: 'Century Gothic', Arial, sans-serif; -} - -input[type="text"] { - font-family: 'Century Gothic', Arial, sans-serif; -} - -#applyFilters { - font-family: 'Century Gothic', Arial, sans-serif; - margin-top: 20px; - padding: 10px 30px; - background-color: #003366; - color: white; - border: none; - border-radius: 20px; - cursor: pointer; -} - -/* Стили для кастомного выпадающего списка */ -.dropdown { - position: relative; - width: 90%; -} - -#regionInput, -#cityInput { - width: 90%; - padding: 10px; - border: 1px solid #003366; - border-radius: 5px; - cursor: pointer; -} - -.dropdown-content { - display: none; - position: absolute; - width: 93%; - max-height: 200px; - overflow-y: auto; - background-color: #fff; - border: 1px solid #003366; - border-radius: 5px; - z-index: 1; -} - -.dropdown-content input[type="text"] { - width: calc(90% - 20px); - padding: 5px; - margin: 10px; - border: 1px solid #ccc; -} - -.checkbox-container { - display: flex; - flex-direction: column; - padding: 10px; -} - -.checkbox-container label { - margin: 5px 0; -} - -/* Показываем выпадающий список при фокусе */ -.dropdown.open .dropdown-content { - display: block; -} - -input[type="range"] { - - width: 300px; - height: 8px; - background: #ddd; /* Цвет трека (линии) */ - border-radius: 5px; - outline: none; -} - - -.downArrow { - position: absolute; - right: 50px; - top: 50%; - transform: translateY(-50%); - width: 24px; - height: 24px; - pointer-events: none; -} +body { + background-color: #DEE9FE; + font-family: Arial, sans-serif; + color: #003366; + text-align: center; + font-family: 'Century Gothic', Arial, sans-serif; +} + +header { + margin-top: 20px; +} + +h1 { + font-size: 3rem; + color: #003366; +} + +.search-container { + display: flex; + justify-content: center; + margin: 20px 0; +} + +select { + font-family: 'Century Gothic', Arial, sans-serif; +} + +#searchInput { + font-family: 'Century Gothic', Arial, sans-serif; + padding: 10px; + width: 700px; /* Увеличенная ширина */ + border-radius: 20px; + border: 2px solid #003366; +} + +#searchButton, #returnButton { + font-family: 'Century Gothic', Arial, sans-serif; + margin-left: 10px; + padding: 10px 20px; + background-color: #003366; + color: white; + border: none; + border-radius: 20px; + cursor: pointer; +} + +.filters { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + padding: 20px; +} + +.filter-group { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #DEE9FE; + padding: 15px; + border-radius: 10px; + /* box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); */ + text-align: left; + border: 2px solid #003366; +} + +.rating { + text-align: left; /* Выравнивание по левому краю */ + margin-top: 15px; +} + +.rating label { + margin-left: 5px; +} + +.range-container { + display: flex; + align-items: center; + gap: 10px; +} +/* Цвет звездочек */ +.star { + color: #003366; + /* font-size: 1.5rem; */ +} + +/* Убедитесь, что цвета не переопределяются другими стилями */ +input[type="checkbox"]:checked + .star { + color: #003366; + font-family: 'Century Gothic', Arial, sans-serif; +} + + +input[type="number"] { + width: 80px; + padding: 5px; + border-radius: 5px; + border: 1px solid #ccc; + font-family: 'Century Gothic', Arial, sans-serif; +} + +input[type="text"] { + font-family: 'Century Gothic', Arial, sans-serif; +} + +#applyFilters { + font-family: 'Century Gothic', Arial, sans-serif; + margin-top: 20px; + padding: 10px 30px; + background-color: #003366; + color: white; + border: none; + border-radius: 20px; + cursor: pointer; +} + +/* Стили для кастомного выпадающего списка */ +.dropdown { + position: relative; + width: 90%; +} + +#regionInput, +#cityInput { + width: 90%; + padding: 10px; + border: 1px solid #003366; + border-radius: 5px; + cursor: pointer; +} + +.dropdown-content { + display: none; + position: absolute; + width: 93%; + max-height: 200px; + overflow-y: auto; + background-color: #fff; + border: 1px solid #003366; + border-radius: 5px; + z-index: 1; +} + +.dropdown-content input[type="text"] { + width: calc(90% - 20px); + padding: 5px; + margin: 10px; + border: 1px solid #ccc; +} + +.checkbox-container { + display: flex; + flex-direction: column; + padding: 10px; +} + +.checkbox-container label { + margin: 5px 0; +} + +/* Показываем выпадающий список при фокусе */ +.dropdown.open .dropdown-content { + display: block; +} + +input[type="range"] { + + width: 300px; + height: 8px; + background: #ddd; /* Цвет трека (линии) */ + border-radius: 5px; + outline: none; +} + + +.downArrow { + position: absolute; + right: 50px; + top: 50%; + transform: translateY(-50%); + width: 24px; + height: 24px; + pointer-events: none; +} \ No newline at end of file diff --git a/lake-catalog/src/main/resources/templates/home/home.html b/src/main/resources/templates/home/home.html similarity index 97% rename from lake-catalog/src/main/resources/templates/home/home.html rename to src/main/resources/templates/home/home.html index dfe8821..2853cdb 100644 --- a/lake-catalog/src/main/resources/templates/home/home.html +++ b/src/main/resources/templates/home/home.html @@ -1,33 +1,33 @@ - - - - - - LakeView - - - -
-

LakeView - Ваш проводник по озерам РФ!

-
- - - -
-
- - - + + + + + + LakeView + + + +
+

LakeView - Ваш проводник по озерам РФ!

+
+ + + +
+
+ + + \ No newline at end of file diff --git a/lake-catalog/src/main/resources/templates/home/home_script.js b/src/main/resources/templates/home/home_script.js similarity index 95% rename from lake-catalog/src/main/resources/templates/home/home_script.js rename to src/main/resources/templates/home/home_script.js index 81088b4..3c1ab43 100644 --- a/lake-catalog/src/main/resources/templates/home/home_script.js +++ b/src/main/resources/templates/home/home_script.js @@ -1,11 +1,11 @@ -function register() { - window.location.href = "../register/register.html"; -} - -function login() { - window.location.href = "../login/login.html"; -} - -function continueWithoutRegister() { - window.location.href = "../main/main.html"; -} +function register() { + window.location.href = "../register/register.html"; +} + +function login() { + window.location.href = "../login/login.html"; +} + +function continueWithoutRegister() { + window.location.href = "../main/main.html"; +} diff --git a/lake-catalog/src/main/resources/templates/home/home_style.css b/src/main/resources/templates/home/home_style.css similarity index 94% rename from lake-catalog/src/main/resources/templates/home/home_style.css rename to src/main/resources/templates/home/home_style.css index e6a5517..aa74fb1 100644 --- a/lake-catalog/src/main/resources/templates/home/home_style.css +++ b/src/main/resources/templates/home/home_style.css @@ -1,66 +1,66 @@ - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: 'Century Gothic', Arial, sans-serif; /* Указание шрифта Century Gothic */ - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - background-color: #e6f0ff; -} - -.container { - text-align: center; - padding: 20px; - border-radius: 8px; -} - -.title { - font-size: 4rem; - color: #003366; - margin-bottom: 50px; -} - -.button-container { - display: flex; - flex-direction: column; - gap: 10px; - align-items: center; -} - -.btn { - width: 350px; - padding: 15px 20px; - font-size: 1rem; - cursor: pointer; - /* border-radius: 4px; */ - border: none; - transition: background-color 0.3s; - font-family: 'Century Gothic', Arial, sans-serif; /* Указываем шрифт для кнопок */ -} - - -.primary-btn { - background-color: #003366; - color: #ffffff; -} - -.primary-btn:hover { - background-color: #002244; -} - -.secondary-btn { - background-color: #e6f0ff; - color: #003366; - border: 2px solid #003366; - margin-top: 40px; -} - -.secondary-btn:hover { - background-color: #f0f8ff; + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Century Gothic', Arial, sans-serif; /* Указание шрифта Century Gothic */ + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #e6f0ff; +} + +.container { + text-align: center; + padding: 20px; + border-radius: 8px; +} + +.title { + font-size: 4rem; + color: #003366; + margin-bottom: 50px; +} + +.button-container { + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; +} + +.btn { + width: 350px; + padding: 15px 20px; + font-size: 1rem; + cursor: pointer; + /* border-radius: 4px; */ + border: none; + transition: background-color 0.3s; + font-family: 'Century Gothic', Arial, sans-serif; /* Указываем шрифт для кнопок */ +} + + +.primary-btn { + background-color: #003366; + color: #ffffff; +} + +.primary-btn:hover { + background-color: #002244; +} + +.secondary-btn { + background-color: #e6f0ff; + color: #003366; + border: 2px solid #003366; + margin-top: 40px; +} + +.secondary-btn:hover { + background-color: #f0f8ff; } \ No newline at end of file diff --git a/lake-catalog/src/main/resources/templates/import-export/import-export.html b/src/main/resources/templates/import-export/import-export.html similarity index 97% rename from lake-catalog/src/main/resources/templates/import-export/import-export.html rename to src/main/resources/templates/import-export/import-export.html index 76d32e8..5c339d7 100644 --- a/lake-catalog/src/main/resources/templates/import-export/import-export.html +++ b/src/main/resources/templates/import-export/import-export.html @@ -1,37 +1,37 @@ - - - - - - Экспорт/Импорт - - - -
-
- -

Экспорт/Импорт

- -
- -
-
-

Экспорт

-

Хотите скачать всю базу озер?

- -
-
-

Импорт

-

Хотите добавить новое озеро в общую базу?

-
- - - -
-
-
-
- - - - + + + + + + Экспорт/Импорт + + + +
+
+ +

Экспорт/Импорт

+ +
+ +
+
+

Экспорт

+

Хотите скачать всю базу озер?

+ +
+
+

Импорт

+

Хотите добавить новое озеро в общую базу?

+
+ + + +
+
+
+
+ + + + diff --git a/lake-catalog/src/main/resources/templates/import-export/import-export_script.js b/src/main/resources/templates/import-export/import-export_script.js similarity index 98% rename from lake-catalog/src/main/resources/templates/import-export/import-export_script.js rename to src/main/resources/templates/import-export/import-export_script.js index 40afac0..9a2d129 100644 --- a/lake-catalog/src/main/resources/templates/import-export/import-export_script.js +++ b/src/main/resources/templates/import-export/import-export_script.js @@ -1,19 +1,19 @@ -// Функция для обработки кнопки "Скачать" -document.getElementById('export-button').addEventListener('click', function () { - alert('База данных будет скачана!'); - // Здесь можно добавить логику для экспорта данных (например, через API) -}); - -// Функция для обработки кнопки "Загрузить" -document.getElementById('upload-button').addEventListener('click', function () { - const fileInput = document.getElementById('file-upload'); - if (fileInput.files.length === 0) { - alert('Пожалуйста, выберите файл для загрузки'); - return; - } - alert(`Файл ${fileInput.files[0].name} будет загружен!`); - // Здесь можно добавить логику для загрузки данных (например, через API) -}); -function goToProfile(){ - window.location.href = '/users/profile/current'; +// Функция для обработки кнопки "Скачать" +document.getElementById('export-button').addEventListener('click', function () { + alert('База данных будет скачана!'); + // Здесь можно добавить логику для экспорта данных (например, через API) +}); + +// Функция для обработки кнопки "Загрузить" +document.getElementById('upload-button').addEventListener('click', function () { + const fileInput = document.getElementById('file-upload'); + if (fileInput.files.length === 0) { + alert('Пожалуйста, выберите файл для загрузки'); + return; + } + alert(`Файл ${fileInput.files[0].name} будет загружен!`); + // Здесь можно добавить логику для загрузки данных (например, через API) +}); +function goToProfile(){ + window.location.href = '/users/profile/current'; } \ No newline at end of file diff --git a/lake-catalog/src/main/resources/static/css/import-export_styles.css b/src/main/resources/templates/import-export/import-export_styles.css similarity index 94% rename from lake-catalog/src/main/resources/static/css/import-export_styles.css rename to src/main/resources/templates/import-export/import-export_styles.css index c9db430..3330db1 100644 --- a/lake-catalog/src/main/resources/static/css/import-export_styles.css +++ b/src/main/resources/templates/import-export/import-export_styles.css @@ -1,105 +1,105 @@ -/* Общие стили */ -body { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #e6f0ff; - margin: 0; - padding: 0; -} - -/* Контейнер */ -.container { - max-width: 1200px; - margin: 0 auto; - padding: 20px; -} - -/* Заголовки и кнопки */ -header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 30px; -} - -h1 { - font-size: 2.5rem; - color: #003366; -} - -button { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #003366; - color: white; - border: none; - padding: 10px 20px; - font-size: 1rem; - cursor: pointer; -} - -button:hover { - background-color: #003366; -} - -#back-button { - /* background-color: #ff6347; */ - font-size: 1.2rem; -} - -input { - font-family: 'Century Gothic', Arial, sans-serif; -} - -/* Стиль для аватара */ -.avatar { - width: 50px; - height: 50px; - border-radius: 50%; -} - -/* Секции Экспорт/Импорт */ -.export-import-container { - display: flex; - justify-content: space-between; - text-align: center; - padding: 0px 150px; -} - -.export-section, .import-section { - border: 2px solid #003366; - border-radius: 10px; - padding: 50px 20px; - width: 40%; - box-shadow: 0 4px 8px #0033661a; -} - -h2 { - font-size: 1.8rem; - margin-bottom: 10px; - margin: 0; - color: #003366; -} - - -.export-section p, .import-section p { - font-size: 1rem; - margin-bottom: 20px; - color: #003366; -} - -#file-upload { - display: none; -} - -.custom-file-upload { - background-color: transparent; - border: 2px solid #003366; - color: #003366; - padding: 8px 20px; - font-size: 1rem; - cursor: pointer; - display: inline-block; -} - -#profile-btn { - background-color: transparent; -} +/* Общие стили */ +body { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #e6f0ff; + margin: 0; + padding: 0; +} + +/* Контейнер */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +/* Заголовки и кнопки */ +header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30px; +} + +h1 { + font-size: 2.5rem; + color: #003366; +} + +button { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #003366; + color: white; + border: none; + padding: 10px 20px; + font-size: 1rem; + cursor: pointer; +} + +button:hover { + background-color: #003366; +} + +#back-button { + /* background-color: #ff6347; */ + font-size: 1.2rem; +} + +input { + font-family: 'Century Gothic', Arial, sans-serif; +} + +/* Стиль для аватара */ +.avatar { + width: 50px; + height: 50px; + border-radius: 50%; +} + +/* Секции Экспорт/Импорт */ +.export-import-container { + display: flex; + justify-content: space-between; + text-align: center; + padding: 0px 150px; +} + +.export-section, .import-section { + border: 2px solid #003366; + border-radius: 10px; + padding: 50px 20px; + width: 40%; + box-shadow: 0 4px 8px #0033661a; +} + +h2 { + font-size: 1.8rem; + margin-bottom: 10px; + margin: 0; + color: #003366; +} + + +.export-section p, .import-section p { + font-size: 1rem; + margin-bottom: 20px; + color: #003366; +} + +#file-upload { + display: none; +} + +.custom-file-upload { + background-color: transparent; + border: 2px solid #003366; + color: #003366; + padding: 8px 20px; + font-size: 1rem; + cursor: pointer; + display: inline-block; +} + +#profile-btn { + background-color: transparent; +} diff --git a/lake-catalog/src/main/resources/templates/login/login.html b/src/main/resources/templates/login/login.html similarity index 97% rename from lake-catalog/src/main/resources/templates/login/login.html rename to src/main/resources/templates/login/login.html index b065ae1..e349fb4 100644 --- a/lake-catalog/src/main/resources/templates/login/login.html +++ b/src/main/resources/templates/login/login.html @@ -1,50 +1,50 @@ - - - - - - Вход - - - - - - - - - + + + + + + Вход + + + + + + + + + \ No newline at end of file diff --git a/lake-catalog/src/main/resources/templates/login/login_script.js b/src/main/resources/templates/login/login_script.js similarity index 95% rename from lake-catalog/src/main/resources/templates/login/login_script.js rename to src/main/resources/templates/login/login_script.js index 2ecba90..a4ffa23 100644 --- a/lake-catalog/src/main/resources/templates/login/login_script.js +++ b/src/main/resources/templates/login/login_script.js @@ -1,8 +1,8 @@ -function goBack() { - window.location.href = '../home/home.html'; -} - -function goToMain(event) { - event.preventDefault(); - window.location.href = '../main/main.html'; -} +function goBack() { + window.location.href = '../home/home.html'; +} + +function goToMain(event) { + event.preventDefault(); + window.location.href = '../main/main.html'; +} diff --git a/lake-catalog/src/main/resources/templates/login/login_styles.css b/src/main/resources/templates/login/login_styles.css similarity index 95% rename from lake-catalog/src/main/resources/templates/login/login_styles.css rename to src/main/resources/templates/login/login_styles.css index b2a42f6..58d33ff 100644 --- a/lake-catalog/src/main/resources/templates/login/login_styles.css +++ b/src/main/resources/templates/login/login_styles.css @@ -1,75 +1,75 @@ -body { - font-family: 'Century Gothic', Arial, sans-serif; - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - background-color: #e6f0ff; -} - -.login-container { - width: 100%; - padding: 200px; - text-align: center; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.login-title { - font-size: 3.5rem; - color: #003366; - margin-bottom: 50px; -} - -.back-btn { - font-size: 1rem; - padding: 8px 16px; - color: #003366; - background-color: transparent; - border: 2px solid #003366; - cursor: pointer; - margin-bottom: -120px; /* Чтобы кнопка "Назад" была ближе к заголовку */ - margin-left: 0; - align-self: flex-start; - font-family: 'Century Gothic', Arial, sans-serif; -} - -.back-btn:hover { - background-color: #f0f8ff; -} - -.login-form { - display: flex; - flex-direction: column; - width: 300px; - gap: 15px; -} - -.login-form label { - font-size: 1rem; - color: #003366; - text-align: left; -} - -.login-form input { - padding: 8px; - border: 1px solid #cccccc; - font-size: 1rem; -} - -.submit-btn { - background-color: #003366; - color: #ffffff; - padding: 10px; - border: none; - cursor: pointer; - font-size: 1rem; - font-family: 'Century Gothic', Arial, sans-serif; - margin-top: 40px; -} - -.submit-btn:hover { - background-color: #002244; +body { + font-family: 'Century Gothic', Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #e6f0ff; +} + +.login-container { + width: 100%; + padding: 200px; + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.login-title { + font-size: 3.5rem; + color: #003366; + margin-bottom: 50px; +} + +.back-btn { + font-size: 1rem; + padding: 8px 16px; + color: #003366; + background-color: transparent; + border: 2px solid #003366; + cursor: pointer; + margin-bottom: -120px; /* Чтобы кнопка "Назад" была ближе к заголовку */ + margin-left: 0; + align-self: flex-start; + font-family: 'Century Gothic', Arial, sans-serif; +} + +.back-btn:hover { + background-color: #f0f8ff; +} + +.login-form { + display: flex; + flex-direction: column; + width: 300px; + gap: 15px; +} + +.login-form label { + font-size: 1rem; + color: #003366; + text-align: left; +} + +.login-form input { + padding: 8px; + border: 1px solid #cccccc; + font-size: 1rem; +} + +.submit-btn { + background-color: #003366; + color: #ffffff; + padding: 10px; + border: none; + cursor: pointer; + font-size: 1rem; + font-family: 'Century Gothic', Arial, sans-serif; + margin-top: 40px; +} + +.submit-btn:hover { + background-color: #002244; } \ No newline at end of file diff --git a/lake-catalog/src/main/resources/templates/main/main.html b/src/main/resources/templates/main/main.html similarity index 97% rename from lake-catalog/src/main/resources/templates/main/main.html rename to src/main/resources/templates/main/main.html index 7e9c381..82bb849 100644 --- a/lake-catalog/src/main/resources/templates/main/main.html +++ b/src/main/resources/templates/main/main.html @@ -1,67 +1,67 @@ - - - - - - LakeView - Главная - - - -
-

LakeView

- -
- - - - -
- - - - - - - - - -
- - - + + + + + + LakeView - Главная + + + +
+

LakeView

+ +
+ + + + +
+ + + + + + + + + +
+ + + \ No newline at end of file diff --git a/lake-catalog/src/main/resources/templates/main/main_script.js b/src/main/resources/templates/main/main_script.js similarity index 96% rename from lake-catalog/src/main/resources/templates/main/main_script.js rename to src/main/resources/templates/main/main_script.js index 593a226..297ff81 100644 --- a/lake-catalog/src/main/resources/templates/main/main_script.js +++ b/src/main/resources/templates/main/main_script.js @@ -1,7 +1,7 @@ -function goFilter(){ - window.location.href = '/filtration'; -} - -function goToProfile(){ - window.location.href = '/lakes/profile/current'; +function goFilter(){ + window.location.href = '/filtration'; +} + +function goToProfile(){ + window.location.href = '/lakes/profile/current'; } \ No newline at end of file diff --git a/lake-catalog/src/main/resources/templates/main/main_styles.css b/src/main/resources/templates/main/main_styles.css similarity index 95% rename from lake-catalog/src/main/resources/templates/main/main_styles.css rename to src/main/resources/templates/main/main_styles.css index 1f84d99..ef05820 100644 --- a/lake-catalog/src/main/resources/templates/main/main_styles.css +++ b/src/main/resources/templates/main/main_styles.css @@ -1,150 +1,150 @@ -* { - font-family: 'Century Gothic', Arial, sans-serif; - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #DEE9FE; - display: flex; - flex-direction: column; - align-items: center; - min-height: 100vh; - margin: 0; -} - -header { - display: flex; - align-items: center; - width: 100%; - max-width: 1200px; - padding: 20px; -} - -.logo { - font-size: 2em; - font-weight: bold; - color: #003366; - flex-grow: 1; - text-align: center; -} - -.profile-link { - margin-left: auto; - text-decoration: none; - display: flex; - align-items: center; -} - -.profile-img { - width: 50px; - height: 50px; - border-radius: 50%; - object-fit: cover; - cursor: pointer; -} - -.search-bar { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - max-width: 1200px; - margin: 20px 0; -} - -.search-left { - display: flex; - align-items: center; - gap: 10px; -} - -.search-input, .map-search { - padding: 10px; - border: 1px solid #cccccc; - border-radius: 20px; - width: 300px; -} - -.filter-btn { - padding: 10px 20px; - background-color: #003366; - color: #FFFFFF; - border: none; - border-radius: 20px; - cursor: pointer; -} - -.stats-btn { - background-color: #003366; - color: #FFFFFF; - padding: 10px 20px; - border: none; - font-size: 1rem; - margin-bottom: 20px; - cursor: pointer; - align-self: flex-start; -} - -.main-content { - display: flex; - flex-direction: column; - align-items: center; - max-width: 1200px; - width: 100%; - flex-grow: 1; /* Заставляем main-content занимать оставшееся пространство */ -} - -.lake-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); - gap: 20px; - width: 100%; -} - -.lake-card { - display: block; /* Чтобы занимала всю карточку */ - background-color: #FFFFFF; - border-radius: 10px; - overflow: hidden; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - text-decoration: none; /* Убираем подчеркивание у ссылок */ -} - -.lake-card:hover { - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); -} - -.lake-image { - width: 100%; - height: 200px; - object-fit: cover; -} - -.lake-title { - padding: 10px; - font-size: 1rem; - color: #003366; - text-align: center; -} - -.pagination { - display: flex; - gap: 10px; - margin-top: auto; /* Прижимает пагинацию к низу, если main-content растягивается */ - padding-bottom: 20px; /* Добавьте немного отступа снизу для визуального пространства */ -} - -.pagination a { - text-decoration: none; - padding: 7px 10px; - background-color: #003366; - color: #FFFFFF; - font-size: 1rem; -} - -.pagination a:hover { - background-color: #002244; +* { + font-family: 'Century Gothic', Arial, sans-serif; + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #DEE9FE; + display: flex; + flex-direction: column; + align-items: center; + min-height: 100vh; + margin: 0; +} + +header { + display: flex; + align-items: center; + width: 100%; + max-width: 1200px; + padding: 20px; +} + +.logo { + font-size: 2em; + font-weight: bold; + color: #003366; + flex-grow: 1; + text-align: center; +} + +.profile-link { + margin-left: auto; + text-decoration: none; + display: flex; + align-items: center; +} + +.profile-img { + width: 50px; + height: 50px; + border-radius: 50%; + object-fit: cover; + cursor: pointer; +} + +.search-bar { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + max-width: 1200px; + margin: 20px 0; +} + +.search-left { + display: flex; + align-items: center; + gap: 10px; +} + +.search-input, .map-search { + padding: 10px; + border: 1px solid #cccccc; + border-radius: 20px; + width: 300px; +} + +.filter-btn { + padding: 10px 20px; + background-color: #003366; + color: #FFFFFF; + border: none; + border-radius: 20px; + cursor: pointer; +} + +.stats-btn { + background-color: #003366; + color: #FFFFFF; + padding: 10px 20px; + border: none; + font-size: 1rem; + margin-bottom: 20px; + cursor: pointer; + align-self: flex-start; +} + +.main-content { + display: flex; + flex-direction: column; + align-items: center; + max-width: 1200px; + width: 100%; + flex-grow: 1; /* Заставляем main-content занимать оставшееся пространство */ +} + +.lake-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 20px; + width: 100%; +} + +.lake-card { + display: block; /* Чтобы занимала всю карточку */ + background-color: #FFFFFF; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + text-decoration: none; /* Убираем подчеркивание у ссылок */ +} + +.lake-card:hover { + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); +} + +.lake-image { + width: 100%; + height: 200px; + object-fit: cover; +} + +.lake-title { + padding: 10px; + font-size: 1rem; + color: #003366; + text-align: center; +} + +.pagination { + display: flex; + gap: 10px; + margin-top: auto; /* Прижимает пагинацию к низу, если main-content растягивается */ + padding-bottom: 20px; /* Добавьте немного отступа снизу для визуального пространства */ +} + +.pagination a { + text-decoration: none; + padding: 7px 10px; + background-color: #003366; + color: #FFFFFF; + font-size: 1rem; +} + +.pagination a:hover { + background-color: #002244; } \ No newline at end of file diff --git a/lake-catalog/src/main/resources/templates/profile/profile.html b/src/main/resources/templates/profile/profile.html similarity index 97% rename from lake-catalog/src/main/resources/templates/profile/profile.html rename to src/main/resources/templates/profile/profile.html index 766dd55..dbc362b 100644 --- a/lake-catalog/src/main/resources/templates/profile/profile.html +++ b/src/main/resources/templates/profile/profile.html @@ -1,111 +1,111 @@ - - - - - -Профиль пользователя - - - -
-
- -

Профиль

- -
- -
-
- Фото пользователя - -
-
- -
-

-
-
-

- -
- - - - - - - - -
- -
-

Дата создания:

-

Дата редактирования:

-
- -
- - -
- - - -
- -
- - - - - -
-
-
-
-
- Фото пользователя - -
-
-
- ★★★★★ -
-
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

-
- -
-
-
- -
-
-
- - - - + + + + + +Профиль пользователя + + + +
+
+ +

Профиль

+ +
+ +
+
+ Фото пользователя + +
+
+ +
+

+
+
+

+ +
+ + + + + + + + +
+ +
+

Дата создания:

+

Дата редактирования:

+
+ +
+ + +
+ + + +
+ + + + + + + +
+
+
+
+
+ Фото пользователя + +
+
+
+ ★★★★★ +
+
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

+
+ +
+
+
+ +
+
+
+ + + + diff --git a/lake-catalog/src/main/resources/templates/profile/profile_script.js b/src/main/resources/templates/profile/profile_script.js similarity index 98% rename from lake-catalog/src/main/resources/templates/profile/profile_script.js rename to src/main/resources/templates/profile/profile_script.js index 81e548e..c6ee986 100644 --- a/lake-catalog/src/main/resources/templates/profile/profile_script.js +++ b/src/main/resources/templates/profile/profile_script.js @@ -1,16 +1,16 @@ -document.addEventListener('DOMContentLoaded', () => { - const tabButtons = document.querySelectorAll('.tab-button'); - const tabContents = document.querySelectorAll('.tab-content'); - - tabButtons.forEach(button => { - button.addEventListener('click', () => { - // Убираем активный класс у всех кнопок и контента - tabButtons.forEach(btn => btn.classList.remove('active')); - tabContents.forEach(content => content.classList.remove('active')); - - // Добавляем активный класс к выбранной кнопке и контенту - button.classList.add('active'); - document.getElementById(button.dataset.tab).classList.add('active'); - }); - }); -}); +document.addEventListener('DOMContentLoaded', () => { + const tabButtons = document.querySelectorAll('.tab-button'); + const tabContents = document.querySelectorAll('.tab-content'); + + tabButtons.forEach(button => { + button.addEventListener('click', () => { + // Убираем активный класс у всех кнопок и контента + tabButtons.forEach(btn => btn.classList.remove('active')); + tabContents.forEach(content => content.classList.remove('active')); + + // Добавляем активный класс к выбранной кнопке и контенту + button.classList.add('active'); + document.getElementById(button.dataset.tab).classList.add('active'); + }); + }); +}); diff --git a/lake-catalog/src/main/resources/templates/profile/profile_styles.css b/src/main/resources/templates/profile/profile_styles.css similarity index 94% rename from lake-catalog/src/main/resources/templates/profile/profile_styles.css rename to src/main/resources/templates/profile/profile_styles.css index 6af03be..6280da0 100644 --- a/lake-catalog/src/main/resources/templates/profile/profile_styles.css +++ b/src/main/resources/templates/profile/profile_styles.css @@ -1,235 +1,235 @@ -* { - font-family: 'Century Gothic', Arial, sans-serif; - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - font-family: 'Century Gothic', Arial, sans-serif; - background-color: #DEE9FE; - color: #003366; -} - - - -.profile-container { - width: 85%; - max-width: 1000px; - margin: 20px auto; - padding: 20px; -} - -header { - display: flex; - justify-content: space-between; - align-items: center; - -} - -.profile-info { - display: flex; - align-items: center; - gap: 20px; - margin-top: 80px; - margin-bottom: 50px; -} - -.profile-photo { - width: 150px; - height: 150px; - border-radius: 50%; - object-fit: cover; -} - -.user-details { - flex-grow: 1; - margin-top: -60px; -} - -#edit-profile { - background: none; - border: none; - font-size: 24px; - cursor: pointer; -} - -.profile-dates { - text-align: right; - margin-top: 100px; - color: #666; -} - -#import-export-button, #back-button, #logout-button { - margin-top: 10px; - padding: 10px 20px; - background-color: #003366; - font-family: 'Century Gothic', Arial, sans-serif; - color: #fff; - border: none; - cursor: pointer; -} - -.tabs { - display: flex; - justify-content: space-around; - margin-top: 50px; - -} - -.tab-button { - padding: 10px; - font-weight: 600; - border: none; - cursor: pointer; - background-color: #DEE9FE; - color: #003366; - position: relative; -} - -.tab-button.active { - color: #003366; - font-weight: bold; -} - -.tab-button.active::after { - content: ""; - display: block; - width: 100%; - height: 3px; - background-color: #003366; - position: absolute; - bottom: -2px; - left: 0; -} - -.tab-content { - display: none; - margin-top: 20px; -} - -.tab-content.active { - display: block; -} - -.lake-card { - padding: 10px; - background-color: #cce5ff; - margin-bottom: 10px; -} - -/* Стили для отзыва */ -.review { - display: flex; - flex-direction: column; - padding: 16px; - border: 1px solid #e0e0e0; - border-radius: 8px; - background-color: #fff; - margin-top: 16px; -} - -.review-user { - display: flex; - align-items: center; - margin-bottom: 8px; -} - -.user-photo { - width: 64px; - height: 64px; - border-radius: 50%; - object-fit: cover; - margin-right: 12px; -} - -.user-info { - display: flex; - flex-direction: column; -} - -.user-info h4 { - font-size: 18px; - margin: 0; -} - -.user-info p { - font-size: 14px; - color: #888; - margin: 4px 0 0; -} - -.review-content { - display: flex; - flex-direction: column; -} - -.review-text { - font-size: 16px; - /* margin-bottom: 8px; */ -} - -.review-rating { - font-size: 20px; - color: #ffc107; /* Желтый цвет для звезд */ - margin-bottom: 10px; -} - -.icon-button { - margin-left: 120px; - background: none; - border: none; - cursor: pointer; - padding: 0; - width: 32px; - height: 32px; -} - -.icon { - height: 24px; - width: 24px; -} - -#edit-profile { - width: 24px; - height: 24px; - margin-left: 250px; -} - -/* Основные стили */ -.user-details { - display: flex; - flex-direction: column; - align-items: flex-start; -} - -/* Поля ввода */ -#name-input, #email-input { - margin-top: 10px; - padding: 8px; - width: 100%; - font-size: 16px; - border: 1px solid #ccc; - border-radius: 4px; - display: none; /* Скрыты по умолчанию */ -} - - -#edit-profile { - background-color: none; - color: white; -} - -#save-profile { - background-color: #003366; - color: white; - margin-top: 10px; - padding: 10px 15px; - border: none; - cursor: pointer; - font-size: 16px; -} - -button:hover { - opacity: 0.9; -} +* { + font-family: 'Century Gothic', Arial, sans-serif; + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Century Gothic', Arial, sans-serif; + background-color: #DEE9FE; + color: #003366; +} + + + +.profile-container { + width: 85%; + max-width: 1000px; + margin: 20px auto; + padding: 20px; +} + +header { + display: flex; + justify-content: space-between; + align-items: center; + +} + +.profile-info { + display: flex; + align-items: center; + gap: 20px; + margin-top: 80px; + margin-bottom: 50px; +} + +.profile-photo { + width: 150px; + height: 150px; + border-radius: 50%; + object-fit: cover; +} + +.user-details { + flex-grow: 1; + margin-top: -60px; +} + +#edit-profile { + background: none; + border: none; + font-size: 24px; + cursor: pointer; +} + +.profile-dates { + text-align: right; + margin-top: 100px; + color: #666; +} + +#import-export-button, #back-button, #logout-button { + margin-top: 10px; + padding: 10px 20px; + background-color: #003366; + font-family: 'Century Gothic', Arial, sans-serif; + color: #fff; + border: none; + cursor: pointer; +} + +.tabs { + display: flex; + justify-content: space-around; + margin-top: 50px; + +} + +.tab-button { + padding: 10px; + font-weight: 600; + border: none; + cursor: pointer; + background-color: #DEE9FE; + color: #003366; + position: relative; +} + +.tab-button.active { + color: #003366; + font-weight: bold; +} + +.tab-button.active::after { + content: ""; + display: block; + width: 100%; + height: 3px; + background-color: #003366; + position: absolute; + bottom: -2px; + left: 0; +} + +.tab-content { + display: none; + margin-top: 20px; +} + +.tab-content.active { + display: block; +} + +.lake-card { + padding: 10px; + background-color: #cce5ff; + margin-bottom: 10px; +} + +/* Стили для отзыва */ +.review { + display: flex; + flex-direction: column; + padding: 16px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background-color: #fff; + margin-top: 16px; +} + +.review-user { + display: flex; + align-items: center; + margin-bottom: 8px; +} + +.user-photo { + width: 64px; + height: 64px; + border-radius: 50%; + object-fit: cover; + margin-right: 12px; +} + +.user-info { + display: flex; + flex-direction: column; +} + +.user-info h4 { + font-size: 18px; + margin: 0; +} + +.user-info p { + font-size: 14px; + color: #888; + margin: 4px 0 0; +} + +.review-content { + display: flex; + flex-direction: column; +} + +.review-text { + font-size: 16px; + /* margin-bottom: 8px; */ +} + +.review-rating { + font-size: 20px; + color: #ffc107; /* Желтый цвет для звезд */ + margin-bottom: 10px; +} + +.icon-button { + margin-left: 120px; + background: none; + border: none; + cursor: pointer; + padding: 0; + width: 32px; + height: 32px; +} + +.icon { + height: 24px; + width: 24px; +} + +#edit-profile { + width: 24px; + height: 24px; + margin-left: 250px; +} + +/* Основные стили */ +.user-details { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +/* Поля ввода */ +#name-input, #email-input { + margin-top: 10px; + padding: 8px; + width: 100%; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 4px; + display: none; /* Скрыты по умолчанию */ +} + + +#edit-profile { + background-color: none; + color: white; +} + +#save-profile { + background-color: #003366; + color: white; + margin-top: 10px; + padding: 10px 15px; + border: none; + cursor: pointer; + font-size: 16px; +} + +button:hover { + opacity: 0.9; +} diff --git a/lake-catalog/src/main/resources/templates/register/register.html b/src/main/resources/templates/register/register.html similarity index 97% rename from lake-catalog/src/main/resources/templates/register/register.html rename to src/main/resources/templates/register/register.html index a200b0e..142a6bb 100644 --- a/lake-catalog/src/main/resources/templates/register/register.html +++ b/src/main/resources/templates/register/register.html @@ -1,63 +1,63 @@ - - - - - - Регистрация - - - - - - -
- - -

Регистрация

- -
- - - - - - - - - - - - - - - -
- -
- - - + + + + + + Регистрация + + + + + + +
+ + +

Регистрация

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + \ No newline at end of file diff --git a/lake-catalog/src/main/resources/templates/register/register_script.js b/src/main/resources/templates/register/register_script.js similarity index 95% rename from lake-catalog/src/main/resources/templates/register/register_script.js rename to src/main/resources/templates/register/register_script.js index 8a183c6..606a249 100644 --- a/lake-catalog/src/main/resources/templates/register/register_script.js +++ b/src/main/resources/templates/register/register_script.js @@ -1,8 +1,8 @@ -function goBack() { - window.location.href = '../home/home.html'; -} - -function goToMain(event) { - event.preventDefault(); - window.location.href = '../main/main.html'; -} +function goBack() { + window.location.href = '../home/home.html'; +} + +function goToMain(event) { + event.preventDefault(); + window.location.href = '../main/main.html'; +} diff --git a/lake-catalog/src/main/resources/templates/register/register_styles.css b/src/main/resources/templates/register/register_styles.css similarity index 94% rename from lake-catalog/src/main/resources/templates/register/register_styles.css rename to src/main/resources/templates/register/register_styles.css index 39b5809..4f1700d 100644 --- a/lake-catalog/src/main/resources/templates/register/register_styles.css +++ b/src/main/resources/templates/register/register_styles.css @@ -1,97 +1,97 @@ -body { - font-family: 'Century Gothic', Arial, sans-serif; - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - background-color: #e6f0ff; -} - -.register-container { - width: 100%; - padding: 200px; - text-align: center; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.register-top { - /* width: 100%; */ - display: flex; -} -.d-none { - display: none !important; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; - font-size: 1rem; -} - -/* Красный alert для ошибок */ -.alert-danger { - color: #721c24; - background-color: #f8d7da; - border-color: #f5c6cb; -} -.register-title { - font-size: 3.5rem; - color: #003366; - margin-bottom: 50px; -} - -.back-btn { - font-size: 1rem; - padding: 8px 16px; - color: #003366; - background-color: transparent; - border: 2px solid #003366; - cursor: pointer; - margin-bottom: -120px; - margin-left: 0; - align-self: flex-start; - font-family: 'Century Gothic', Arial, sans-serif; -} - - -.back-btn:hover { - background-color: #f0f8ff; -} - -.register-form { - display: flex; - flex-direction: column; - width: 300px; - gap: 15px; -} - -.register-form label { - font-size: 1rem; - color: #003366; - text-align: left; -} - -.register-form input { - padding: 8px; - border: 1px solid #cccccc; - font-size: 1rem; -} - -.submit-btn { - background-color: #003366; - color: #ffffff; - padding: 10px; - border: none; - cursor: pointer; - font-size: 1rem; - font-family: 'Century Gothic', Arial, sans-serif; - margin-top: 40px; -} - -.submit-btn:hover { - background-color: #002244; +body { + font-family: 'Century Gothic', Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #e6f0ff; +} + +.register-container { + width: 100%; + padding: 200px; + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.register-top { + /* width: 100%; */ + display: flex; +} +.d-none { + display: none !important; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; + font-size: 1rem; +} + +/* Красный alert для ошибок */ +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} +.register-title { + font-size: 3.5rem; + color: #003366; + margin-bottom: 50px; +} + +.back-btn { + font-size: 1rem; + padding: 8px 16px; + color: #003366; + background-color: transparent; + border: 2px solid #003366; + cursor: pointer; + margin-bottom: -120px; + margin-left: 0; + align-self: flex-start; + font-family: 'Century Gothic', Arial, sans-serif; +} + + +.back-btn:hover { + background-color: #f0f8ff; +} + +.register-form { + display: flex; + flex-direction: column; + width: 300px; + gap: 15px; +} + +.register-form label { + font-size: 1rem; + color: #003366; + text-align: left; +} + +.register-form input { + padding: 8px; + border: 1px solid #cccccc; + font-size: 1rem; +} + +.submit-btn { + background-color: #003366; + color: #ffffff; + padding: 10px; + border: none; + cursor: pointer; + font-size: 1rem; + font-family: 'Century Gothic', Arial, sans-serif; + margin-top: 40px; +} + +.submit-btn:hover { + background-color: #002244; } \ No newline at end of file diff --git a/lake-catalog/src/test/java/com/example/lake_catalog/LakeCatalogApplicationTests.java b/src/test/java/com/example/lake_catalog/LakeCatalogApplicationTests.java similarity index 94% rename from lake-catalog/src/test/java/com/example/lake_catalog/LakeCatalogApplicationTests.java rename to src/test/java/com/example/lake_catalog/LakeCatalogApplicationTests.java index afc1ad9..e80edfc 100644 --- a/lake-catalog/src/test/java/com/example/lake_catalog/LakeCatalogApplicationTests.java +++ b/src/test/java/com/example/lake_catalog/LakeCatalogApplicationTests.java @@ -1,13 +1,13 @@ -package com.example.lake_catalog; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class LakeCatalogApplicationTests { - - @Test - void contextLoads() { - } - -} +package com.example.lake_catalog; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class LakeCatalogApplicationTests { + + @Test + void contextLoads() { + } + +}