diff --git a/src/main/java/polish_community_group_11/polish_community/admin/controllers/AdminController.java b/src/main/java/polish_community_group_11/polish_community/admin/controllers/AdminController.java new file mode 100644 index 0000000000000000000000000000000000000000..2a7eb1a083780e7ee273398b10b0384adf86fc1e --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/admin/controllers/AdminController.java @@ -0,0 +1,30 @@ +package polish_community_group_11.polish_community.admin.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; +import polish_community_group_11.polish_community.admin.services.AdminService; + +import java.sql.SQLException; +import java.util.List; + +@Controller +public class AdminController { + private AdminService adminService; + public AdminController(AdminService adminService) { + this.adminService = adminService; + } + + @GetMapping("admin/userInfo") + public ModelAndView getFirstAdminUsers() throws SQLException { + ModelAndView mav = new ModelAndView("admin/userManage"); + mav.addObject("usersAdminInfo",adminService.getUserManagementInfo()); + return mav; + } + @GetMapping("admin") + public ModelAndView getAdminDashboard() throws SQLException { + ModelAndView mav = new ModelAndView("admin/adminBoard"); + mav.addObject("adminBoard",adminService.getBoardManagementInfo()); + return mav; + } +} diff --git a/src/main/java/polish_community_group_11/polish_community/admin/dao/AdminRepository.java b/src/main/java/polish_community_group_11/polish_community/admin/dao/AdminRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..9cad39f168482d32e7060681171a1ce9c5c071e9 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/admin/dao/AdminRepository.java @@ -0,0 +1,12 @@ +package polish_community_group_11.polish_community.admin.dao; + +import polish_community_group_11.polish_community.admin.models.AdminBoard; +import polish_community_group_11.polish_community.admin.models.ManageUser; + +import java.sql.SQLException; +import java.util.List; + +public interface AdminRepository { + List<ManageUser> getUserManagementInfo() throws SQLException; + AdminBoard getBoardManagementInfo() throws SQLException; +} diff --git a/src/main/java/polish_community_group_11/polish_community/admin/dao/AdminRepositoryImpl.java b/src/main/java/polish_community_group_11/polish_community/admin/dao/AdminRepositoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..0f62d3a423ecf0a9626321c5227ad9141be0a7e8 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/admin/dao/AdminRepositoryImpl.java @@ -0,0 +1,96 @@ +package polish_community_group_11.polish_community.admin.dao; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; +import polish_community_group_11.polish_community.admin.models.AdminBoard; +import polish_community_group_11.polish_community.admin.models.ManageUser; + +import java.sql.SQLException; +import java.time.DateTimeException; +import java.util.List; + +@Repository +public class AdminRepositoryImpl implements AdminRepository { + private JdbcTemplate jdbc; // JdbcTemplate instance for performing database operations + private RowMapper<ManageUser> adminManageUserMapper; // RowMapper to map rows of the database result set to Manage User objects + private RowMapper<AdminBoard> adminBoardMapper; + public AdminRepositoryImpl(JdbcTemplate aJdbc){ + this.jdbc = aJdbc; + setAdminManageUserMapper(); + setAdminBoardMapper(); + } + + public void setAdminManageUserMapper(){ + adminManageUserMapper = (rs, i) ->{ + return new ManageUser( + rs.getInt("id"), + rs.getString("fullname"), + rs.getString("email"), + rs.getBoolean("enabled"), + rs.getString("role_name") + ); + }; + } + public void setAdminBoardMapper(){ + adminBoardMapper = (rs, i) ->{ + return new AdminBoard( + rs.getInt("total_users"), + rs.getInt("total_posts"), + rs.getInt("upcoming_events"), + rs.getInt("new_comments") + ); + }; + } + + public List<ManageUser> getUserManagementInfo() throws SQLException { + String sql="select u.id, u.fullname, u.email, u.enabled, rol.role_name from users u" + + " left join user_roles u_rol" + + " on u.id = u_rol.user_id" + + " join roles rol" + + " on u.role_id = rol.id" + + " order by u.id asc" + + " limit 10"; + List<ManageUser> users=null; + try{ + users=jdbc.query(sql, adminManageUserMapper); + } + catch(EmptyResultDataAccessException ex){ + // Handle case where no records were found + throw new EmptyResultDataAccessException("Did not find any records with selected id", 0); + } + catch (IncorrectResultSizeDataAccessException ex) { + // Handle case where multiple results were found + throw new IncorrectResultSizeDataAccessException("Multiple records generated, only one record expected",1); + } + catch (DataAccessException e) { + // Handle other database-related exceptions + throw new SQLException("Some unexpected database error occured."); + } + return users; + } + + public AdminBoard getBoardManagementInfo() throws SQLException { + String sql="SELECT" + + "(SELECT COUNT(id) FROM users) AS total_users," + + "(SELECT COUNT(post_id) FROM feed) AS total_posts," + + "(SELECT COUNT(event_id) FROM event WHERE event_date > CURRENT_DATE) AS upcoming_events," + + "(SELECT COUNT(id) FROM comment WHERE DATEDIFF(CURRENT_DATE, created_date) <= 3) AS new_comments"; + AdminBoard dashboard=null; + try{ + dashboard=jdbc.queryForObject(sql, adminBoardMapper); + } + catch (IncorrectResultSizeDataAccessException ex) { + // Handle case where multiple results were found + throw new IncorrectResultSizeDataAccessException("Multiple records generated, only one record expected",1); + } + catch (DataAccessException e) { + // Handle other database-related exceptions + throw new SQLException("Some unexpected database error occured."); + } + return dashboard; + } +} diff --git a/src/main/java/polish_community_group_11/polish_community/admin/models/AdminBoard.java b/src/main/java/polish_community_group_11/polish_community/admin/models/AdminBoard.java new file mode 100644 index 0000000000000000000000000000000000000000..d26e64d4c47cbc996691ff7f7283dd8e2031831d --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/admin/models/AdminBoard.java @@ -0,0 +1,15 @@ +package polish_community_group_11.polish_community.admin.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AdminBoard { + private int totalNoOfUsers; + private int totalPosts; + private int upcomingEvents; + private int newComments; +} diff --git a/src/main/java/polish_community_group_11/polish_community/admin/models/ManageUser.java b/src/main/java/polish_community_group_11/polish_community/admin/models/ManageUser.java new file mode 100644 index 0000000000000000000000000000000000000000..73c638cb0f9cb0ad14cad5a818638042eaff43b2 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/admin/models/ManageUser.java @@ -0,0 +1,16 @@ +package polish_community_group_11.polish_community.admin.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ManageUser { + private int id; + private String fullName; + private String email; + private Boolean enabled; + private String role; +} diff --git a/src/main/java/polish_community_group_11/polish_community/admin/services/AdminService.java b/src/main/java/polish_community_group_11/polish_community/admin/services/AdminService.java new file mode 100644 index 0000000000000000000000000000000000000000..da2d1b9a9d4958fa18968dd108be1d2c7c1c9d30 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/admin/services/AdminService.java @@ -0,0 +1,12 @@ +package polish_community_group_11.polish_community.admin.services; + +import polish_community_group_11.polish_community.admin.models.AdminBoard; +import polish_community_group_11.polish_community.admin.models.ManageUser; + +import java.sql.SQLException; +import java.util.List; + +public interface AdminService { + List<ManageUser> getUserManagementInfo() throws SQLException; + AdminBoard getBoardManagementInfo() throws SQLException; +} diff --git a/src/main/java/polish_community_group_11/polish_community/admin/services/AdminServiceImpl.java b/src/main/java/polish_community_group_11/polish_community/admin/services/AdminServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b3e910351840621817894f80002d7faabaa2038c --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/admin/services/AdminServiceImpl.java @@ -0,0 +1,24 @@ +package polish_community_group_11.polish_community.admin.services; + +import org.springframework.stereotype.Service; +import polish_community_group_11.polish_community.admin.dao.AdminRepository; +import polish_community_group_11.polish_community.admin.models.AdminBoard; +import polish_community_group_11.polish_community.admin.models.ManageUser; + +import java.sql.SQLException; +import java.util.List; + +@Service +public class AdminServiceImpl implements AdminService { + private final AdminRepository adminRepository; + + public AdminServiceImpl(AdminRepository adminRepository) { + this.adminRepository = adminRepository; + } + public List<ManageUser> getUserManagementInfo() throws SQLException { + return adminRepository.getUserManagementInfo(); + } + public AdminBoard getBoardManagementInfo() throws SQLException{ + return adminRepository.getBoardManagementInfo(); + } +} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 276eed482ec45c20f470da85f218e0cffe79fc64..ecc66b7c659c93ac8b24fb95e50361d85954d2aa 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -67,8 +67,9 @@ create table if not exists users ( password VARCHAR(255) NOT NULL, fullname VARCHAR(255) NOT NULL, dob DATE, - role_id INT NOT NULL, + role_id INT NOT NULL default 2, organization varchar(255), + enabled boolean default true, FOREIGN KEY (role_id) REFERENCES roles(id) ); diff --git a/src/main/resources/static/css/admin/adminBoardStyles.css b/src/main/resources/static/css/admin/adminBoardStyles.css new file mode 100644 index 0000000000000000000000000000000000000000..f7c09ed7ecf8207b18ccee71e55ea0ae5114a5c1 --- /dev/null +++ b/src/main/resources/static/css/admin/adminBoardStyles.css @@ -0,0 +1,97 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.header { + text-align: center; + margin-bottom: 20px; +} + +.header h1 { + font-size: 24px; + color: #333; +} + +.statistics { + display: flex; + justify-content: center; + gap: 20px; + margin-bottom: 20px; +} + +.statistics .stat { + background-color: #f9f9f9; + border: 1px solid #ddd; + border-radius: 8px; + padding: 10px 20px; + text-align: center; + font-size: 16px; + color: #333; +} + +.dashboard { + display: flex; + flex-wrap: wrap; + gap: 20px; + justify-content: center; +} + +.card { + background-color: #fff; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + width: 250px; + text-align: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: transform 0.2s; +} + +.card:hover { + transform: translateY(-5px); +} + +.card h3 { + font-size: 18px; + color: #333; + margin-bottom: 10px; +} + +.card p { + font-size: 14px; + color: #666; + margin-bottom: 15px; +} + +.card button { + background-color: #000; + color: #fff; + border: none; + border-radius: 4px; + padding: 10px 20px; + cursor: pointer; + font-size: 14px; +} + +.card button:hover { + background-color: #444; +} + +.footer { + text-align: center; + margin-top: 30px; + font-size: 14px; + color: #666; +} + +.footer a { + color: #000; + text-decoration: none; + margin: 0 10px; +} + +.footer a:hover { + text-decoration: underline; +} \ No newline at end of file diff --git a/src/main/resources/static/css/admin/userManageStyles.css b/src/main/resources/static/css/admin/userManageStyles.css new file mode 100644 index 0000000000000000000000000000000000000000..56bf54b203b671904cd93b69c1c48e8fbe5dc441 --- /dev/null +++ b/src/main/resources/static/css/admin/userManageStyles.css @@ -0,0 +1,24 @@ +table { + width: 100%; + border-collapse: collapse; +} +th, td { + padding: 10px; + border: 1px solid #ccc; + text-align: center; +} +th { + background-color: #f4f4f4; +} +.btn { + padding: 5px 10px; + margin: 5px; + cursor: pointer; + border: none; + background-color: #4CAF50; + color: white; + border-radius: 5px; +} +.btn-disable { + background-color: #f44336; +} \ No newline at end of file diff --git a/src/main/resources/templates/admin/adminBoard.html b/src/main/resources/templates/admin/adminBoard.html new file mode 100644 index 0000000000000000000000000000000000000000..2e81ca319162155c2e256b4b885cf963e60aeb8a --- /dev/null +++ b/src/main/resources/templates/admin/adminBoard.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org" + xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" + layout:decorate="~{layout/layout}"> +<head> + <meta charset="UTF-8"> + <title>Title</title> + <link rel="stylesheet" href="/css/admin/adminBoardStyles.css"> +</head> +<section layout:fragment="content"> + <div class="header"> + <h1>Admin Dashboard</h1> + </div> + + <div class="statistics"> + <div class="stat">Total Users: <span th:text="*{adminBoard.getTotalNoOfUsers()}"></span></div> + <div class="stat">Total Posts: <span th:text="*{adminBoard.getTotalPosts()}"></span></div> + <div class="stat">Upcoming Events: <span th:text="*{adminBoard.getUpcomingEvents()}"></span></div> + <div class="stat">New Comments: <span th:text="*{adminBoard.getNewComments()}"></span></div> + </div> + + <div class="dashboard"> + <div class="card"> + <h3>Manage Users</h3> + <p>View and manage user accounts</p> + <a th:href="@{/admin/userInfo}"><button>Manage</button></a> + </div> + + <div class="card"> + <h3>Content Moderation</h3> + <p>Review and moderate community content</p> + <button>Manage</button> + </div> + + <div class="card"> + <h3>Event Management</h3> + <p>Create and manage community events</p> + <button>Manage</button> + </div> + + <div class="card"> + <h3>Database Management</h3> + <p>Manage community information database</p> + <button>Manage</button> + </div> + + <div class="card"> + <h3>Comments Moderation</h3> + <p>Review and moderate user comments</p> + <button>Manage</button> + </div> + + <div class="card"> + <h3>Site Settings</h3> + <p>Configure website settings and preferences</p> + <button>Manage</button> + </div> + </div> + +</section> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/admin/userManage.html b/src/main/resources/templates/admin/userManage.html new file mode 100644 index 0000000000000000000000000000000000000000..6fd4939d17420a1fe93763bf65515927400c15ec --- /dev/null +++ b/src/main/resources/templates/admin/userManage.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org" + xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" + layout:decorate="~{layout/layout}"> +<head> + <title>Manage Users</title> +<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">--> + <link rel="stylesheet" href="/css/admin/userManageStyles.css"> +</head> +<section layout:fragment="content"> + <h2>User Management</h2> + + <table> + <thead> + <tr> + <th>ID</th> + <th>Full Name</th> + <th>Email</th> + <th>Role</th> + <th>Enabled</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + <tr th:each="user : ${usersAdminInfo}"> + <td th:text="${user.id}"></td> + <td th:text="${user.fullName}"></td> + <td th:text="${user.email}"></td> + <td th:text="${user.role}"></td> + <td> + <span th:text="${user.enabled ? 'Enabled' : 'Disabled'}"></span> + </td> + <td> + <a th:href="@{/user/edit/{id}(id=${user.id})}" class="btn">Edit</a> + <button th:if="${user.enabled}" th:onclick="|location.href='/user/disable/{id}'|" + class="btn btn-disable" th:text="'Disable'">Disable</button> + <button th:if="${!user.enabled}" th:onclick="|location.href='/user/enable/{id}'|" + class="btn" th:text="'Enable'">Enable</button> + </td> + </tr> + </tbody> + </table> +</section> +</html>