diff --git a/users_microservice/src/main/resources/static/css/global-styles.css b/users_microservice/src/main/resources/static/css/global-styles.css index 8042c783..794f85ab 100644 --- a/users_microservice/src/main/resources/static/css/global-styles.css +++ b/users_microservice/src/main/resources/static/css/global-styles.css @@ -1,3 +1,7 @@ +/* ================================================ + GLOBAL STYLES - site-wide variables & resets + ================================================ */ + :root { --black: #000000; --dark-grey: #111111; @@ -5,7 +9,6 @@ --light-orange: #ffb74d; --light-green: #81c784; --focus-green: #4caf50; - --pink: #ff69b4; --light-grey: #1e1e1e; --medium-grey: #2a2a2a; --text-light: #e0e0e0; @@ -15,240 +18,109 @@ * { box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + scroll-behavior: smooth; } body { - background-color: #0a0a0a; + background-color: var(--black); color: var(--text-light); font-family: 'Space Grotesk', system-ui, sans-serif; line-height: 1.6; - scroll-behavior: smooth; padding-top: var(--nav-height); margin: 0; + min-height: 100vh; } -h1, h2, h3, h4 { +h1, h2, h3, h4, h5, h6 { color: white; font-weight: 700; + line-height: 1.2; } -section { - padding-top: 3.5rem; - padding-bottom: 4rem; -} - -.section-grey, -.section-medium { - border: 2px solid var(--pink); - border-radius: 12px; - background-color: var(--light-grey); - margin: 0 auto 2rem auto; - max-width: 1400px; - color: var(--text-light); -} - -.section-medium { - background-color: var(--medium-grey); -} - -.card { - background-color: #111111; - border: 2px solid var(--pink) !important; - border-radius: 10px; - color: var(--text-light); - transition: transform 0.2s ease, box-shadow 0.2s ease; -} - -.card h4 { - color: white; -} - -.card p { - color: var(--text-muted); +a { + text-decoration: none; + color: inherit; } -.card:hover { - transform: translateY(-6px); - box-shadow: 0 12px 24px rgba(255, 105, 180, 0.25); -} - -.feature-img { - max-height: 160px; - object-fit: cover; - border-radius: 8px; - border: 1px solid var(--pink); - margin-bottom: 1.25rem; - width: 100%; +img { + max-width: 100%; + height: auto; + display: block; } -.btn-black, -.btn-primary-custom { - background-color: white; - color: var(--black); - border: 2px solid white; +/* Buttons - global base styles */ +.btn { font-weight: 600; transition: all 0.3s ease; + border-radius: 6px; } -.btn-black:hover, -.btn-primary-custom:hover { - background-color: var(--dark-orange); - color: white; - border-color: var(--dark-orange); - transform: translateY(-2px); - box-shadow: 0 8px 20px rgba(230, 81, 0, 0.4); -} - -.btn-login { - background-color: transparent; - color: var(--text-light); - border: 2px solid var(--text-light); - font-weight: 500; - transition: all 0.3s ease; - padding: 0.375rem 1rem; -} - -.btn-login:hover { - background-color: var(--dark-orange); - color: white; - border-color: var(--dark-orange); +.btn:hover { transform: translateY(-2px); } -.btn-learn-more { - background-color: var(--light-orange); - color: var(--black); - border: 2px solid var(--focus-green); - font-weight: 600; - transition: all 0.3s ease; - box-shadow: 0 4px 12px rgba(129, 199, 132, 0.3); -} - -.btn-learn-more:hover { - background-color: #ff9800; - border-color: #388e3c; - color: white; - transform: translateY(-3px) scale(1.05); - box-shadow: 0 12px 28px rgba(76, 175, 80, 0.4); -} - -.btn-learn-more:focus { - outline: none; - box-shadow: 0 0 0 0.3rem rgba(76, 175, 80, 0.4); -} - -.nav-link { - color: white !important; - font-weight: 500; - position: relative; - padding-bottom: 0.6rem; - transition: color 0.25s ease; -} - -.nav-link:hover, -.nav-link.active { - color: var(--light-orange) !important; -} - -.nav-link::after { - content: ''; - position: absolute; - bottom: 0; - left: 5%; - width: 0; - height: 3px; - background-color: var(--light-orange); - border-radius: 3px; - transition: width 0.35s ease, left 0.35s ease; -} - -.nav-link.active::after, -.nav-link:hover::after { - width: 90%; - left: 5%; +/* Container helper */ +.container { + width: 100%; + padding-right: 1rem; + padding-left: 1rem; + margin-right: auto; + margin-left: auto; + max-width: 1400px; } -nav { - border-bottom: 3px solid var(--pink); - background-color: #111111; +/* Section spacing – reduced vertical padding & margin */ +section { + padding: 2.5rem 1rem; } -.hero { - min-height: 70vh; - display: flex; - align-items: center; - border-bottom: 4px solid var(--pink); - margin-bottom: 3rem; - padding: 5rem 0 4rem; - background-color: #0a0a0a; +.section-grey, +.section-medium { + border-radius: 12px; + margin: 0 auto 1.5rem; + max-width: 1400px; + padding: 2.5rem 1.5rem; } -.hero-text h1 { - color: var(--light-orange); +/* Cards */ +.card { + background-color: #0d0d0d; + border: 1px solid #222; + border-radius: 10px; + color: var(--text-light); + transition: transform 0.25s ease, box-shadow 0.25s ease; + overflow: hidden; } -.hero-img img { - max-width: 100%; - height: auto; - border-radius: 16px; - border: 3px solid var(--pink); +.card:hover { + transform: translateY(-6px); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6); } -@media (max-width: 991px) { - .hero-text { - text-align: center; - margin-bottom: 3rem; - } - .hero-img { - text-align: center; - } -} - -/* Form styling in dark mode */ -.form-control { - background-color: #222; - color: white; - border: 1px solid #444; -} - -.form-control:focus { - border-color: var(--focus-green) !important; - box-shadow: 0 0 0 0.25rem rgba(76, 175, 80, 0.25) !important; - background-color: #222; +.card h4 { color: white; + margin-bottom: 1rem; } -.form-label { - color: var(--text-light); -} - -.form-group:focus-within .form-label { - color: var(--focus-green) !important; -} - -/* Footer */ -footer { - background-color: #111111; - border-top: 4px solid var(--pink); - color: var(--text-muted); - padding: 3rem 0 2rem; -} - -footer p { +.card p { color: var(--text-muted); } -footer a { - color: var(--light-orange); -} - -/* Extra spacing helpers */ -.py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; +/* Force consistent black background + reduced spacing override */ +.hero, +.section-grey, +.section-medium, +nav, +footer, +.card { + background-color: var(--black) !important; } -.mt-5 { - margin-top: 3rem !important; +.card { + border: 1px solid #222; } \ No newline at end of file diff --git a/users_microservice/src/main/resources/static/css/inputs.css b/users_microservice/src/main/resources/static/css/inputs.css new file mode 100644 index 00000000..be7e0b37 --- /dev/null +++ b/users_microservice/src/main/resources/static/css/inputs.css @@ -0,0 +1,111 @@ +/* Reset default input/textarea styling */ +#contactForm input, +#contactForm textarea { + background-color: #fff; + border: 2px solid #dee2e6; /* light gray default border */ + border-radius: 9999px; /* fully circular/rounded */ + padding: 0.75rem 1.25rem; /* comfortable inner spacing */ + font-size: 1rem; + transition: all 0.25s ease; + outline: none; + box-shadow: none; +} + +/* Remove Bootstrap's default focus shadow and change to green */ +#contactForm input:focus, +#contactForm textarea:focus { + border-color: #28a745; /* Bootstrap success green */ + box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.25); /* subtle green glow */ +} + +/* Hover state (optional but nice) */ +#contactForm input:hover:not(:focus), +#contactForm textarea:hover:not(:focus) { + border-color: #adb5bd; +} + +/* Make sure placeholder looks good */ +#contactForm input::placeholder, +#contactForm textarea::placeholder { + color: #adb5bd; + opacity: 1; +} + +/* Textarea specific - still rounded but looks natural */ +#contactForm textarea { + border-radius: 1.5rem; /* slightly less extreme rounding for textarea */ + resize: vertical; /* allow vertical resize only */ + min-height: 140px; +} + +/* Optional: make labels look better with rounded inputs */ +#contactForm .form-label { + margin-bottom: 0.5rem; + font-weight: 500; +} + +/* Success message (optional styling) */ +#formStatus.text-success { + color: #28a745 !important; +} + +/* Error message (optional) */ +#formStatus.text-danger { + color: #dc3545 !important; +} + +/* Reset and base styling for inputs + textarea */ +#contactForm input, +#contactForm textarea { + background-color: #2d2d2d; /* dark grey background */ + color: #ffffff; /* white text when typing */ + border: 2px solid #444444; /* darker default border */ + border-radius: 9999px; /* pill / very round shape */ + padding: 0.75rem 1.25rem; + font-size: 1rem; + transition: border-color 0.25s ease, box-shadow 0.25s ease; + outline: none; + box-shadow: none; +} + +/* Placeholder - plain white, no extra styling */ +#contactForm input::placeholder, +#contactForm textarea::placeholder { + color: #ffffff; /* white placeholder */ + opacity: 0.7; /* slightly faded so it's readable but not too strong */ +} + +/* Focus state - green border + subtle glow */ +#contactForm input:focus, +#contactForm textarea:focus { + border-color: #28a745; /* green on focus */ + box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.3); + background-color: #2d2d2d; /* keep dark even when focused */ +} + +/* Hover (optional - subtle feedback) */ +#contactForm input:hover:not(:focus), +#contactForm textarea:hover:not(:focus) { + border-color: #666666; +} + +/* Textarea adjustments - still rounded but comfortable */ +#contactForm textarea { + border-radius: 1.5rem; /* softer rounding for multiline */ + resize: vertical; + min-height: 140px; +} + +/* Optional: make sure form looks cohesive in dark mode */ +#contactForm .form-label { + color: #e0e0e0; /* light grey labels for contrast */ + font-weight: 500; +} + +/* Status messages (if you use success/error) */ +#formStatus.text-success { + color: #28a745 !important; +} +#formStatus.text-danger { + color: #ff6b6b !important; +} \ No newline at end of file diff --git a/users_microservice/src/main/resources/static/css/landing-page.css b/users_microservice/src/main/resources/static/css/landing-page.css index 0263a1c3..1750a094 100644 --- a/users_microservice/src/main/resources/static/css/landing-page.css +++ b/users_microservice/src/main/resources/static/css/landing-page.css @@ -1,18 +1,162 @@ -/* css/landing.css */ +/* ================================================ + ALCOHOL AGENT LANDING PAGE – CLEANED & OPTIMIZED + ================================================ */ +/* ================================================ + ROOT / VARIABLES (assumed – add if missing) + ================================================ */ +:root { + --black: #000000; + --dark-orange: #e65100; + --light-orange: #ff5722; + --focus-green: #4caf50; + --text-light: #f0f0f0; + --text-muted: #aaaaaa; +} + +/* ================================================ + GLOBAL FORM & INPUT STYLES + ================================================ */ +.form-control, +.form-control::placeholder { + background-color: #111; + color: white; + border: 1px solid #333; +} + +.form-control::placeholder { + color: rgba(255, 255, 255, 0.7); + opacity: 1; +} + +.form-control:focus { + border-color: var(--focus-green); + box-shadow: 0 0 0 0.25rem rgba(76, 175, 80, 0.25); + background-color: #111; + color: white; +} + +.form-label { + color: var(--text-light); + font-weight: 500; +} + +/* ================================================ + BUTTONS + ================================================ */ +.btn { + border-radius: 50px; + font-weight: 600; + transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1); +} + +.btn:hover, +.btn:active, +.btn:focus { + transform: none !important; +} + +.btn-primary-custom, +.btn-black { + background-color: white; + color: var(--black); + border: 2px solid white; + padding: 0.55rem 1.6rem; + font-size: 0.95rem; +} + +.btn-primary-custom:hover, +.btn-black:hover { + background-color: var(--light-orange); + color: white; + border-color: var(--light-orange); +} + +.btn-login { + background: transparent; + color: var(--text-light); + border: 2px solid var(--text-light); + padding: 0.4rem 1rem; + font-size: 0.9rem; +} + +.btn-login:hover { + background-color: var(--dark-orange); + color: white; + border-color: var(--dark-orange); +} + +.btn-learn-more { + background-color: var(--light-orange); + color: var(--black); + border: 2px solid white; + padding: 0.55rem 1.6rem; + font-size: 0.95rem; +} + +.btn-learn-more:hover { + background-color: #f57c00; + border-color: white; + color: white; +} + +#submitBtn { + padding: 0.65rem 2rem; + font-size: 1.05rem; +} + +#submitBtn:hover { + background-color: var(--dark-orange); + border-color: var(--dark-orange); + color: white; +} + +/* ================================================ + NAVIGATION + ================================================ */ +nav { + background-color: var(--black); + z-index: 1000; +} + +.nav-link { + color: white; + font-weight: 500; + transition: color 0.25s ease; +} + +.nav-link:hover, +.nav-link.active { + color: var(--light-orange); +} + +/* Mobile logo centering */ +#mobile-logo-wrapper { + padding-top: 0.75rem; +} + +#mobile-logo { + display: block; + margin-left: auto; + margin-right: auto; + width: 70px; +} + +/* ================================================ + HERO SECTION + ================================================ */ .hero { - min-height: 80vh; - display: flex; - align-items: center; - background: linear-gradient(135deg, #0a0a0a 0%, #111111 100%); - border-bottom: 4px solid var(--pink); + background: var(--black); text-align: center; - padding: 6rem 1rem 8rem; + display: flex; + flex-direction: column; + justify-content: flex-start; + padding: 9rem 1rem 5rem; + min-height: auto; } .hero h1 { color: var(--light-orange); - font-size: clamp(2.8rem, 8vw, 5.5rem); margin-bottom: 1.5rem; } @@ -23,39 +167,238 @@ margin: 0 auto 2.5rem; } -.section-features { - padding: 6rem 1rem; +.hero-img img { + border-radius: 16px; + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6); +} + +/* Hero logo (desktop) */ +#hero-logo-wrapper { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + margin-bottom: 2.5rem; +} + +#hero-logo { + max-width: 280px; + width: auto; + height: auto; + display: block; + margin: 0 auto; +} + +/* ================================================ + RESPONSIVE – HERO & TYPOGRAPHY + ================================================ */ +@media (min-width: 992px) { + .hero { + min-height: 85vh; + padding: 12rem 1rem 6rem; + align-items: center; + justify-content: center; + } + + #hero-logo { + max-width: 280px; + } + + .hero h1 { + font-size: 4.2rem; + } +} + +@media (max-width: 991px) { + #hero-logo, + #hero-logo-wrapper { + display: none !important; + } + + .hero h1 { + font-size: 3rem; + } +} + +@media (max-width: 575px) { + .hero { + padding-top: 8rem; + padding-bottom: 4rem; + } + + .hero h1 { + font-size: 2.6rem !important; + } + + .lead { + font-size: 1.2rem !important; + } +} + +/* ================================================ + SECTIONS & CARDS (global dark theme) + ================================================ */ +.hero, +.section-grey, +.section-medium, +nav, +footer, +#features, +#pricing, +#contact, +#stats, +#testimonials { + background-color: var(--black); + margin-top: 0; + margin-bottom: 1.5rem; +} + +section, +.hero { + padding: 2.5rem 1rem; } -.feature-card { - background: var(--light-grey); - border: 2px solid var(--pink); +.card, +.testimonial-card { + background-color: #0d0d0d; + border: 1px solid #222; border-radius: 12px; - padding: 2rem; - transition: all 0.3s ease; + color: var(--text-light); + transition: box-shadow 0.3s ease; +} + +.card:hover, +.testimonial-card:hover { + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6); +} + +/* ================================================ + STATS / AVATARS + ================================================ */ +.avatar-circle { + width: 140px; + height: 140px; + overflow: hidden; + border-radius: 50%; + background: transparent; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.7); + margin: 0 auto 1.5rem; +} + +.avatar-circle img { + width: 100%; + height: 100%; + object-fit: cover; + border: none; + display: block; +} + +.stats-number { + color: var(--light-orange); + min-height: 60px; + text-shadow: 0 2px 10px rgba(255, 183, 77, 0.3); +} + +/* ================================================ + TESTIMONIALS CAROUSEL + ================================================ */ +#testimonialsCarousel { + max-width: 760px; + margin: 0 auto; } -.feature-card:hover { - transform: translateY(-8px); - box-shadow: 0 16px 32px rgba(255, 105, 180, 0.18); +.testimonial-card { + min-height: 180px; + display: flex; + flex-direction: column; + justify-content: center; + padding: 1.8rem 1.5rem; +} + +.carousel-fade .carousel-item { + transition: opacity 0.65s ease-in-out; + opacity: 0; + position: absolute; + top: 0; + left: 0; + width: 100%; +} + +.carousel-fade .carousel-item.active { + opacity: 1; + position: relative; +} + +.carousel-indicators { + display: none !important; +} + +.carousel-inner { + position: relative; + min-height: 220px; +} + +/* ================================================ + FOOTER + ================================================ */ +footer { + background-color: var(--black); + color: var(--text-muted); + padding: 2.5rem 0 2rem; + text-align: center; } -/* Optional: make it even more visible or add italic style */ -#contactForm .form-control::placeholder { - /* color: rgba(255, 255, 255, 0.9); */ /* alternative - almost fully white */ - /* font-style: italic; */ +footer a { + color: var(--light-orange); +} + +/* ================================================ + MISC / LEGACY CLEANUP + ================================================ */ +@media (max-width: 991px) { + .hero-text { + text-align: center; + margin-bottom: 2rem; + } } -/* If your background is dark → ensure inputs have dark background so white text shows */ -#contactForm .form-control { - background-color: #2c2c2c; /* dark grey/black - adjust to your theme */ - color: #ffffff; /* white text when typing */ - border: 1px solid #555; /* subtle border */ +/* ================================================ + COLOR OVERRIDES FOR SPECIFIC ELEMENTS + ================================================ */ + +/* Testimonial location text (e.g. "Cape Town", "Johannesburg") */ +.testimonial-card .text-muted.small { + color: var(--light-orange) !important; +} + +/* Footer "Made with focus on clarity & responsibility" text */ +footer p.text-muted.small.mb-0 { + color: var(--light-orange) !important; +} + +.testimonial-card .text-muted.small, +footer p.text-muted.small.mb-0 { + color: var(--dark-orange) !important; /* #e65100 – deeper orange */ +} + +@media (max-width: 991.98px) { + .hero-title { + display: none; + } +} + +.hero { + padding-top: 5rem; /* fallback / small screens ~80px */ } -/* Optional: placeholder changes color slightly on focus */ -#contactForm .form-control:focus::placeholder { - color: rgba(255, 255, 255, 0.4); /* fades a bit when focused */ +@media (max-width: 991.98px) { /* mobile & tablet – up to lg breakpoint */ + .hero { + padding-top: 11rem !important; /* ≈176px – adjust 10.5rem–12rem as needed */ + } } -/* ... add your other landing sections: CTA, testimonials, etc. ... */ \ No newline at end of file +@media (min-width: 992px) { /* desktop lg+ */ + .hero { + padding-top: 4rem; /* or 4rem–6rem – whatever looks balanced on desktop */ + } +} \ No newline at end of file diff --git a/users_microservice/src/main/resources/static/js/count.js b/users_microservice/src/main/resources/static/js/count.js new file mode 100644 index 00000000..05eddf76 --- /dev/null +++ b/users_microservice/src/main/resources/static/js/count.js @@ -0,0 +1,32 @@ +document.addEventListener('DOMContentLoaded', () => { + const counters = document.querySelectorAll('.stats-number'); + + const options = { + threshold: 0.2, + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const target = parseInt(entry.target.getAttribute('data-target')) || 0; + animateValue(entry.target, 0, target, 1800); + observer.unobserve(entry.target); + } + }); + }, options); + + counters.forEach(el => observer.observe(el)); + + function animateValue(obj, start, end, duration) { + let startTimestamp = null; + const step = (timestamp) => { + if (!startTimestamp) startTimestamp = timestamp; + const progress = Math.min((timestamp - startTimestamp) / duration, 1); + obj.textContent = Math.floor(progress * (end - start) + start).toLocaleString(); + if (progress < 1) { + window.requestAnimationFrame(step); + } + }; + window.requestAnimationFrame(step); + } +}); \ No newline at end of file diff --git a/users_microservice/src/main/resources/static/js/register.js b/users_microservice/src/main/resources/static/js/register.js index ecbda25d..f68e38ec 100644 --- a/users_microservice/src/main/resources/static/js/register.js +++ b/users_microservice/src/main/resources/static/js/register.js @@ -1,3 +1,42 @@ +// ──────────────────────────────────────────────────────────────── +// Reusable showAlert with scroll +// ──────────────────────────────────────────────────────────────── +function showAlert(text, type = 'danger') { + const alertDiv = document.getElementById('registerAlert'); + if (!alertDiv) { + console.error('registerAlert element not found'); + return; + } + + // Reset classes & set new style + alertDiv.textContent = text; + alertDiv.className = `alert alert-${type} mb-3`; // clean reset + alertDiv.classList.remove('d-none'); + + // Force browser reflow (critical after display change) + void alertDiv.offsetHeight; + + // Scroll after short delay (timing fix for most cases) + setTimeout(() => { + alertDiv.scrollIntoView({ + behavior: 'smooth', + block: 'center', // 'center' / 'nearest' / 'start' — try different if needed + inline: 'nearest' + }); + + // Optional fallback if scrollIntoView fails (e.g. fixed header issue) + // Uncomment and adjust offset if alert hides behind navbar + /* + const rect = alertDiv.getBoundingClientRect(); + const offset = -100; // negative = scroll higher (navbar height + margin) + window.scrollTo({ + top: rect.top + window.scrollY + offset, + behavior: 'smooth' + }); + */ + }, 150); // 120–200 ms — increase to 250/300 if still not working +} + // ──────────────────────────────────────────────────────────────── // Load privileges when the page loads // ──────────────────────────────────────────────────────────────── @@ -10,9 +49,7 @@ async function loadPrivileges() { try { const response = await fetch('http://localhost:8081/dev/privileges/api/printPrivilege', { method: 'GET', - headers: { - 'Accept': 'application/json' - } + headers: { 'Accept': 'application/json' } }); if (!response.ok) { @@ -21,23 +58,20 @@ async function loadPrivileges() { const data = await response.json(); - // Clear loading state select.innerHTML = ''; - // Normalize to array (in case single object is returned) const privilegesList = Array.isArray(data) ? data : [data]; privilegesList.forEach(item => { if (item && typeof item === 'object' && 'id' in item && 'privilegeName' in item) { const option = document.createElement('option'); - option.value = item.id; // value = id (for easy retrieval) - option.textContent = item.privilegeName; // what the user sees - option.dataset.name = item.privilegeName; // store name in data attribute + option.value = item.id; + option.textContent = item.privilegeName; + option.dataset.name = item.privilegeName; select.appendChild(option); } }); - // Optional: auto-select first real option if (select.options.length > 1) { select.selectedIndex = 1; } @@ -56,7 +90,7 @@ async function loadPrivileges() { // Form submission handler // ──────────────────────────────────────────────────────────────── document.addEventListener('DOMContentLoaded', () => { - loadPrivileges(); // Load privileges immediately + loadPrivileges(); const form = document.getElementById('registerForm'); if (!form) return; @@ -64,7 +98,7 @@ document.addEventListener('DOMContentLoaded', () => { form.addEventListener('submit', async function(e) { e.preventDefault(); - const alertDiv = document.getElementById('registerAlert'); + const alertDiv = document.getElementById('registerAlert'); const submitBtn = document.getElementById('registerBtn'); // Reset alert @@ -72,26 +106,22 @@ document.addEventListener('DOMContentLoaded', () => { alertDiv.classList.remove('alert-success', 'alert-danger'); alertDiv.textContent = ''; - // Disable button during request submitBtn.disabled = true; submitBtn.textContent = 'Registering...'; - // Gather form values const fullName = document.getElementById('fullName')?.value.trim() || ''; const email = document.getElementById('email')?.value.trim() || ''; const cellphone = document.getElementById('cellphone')?.value.trim() || ''; const identityNo = document.getElementById('identityNo')?.value.trim() || ''; const password = document.getElementById('password')?.value || ''; const confirmPassword = document.getElementById('confirmPassword')?.value || ''; - const dob = document.getElementById('dob')?.value || ''; + // const dob = document.getElementById('dob')?.value || ''; - // Get selected privilege - const select = document.getElementById('privileges'); + const select = document.getElementById('privileges'); const selectedOption = select.options[select.selectedIndex]; const privilegesId = selectedOption.value ? parseInt(selectedOption.value, 10) : NaN; const privilegeName = selectedOption.dataset.name || selectedOption.textContent || ''; - // Basic client-side validation if (password !== confirmPassword) { showAlert('Passwords do not match!', 'danger'); resetButton(); @@ -104,7 +134,6 @@ document.addEventListener('DOMContentLoaded', () => { return; } - // Prepare payload (now sending object with id + name) const payload = { userFullName: fullName, userEmailAddress: email, @@ -112,12 +141,12 @@ document.addEventListener('DOMContentLoaded', () => { userIdentityNo: identityNo, userPassword: password, confirmPassword: confirmPassword, - userStatus: 1, // adjust as needed + userStatus: 1, privileges: { id: privilegesId, privilegeName: privilegeName - }, - // dateOfBirth: dob, // uncomment if needed + } + // dateOfBirth: dob, }; try { @@ -133,21 +162,14 @@ document.addEventListener('DOMContentLoaded', () => { if (response.ok) { showAlert('Registration successful! Redirecting to login...', 'success'); setTimeout(() => { - window.location.href = '/login'; // ← adjust path + window.location.href = '/login'; // adjust if needed }, 1800); } else { - let errorData = null; - try { - errorData = await response.json(); - } catch {} - let message = `Registration failed (${response.status})`; - - if (errorData) { + try { + const errorData = await response.json(); if (Array.isArray(errorData) && errorData.length > 0) { - message = errorData[0].resolveIssueDetails - || errorData[0].message - || message; + message = errorData[0].resolveIssueDetails || errorData[0].message || message; } else if (errorData.resolveIssueDetails) { message = errorData.resolveIssueDetails; } else if (errorData.message) { @@ -155,8 +177,9 @@ document.addEventListener('DOMContentLoaded', () => { } else if (errorData.detail) { message = errorData.detail; } + } catch { + // parsing failed → keep default message } - showAlert(message, 'danger'); } } catch (err) { @@ -166,12 +189,6 @@ document.addEventListener('DOMContentLoaded', () => { resetButton(); } - function showAlert(text, type) { - alertDiv.textContent = text; - alertDiv.classList.add(`alert-${type}`); - alertDiv.classList.remove('d-none'); - } - function resetButton() { submitBtn.disabled = false; submitBtn.textContent = 'Register'; diff --git a/users_microservice/src/main/resources/static/js/slider.js b/users_microservice/src/main/resources/static/js/slider.js new file mode 100644 index 00000000..2527e408 --- /dev/null +++ b/users_microservice/src/main/resources/static/js/slider.js @@ -0,0 +1,58 @@ +// Simple testimonial slider +document.addEventListener('DOMContentLoaded', () => { + const track = document.getElementById('sliderTrack'); + const slides = document.querySelectorAll('.testimonial-slide'); + const prevBtn = document.getElementById('prevBtn'); + const nextBtn = document.getElementById('nextBtn'); + const dotsContainer = document.getElementById('sliderDots'); + + let currentIndex = 0; + let autoSlideInterval; + + // Create dots + slides.forEach((_, index) => { + const dot = document.createElement('div'); + dot.classList.add('slider-dot'); + if (index === 0) dot.classList.add('active'); + dot.addEventListener('click', () => goToSlide(index)); + dotsContainer.appendChild(dot); + }); + + const dots = document.querySelectorAll('.slider-dot'); + + function updateSlider() { + track.style.transform = `translateX(-${currentIndex * 100}%)`; + dots.forEach((dot, i) => { + dot.classList.toggle('active', i === currentIndex); + }); + } + + function goToSlide(index) { + currentIndex = (index + slides.length) % slides.length; + updateSlider(); + resetAutoSlide(); + } + + function nextSlide() { + goToSlide(currentIndex + 1); + } + + function prevSlide() { + goToSlide(currentIndex - 1); + } + + function resetAutoSlide() { + clearInterval(autoSlideInterval); + autoSlideInterval = setInterval(nextSlide, 6000); // change every 6 seconds + } + + prevBtn.addEventListener('click', prevSlide); + nextBtn.addEventListener('click', nextSlide); + + // Auto slide + resetAutoSlide(); + + // Pause on hover + track.addEventListener('mouseenter', () => clearInterval(autoSlideInterval)); + track.addEventListener('mouseleave', resetAutoSlide); +}); diff --git a/users_microservice/src/main/resources/static/js/testimonial.js b/users_microservice/src/main/resources/static/js/testimonial.js new file mode 100644 index 00000000..8566f597 --- /dev/null +++ b/users_microservice/src/main/resources/static/js/testimonial.js @@ -0,0 +1,36 @@ +// Fix Bootstrap carousel fade + dots generation +document.addEventListener('DOMContentLoaded', function () { + const carousel = document.querySelector('#testimonialsCarousel'); + if (!carousel) return; + + const carouselInstance = new bootstrap.Carousel(carousel, { + interval: 7000, + ride: true, + pause: 'hover', + touch: true + }); + + // Generate indicators dynamically + const indicators = carousel.querySelector('.carousel-indicators'); + const items = carousel.querySelectorAll('.carousel-item'); + + items.forEach((item, index) => { + const button = document.createElement('button'); + button.type = 'button'; + button.dataset.bsTarget = '#testimonialsCarousel'; + button.dataset.bsSlideTo = index; + button.setAttribute('aria-label', `Slide ${index + 1}`); + + if (index === 0) { + button.classList.add('active'); + button.setAttribute('aria-current', 'true'); + } + + indicators.appendChild(button); + }); + + // Force first slide to be visible (fixes disappear bug) + setTimeout(() => { + carousel.querySelector('.carousel-item.active').style.opacity = '1'; + }, 100); +}); diff --git a/users_microservice/src/main/resources/templates/home/index.html b/users_microservice/src/main/resources/templates/home/index.html index e40cacf5..70a2d2a9 100644 --- a/users_microservice/src/main/resources/templates/home/index.html +++ b/users_microservice/src/main/resources/templates/home/index.html @@ -1,97 +1,145 @@ - - -
- - + + +