diff --git a/src/main/java/com/basecamp/HyprLink/controller/DashboardController.java b/src/main/java/com/basecamp/HyprLink/controller/DashboardController.java index db1592b..ef30295 100644 --- a/src/main/java/com/basecamp/HyprLink/controller/DashboardController.java +++ b/src/main/java/com/basecamp/HyprLink/controller/DashboardController.java @@ -88,6 +88,8 @@ public String saveProfile(@ModelAttribute("user") User updatedData, Principal pr existingUser.setLinkStyle(updatedData.getLinkStyle()); existingUser.setTextAlign(updatedData.getTextAlign()); existingUser.setButtonColor(updatedData.getButtonColor()); + // Persist custom hex color (if any) - normalize to #rrggbb or null + existingUser.setCustomButtonColor(normalizeHex(updatedData.getCustomButtonColor())); existingUser.setFontFamily(updatedData.getFontFamily()); // Keep only links that have both a title and a URL @@ -108,6 +110,22 @@ public String saveProfile(@ModelAttribute("user") User updatedData, Principal pr return "redirect:/dashboard?success"; } + // Normalize hex color strings: accepts '#abc', 'abc', '#aabbcc', 'aabbcc' -> returns '#aabbcc' or null if invalid + private String normalizeHex(String input) { + if (input == null) return null; + String v = input.trim(); + if (v.isEmpty()) return null; + if (v.charAt(0) == '#') v = v.substring(1); + if (v.length() == 3 && v.matches("[0-9A-Fa-f]{3}")) { + // expand + return "#" + v.charAt(0) + v.charAt(0) + v.charAt(1) + v.charAt(1) + v.charAt(2) + v.charAt(2); + } + if (v.length() == 6 && v.matches("[0-9A-Fa-f]{6}")) { + return "#" + v; + } + return null; + } + @PostMapping("/dashboard/clear-background") public String clearBackground(Principal principal) { User user = userRepository.findByUsername(principal.getName()).orElseThrow(() -> new RuntimeException("User not found")); @@ -198,7 +216,17 @@ private void addDashboardModelData(Model model, User user) { model.addAttribute("linkStyles", java.util.Arrays.asList("pill", "box", "underline")); model.addAttribute("textAlignments", java.util.Arrays.asList("center", "left")); model.addAttribute("buttonColors", java.util.Arrays.asList("blue", "green", "red", "purple", "orange")); - model.addAttribute("fontFamilies", java.util.Arrays.asList("System", "Georgia", "Courier", "Arial")); + model.addAttribute("fontFamilies", java.util.Arrays.asList( + "System", + "Georgia", + "Courier", + "Arial", + "Helvetica", + "Verdana", + "Times", + "Trebuchet", + "Palatino" + )); model.addAttribute("backgrounds", loadBackgrounds()); } diff --git a/src/main/java/com/basecamp/HyprLink/controller/ProfileController.java b/src/main/java/com/basecamp/HyprLink/controller/ProfileController.java index 51a1b09..49367da 100644 --- a/src/main/java/com/basecamp/HyprLink/controller/ProfileController.java +++ b/src/main/java/com/basecamp/HyprLink/controller/ProfileController.java @@ -28,18 +28,64 @@ public String getCurrentUserProfile(Principal principal) { return "redirect:/dashboard"; } - return "redirect:/profile/" + user.getId(); + // Redirect to the explicit ID-based route to avoid ambiguity with username-based route + return "redirect:/profile/id/" + user.getId(); } - @GetMapping("/profile/{id}") + @GetMapping("/profile/id/{id}") public String getProfileById(@PathVariable Long id, Model model) { User user = profileService.getUserProfileById(id); + User user = profileService.getUserProfileByUsername(principal.getName()); + if (user == null || user.getId() == null) { + return "redirect:/dashboard"; + } + + model.addAttribute("user", user); + return "profile"; + } + + @GetMapping("/profile") + public String searchProfile(Principal principal, Model model) { + if (principal == null) { + return "index"; + } + return processUserInfoByUsername(principal.getName(), principal, model); + } + + @GetMapping("/profile/{userName}") + public String getProfileByUsername(@PathVariable String userName, Principal principal, Model model) { + return processUserInfoByUsername(userName, principal, model); + } + + // Helper Methods + private String processUserInfoByUsername(String userName, Principal principal, Model model) { + User user = profileService.getUserProfileByUsername(userName); + return settingMethodAttributes(principal, model, user); + } + + private String processUserInfoById(Long id, Principal principal, Model model) { + User user = profileService.getUserProfileById(id); + + private String settingMethodAttributes(Principal principal, Model model, User user) { if (user == null) { - return "error/404"; + return "index"; + } + if (principal == null) { + model.addAttribute("signedIn", false); + model.addAttribute("principalName", null); + } else if (profileService.getUserProfileByUsername(principal.getName()) != null) { + model.addAttribute("signedIn", true); + model.addAttribute("principalName", principal.getName()); + } + if (principal != null && user.getUsername() != null && user.getUsername().equals(principal.getName())) { + model.addAttribute("usersProfile", true); + } else { + model.addAttribute("usersProfile", false); } model.addAttribute("user", user); return "profile"; } + } \ No newline at end of file diff --git a/src/main/java/com/basecamp/HyprLink/entity/User.java b/src/main/java/com/basecamp/HyprLink/entity/User.java index 6610094..44ebc59 100644 --- a/src/main/java/com/basecamp/HyprLink/entity/User.java +++ b/src/main/java/com/basecamp/HyprLink/entity/User.java @@ -28,6 +28,8 @@ public class User { private String linkStyle; private String textAlign; private String buttonColor; + // Optional custom hex color input provided by user (e.g. #ff8800) + private String customButtonColor; private String fontFamily; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) diff --git a/src/main/java/com/basecamp/HyprLink/security/SecurityConfig.java b/src/main/java/com/basecamp/HyprLink/security/SecurityConfig.java index 92315ef..22fe333 100644 --- a/src/main/java/com/basecamp/HyprLink/security/SecurityConfig.java +++ b/src/main/java/com/basecamp/HyprLink/security/SecurityConfig.java @@ -20,8 +20,12 @@ public PasswordEncoder passwordEncoder() { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth - .requestMatchers("/register", "/login", "/css/**", "/images/**", "/profile/**", "/").permitAll() // Public pages - .anyRequest().authenticated() // Everything else requires login + // Public: home, login, register, static assets and the error page + .requestMatchers("/", "/login", "/register", "/register/check", "/css/**", "/images/**", "/error").permitAll() + // Public: profile pages and templates + .requestMatchers("/profile/**", "/templates").permitAll() + // Everything else requires authentication + .anyRequest().authenticated() ) .formLogin(form -> form .loginPage("/login") // Custom login page diff --git a/src/main/resources/static/css/dashboard.css b/src/main/resources/static/css/dashboard.css index 9fd9cba..adfbda5 100644 --- a/src/main/resources/static/css/dashboard.css +++ b/src/main/resources/static/css/dashboard.css @@ -1,12 +1,10 @@ -* { - box-sizing: border-box; -} +* { box-sizing: border-box; } body { font-family: "Segoe UI", Arial, sans-serif; margin: 0; - padding: 96px 32px 32px; - padding-right: 360px; + /* Reserve room on the right for the live preview panel */ + padding: 110px 360px 32px 32px; color: #1f2b4d; background: #f7faff; max-width: 1200px; @@ -20,16 +18,22 @@ body { display: grid; grid-template-columns: 1fr auto 1fr; padding: 1rem 1rem; - border-bottom: 1px solid #e0e0e0; - background: linear-gradient(90deg, #fafbfc 0%, #f5f7fa 50%, #fafbfc 100%); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04); - position: fixed; - top: 0; + width: 100%; left: 0; - right: 0; - width: 100vw; margin: 0; + + /* sticky top */ + position: fixed; + top: 0; z-index: 1000; + + /* Frosted glass look */ + background: rgba(250, 251, 252, 0.75); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + + border-bottom: 1px solid rgba(224, 224, 224, 0.5); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.05); } .nav a { @@ -39,54 +43,72 @@ body { transition: color 0.3s ease; } -.nav a:hover:not(#link-garden, #sign-up-btn) { - color: #3b82f6; -} +.nav a:hover:not(#link-garden, #sign-up-btn) { color: #3b82f6; } -.nav #link-garden { - font-weight: bold; - font-size: 1.1rem; - color: #1a1a2e; -} +.nav #link-garden { font-weight: bold; font-size: 1.2rem; color: #1a1a2e; } -.nav-left, .nav-center, .nav-right { - display: flex; - align-items: center; -} +.nav-left, .nav-center, .nav-right { display: flex; align-items: center; } +.nav-left a { display: inline-flex; align-items: center; gap: 0.5rem; } -.nav-left a { - display: inline-flex; - align-items: center; - gap: 0.5rem; -} +.nav-left svg { width: 30px; height: 30px; display: block; transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); } -.nav-left svg { - width: 30px; - height: 30px; - display: block; -} +.nav-left a:hover svg { transform: rotate(-10deg) scale(1.1); color: #3b82f6 !important; } -.nav-center { - justify-content: center; - justify-self: center; -} +.nav-center { justify-content: center; justify-self: center; gap: 1rem; align-items: center; } -.nav-center a { - font-size: 1rem; -} +.nav-center a, .nav-center button { font-size: 1.2rem; position: relative; text-decoration: none; } -.nav-left { - justify-self: start; - margin-left: 10rem; +.nav-center a::after { + content: ''; + position: absolute; + width: 0; + height: 2px; + bottom: -4px; + left: 0; + background: linear-gradient(90deg, #3b82f6, #0ea5e9); + transition: width 0.3s cubic-bezier(0.165, 0.84, 0.44, 1); } -.nav-right { - justify-self: end; - gap: 0.75rem; - margin-right: 10rem; +.nav-center a:hover::after { width: 100%; } + +.nav button:hover { color: #3b82f6; } + +.nav-left { justify-self: start; margin-left: 10rem; } + +.nav-right { justify-self: end; gap: 0.75rem; margin-right: 10rem; } + +.nav-right a, .nav-right form button { display: inline-flex; align-items: center; line-height: 1; } + +.nav-right a:not(#sign-up-btn)::after { + content: ''; + position: absolute; + width: 0; + height: 2px; + bottom: -4px; + left: 0; + background: linear-gradient(90deg, #3b82f6, #0ea5e9); + transition: width 0.3s cubic-bezier(0.165, 0.84, 0.44, 1); } +.nav-right a:not(#sign-up-btn):hover::after { width: 100%; } + +.nav-center button, .nav-right button { border: none; background: none; padding: 0 0 3px 0; } + +.nav-center button:hover, .nav-right button:hover { color: #3b82f6; cursor: pointer; } + +.logout-form { margin: 0; padding: 0; display: flex; } + +#logout-btn { + background: transparent; + color: #ef4444; + border: 1px solid #ef4444; + padding: 0.4rem 1rem; + border-radius: 0.5rem; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; .nav-right a, .nav-right form button { display: inline-flex; @@ -104,6 +126,10 @@ body { box-shadow: none; } +#logout-btn:hover { background: #ef4444; color: white; box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3); transform: translateY(-2px); } + +.nav-right form { margin: 0; max-width: none; background: transparent; border: none; border-radius: 0; padding: 0; box-shadow: none; } + #sign-up-btn { padding: 0.5rem 1rem; background: linear-gradient(135deg, #3b82f6 0%, #0ea5e9 100%); @@ -113,15 +139,27 @@ body { cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(59, 130, 246, 0.2); + position: relative; + overflow: hidden; font-size: 0.92rem; font-family: inherit; } -#sign-up-btn:hover { - box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); - transform: translateY(-1px); +#sign-up-btn::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 50%; + height: 100%; + background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.3) 50%, rgba(255,255,255,0) 100%); + transform: skewX(-25deg); + animation: shimmer-beam 5s infinite; } +@keyframes shimmer-beam { 0%, 70% { left: -100%; } 100% { left: 200%; } } + +h2 { margin: 0 0 12px; font-size: 2rem; } h2 { margin: 0 0 12px; font-size: 2rem; @@ -132,10 +170,9 @@ h3 { font-size: 1.2rem; } -p { - color: #5f6d8c; - margin: 0 0 16px; -} +h3 { margin: 24px 0 10px; font-size: 1.2rem; } + +p { color: #5f6d8c; margin: 0 0 16px; } form { max-width: 760px; @@ -146,11 +183,7 @@ form { box-shadow: 0 8px 24px rgba(40, 84, 160, 0.1); } -.tab-buttons { - display: flex; - gap: 8px; - margin-bottom: 14px; -} +.tab-buttons { display: flex; gap: 8px; margin-bottom: 14px; } .tab-btn { border: 1px solid #c4dafc; @@ -162,343 +195,118 @@ form { cursor: pointer; } -.tab-btn.active { - background: #2a7de1; - color: #fff; - border-color: #2a7de1; -} - -.tab-content { - display: none; -} - -.tab-content.active { - display: block; -} +.tab-btn.active { background: #2a7de1; color: #fff; border-color: #2a7de1; } -.background-grid { - display: flex; - flex-wrap: wrap; - gap: 14px; - margin-top: 8px; -} +.tab-content { display: none; } +.tab-content.active { display: block; } -.background-item { - width: 120px; - text-align: center; -} +.background-grid { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 8px; } +.background-item { width: 120px; text-align: center; } -.background-thumb { - width: 100px; - height: 60px; - object-fit: cover; - border-radius: 8px; - border: 2px solid #c4dafc; - display: block; - margin: 6px auto; -} +.background-thumb { width: 100px; height: 60px; object-fit: cover; border-radius: 8px; border: 2px solid #c4dafc; display: block; margin: 6px auto; } -.background-name { - font-size: 0.75rem; - color: #5f6d8c; - word-break: break-word; -} +.background-name { font-size: 0.75rem; color: #5f6d8c; word-break: break-word; } -.design-input { - display: block; - width: min(100%, 260px); - padding: 8px 10px; - margin: 6px 0 12px; - border: 1px solid #c4dafc; - border-radius: 8px; - background: #fff; - color: #1f2b4d; -} +.design-input { display: block; width: min(100%, 260px); padding: 8px 10px; margin: 6px 0 12px; border: 1px solid #c4dafc; border-radius: 8px; background: #fff; color: #1f2b4d; } -.file-input { - display: block; - margin: 8px 0 12px; - padding: 8px; - border: 1px solid #c4dafc; - border-radius: 8px; - background: #f3f8ff; - color: #1f2b4d; - font-size: 0.9rem; - cursor: pointer; -} +/* Custom color input tweaks */ +#customButtonColor { width: 180px; } -.small-preview-pic { - width: 80px; - height: 80px; - border-radius: 8px; - object-fit: cover; - border: 2px solid #c4dafc; - display: block; - margin: 8px 0; -} +/* Utility class applied when using a custom color */ +.preview-card.button-color-custom .preview-links a { /* default will be overridden via inline style from JS */ } -.preview-card { - min-height: 520px; - max-height: 520px; - overflow-y: auto; - padding: 18px 16px 22px; - color: #28406b; -} +.file-input { display: block; margin: 8px 0 12px; padding: 8px; border: 1px solid #c4dafc; border-radius: 8px; background: #f3f8ff; color: #1f2b4d; font-size: 0.9rem; cursor: pointer; } -.edit-row { - background: #f9fcff; - border: 1px solid #e3edff; - border-radius: 12px; - padding: 14px; - margin-bottom: 12px; -} +.small-preview-pic { width: 80px; height: 80px; border-radius: 8px; object-fit: cover; border: 2px solid #c4dafc; display: block; margin: 8px 0; } -.edit-row strong { - display: inline-block; - margin-bottom: 6px; - font-size: 0.9rem; - text-transform: uppercase; - color: #335084; -} +.preview-card { min-height: 520px; max-height: 520px; overflow-y: auto; padding: 18px 16px 22px; color: #28406b; } -.display-text { - display: inline-block; - margin-bottom: 8px; - margin-right: 10px; - color: #26395f; -} +.edit-row { background: #f9fcff; border: 1px solid #e3edff; border-radius: 12px; padding: 14px; margin-bottom: 12px; } -.hidden-input { - display: none; - padding: 8px 10px; - border: 1px solid #c4dafc; - border-radius: 8px; - background: #fff; - width: min(100%, 420px); - margin: 0 8px 8px 0; -} +.edit-row strong { display: inline-block; margin-bottom: 6px; font-size: 0.9rem; text-transform: uppercase; color: #335084; } -.bio-input { - width: min(100%, 560px); - min-height: 90px; - resize: vertical; -} +.display-text { display: inline-block; margin-bottom: 8px; margin-right: 10px; color: #26395f; } -.action-btn { - border: none; - border-radius: 999px; - padding: 8px 14px; - font-weight: 600; - cursor: pointer; - background: #2a7de1; - color: #fff; - margin-right: 6px; -} +.hidden-input { display: none; padding: 8px 10px; border: 1px solid #c4dafc; border-radius: 8px; background: #fff; width: min(100%, 420px); margin: 0 8px 8px 0; } -.action-btn:hover { - background: #1f69c4; -} +.bio-input { width: min(100%, 560px); min-height: 90px; resize: vertical; } -.save-btn { - display: none; -} +.action-btn { border: none; border-radius: 999px; padding: 8px 14px; font-weight: 600; cursor: pointer; background: #2a7de1; color: #fff; margin-right: 6px; } +.action-btn:hover { background: #1f69c4; } -.edit-row div.hidden-input { - padding: 0; - border: none; - background: transparent; - width: 100%; -} +.save-btn { display: none; } -.edit-row div.hidden-input input { - display: block; - width: min(100%, 560px); - padding: 8px 10px; - border: 1px solid #c4dafc; - border-radius: 8px; - margin: 8px 0; -} +.edit-row div.hidden-input { padding: 0; border: none; background: transparent; width: 100%; } -.success-message { - display: inline-block; - margin-bottom: 12px; - padding: 10px 12px; - border-radius: 10px; - border: 1px solid #bfeccd; - background: #e9fbf0; - color: #0f7a3f; - font-weight: 600; -} +.edit-row div.hidden-input input { display: block; width: min(100%, 560px); padding: 8px 10px; border: 1px solid #c4dafc; border-radius: 8px; margin: 8px 0; } -.field-error { - color: #d72f2f; - font-size: 0.9rem; - margin-bottom: 8px; -} +.success-message { display: inline-block; margin-bottom: 12px; padding: 10px 12px; border-radius: 10px; border: 1px solid #bfeccd; background: #e9fbf0; color: #0f7a3f; font-weight: 600; } -.live-preview { - position: fixed; - right: 40px; - top: 70px; - width: 280px; - border-radius: 28px; - border: 2px solid #9fc6ff; - background: #fff; - box-shadow: 0 18px 35px rgba(21, 64, 134, 0.22); - overflow: hidden; -} +.field-error { color: #d72f2f; font-size: 0.9rem; margin-bottom: 8px; } -.live-preview-header { - background: linear-gradient(90deg, #2a7de1, #2ec4ff); - color: #fff; - font-weight: 700; - padding: 14px 16px; - font-size: 1rem; -} +.live-preview { position: fixed; right: 40px; top: 70px; width: 280px; border-radius: 28px; border: 2px solid #9fc6ff; background: #fff; box-shadow: 0 18px 35px rgba(21, 64, 134, 0.22); overflow: hidden; } -.preview-card { - min-height: 520px; - max-height: 520px; - overflow-y: auto; - padding: 18px 16px 22px; - color: #28406b; -} +.live-preview-header { background: linear-gradient(90deg, #2a7de1, #2ec4ff); color: #fff; font-weight: 700; padding: 14px 16px; font-size: 1rem; } -.preview-profile-pic { - width: 70px; - height: 70px; - border-radius: 50%; - object-fit: cover; - border: 2px solid #fff; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.16); - display: block; - margin: 0 auto 10px; -} +.preview-profile-pic { width: 70px; height: 70px; border-radius: 50%; object-fit: cover; border: 2px solid #fff; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.16); display: block; margin: 0 auto 10px; } -.preview-card h4 { - margin: 0; - text-align: center; - font-size: 1.15rem; -} +.preview-card h4 { margin: 0; text-align: center; font-size: 1.15rem; } -.preview-card p { - margin-top: 8px; - margin-bottom: 8px; - text-align: center; - color: #28406b; - font-weight: 600; -} +.preview-card p { margin-top: 8px; margin-bottom: 8px; text-align: center; color: #28406b; font-weight: 600; } .preview-card.text-align-center, .preview-card.text-align-center h4, .preview-card.text-align-center p, -.preview-card.text-align-center li { - text-align: center; -} +.preview-card.text-align-center li { text-align: center; } .preview-card.text-align-left, .preview-card.text-align-left h4, .preview-card.text-align-left p, -.preview-card.text-align-left li { - text-align: left; -} +.preview-card.text-align-left li { text-align: left; } -.preview-links { - list-style: none; - padding: 0; - margin: 14px 0 0; -} +.preview-links { list-style: none; padding: 0; margin: 14px 0 0; } -.preview-links li { - margin-bottom: 10px; - text-align: center; -} +.preview-links li { margin-bottom: 10px; text-align: center; } -.preview-links a { - display: block; - text-decoration: none; - color: #1f2b4d; - font-weight: 700; - background: rgba(255, 255, 255, 0.88); - border: 1px solid #dbe8ff; - border-radius: 999px; - padding: 10px 12px; -} +.preview-links a { display: block; text-decoration: none; color: #1f2b4d; font-weight: 700; background: rgba(255, 255, 255, 0.88); border: 1px solid #dbe8ff; border-radius: 999px; padding: 10px 12px; } -.preview-card.link-style-pill .preview-links a { - border-radius: 999px; - background: rgba(255, 255, 255, 0.88); - border: 1px solid #dbe8ff; -} +.preview-card.link-style-pill .preview-links a { border-radius: 999px; background: rgba(255, 255, 255, 0.88); border: 1px solid #dbe8ff; } -.preview-card.link-style-box .preview-links a { - border-radius: 10px; - background: rgba(233, 243, 255, 0.95); - border: 1px solid #b8d3ff; -} +.preview-card.link-style-box .preview-links a { border-radius: 10px; background: rgba(233, 243, 255, 0.95); border: 1px solid #b8d3ff; } -.preview-card.link-style-underline .preview-links a { - background: transparent; - border: none; - border-radius: 0; - text-decoration: underline; - padding-left: 0; - padding-right: 0; -} +.preview-card.link-style-underline .preview-links a { background: transparent; border: none; border-radius: 0; text-decoration: underline; padding-left: 0; padding-right: 0; } -.preview-card.button-color-blue .preview-links a { - background: #2a7de1; - color: #fff; - border-color: #2a7de1; -} +.preview-card.button-color-blue .preview-links a { background: #2a7de1; color: #fff; border-color: #2a7de1; } -.preview-card.button-color-green .preview-links a { - background: #22c55e; - color: #fff; - border-color: #22c55e; -} +.preview-card.button-color-green .preview-links a { background: #22c55e; color: #fff; border-color: #22c55e; } -.preview-card.button-color-red .preview-links a { - background: #ef4444; - color: #fff; - border-color: #ef4444; -} +.preview-card.button-color-red .preview-links a { background: #ef4444; color: #fff; border-color: #ef4444; } -.preview-card.button-color-purple .preview-links a { - background: #a855f7; - color: #fff; - border-color: #a855f7; -} +.preview-card.button-color-purple .preview-links a { background: #a855f7; color: #fff; border-color: #a855f7; } -.preview-card.button-color-orange .preview-links a { - background: #f97316; - color: #fff; - border-color: #f97316; -} +.preview-card.button-color-orange .preview-links a { background: #f97316; color: #fff; border-color: #f97316; } -.preview-card.font-family-system { - font-family: "Segoe UI", Arial, sans-serif; -} +.preview-card.font-family-system { font-family: "Segoe UI", Arial, sans-serif; } -.preview-card.font-family-georgia { - font-family: Georgia, serif; -} +.preview-card.font-family-georgia { font-family: Georgia, serif; } -.preview-card.font-family-courier { - font-family: "Courier New", monospace; -} +.preview-card.font-family-courier { font-family: "Courier New", monospace; } -.preview-card.font-family-arial { - font-family: Arial, sans-serif; -} +.preview-card.font-family-arial { font-family: Arial, sans-serif; } -@media (max-width: 1050px) { - body { - padding: 24px; - } +.preview-card.font-family-helvetica { font-family: Helvetica, Arial, sans-serif; } +.preview-card.font-family-verdana { font-family: Verdana, Geneva, sans-serif; } +.preview-card.font-family-times { font-family: "Times New Roman", Times, serif; } +.preview-card.font-family-trebuchet { font-family: "Trebuchet MS", Helvetica, sans-serif; } +.preview-card.font-family-palatino { font-family: Palatino, "Palatino Linotype", serif; } - .live-preview { - display: none; - } +@media (max-width: 1050px) { body { padding: 24px; } .live-preview { display: none; } } -} \ No newline at end of file +/* Share box (partner additions) */ +.share-box-container { background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 10px; padding: 20px; margin-bottom: 30px; text-align: center; box-shadow: 0 2px 5px rgba(0,0,0,0.05); } +.share-box-container h3 { margin-top: 0; margin-bottom: 5px; font-size: 1.2rem; color: #333; } +.share-box-container p { color: #6c757d; font-size: 0.9rem; margin-bottom: 15px; } +.share-input-group { display: flex; max-width: 500px; margin: 0 auto; border: 2px solid #ddd; border-radius: 6px; overflow: hidden; } +.share-input-group input { flex-grow: 1; padding: 10px 15px; border: none; background-color: #fff; color: #495057; font-family: monospace; font-size: 1rem; outline: none; } +.share-input-group button { background-color: #007bff; color: white; border: none; padding: 10px 20px; font-weight: bold; cursor: pointer; transition: background-color 0.2s ease; } +.share-input-group button:hover { background-color: #0056b3; } diff --git a/src/main/resources/static/css/register.css b/src/main/resources/static/css/register.css index 3f45c6c..3c9e011 100644 --- a/src/main/resources/static/css/register.css +++ b/src/main/resources/static/css/register.css @@ -591,4 +591,4 @@ footer { #sign-up-btn { width: 100%; } -} \ No newline at end of file +} diff --git a/src/main/resources/static/css/templates.css b/src/main/resources/static/css/templates.css index 56ab1fe..e048503 100644 --- a/src/main/resources/static/css/templates.css +++ b/src/main/resources/static/css/templates.css @@ -28,6 +28,37 @@ body { gap: 6rem; z-index: 50; align-items: center; + display: grid; + grid-template-columns: 1fr auto 1fr; + padding: 1rem 1rem; + width: 100%; + left: 0; + margin: 0; + + /* 1. Make it sticky at the top */ + position: fixed; + top: 0; + z-index: 1000; + + /* 2. The Frosted Glass Effect */ + background: rgba(250, 251, 252, 0.75); /* Semi-transparent white */ + backdrop-filter: blur(12px); /* Blurs whatever is behind the nav */ + -webkit-backdrop-filter: blur(12px); /* Safari support */ + + /* 3. A subtle border to define the edge */ + border-bottom: 1px solid rgba(224, 224, 224, 0.5); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.05); +} + +.nav a { + text-decoration: none; + color: #1a1a2e; + font-size: 0.92rem; + transition: color 0.3s ease; +} + +.nav a:hover:not(#link-garden, #sign-up-btn) { + color: #3b82f6; } .nav::after { @@ -88,19 +119,54 @@ body { font-size: 1.4rem; } -.nav span, .nav form button { - font-size: 0.8rem; - font-weight: 600; - margin-top: 4px; +.nav-left a { + display: inline-flex; + align-items: center; + gap: 0.5rem; } -.nav form button { - background: transparent; - border: none; - color: #1a1a2e; - cursor: pointer; - padding: 0; - font-family: inherit; +.nav-left svg { + width: 30px; + height: 30px; + display: block; + transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); +} + +.nav-left a:hover svg { + transform: rotate(-10deg) scale(1.1); + color: #3b82f6 !important; +} + +.nav-center { + justify-content: center; + justify-self: center; + gap: 1rem; + align-items: center; +} + +.nav-center a, .nav-center button { + font-size: 1.2rem; + position: relative; + text-decoration: none; +} + +.nav-center a::after { + content: ''; + position: absolute; + width: 0; + height: 2px; + bottom: -4px; + left: 0; + background: linear-gradient(90deg, #3b82f6, #0ea5e9); + transition: width 0.3s cubic-bezier(0.165, 0.84, 0.44, 1); +} + +.nav-center a:hover::after { + width: 100%; +} + +.nav button:hover { + color: #3b82f6; } .nav form button:hover { @@ -141,12 +207,17 @@ body { content: ''; position: absolute; top: 0; - left: -100%; /* Start completely off the left edge */ + left: -100%; width: 50%; height: 100%; background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.3) 50%, rgba(255,255,255,0) 100%); transform: skewX(-25deg); - animation: shimmer-beam 5s infinite; /* Runs endlessly */ + animation: shimmer-beam 5s infinite; +} + +@keyframes shimmer-beam { + 0%, 70% { left: -100%; } + 100% { left: 200%; } } #sign-up-btn:hover { @@ -167,7 +238,6 @@ body { - /* TEMPLATE PREVIEW STYLING */ .intro { text-align: center; diff --git a/src/main/resources/templates/dashboard.html b/src/main/resources/templates/dashboard.html index ed4670b..192ee7d 100644 --- a/src/main/resources/templates/dashboard.html +++ b/src/main/resources/templates/dashboard.html @@ -173,6 +173,19 @@
#2a7de1 or pick a color using the picker.