Nice things
This commit is contained in:
parent
facade014a
commit
da7d58b548
11 changed files with 768 additions and 51 deletions
|
|
@ -779,15 +779,17 @@ For ActivityPub federated posts and thumbnails:
|
|||
- [x] Heart rate chart over time on activity details (Chart.js line chart, elapsed time x-axis, heart rate y-axis)
|
||||
- [x] Speed/pace chart over time on activity details (Chart.js line chart with smoothing, displays speed in km/h with pace in tooltip)
|
||||
- [x] Notifications system (Notification entity, NotificationRepository, NotificationService, NotificationController REST API, notifications.html UI, notification bell in nav with unread count, polling every 30s, mark as read/delete, all/unread filter tabs)
|
||||
- [x] Custom 404 Not Found page (error/404.html with animated compass icon, suggestions, gradient background)
|
||||
- [x] Custom 403 Forbidden page (error/403.html with shield-lock icon, auth-aware login button, gradient background)
|
||||
- [x] Custom 500 Internal Server Error page (error/500.html with tools icon, recovery suggestions)
|
||||
- [x] Generic error page (error/error.html with dynamic error code display, technical details toggle)
|
||||
- [x] Empty state illustrations (reusable CSS classes in fitpub.css, floating animation, variant colors for activities/notifications/timeline/users/search, updated all timeline pages, notifications page, and discover page)
|
||||
- [ ] Enhanced privacy controls UI
|
||||
- [ ] Follow/unfollow buttons on user profiles
|
||||
- [ ] Activity visibility to followers (implement FOLLOWERS visibility enforcement)
|
||||
- [ ] Breadcrumb navigation
|
||||
- [ ] Active route highlighting in navigation
|
||||
- [ ] Global error boundary/handler
|
||||
- [ ] Custom 404 Not Found page
|
||||
- [ ] Custom 403 Forbidden page
|
||||
- [ ] Empty state illustrations
|
||||
|
||||
### Phase 3: Advanced Analytics
|
||||
- [ ] Personal records tracking
|
||||
|
|
|
|||
|
|
@ -290,6 +290,68 @@ body {
|
|||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
/* Empty State Illustrations */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.empty-state-icon {
|
||||
font-size: 5rem;
|
||||
color: #d1d5db;
|
||||
margin-bottom: 1.5rem;
|
||||
display: inline-block;
|
||||
animation: float-gentle 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float-gentle {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.empty-state-message {
|
||||
font-size: 1rem;
|
||||
color: #6b7280;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.empty-state-action {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
/* Empty state variants */
|
||||
.empty-state-activities .empty-state-icon {
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
.empty-state-notifications .empty-state-icon {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
.empty-state-timeline .empty-state-icon {
|
||||
color: #a78bfa;
|
||||
}
|
||||
|
||||
.empty-state-users .empty-state-icon {
|
||||
color: #86efac;
|
||||
}
|
||||
|
||||
.empty-state-search .empty-state-icon {
|
||||
color: #fda4af;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.map-container {
|
||||
|
|
@ -303,4 +365,16 @@ body {
|
|||
.metric-value {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
padding: 3rem 1rem;
|
||||
}
|
||||
|
||||
.empty-state-icon {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.empty-state-title {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
184
src/main/resources/templates/error/403.html
Normal file
184
src/main/resources/templates/error/403.html
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>403 - Access Denied - FitPub</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" th:href="@{/css/fitpub.css}">
|
||||
<style>
|
||||
.error-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #fc5c7d 0%, #6a82fb 100%);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.error-card {
|
||||
background: white;
|
||||
border-radius: 1rem;
|
||||
padding: 3rem;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 8rem;
|
||||
color: #fc5c7d;
|
||||
margin-bottom: 1rem;
|
||||
animation: shake 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%, 100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
25% {
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
75% {
|
||||
transform: translateX(10px);
|
||||
}
|
||||
}
|
||||
|
||||
.error-code {
|
||||
font-size: 6rem;
|
||||
font-weight: 700;
|
||||
color: #fc5c7d;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
margin: 1rem 0;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1.1rem;
|
||||
color: #718096;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.error-suggestions {
|
||||
text-align: left;
|
||||
background: #f7fafc;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.error-suggestions h5 {
|
||||
color: #2d3748;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error-suggestions ul {
|
||||
margin: 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.error-suggestions li {
|
||||
color: #4a5568;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-home {
|
||||
background: linear-gradient(135deg, #fc5c7d 0%, #6a82fb 100%);
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
font-size: 1.1rem;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.btn-home:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 25px rgba(252, 92, 125, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
font-size: 1.1rem;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
transition: transform 0.2s;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4);
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-container">
|
||||
<div class="error-card">
|
||||
<div class="error-icon">
|
||||
<i class="bi bi-shield-lock"></i>
|
||||
</div>
|
||||
|
||||
<h1 class="error-code">403</h1>
|
||||
<h2 class="error-title">Access Denied</h2>
|
||||
<p class="error-message">
|
||||
You don't have permission to access this resource. This area is restricted.
|
||||
</p>
|
||||
|
||||
<div class="error-suggestions">
|
||||
<h5><i class="bi bi-info-circle"></i> Why am I seeing this?</h5>
|
||||
<ul>
|
||||
<li>You may need to log in to access this page</li>
|
||||
<li>Your account may not have the required permissions</li>
|
||||
<li>This content may be private or restricted</li>
|
||||
<li>The resource owner hasn't shared it with you</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<a th:href="@{/}" class="btn-home">
|
||||
<i class="bi bi-house-door"></i> Back to Home
|
||||
</a>
|
||||
<a th:href="@{/login}" class="btn-login" id="loginBtn">
|
||||
<i class="bi bi-box-arrow-in-right"></i> Login
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<small class="text-muted">
|
||||
Error Code: 403 | Forbidden
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script th:src="@{/js/auth.js}"></script>
|
||||
<script>
|
||||
// Hide login button if already authenticated
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (FitPubAuth.isAuthenticated()) {
|
||||
const loginBtn = document.getElementById('loginBtn');
|
||||
if (loginBtn) {
|
||||
loginBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
161
src/main/resources/templates/error/404.html
Normal file
161
src/main/resources/templates/error/404.html
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>404 - Page Not Found - FitPub</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" th:href="@{/css/fitpub.css}">
|
||||
<style>
|
||||
.error-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.error-card {
|
||||
background: white;
|
||||
border-radius: 1rem;
|
||||
padding: 3rem;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 8rem;
|
||||
color: #667eea;
|
||||
margin-bottom: 1rem;
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
}
|
||||
|
||||
.error-code {
|
||||
font-size: 6rem;
|
||||
font-weight: 700;
|
||||
color: #667eea;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
margin: 1rem 0;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1.1rem;
|
||||
color: #718096;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.error-suggestions {
|
||||
text-align: left;
|
||||
background: #f7fafc;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.error-suggestions h5 {
|
||||
color: #2d3748;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error-suggestions ul {
|
||||
margin: 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.error-suggestions li {
|
||||
color: #4a5568;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-home {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
font-size: 1.1rem;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.btn-home:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-back {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
margin-left: 1rem;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.btn-back:hover {
|
||||
color: #764ba2;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-container">
|
||||
<div class="error-card">
|
||||
<div class="error-icon">
|
||||
<i class="bi bi-compass"></i>
|
||||
</div>
|
||||
|
||||
<h1 class="error-code">404</h1>
|
||||
<h2 class="error-title">Page Not Found</h2>
|
||||
<p class="error-message">
|
||||
Oops! Looks like you've ventured off the trail. The page you're looking for doesn't exist.
|
||||
</p>
|
||||
|
||||
<div class="error-suggestions">
|
||||
<h5><i class="bi bi-lightbulb"></i> What can you do?</h5>
|
||||
<ul>
|
||||
<li>Check the URL for typos</li>
|
||||
<li>Go back to the previous page</li>
|
||||
<li>Visit our homepage and start fresh</li>
|
||||
<li>Use the navigation menu to find what you need</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<a th:href="@{/}" class="btn-home">
|
||||
<i class="bi bi-house-door"></i> Back to Home
|
||||
</a>
|
||||
<a href="javascript:history.back()" class="btn-back">
|
||||
<i class="bi bi-arrow-left"></i> Go Back
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<small class="text-muted">
|
||||
Error Code: 404 | Not Found
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
161
src/main/resources/templates/error/500.html
Normal file
161
src/main/resources/templates/error/500.html
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>500 - Internal Server Error - FitPub</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" th:href="@{/css/fitpub.css}">
|
||||
<style>
|
||||
.error-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.error-card {
|
||||
background: white;
|
||||
border-radius: 1rem;
|
||||
padding: 3rem;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 8rem;
|
||||
color: #fa709a;
|
||||
margin-bottom: 1rem;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.error-code {
|
||||
font-size: 6rem;
|
||||
font-weight: 700;
|
||||
color: #fa709a;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
margin: 1rem 0;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1.1rem;
|
||||
color: #718096;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.error-suggestions {
|
||||
text-align: left;
|
||||
background: #f7fafc;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.error-suggestions h5 {
|
||||
color: #2d3748;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error-suggestions ul {
|
||||
margin: 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.error-suggestions li {
|
||||
color: #4a5568;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-home {
|
||||
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
font-size: 1.1rem;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.btn-home:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 25px rgba(250, 112, 154, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-back {
|
||||
color: #fa709a;
|
||||
text-decoration: none;
|
||||
margin-left: 1rem;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.btn-back:hover {
|
||||
color: #fee140;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-container">
|
||||
<div class="error-card">
|
||||
<div class="error-icon">
|
||||
<i class="bi bi-tools"></i>
|
||||
</div>
|
||||
|
||||
<h1 class="error-code">500</h1>
|
||||
<h2 class="error-title">Internal Server Error</h2>
|
||||
<p class="error-message">
|
||||
Oops! Something went wrong on our end. Our team has been notified and we're working on it.
|
||||
</p>
|
||||
|
||||
<div class="error-suggestions">
|
||||
<h5><i class="bi bi-info-circle"></i> What can you do?</h5>
|
||||
<ul>
|
||||
<li>Try refreshing the page</li>
|
||||
<li>Wait a few moments and try again</li>
|
||||
<li>Go back to the previous page</li>
|
||||
<li>Visit our homepage</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<a th:href="@{/}" class="btn-home">
|
||||
<i class="bi bi-house-door"></i> Back to Home
|
||||
</a>
|
||||
<a href="javascript:history.back()" class="btn-back">
|
||||
<i class="bi bi-arrow-left"></i> Go Back
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<small class="text-muted">
|
||||
Error Code: 500 | Internal Server Error
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
129
src/main/resources/templates/error/error.html
Normal file
129
src/main/resources/templates/error/error.html
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Error - FitPub</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" th:href="@{/css/fitpub.css}">
|
||||
<style>
|
||||
.error-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.error-card {
|
||||
background: white;
|
||||
border-radius: 1rem;
|
||||
padding: 3rem;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 8rem;
|
||||
color: #f5576c;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
font-size: 6rem;
|
||||
font-weight: 700;
|
||||
color: #f5576c;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
margin: 1rem 0;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1.1rem;
|
||||
color: #718096;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.btn-home {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
font-size: 1.1rem;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.btn-home:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 25px rgba(245, 87, 108, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-back {
|
||||
color: #f5576c;
|
||||
text-decoration: none;
|
||||
margin-left: 1rem;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.btn-back:hover {
|
||||
color: #f093fb;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-container">
|
||||
<div class="error-card">
|
||||
<div class="error-icon">
|
||||
<i class="bi bi-exclamation-triangle"></i>
|
||||
</div>
|
||||
|
||||
<h1 class="error-code" th:text="${status} ?: '500'">500</h1>
|
||||
<h2 class="error-title" th:text="${error} ?: 'Something Went Wrong'">Something Went Wrong</h2>
|
||||
<p class="error-message">
|
||||
We encountered an unexpected error while processing your request.
|
||||
<span th:if="${message}" th:text="' ' + ${message}"></span>
|
||||
</p>
|
||||
|
||||
<div class="mt-4">
|
||||
<a th:href="@{/}" class="btn-home">
|
||||
<i class="bi bi-house-door"></i> Back to Home
|
||||
</a>
|
||||
<a href="javascript:history.back()" class="btn-back">
|
||||
<i class="bi bi-arrow-left"></i> Go Back
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<small class="text-muted">
|
||||
Error Code: <span th:text="${status} ?: '500'">500</span>
|
||||
<span th:if="${timestamp}">
|
||||
| <span th:text="${timestamp}">timestamp</span>
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="mt-3" th:if="${trace}">
|
||||
<details class="text-start">
|
||||
<summary class="btn btn-sm btn-outline-secondary">Show Technical Details</summary>
|
||||
<pre class="mt-2 p-3 bg-light text-start" style="max-height: 300px; overflow-y: auto; font-size: 0.75rem;" th:text="${trace}"></pre>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<html lang="en"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{layout}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Notifications - FitPub</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" th:href="@{/css/fitpub.css}">
|
||||
<style>
|
||||
.notification-item {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
|
|
@ -82,16 +80,6 @@
|
|||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.empty-notifications {
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
}
|
||||
|
||||
.empty-notifications i {
|
||||
font-size: 4rem;
|
||||
color: #dee2e6;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.filter-tabs {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
|
|
@ -100,9 +88,8 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div th:replace="~{layout :: header}"></div>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div layout:fragment="content">
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 offset-lg-2">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
|
|
@ -249,10 +236,12 @@
|
|||
|
||||
if (data.content.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="empty-notifications">
|
||||
<i class="bi bi-bell-slash"></i>
|
||||
<h4>No notifications</h4>
|
||||
<p class="text-muted">You're all caught up!</p>
|
||||
<div class="empty-state empty-state-notifications">
|
||||
<div class="empty-state-icon">
|
||||
<i class="bi bi-bell-slash"></i>
|
||||
</div>
|
||||
<h4 class="empty-state-title">No notifications</h4>
|
||||
<p class="empty-state-message">You're all caught up! Check back later for updates.</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -54,13 +54,20 @@
|
|||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div id="emptyState" class="text-center py-5 d-none">
|
||||
<i class="bi bi-inbox" style="font-size: 4rem; color: #d1d5db;"></i>
|
||||
<h4 class="mt-3">No Activities Yet</h4>
|
||||
<p class="text-muted">Follow other athletes to see their activities here!</p>
|
||||
<a th:href="@{/timeline}" class="btn btn-primary mt-3">
|
||||
<i class="bi bi-globe"></i> Explore Public Timeline
|
||||
</a>
|
||||
<div id="emptyState" class="empty-state empty-state-timeline d-none">
|
||||
<div class="empty-state-icon">
|
||||
<i class="bi bi-people"></i>
|
||||
</div>
|
||||
<h4 class="empty-state-title">No Activities Yet</h4>
|
||||
<p class="empty-state-message">Follow other athletes to see their activities here!</p>
|
||||
<div class="empty-state-action">
|
||||
<a th:href="@{/discover}" class="btn btn-primary">
|
||||
<i class="bi bi-search"></i> Discover Athletes
|
||||
</a>
|
||||
<a th:href="@{/timeline}" class="btn btn-outline-primary ms-2">
|
||||
<i class="bi bi-globe"></i> Public Timeline
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
|
|
|
|||
|
|
@ -54,13 +54,17 @@
|
|||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div id="emptyState" class="text-center py-5 d-none">
|
||||
<i class="bi bi-inbox" style="font-size: 4rem; color: #d1d5db;"></i>
|
||||
<h4 class="mt-3">No Activities Yet</h4>
|
||||
<p class="text-muted">Be the first to share your fitness activities!</p>
|
||||
<a th:href="@{/activities/upload}" class="btn btn-primary mt-3" id="uploadLinkEmpty" style="display: none;">
|
||||
<i class="bi bi-cloud-upload"></i> Upload Activity
|
||||
</a>
|
||||
<div id="emptyState" class="empty-state empty-state-timeline d-none">
|
||||
<div class="empty-state-icon">
|
||||
<i class="bi bi-globe"></i>
|
||||
</div>
|
||||
<h4 class="empty-state-title">No Activities Yet</h4>
|
||||
<p class="empty-state-message">Be the first to share your fitness activities with the community!</p>
|
||||
<div class="empty-state-action">
|
||||
<a th:href="@{/activities/upload}" class="btn btn-primary" id="uploadLinkEmpty" style="display: none;">
|
||||
<i class="bi bi-cloud-upload"></i> Upload Activity
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
|
|
|
|||
|
|
@ -54,13 +54,17 @@
|
|||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div id="emptyState" class="text-center py-5 d-none">
|
||||
<i class="bi bi-inbox" style="font-size: 4rem; color: #d1d5db;"></i>
|
||||
<h4 class="mt-3">No Activities Yet</h4>
|
||||
<p class="text-muted">Upload your first FIT file to get started!</p>
|
||||
<a th:href="@{/activities/upload}" class="btn btn-primary mt-3">
|
||||
<i class="bi bi-cloud-upload"></i> Upload Activity
|
||||
</a>
|
||||
<div id="emptyState" class="empty-state empty-state-activities d-none">
|
||||
<div class="empty-state-icon">
|
||||
<i class="bi bi-activity"></i>
|
||||
</div>
|
||||
<h4 class="empty-state-title">No Activities Yet</h4>
|
||||
<p class="empty-state-message">Upload your first FIT file to start tracking your fitness journey!</p>
|
||||
<div class="empty-state-action">
|
||||
<a th:href="@{/activities/upload}" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-cloud-upload"></i> Upload Activity
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
|
|
|
|||
|
|
@ -86,10 +86,12 @@
|
|||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div id="emptyState" class="text-center py-5 d-none">
|
||||
<i class="bi bi-person-x" style="font-size: 4rem; color: #d1d5db;"></i>
|
||||
<h4 class="mt-3 text-muted">No users found</h4>
|
||||
<p class="text-muted">Try adjusting your search or browse all users</p>
|
||||
<div id="emptyState" class="empty-state empty-state-users d-none">
|
||||
<div class="empty-state-icon">
|
||||
<i class="bi bi-person-x"></i>
|
||||
</div>
|
||||
<h4 class="empty-state-title">No Users Found</h4>
|
||||
<p class="empty-state-message">Try adjusting your search or browse all users in the community</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue