Skip to content

Commit 785ddb3

Browse files
Ashish MishraAshish Mishra
authored andcommitted
adding thymleaf for UI
1 parent 44dc52d commit 785ddb3

3 files changed

Lines changed: 377 additions & 1 deletion

File tree

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
This project demonstrates how to containerize a **Spring Boot application** with **MySQL** and **Adminer** using **Docker Compose**.
44

5-
> Swagger Url: http://localhost:8080/swagger-ui/index.html#/
5+
> - Swagger Url: http://localhost:8080/swagger-ui/index.html#/
6+
> - UI Url: http://localhost:8080/
67
78
## 📌 Project Structure
89

@@ -15,6 +16,13 @@ project-root/
1516
├── README.md # Project documentation
1617
```
1718

19+
## 🧑‍💻 Technologies Used
20+
21+
- Spring Boot
22+
- Spring Web
23+
- Thymeleaf
24+
- HTML/CSS/JavaScript
25+
1826
## 🔧 Services Overview
1927

2028
- **MySQL (****`mysql-service`****)**

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@
9595
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
9696
<version>2.6.0</version>
9797
</dependency>
98+
<dependency>
99+
<groupId>org.springframework.boot</groupId>
100+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
101+
</dependency>
102+
98103
</dependencies>
99104

100105
<build>
Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>Spring Boot User Management - Dark Mode</title>
7+
<style>
8+
body {
9+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10+
background-color: #121212;
11+
color: #e0e0e0;
12+
margin: 0;
13+
padding: 20px;
14+
}
15+
16+
h2 {
17+
color: #e0e0e0;
18+
text-align: center;
19+
margin-bottom: 30px;
20+
}
21+
22+
.section {
23+
background: #1e1e1e;
24+
padding: 25px;
25+
margin-bottom: 30px;
26+
border-radius: 10px;
27+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
28+
max-width: 900px;
29+
margin-left: auto;
30+
margin-right: auto;
31+
}
32+
33+
button {
34+
padding: 12px 25px;
35+
border: none;
36+
border-radius: 6px;
37+
font-size: 1rem;
38+
font-weight: 600;
39+
color: white;
40+
cursor: pointer;
41+
margin-top: 10px;
42+
margin-right: 10px;
43+
transition: background-color 0.3s ease;
44+
box-shadow: 0 3px 6px rgba(0,0,0,0.3);
45+
}
46+
47+
button.get {
48+
background-color: #28a745;
49+
}
50+
51+
button.get:hover {
52+
background-color: #218838;
53+
}
54+
55+
button.post {
56+
background-color: #007bff;
57+
}
58+
59+
button.post:hover {
60+
background-color: #0069d9;
61+
}
62+
63+
button.delete {
64+
background-color: #dc3545;
65+
}
66+
67+
button.delete:hover {
68+
background-color: #b02a37;
69+
}
70+
71+
table {
72+
width: 100%;
73+
max-width: 100%;
74+
border-collapse: collapse;
75+
margin-top: 20px;
76+
border: 1px solid #333;
77+
border-radius: 8px;
78+
overflow: hidden;
79+
box-shadow: 0 2px 10px rgba(0,0,0,0.6);
80+
}
81+
82+
thead tr {
83+
background-color: #333;
84+
color: #f1f1f1;
85+
}
86+
87+
th, td {
88+
padding: 14px 20px;
89+
border-bottom: 1px solid #444;
90+
text-align: left;
91+
}
92+
93+
tbody tr:hover {
94+
background-color: #2a2a2a;
95+
}
96+
97+
.error {
98+
color: #ff6b6b;
99+
margin-top: 15px;
100+
font-weight: 600;
101+
}
102+
103+
.success {
104+
color: #4caf50;
105+
margin-top: 15px;
106+
font-weight: 600;
107+
}
108+
109+
.input-group {
110+
margin-top: 15px;
111+
margin-bottom: 15px;
112+
}
113+
114+
label {
115+
display: inline-block;
116+
width: 120px;
117+
font-weight: 600;
118+
color: #ccc;
119+
}
120+
121+
input[type="text"], input[type="email"], input[type="number"] {
122+
padding: 8px 12px;
123+
border-radius: 6px;
124+
border: 1px solid #555;
125+
background-color: #222;
126+
color: #eee;
127+
width: 200px;
128+
font-size: 1rem;
129+
outline: none;
130+
transition: border-color 0.3s ease;
131+
}
132+
133+
input[type="text"]:focus, input[type="email"]:focus, input[type="number"]:focus {
134+
border-color: #007bff;
135+
box-shadow: 0 0 5px #007bff;
136+
}
137+
138+
/* Result areas styling */
139+
.result-area {
140+
margin-top: 20px;
141+
min-height: 40px;
142+
white-space: pre-wrap;
143+
font-family: Consolas, monospace;
144+
}
145+
146+
</style>
147+
</head>
148+
<body>
149+
<h2>Spring Boot User Management</h2>
150+
151+
<!-- Get All Users Section -->
152+
<div class="section">
153+
<button class="get" onclick="fetchAllUsers()">Get All Users</button>
154+
<div id="resultAllUsers" class="result-area"></div>
155+
</div>
156+
157+
<!-- Get User By ID Section -->
158+
<div class="section">
159+
<div class="input-group">
160+
<label for="userIdFetch">User ID:</label>
161+
<input type="number" id="userIdFetch" min="1" placeholder="Enter user ID" />
162+
<button class="get" onclick="fetchUserById()">Get User By ID</button>
163+
</div>
164+
<div id="resultUserById" class="result-area"></div>
165+
</div>
166+
167+
<!-- Get Email by User ID Section -->
168+
<div class="section">
169+
<div class="input-group">
170+
<label for="userIdEmail">User ID:</label>
171+
<input type="number" id="userIdEmail" min="1" placeholder="Enter user ID" />
172+
<button class="post" onclick="fetchEmailByUserId()">Get Email By User ID</button>
173+
</div>
174+
<div id="resultEmailByUserId" class="result-area"></div>
175+
</div>
176+
177+
<!-- Create User Section -->
178+
<div class="section">
179+
<div class="input-group">
180+
<label for="firstName">First Name:</label>
181+
<input type="text" id="firstName" placeholder="First Name" />
182+
</div>
183+
<div class="input-group">
184+
<label for="lastName">Last Name:</label>
185+
<input type="text" id="lastName" placeholder="Last Name" />
186+
</div>
187+
<div class="input-group">
188+
<label for="email">Email:</label>
189+
<input type="email" id="email" placeholder="Email" />
190+
</div>
191+
<button class="post" onclick="createUser()">Create User</button>
192+
<div id="resultCreateUser" class="result-area"></div>
193+
</div>
194+
195+
<!-- Delete User Section -->
196+
<div class="section">
197+
<div class="input-group">
198+
<label for="userIdDelete">User ID:</label>
199+
<input type="number" id="userIdDelete" min="1" placeholder="Enter user ID to delete" />
200+
<button class="delete" onclick="deleteUser()">Delete User</button>
201+
</div>
202+
<div id="resultDeleteUser" class="result-area"></div>
203+
</div>
204+
205+
<script>
206+
const baseUrl = "http://localhost:8080/api/user";
207+
208+
// Fetch all users
209+
function fetchAllUsers() {
210+
document.getElementById("resultAllUsers").textContent = "Loading...";
211+
fetch(`${baseUrl}/getAllUser`)
212+
.then(response => response.json())
213+
.then(users => {
214+
if(users.length === 0) {
215+
document.getElementById("resultAllUsers").textContent = "No users found.";
216+
return;
217+
}
218+
let html = `
219+
<table>
220+
<thead>
221+
<tr>
222+
<th>ID</th>
223+
<th>First Name</th>
224+
<th>Last Name</th>
225+
<th>Email</th>
226+
</tr>
227+
</thead>
228+
<tbody>
229+
${users.map(u => `
230+
<tr>
231+
<td>${u.id}</td>
232+
<td>${u.firstName}</td>
233+
<td>${u.lastName}</td>
234+
<td>${u.email}</td>
235+
</tr>`).join('')}
236+
</tbody>
237+
</table>`;
238+
document.getElementById("resultAllUsers").innerHTML = html;
239+
})
240+
.catch(err => {
241+
document.getElementById("resultAllUsers").textContent = `Error: ${err.message}`;
242+
});
243+
}
244+
245+
// Fetch user by ID
246+
function fetchUserById() {
247+
const id = document.getElementById("userIdFetch").value.trim();
248+
document.getElementById("resultUserById").textContent = "Loading...";
249+
fetch(`${baseUrl}/getUserById/${id}`)
250+
.then(response => response.json())
251+
.then(user => {
252+
if(user.email && user.email.startsWith("email should not")) {
253+
document.getElementById("resultUserById").textContent = user.email;
254+
return;
255+
}
256+
let html = `
257+
<table>
258+
<thead>
259+
<tr>
260+
<th>ID</th><th>First Name</th><th>Last Name</th><th>Email</th>
261+
</tr>
262+
</thead>
263+
<tbody>
264+
<tr>
265+
<td>${user.id}</td>
266+
<td>${user.firstName}</td>
267+
<td>${user.lastName}</td>
268+
<td>${user.email}</td>
269+
</tr>
270+
</tbody>
271+
</table>`;
272+
document.getElementById("resultUserById").innerHTML = html;
273+
})
274+
.catch(err => {
275+
document.getElementById("resultUserById").textContent = `Error: ${err.message}`;
276+
});
277+
}
278+
279+
// Fetch email by user ID (POST)
280+
function fetchEmailByUserId() {
281+
const id = document.getElementById("userIdEmail").value.trim();
282+
document.getElementById("resultEmailByUserId").textContent = "Loading...";
283+
fetch(`${baseUrl}/getEmailFromId`, {
284+
method: "POST",
285+
headers: { "Content-Type": "application/json" },
286+
body: JSON.stringify({ userId: Number(id) }),
287+
})
288+
.then(response => response.json())
289+
.then(data => {
290+
if(data.email && data.email.startsWith("email should not")) {
291+
document.getElementById("resultEmailByUserId").textContent = data.email;
292+
return;
293+
}
294+
document.getElementById("resultEmailByUserId").textContent = `Email: ${data.email}`;
295+
})
296+
.catch(err => {
297+
document.getElementById("resultEmailByUserId").textContent = `Error: ${err.message}`;
298+
});
299+
}
300+
301+
// Create user (POST)
302+
function createUser() {
303+
const firstName = document.getElementById("firstName").value.trim();
304+
const lastName = document.getElementById("lastName").value.trim();
305+
const email = document.getElementById("email").value.trim();
306+
document.getElementById("resultCreateUser").textContent = "Processing...";
307+
308+
fetch(`${baseUrl}/create`, {
309+
method: "POST",
310+
headers: { "Content-Type": "application/json" },
311+
body: JSON.stringify({ firstName, lastName, email }),
312+
})
313+
.then(response => response.json())
314+
.then(user => {
315+
if(user.email && user.email.startsWith("email should not")) {
316+
document.getElementById("resultCreateUser").textContent = user.email;
317+
return;
318+
}
319+
let html = `
320+
<table>
321+
<thead>
322+
<tr>
323+
<th>ID</th><th>First Name</th><th>Last Name</th><th>Email</th>
324+
</tr>
325+
</thead>
326+
<tbody>
327+
<tr>
328+
<td>${user.id}</td>
329+
<td>${user.firstName}</td>
330+
<td>${user.lastName}</td>
331+
<td>${user.email}</td>
332+
</tr>
333+
</tbody>
334+
</table>`;
335+
document.getElementById("resultCreateUser").innerHTML = html;
336+
337+
// Clear input fields
338+
document.getElementById("firstName").value = "";
339+
document.getElementById("lastName").value = "";
340+
document.getElementById("email").value = "";
341+
})
342+
.catch(err => {
343+
document.getElementById("resultCreateUser").textContent = `Error: ${err.message}`;
344+
});
345+
}
346+
347+
// Delete user (DELETE)
348+
function deleteUser() {
349+
const id = document.getElementById("userIdDelete").value.trim();
350+
document.getElementById("resultDeleteUser").textContent = "Processing...";
351+
fetch(`${baseUrl}/delete/${id}`, { method: "DELETE" })
352+
.then(response => response.text())
353+
.then(msg => {
354+
document.getElementById("resultDeleteUser").textContent = msg;
355+
document.getElementById("userIdDelete").value = "";
356+
})
357+
.catch(err => {
358+
document.getElementById("resultDeleteUser").textContent = `Error: ${err.message}`;
359+
});
360+
}
361+
</script>
362+
</body>
363+
</html>

0 commit comments

Comments
 (0)