From 33e004ecb51c55757a5931790a84f2512651755a Mon Sep 17 00:00:00 2001 From: Blake Ridgway Date: Sat, 28 Dec 2024 12:01:49 -0600 Subject: [PATCH 1/7] (feat): Dockerfile --- Dockerfile | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..10bd218 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# Use an official Node.js runtime as a parent image +FROM node:18-alpine AS build + +# Set the working directory in the container +WORKDIR /app + +# Copy only package.json and package-lock.json to avoid unnecessary rebuilds +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Copy the rest of the application code to the container +COPY . . + +# Build the Vue.js app for production +RUN npm run build + +# Use a lightweight web server to serve the app +FROM nginx:1.25-alpine + +# Copy the built files to the Nginx web server's directory +COPY --from=build /app/dist /usr/share/nginx/html + +# Expose port 80 +EXPOSE 80 + +# Start Nginx +CMD ["nginx", "-g", "daemon off;"] + From 7928abf9e8cb98cca989700a804a898ae871bb7c Mon Sep 17 00:00:00 2001 From: Cipher Vance Date: Wed, 27 Aug 2025 18:48:53 -0500 Subject: [PATCH 2/7] feat: lintOnSave: true --- vue.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vue.config.js b/vue.config.js index 910e297..51a265b 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,4 +1,5 @@ const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ - transpileDependencies: true + transpileDependencies: true, + lintOnSave: true }) From 3862c3c3feed22fa3b323cf7252d5fc533ed7870 Mon Sep 17 00:00:00 2001 From: Cipher Vance Date: Wed, 27 Aug 2025 18:49:17 -0500 Subject: [PATCH 3/7] refactor: went to router-view instead of hard loading --- src/App.vue | 17 +++++------------ src/router/index.js | 39 +++++++++++++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/App.vue b/src/App.vue index 0065815..4ddcae8 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,18 +1,12 @@ @@ -23,6 +17,5 @@ export default { -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; - margin-top: 60px; } - + \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index a5868bb..0afb82f 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,11 +1,24 @@ import { createRouter, createWebHistory } from 'vue-router'; -import Login from '../components/UserLogin.vue'; -import LoggedinPage from '@/components/LoggedinPage.vue'; +// Lazy load components for better performance const routes = [ - { path: '/', component: Login }, - { path: '/logged-in', component: LoggedinPage} - //{ path: '/dashboard', component: () => import('../components/Dashboard.vue') }, // Placeholder for a dashboard page + { + path: '/', + name: 'Home', + component: () => import('../components/Home.vue') + }, + { + path: '/login', + name: 'Login', + component: () => import('../components/UserLogin.vue') + }, + { + path: '/logged-in', + name: 'LoggedIn', + component: () => import('../components/LoggedinPage.vue'), + // Optional: Add route guard to check if user is actually logged in + meta: { requiresAuth: true } + } ]; const router = createRouter({ @@ -13,4 +26,18 @@ const router = createRouter({ routes, }); -export default router; +// Optional: Navigation guard example +router.beforeEach((to, from, next) => { + // Check if route requires authentication + if (to.meta.requiresAuth) { + // Check if user is logged in (implement your auth logic) + const isLoggedIn = localStorage.getItem('user') || sessionStorage.getItem('user'); + if (!isLoggedIn) { + next('/login'); + return; + } + } + next(); +}); + +export default router; \ No newline at end of file From 752faff486f0abdbae35d2f5bb85dd2ec70c638a Mon Sep 17 00:00:00 2001 From: Cipher Vance Date: Wed, 27 Aug 2025 18:49:27 -0500 Subject: [PATCH 4/7] feat: add templated homepage --- src/assets/css/components/homepage.css | 513 +++++++++++++++++++++++++ src/components/Home.vue | 354 +++++++++++++++++ 2 files changed, 867 insertions(+) create mode 100644 src/assets/css/components/homepage.css create mode 100644 src/components/Home.vue diff --git a/src/assets/css/components/homepage.css b/src/assets/css/components/homepage.css new file mode 100644 index 0000000..62bcac0 --- /dev/null +++ b/src/assets/css/components/homepage.css @@ -0,0 +1,513 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.homepage { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + background: linear-gradient(135deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%); + color: #ffffff; + overflow-x: hidden; + min-height: 100vh; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 20px; +} + +/* Navigation */ +.navbar { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + backdrop-filter: blur(20px); + background: rgba(12, 12, 12, 0.8); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + transition: all 0.3s ease; +} + +.nav-content { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 2rem; + max-width: 1200px; + margin: 0 auto; +} + +.logo { + font-size: 1.8rem; + font-weight: 800; + color: #ffffff; + cursor: pointer; + text-decoration: none; +} + +.logo-accent { + background: linear-gradient(45deg, #00d4ff, #7c3aed); + background-clip: text; + -webkit-background-clip: text; + color: transparent; +} + +.nav-links { + display: flex; + list-style: none; + gap: 2rem; +} + +.nav-links a { + color: #ffffff; + text-decoration: none; + font-weight: 500; + transition: all 0.3s ease; + position: relative; +} + +.nav-links a:hover { + color: #00d4ff; +} + +.nav-links a::after { + content: ''; + position: absolute; + bottom: -5px; + left: 0; + width: 0; + height: 2px; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + transition: width 0.3s ease; +} + +.nav-links a:hover::after { + width: 100%; +} + +/* Hero Section */ +.hero { + min-height: 100vh; + display: flex; + align-items: center; + position: relative; + overflow: hidden; +} + +.hero-bg { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: radial-gradient(circle at 30% 70%, rgba(0, 212, 255, 0.1) 0%, transparent 50%), + radial-gradient(circle at 80% 20%, rgba(124, 58, 237, 0.1) 0%, transparent 50%); +} + +.floating-elements { + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; +} + +.floating-element { + position: absolute; + width: 4px; + height: 4px; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + border-radius: 50%; + animation: float 6s ease-in-out infinite; +} + +@keyframes float { + 0%, 100% { + transform: translateY(0px) rotate(0deg); + opacity: 0.5; + } + 50% { + transform: translateY(-20px) rotate(180deg); + opacity: 1; + } +} + +.hero-container { + max-width: 1200px; + margin: 0 auto; + padding: 0 2rem; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; + position: relative; + z-index: 2; +} + +.hero-content { + max-width: 600px; +} + +.hero-title { + font-size: 3.5rem; + font-weight: 900; + line-height: 1.1; + margin-bottom: 1.5rem; + background: linear-gradient(135deg, #ffffff 0%, #00d4ff 50%, #7c3aed 100%); + background-clip: text; + -webkit-background-clip: text; + color: transparent; + animation: slideInUp 1s ease-out; +} + +.hero-subtitle { + font-size: 1.25rem; + color: #a0a0a0; + margin-bottom: 2rem; + line-height: 1.6; + animation: slideInUp 1s ease-out 0.2s both; +} + +.cta-section { + animation: slideInUp 1s ease-out 0.4s both; +} + +.cta-section h3 { + font-size: 1.5rem; + margin-bottom: 0.5rem; + color: #00d4ff; +} + +.cta-section p { + color: #a0a0a0; + margin-bottom: 1.5rem; +} + +.email-form { + display: flex; + gap: 1rem; + max-width: 400px; +} + +.email-input { + flex: 1; + padding: 1rem; + border: 2px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; + background: rgba(255, 255, 255, 0.05); + color: #ffffff; + font-size: 1rem; + backdrop-filter: blur(10px); + transition: all 0.3s ease; +} + +.email-input:focus { + outline: none; + border-color: #00d4ff; + box-shadow: 0 0 20px rgba(0, 212, 255, 0.2); +} + +.email-input::placeholder { + color: #888; +} + +.notify-btn { + padding: 1rem 1.5rem; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + color: white; + border: none; + border-radius: 12px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + white-space: nowrap; +} + +.notify-btn:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 10px 30px rgba(0, 212, 255, 0.4); +} + +.notify-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Phone Mockup */ +.hero-visual { + display: flex; + justify-content: center; + align-items: center; +} + +.phone-mockup { + width: 280px; + height: 560px; + background: linear-gradient(145deg, #2a2a3e, #1a1a2e); + border-radius: 35px; + padding: 15px; + position: relative; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); +} + +.phone-mockup::before { + content: ''; + position: absolute; + top: 20px; + left: 50%; + transform: translateX(-50%); + width: 120px; + height: 4px; + background: #444; + border-radius: 2px; +} + +.screen { + width: 100%; + height: 100%; + background: linear-gradient(135deg, #0c0c0c, #1a1a2e); + border-radius: 25px; + padding: 30px 20px; + position: relative; + overflow: hidden; +} + +.app-interface { + height: 100%; + display: flex; + flex-direction: column; +} + +.app-logo { + text-align: center; + font-size: 1.5rem; + font-weight: 800; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + background-clip: text; + -webkit-background-clip: text; + color: transparent; + margin-bottom: 2rem; +} + +.stats-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + flex: 1; +} + +.stat-card { + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 15px; + padding: 1.5rem 1rem; + text-align: center; + transition: all 0.3s ease; +} + +.stat-card.active { + background: rgba(0, 212, 255, 0.1); + border-color: rgba(0, 212, 255, 0.3); + transform: scale(1.05); +} + +.stat-number { + font-size: 1.8rem; + font-weight: 800; + color: #00d4ff; + margin-bottom: 0.5rem; +} + +.stat-label { + font-size: 0.75rem; + color: #888; + font-weight: 600; + letter-spacing: 0.5px; +} + +/* Features Section */ +.features { + padding: 8rem 0; + position: relative; +} + +.section-header { + text-align: center; + margin-bottom: 4rem; + max-width: 800px; + margin-left: auto; + margin-right: auto; +} + +.section-title { + font-size: 3rem; + font-weight: 800; + margin-bottom: 1rem; + background: linear-gradient(135deg, #ffffff, #00d4ff); + background-clip: text; + -webkit-background-clip: text; + color: transparent; +} + +.section-subtitle { + font-size: 1.2rem; + color: #a0a0a0; + line-height: 1.6; +} + +.features-container { + max-width: 1200px; + margin: 0 auto; + padding: 0 2rem; +} + +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); + gap: 2rem; +} + +.feature-card { + background: rgba(255, 255, 255, 0.05); + backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 20px; + padding: 2.5rem; + transition: all 0.3s ease; + cursor: pointer; +} + +.feature-card:hover { + border-color: rgba(0, 212, 255, 0.3); + box-shadow: 0 20px 60px rgba(0, 212, 255, 0.1); +} + +.feature-icon { + width: 60px; + height: 60px; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + border-radius: 15px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1.5rem; + font-size: 1.5rem; + color: white; +} + +.feature-title { + font-size: 1.5rem; + font-weight: 700; + margin-bottom: 1.5rem; + color: #ffffff; +} + +.feature-list { + list-style: none; + margin: 0; + padding: 0; +} + +.feature-list li { + margin-bottom: 1rem; + color: #a0a0a0; + line-height: 1.6; +} + +.feature-list li:last-child { + margin-bottom: 0; +} + +.feature-list strong { + color: #00d4ff; +} + +/* Stats Section */ +.stats { + padding: 6rem 0; + background: rgba(255, 255, 255, 0.03); + backdrop-filter: blur(20px); +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 3rem; + text-align: center; +} + +.stat-number { + font-size: 3.5rem; + font-weight: 900; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + background-clip: text; + -webkit-background-clip: text; + color: transparent; + margin-bottom: 0.5rem; +} + +.stat-label { + font-size: 1.1rem; + color: #a0a0a0; + font-weight: 500; +} + +/* Footer */ +.footer { + padding: 4rem 0 2rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); + text-align: center; +} + +.footer-content { + color: #a0a0a0; +} + +/* Animations */ +@keyframes slideInUp { + from { + opacity: 0; + transform: translateY(50px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Responsive */ +@media (max-width: 968px) { + .hero-container { + grid-template-columns: 1fr; + text-align: center; + } + + .hero-title { + font-size: 2.5rem; + } + + .features-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 768px) { + .nav-links { + display: none; + } + + .email-form { + flex-direction: column; + } + + .phone-mockup { + width: 240px; + height: 480px; + } + + .hero-container { + padding: 0 1rem; + } +} \ No newline at end of file diff --git a/src/components/Home.vue b/src/components/Home.vue new file mode 100644 index 0000000..9d90b71 --- /dev/null +++ b/src/components/Home.vue @@ -0,0 +1,354 @@ + + + \ No newline at end of file From 6422189f6c2dabf3e5e33966cdf888603ac4ff0d Mon Sep 17 00:00:00 2001 From: Cipher Vance Date: Wed, 27 Aug 2025 18:49:46 -0500 Subject: [PATCH 5/7] refactor: modernized login --- src/assets/css/components/login.css | 395 ++++++++++++++++++++++++++++ src/components/UserLogin.vue | 181 +++++++++++-- 2 files changed, 558 insertions(+), 18 deletions(-) create mode 100644 src/assets/css/components/login.css diff --git a/src/assets/css/components/login.css b/src/assets/css/components/login.css new file mode 100644 index 0000000..028b58d --- /dev/null +++ b/src/assets/css/components/login.css @@ -0,0 +1,395 @@ +/* login.css */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.login-page { + min-height: 100vh; + background: linear-gradient(135deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%); + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; +} + +.login-bg { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: radial-gradient(circle at 20% 80%, rgba(0, 212, 255, 0.05) 0%, transparent 50%), + radial-gradient(circle at 80% 20%, rgba(124, 58, 237, 0.05) 0%, transparent 50%); +} + +.floating-elements { + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; +} + +.floating-element { + position: absolute; + width: 3px; + height: 3px; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + border-radius: 50%; + animation: float 8s ease-in-out infinite; +} + +@keyframes float { + 0%, 100% { + transform: translateY(0px) rotate(0deg); + opacity: 0.3; + } + 50% { + transform: translateY(-30px) rotate(180deg); + opacity: 0.8; + } +} + +.login-container { + position: relative; + z-index: 2; + width: 100%; + max-width: 420px; + padding: 2rem; +} + +.login-card { + background: rgba(255, 255, 255, 0.08); + backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 24px; + padding: 3rem; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + animation: slideInUp 0.8s ease-out; +} + +@keyframes slideInUp { + from { + opacity: 0; + transform: translateY(50px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.login-header { + text-align: center; + margin-bottom: 2.5rem; +} + +.logo { + font-size: 2rem; + font-weight: 800; + color: #ffffff; + margin-bottom: 1rem; +} + +.logo-accent { + background: linear-gradient(45deg, #00d4ff, #7c3aed); + background-clip: text; + -webkit-background-clip: text; + color: transparent; +} + +.login-title { + font-size: 1.75rem; + font-weight: 700; + color: #ffffff; + margin-bottom: 0.5rem; +} + +.login-subtitle { + color: #a0a0a0; + font-size: 0.95rem; +} + +.login-form { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.form-label { + font-weight: 600; + color: #ffffff; + font-size: 0.9rem; +} + +.input-wrapper { + position: relative; +} + +.input-icon { + position: absolute; + left: 1rem; + top: 50%; + transform: translateY(-50%); + color: #888; + font-size: 0.9rem; +} + +.form-input { + width: 100%; + padding: 1rem 1rem 1rem 2.75rem; + background: rgba(255, 255, 255, 0.05); + border: 2px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; + color: #ffffff; + font-size: 1rem; + transition: all 0.3s ease; + backdrop-filter: blur(10px); +} + +.form-input:focus { + outline: none; + border-color: #00d4ff; + box-shadow: 0 0 20px rgba(0, 212, 255, 0.2); + background: rgba(255, 255, 255, 0.08); +} + +.form-input.error { + border-color: #ff4757; + box-shadow: 0 0 20px rgba(255, 71, 87, 0.2); +} + +.form-input::placeholder { + color: #666; +} + +.password-toggle { + position: absolute; + right: 1rem; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + color: #888; + cursor: pointer; + padding: 0.25rem; + transition: color 0.3s ease; +} + +.password-toggle:hover { + color: #00d4ff; +} + +.form-options { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; +} + +.checkbox-wrapper { + display: flex; + align-items: center; + cursor: pointer; + gap: 0.5rem; +} + +.checkbox-input { + display: none; +} + +.checkbox-custom { + width: 18px; + height: 18px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; +} + +.checkbox-input:checked + .checkbox-custom { + background: linear-gradient(45deg, #00d4ff, #7c3aed); + border-color: transparent; +} + +.checkbox-input:checked + .checkbox-custom::after { + content: '✓'; + color: white; + font-size: 12px; + font-weight: bold; +} + +.checkbox-label { + color: #a0a0a0; + font-size: 0.9rem; +} + +.forgot-password { + color: #00d4ff; + text-decoration: none; + font-size: 0.9rem; + transition: color 0.3s ease; +} + +.forgot-password:hover { + color: #7c3aed; +} + +.login-button { + width: 100%; + padding: 1rem; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + color: white; + border: none; + border-radius: 12px; + font-weight: 600; + font-size: 1.1rem; + cursor: pointer; + transition: all 0.3s ease; + margin-top: 0.5rem; +} + +.login-button:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 15px 40px rgba(0, 212, 255, 0.4); +} + +.login-button:disabled { + opacity: 0.7; + cursor: not-allowed; + transform: none; +} + +.loading-text { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.divider { + position: relative; + text-align: center; + margin: 1.5rem 0; + color: #666; +} + +.divider::before { + content: ''; + position: absolute; + top: 50%; + left: 0; + right: 0; + height: 1px; + background: rgba(255, 255, 255, 0.1); +} + +.divider span { + background: rgba(255, 255, 255, 0.08); + padding: 0 1rem; + position: relative; + z-index: 1; +} + +.social-login { + width: 100%; + padding: 0.875rem; + background: rgba(255, 255, 255, 0.05); + border: 2px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; + color: #ffffff; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; + margin-bottom: 0.75rem; + backdrop-filter: blur(10px); +} + +.social-login:hover { + background: rgba(255, 255, 255, 0.08); + border-color: rgba(255, 255, 255, 0.2); +} + +.social-login.google:hover { + border-color: rgba(219, 68, 55, 0.5); + box-shadow: 0 0 20px rgba(219, 68, 55, 0.1); +} + +.social-login.github:hover { + border-color: rgba(255, 255, 255, 0.5); + box-shadow: 0 0 20px rgba(255, 255, 255, 0.1); +} + +.error-message { + background: rgba(255, 71, 87, 0.1); + border: 1px solid rgba(255, 71, 87, 0.3); + border-radius: 12px; + padding: 1rem; + color: #ff4757; + font-size: 0.9rem; + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 1rem; + animation: shake 0.5s ease-in-out; +} + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-5px); } + 75% { transform: translateX(5px); } +} + +.signup-prompt { + text-align: center; + margin-top: 2rem; + padding-top: 2rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +.signup-prompt p { + color: #a0a0a0; +} + +.signup-link { + color: #00d4ff; + text-decoration: none; + font-weight: 600; + transition: color 0.3s ease; +} + +.signup-link:hover { + color: #7c3aed; +} + +/* Responsive */ +@media (max-width: 480px) { + .login-container { + padding: 1rem; + } + + .login-card { + padding: 2rem; + } + + .form-options { + flex-direction: column; + gap: 1rem; + align-items: flex-start; + } +} \ No newline at end of file diff --git a/src/components/UserLogin.vue b/src/components/UserLogin.vue index 00d9617..471661c 100644 --- a/src/components/UserLogin.vue +++ b/src/components/UserLogin.vue @@ -1,23 +1,126 @@ + \ No newline at end of file From aa8dad9809ef3452fbe82c150840f4e70c3b60c1 Mon Sep 17 00:00:00 2001 From: Cipher Vance Date: Tue, 9 Sep 2025 08:30:05 -0500 Subject: [PATCH 6/7] feat(ui): integrate login/signup into navbar, enhance login flow, and set dynamic titles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed outdated comments and unused component - Cleaned up login.css header comment - Updated Home.vue navigation to use for Login/Sign Up - Enhanced UserLogin.vue: • Added loginSuccess state, success styling, and green “Success! Redirecting” feedback • Redirect to homepage (/) after sign-in • Persist user data & simple auth token in local/session storage • Updated API call URL to /auth/login - Added route definitions for /signup in router - Added meta.title to Home/Login/Sign Up routes and global afterEach hook to update document.title - Simplified unused methods and cleaned up commented code in Home.vue --- src/assets/css/components/login.css | 1 - src/assets/css/components/signup.css | 517 +++++++++++++++++++++++++++ src/components/Home.vue | 24 +- src/components/LoggedinPage.vue | 19 - src/components/UserLogin.vue | 45 ++- src/components/UserSignup.vue | 344 ++++++++++++++++++ src/router/index.js | 47 +-- 7 files changed, 924 insertions(+), 73 deletions(-) create mode 100644 src/assets/css/components/signup.css delete mode 100644 src/components/LoggedinPage.vue create mode 100644 src/components/UserSignup.vue diff --git a/src/assets/css/components/login.css b/src/assets/css/components/login.css index 028b58d..4690cd8 100644 --- a/src/assets/css/components/login.css +++ b/src/assets/css/components/login.css @@ -1,4 +1,3 @@ -/* login.css */ * { margin: 0; padding: 0; diff --git a/src/assets/css/components/signup.css b/src/assets/css/components/signup.css new file mode 100644 index 0000000..5e8afc9 --- /dev/null +++ b/src/assets/css/components/signup.css @@ -0,0 +1,517 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.signup-page { + min-height: 100vh; + background: linear-gradient(135deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%); + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; +} + +.signup-bg { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: radial-gradient(circle at 20% 80%, rgba(0, 212, 255, 0.05) 0%, transparent 50%), + radial-gradient(circle at 80% 20%, rgba(124, 58, 237, 0.05) 0%, transparent 50%); +} + +.floating-elements { + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; +} + +.floating-element { + position: absolute; + width: 3px; + height: 3px; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + border-radius: 50%; + animation: float 8s ease-in-out infinite; +} + +@keyframes float { + 0%, 100% { + transform: translateY(0px) rotate(0deg); + opacity: 0.3; + } + 50% { + transform: translateY(-30px) rotate(180deg); + opacity: 0.8; + } +} + +.signup-container { + position: relative; + z-index: 2; + width: 100%; + max-width: 480px; + padding: 2rem; +} + +.signup-card { + background: rgba(255, 255, 255, 0.08); + backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 24px; + padding: 3rem; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + animation: slideInUp 0.8s ease-out; +} + +@keyframes slideInUp { + from { + opacity: 0; + transform: translateY(50px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.signup-header { + text-align: center; + margin-bottom: 2.5rem; +} + +.logo { + font-size: 2rem; + font-weight: 800; + color: #ffffff; + margin-bottom: 1rem; +} + +.logo-accent { + background: linear-gradient(45deg, #00d4ff, #7c3aed); + background-clip: text; + -webkit-background-clip: text; + color: transparent; +} + +.signup-title { + font-size: 1.75rem; + font-weight: 700; + color: #ffffff; + margin-bottom: 0.5rem; +} + +.signup-subtitle { + color: #a0a0a0; + font-size: 0.95rem; +} + +.signup-form { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.form-row { + display: flex; + gap: 1rem; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 1rem; +} + +.form-group.half-width { + flex: 1; + margin-bottom: 0; +} + +.form-label { + font-weight: 600; + color: #ffffff; + font-size: 0.9rem; +} + +.input-wrapper { + position: relative; +} + +.input-icon { + position: absolute; + left: 1rem; + top: 50%; + transform: translateY(-50%); + color: #888; + font-size: 0.9rem; +} + +.form-input { + width: 100%; + padding: 1rem 1rem 1rem 2.75rem; + background: rgba(255, 255, 255, 0.05); + border: 2px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; + color: #ffffff; + font-size: 1rem; + transition: all 0.3s ease; + backdrop-filter: blur(10px); +} + +.form-input:focus { + outline: none; + border-color: #00d4ff; + box-shadow: 0 0 20px rgba(0, 212, 255, 0.2); + background: rgba(255, 255, 255, 0.08); +} + +.form-input.error { + border-color: #ff4757; + box-shadow: 0 0 20px rgba(255, 71, 87, 0.2); +} + +.form-input::placeholder { + color: #666; +} + +.password-toggle { + position: absolute; + right: 1rem; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + color: #888; + cursor: pointer; + padding: 0.25rem; + transition: color 0.3s ease; +} + +.password-toggle:hover { + color: #00d4ff; +} + +.password-strength { + margin-top: 0.5rem; +} + +.strength-bar { + width: 100%; + height: 4px; + background-color: rgba(255, 255, 255, 0.1); + border-radius: 2px; + overflow: hidden; + margin-bottom: 0.25rem; +} + +.strength-fill { + height: 100%; + transition: width 0.3s ease, background-color 0.3s ease; +} + +.strength-fill.weak { + background: linear-gradient(45deg, #ff4757, #ff6b7a); +} + +.strength-fill.fair { + background: linear-gradient(45deg, #ffa502, #ffb347); +} + +.strength-fill.good { + background: linear-gradient(45deg, #2ed573, #7bed9f); +} + +.strength-fill.strong { + background: linear-gradient(45deg, #00d4ff, #7c3aed); +} + +.strength-text { + font-size: 0.75rem; + font-weight: 500; +} + +.strength-text.weak { + color: #ff4757; +} + +.strength-text.fair { + color: #ffa502; +} + +.strength-text.good { + color: #2ed573; +} + +.strength-text.strong { + background: linear-gradient(45deg, #00d4ff, #7c3aed); + background-clip: text; + -webkit-background-clip: text; + color: transparent; +} + +.password-mismatch { + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 0.5rem; + font-size: 0.75rem; + color: #ff4757; + animation: shake 0.5s ease-in-out; +} + +.form-options { + display: flex; + flex-direction: column; + gap: 1rem; + margin-bottom: 0.5rem; +} + +.checkbox-wrapper { + display: flex; + align-items: flex-start; + cursor: pointer; + gap: 0.75rem; +} + +.checkbox-input { + display: none; +} + +.checkbox-custom { + width: 18px; + height: 18px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + flex-shrink: 0; + margin-top: 1px; +} + +.checkbox-input:checked + .checkbox-custom { + background: linear-gradient(45deg, #00d4ff, #7c3aed); + border-color: transparent; +} + +.checkbox-input:checked + .checkbox-custom::after { + content: '✓'; + color: white; + font-size: 12px; + font-weight: bold; +} + +.checkbox-label { + color: #a0a0a0; + font-size: 0.9rem; + line-height: 1.4; +} + +.link { + color: #00d4ff; + text-decoration: none; + transition: color 0.3s ease; +} + +.link:hover { + color: #7c3aed; + text-decoration: underline; +} + +.signup-button { + width: 100%; + padding: 1rem; + background: linear-gradient(45deg, #00d4ff, #7c3aed); + color: white; + border: none; + border-radius: 12px; + font-weight: 600; + font-size: 1.1rem; + cursor: pointer; + transition: all 0.3s ease; + margin-top: 0.5rem; +} + +.signup-button:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 15px 40px rgba(0, 212, 255, 0.4); +} + +.signup-button:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; + background: rgba(255, 255, 255, 0.1); +} + +.loading-text { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.divider { + position: relative; + text-align: center; + margin: 1.5rem 0; + color: #666; +} + +.divider::before { + content: ''; + position: absolute; + top: 50%; + left: 0; + right: 0; + height: 1px; + background: rgba(255, 255, 255, 0.1); +} + +.divider span { + background: rgba(255, 255, 255, 0.08); + padding: 0 1rem; + position: relative; + z-index: 1; +} + +.social-signup { + width: 100%; + padding: 0.875rem; + background: rgba(255, 255, 255, 0.05); + border: 2px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; + color: #ffffff; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; + margin-bottom: 0.75rem; + backdrop-filter: blur(10px); +} + +.social-signup:hover { + background: rgba(255, 255, 255, 0.08); + border-color: rgba(255, 255, 255, 0.2); +} + +.social-signup.google:hover { + border-color: rgba(219, 68, 55, 0.5); + box-shadow: 0 0 20px rgba(219, 68, 55, 0.1); +} + +.social-signup.github:hover { + border-color: rgba(255, 255, 255, 0.5); + box-shadow: 0 0 20px rgba(255, 255, 255, 0.1); +} + +.error-message { + background: rgba(255, 71, 87, 0.1); + border: 1px solid rgba(255, 71, 87, 0.3); + border-radius: 12px; + padding: 1rem; + color: #ff4757; + font-size: 0.9rem; + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 1rem; + animation: shake 0.5s ease-in-out; +} + +.success-message { + background: rgba(46, 213, 115, 0.1); + border: 1px solid rgba(46, 213, 115, 0.3); + border-radius: 12px; + padding: 1rem; + color: #2ed573; + font-size: 0.9rem; + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 1rem; + animation: slideInUp 0.5s ease-out; +} + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-5px); } + 75% { transform: translateX(5px); } +} + +.login-prompt { + text-align: center; + margin-top: 2rem; + padding-top: 2rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +.login-prompt p { + color: #a0a0a0; +} + +.login-link { + color: #00d4ff; + text-decoration: none; + font-weight: 600; + transition: color 0.3s ease; +} + +.login-link:hover { + color: #7c3aed; +} + +/* Responsive */ +@media (max-width: 640px) { + .signup-container { + padding: 1rem; + max-width: 100%; + } + + .signup-card { + padding: 2rem; + } + + .form-row { + flex-direction: column; + gap: 0; + } + + .form-group.half-width { + margin-bottom: 1rem; + } + + .checkbox-wrapper { + align-items: flex-start; + } + + .checkbox-label { + font-size: 0.85rem; + } +} + +@media (max-width: 480px) { + .signup-card { + padding: 1.5rem; + } + + .signup-title { + font-size: 1.5rem; + } + + .logo { + font-size: 1.75rem; + } +} \ No newline at end of file diff --git a/src/components/Home.vue b/src/components/Home.vue index 9d90b71..9ad41b5 100644 --- a/src/components/Home.vue +++ b/src/components/Home.vue @@ -1,19 +1,24 @@