From c82572935caae3b32294689834cfa89612822ced Mon Sep 17 00:00:00 2001 From: mARTin Date: Mon, 23 Feb 2026 23:33:25 +0100 Subject: [PATCH] feat: dark mode color adjustment, multi-source quotes, UI text wrapping, and versioning rules - Add dark mode color adjustment helper for category colors - Support multiple motivational quote source URLs - Fix text wrapping for tasks/events to prevent horizontal overflow - Improve someday list item layout for long text - Add CLAUDE.md with semantic versioning and git workflow rules - Update test/doc URLs and planning docs v1.1.0 Co-Authored-By: Claude Opus 4.6 --- .../01-setup-and-authentication/01-01-PLAN.md | 6 +- .../01-setup-and-authentication/01-02-PLAN.md | 12 +- .../01-setup-and-authentication/01-03-PLAN.md | 2 +- .../03-calendar-integration/03-02-PLAN.md | 2 +- .../03-calendar-integration/03-04-PLAN.md | 4 +- CLAUDE.md | 52 ++++ Dockerfile | 2 +- README.md | 2 +- SETUP.md | 8 +- TEST_PLAN.md | 2 +- TEST_PLAN_SUMMARY.md | 2 +- debug-login-page.js | 10 +- debug-signup.js | 20 +- deployment-notes.md | 2 +- package.json | 2 +- playwright.config.ts | 4 +- src/app/globals.css | 28 +- src/components/WeeklyView.tsx | 246 +++++++++++++----- test-account-setup.js | 12 +- test-api.js | 12 +- tests/auth-flow-test.spec.ts | 12 +- tests/css-debug-test.spec.ts | 18 +- tests/local-deployment-test.spec.ts | 10 +- tests/login-basic-debug.spec.ts | 26 +- tests/login-debug-simplified.spec.ts | 28 +- tests/login-debug.spec.ts | 52 ++-- tests/login-simple-debug.spec.ts | 36 +-- 27 files changed, 394 insertions(+), 218 deletions(-) create mode 100644 CLAUDE.md diff --git a/.planning/phases/01-setup-and-authentication/01-01-PLAN.md b/.planning/phases/01-setup-and-authentication/01-01-PLAN.md index fa56ce7..f318a9e 100644 --- a/.planning/phases/01-setup-and-authentication/01-01-PLAN.md +++ b/.planning/phases/01-setup-and-authentication/01-01-PLAN.md @@ -127,7 +127,7 @@ We need to implement the full authentication logic and ensure proper session man - Set cookie with security flags (httpOnly, secure, sameSite) - Use jose library instead of jsonwebtoken to avoid CommonJS issues with Edge runtime - Run `curl -X POST http://localhost:3000/api/auth/signup -H "Content-Type: application/json" -d '{"email":"test@example.com","password":"password"}'` and verify response structure + Run `curl -X POST http://localhost:3001/api/auth/signup -H "Content-Type: application/json" -d '{"email":"test@example.com","password":"password"}'` and verify response structure User can successfully sign up and receive a valid session token in HTTP-only cookie @@ -151,7 +151,7 @@ We need to implement the full authentication logic and ensure proper session man - Use jose library instead of jsonwebtoken to avoid CommonJS issues with Edge runtime - Prevent timing attacks by comparing passwords in constant time - Run `curl -X POST http://localhost:3000/api/auth/login -H "Content-Type: application/json" -d '{"email":"test@example.com","password":"password"}'` and verify response structure + Run `curl -X POST http://localhost:3001/api/auth/login -H "Content-Type: application/json" -d '{"email":"test@example.com","password":"password"}'` and verify response structure User can successfully authenticate and receive a valid session token in HTTP-only cookie @@ -164,7 +164,7 @@ We need to implement the full authentication logic and ensure proper session man - Return success message to the client - Ensure cookie flags match those used during login (httpOnly, secure, sameSite) - Run `curl -X POST http://localhost:3000/api/auth/logout` and verify cookie is cleared + Run `curl -X POST http://localhost:3001/api/auth/logout` and verify cookie is cleared User can successfully log out and session cookie is cleared diff --git a/.planning/phases/01-setup-and-authentication/01-02-PLAN.md b/.planning/phases/01-setup-and-authentication/01-02-PLAN.md index 2e7772f..0a8e86b 100644 --- a/.planning/phases/01-setup-and-authentication/01-02-PLAN.md +++ b/.planning/phases/01-setup-and-authentication/01-02-PLAN.md @@ -102,7 +102,7 @@ Building upon the foundation established in Plan 1, this plan implements the rem - Send email using configured email service (details in infrastructure) - Not reveal if email exists in system to prevent enumeration - Run `curl -X POST http://localhost:3000/api/auth/forgot-password -H "Content-Type: application/json" -d '{"email":"test@example.com"}'` and verify behavior + Run `curl -X POST http://localhost:3001/api/auth/forgot-password -H "Content-Type: application/json" -d '{"email":"test@example.com"}'` and verify behavior User can request password reset and receive confirmation without revealing account existence @@ -125,7 +125,7 @@ Building upon the foundation established in Plan 1, this plan implements the rem - Return appropriate error responses for invalid or expired tokens - Use bcryptjs for password hashing - Run `curl -X POST http://localhost:3000/api/auth/reset-password -H "Content-Type: application/json" -d '{"email":"test@example.com","token":"reset-token","password":"newpassword"}'` and verify behavior + Run `curl -X POST http://localhost:3001/api/auth/reset-password -H "Content-Type: application/json" -d '{"email":"test@example.com","token":"reset-token","password":"newpassword"}'` and verify behavior User can complete password reset with valid token and receive confirmation @@ -148,7 +148,7 @@ Building upon the foundation established in Plan 1, this plan implements the rem - Clear token from database after verification - Handle invalid/missing/expired tokens gracefully - Visit `http://localhost:3000/api/auth/verify-email?token=verification-token` and verify behavior + Visit `http://localhost:3001/api/auth/verify-email?token=verification-token` and verify behavior User can verify email address with valid token and receive confirmation @@ -169,7 +169,7 @@ Building upon the foundation established in Plan 1, this plan implements the rem - Be styled consistently with the rest of the application - Have a link back to login page - Visit http://localhost:3000/auth/forgot-password and verify form renders correctly + Visit http://localhost:3001/auth/forgot-password and verify form renders correctly Forgot password page is accessible, visually consistent, and functional @@ -192,7 +192,7 @@ Building upon the foundation established in Plan 1, this plan implements the rem - Be styled consistently with the rest of the application - Have a link back to login page - Visit http://localhost:3000/auth/reset-password?token=test-token and verify form renders correctly + Visit http://localhost:3001/auth/reset-password?token=test-token and verify form renders correctly Password reset page is accessible, visually consistent, and functional @@ -213,7 +213,7 @@ Building upon the foundation established in Plan 1, this plan implements the rem - Provide clear feedback on verification status - Be styled consistently with the rest of the application - Visit http://localhost:3000/auth/verify-email?token=test-token and verify behavior + Visit http://localhost:3001/auth/verify-email?token=test-token and verify behavior Email verification page is accessible, visually consistent, and functional diff --git a/.planning/phases/01-setup-and-authentication/01-03-PLAN.md b/.planning/phases/01-setup-and-authentication/01-03-PLAN.md index a5263a2..925c062 100644 --- a/.planning/phases/01-setup-and-authentication/01-03-PLAN.md +++ b/.planning/phases/01-setup-and-authentication/01-03-PLAN.md @@ -88,7 +88,7 @@ The previous plans have established core authentication flows. This plan focuses - Send email using configured email service (details in infrastructure) - Return clear success/failure messages - Run `curl -X POST http://localhost:3000/api/auth/send-verification-email -H "Content-Type: application/json" -d '{"email":"test@example.com"}'` and verify behavior + Run `curl -X POST http://localhost:3001/api/auth/send-verification-email -H "Content-Type: application/json" -d '{"email":"test@example.com"}'` and verify behavior User can request verification email to be resent and receive confirmation diff --git a/.planning/phases/03-calendar-integration/03-02-PLAN.md b/.planning/phases/03-calendar-integration/03-02-PLAN.md index 1bf80e3..8319475 100644 --- a/.planning/phases/03-calendar-integration/03-02-PLAN.md +++ b/.planning/phases/03-calendar-integration/03-02-PLAN.md @@ -85,7 +85,7 @@ Output: Bidirectional synchronization logic, sync API endpoint, and calendar set - Handles errors gracefully with appropriate messaging Follow the API endpoint pattern established in Phase 2 and authentication flow from Phase 1. - curl -X POST http://localhost:3000/api/calendar/sync returns 200 with successful sync status + curl -X POST http://localhost:3001/api/calendar/sync returns 200 with successful sync status Endpoint successfully triggers sync process and returns status JSON diff --git a/.planning/phases/03-calendar-integration/03-04-PLAN.md b/.planning/phases/03-calendar-integration/03-04-PLAN.md index cb4a0ae..9fe9ee2 100644 --- a/.planning/phases/03-calendar-integration/03-04-PLAN.md +++ b/.planning/phases/03-calendar-integration/03-04-PLAN.md @@ -80,7 +80,7 @@ Functional OAuth endpoints that exchange authorization codes for real tokens and - Handle and log token exchange errors appropriately - Store the tokens in the CalendarConnection database record - Run `curl -X GET "http://localhost:3000/api/calendar/google/oauth?code=valid_code"` and verify tokens are stored in database instead of just console logged + Run `curl -X GET "http://localhost:3001/api/calendar/google/oauth?code=valid_code"` and verify tokens are stored in database instead of just console logged Google OAuth callback endpoint exchanges real tokens and stores them in database @@ -102,7 +102,7 @@ Functional OAuth endpoints that exchange authorization codes for real tokens and - Handle and log token exchange errors appropriately - Store the tokens in the CalendarConnection database record - Run `curl -X GET "http://localhost:3000/api/calendar/apple/oauth?code=valid_code"` and verify tokens are stored in database instead of just console logged + Run `curl -X GET "http://localhost:3001/api/calendar/apple/oauth?code=valid_code"` and verify tokens are stored in database instead of just console logged Apple OAuth callback endpoint exchanges real tokens and stores them in database diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d6e45ec --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,52 @@ +# Project Instructions for Claude Code + +## Project Overview +My Weekly ToDo List - A web-based weekly task management application built with Next.js. +Deployed at: todo.martin-bierschenk.de + +## Git Workflow & Versioning + +### Branch Strategy (Git Flow Lite) +- `main` - stable, deployed to todo.martin-bierschenk.de +- `dev` - working branch for new features +- Feature branches off `dev` for larger work +- Merge `dev` → `main` when ready to deploy +- `main` always reflects what's live + +### Semantic Versioning (REQUIRED for every commit) +Use semver (MAJOR.MINOR.PATCH) in `package.json`: + +**Version bump rules:** +- **PATCH** (x.x.+1): Bug fixes, typo corrections, minor CSS tweaks, dependency updates + - Commit prefix: `fix:`, `chore:`, `style:`, `docs:` +- **MINOR** (x.+1.0): New features, significant UI changes, new integrations + - Commit prefix: `feat:` +- **MAJOR** (+1.0.0): Breaking changes (database migrations, API changes, auth changes) + - Commit prefix: any with `BREAKING CHANGE` in body + +**On every git commit, you MUST:** +1. Determine the version bump type based on changes (patch/minor/major) +2. Update the `version` field in `package.json` +3. Include the new version in the commit message footer, e.g.: `v1.1.0` +4. After committing, create a git tag: `git tag v` + +**Commit message format:** +``` +: + + + +v +``` + +Example: +``` +feat: add Microsoft To-Do integration + +Sync tasks bidirectionally with Microsoft To-Do API. + +v1.1.0 +``` + +### Current Version +Check `package.json` → `version` field for the current version before bumping. diff --git a/Dockerfile b/Dockerfile index 6e21cc3..ffe5836 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,6 @@ USER nextjs EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ - CMD curl -f http://localhost:3000/api/auth/session || exit 1 + CMD curl -f http://localhost:3001/api/auth/session || exit 1 CMD ["npm", "run", "start"] diff --git a/README.md b/README.md index 5631fdd..9f97396 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ This application can be deployed using Docker and Docker Compose. ```bash docker-compose up --build ``` -3. Access the application at http://localhost:3000 +3. Access the application at http://localhost:3001 ### Environment Variables diff --git a/SETUP.md b/SETUP.md index 9cd6e13..79d3c5b 100644 --- a/SETUP.md +++ b/SETUP.md @@ -39,7 +39,7 @@ This will create all necessary tables: npm run dev ``` -Visit: **http://localhost:3000** +Visit: **http://localhost:3001** --- @@ -47,14 +47,14 @@ Visit: **http://localhost:3000** ### Sign Up (Email/Password) -1. Go to http://localhost:3000/auth/signup +1. Go to http://localhost:3001/auth/signup 2. Enter email and password (min 8 characters) 3. Click "Sign up" 4. You'll be automatically signed in ### Sign In (Email/Password) -1. Go to http://localhost:3000/auth/login +1. Go to http://localhost:3001/auth/login 2. Enter your credentials 3. Click "Sign in" @@ -82,7 +82,7 @@ To enable "Sign in with Google": 4. Click **Create Credentials** → **OAuth client ID** 5. Choose **Web application** 6. Add these redirect URIs: - - `http://localhost:3000/api/auth/callback/google` + - `http://localhost:3001/api/auth/callback/google` - `https://todo.martin-bierschenk.de/api/auth/callback/google` (via Reverse Proxy) - `https://todo.martin-bierschenk.de/api/calendar/google/oauth` (via Reverse Proxy) 7. Copy your **Client ID** and **Client Secret** diff --git a/TEST_PLAN.md b/TEST_PLAN.md index 162a9bd..93dda54 100644 --- a/TEST_PLAN.md +++ b/TEST_PLAN.md @@ -161,7 +161,7 @@ The application follows a standard Next.js 14 architecture with: ## Assumptions -1. Application runs on localhost:3000 (development server) +1. Application runs on localhost:3001 (development server) 2. All components are properly rendered 3. Network connectivity allows for API calls 4. Required environment variables are configured diff --git a/TEST_PLAN_SUMMARY.md b/TEST_PLAN_SUMMARY.md index b76cd84..7ebb9b4 100644 --- a/TEST_PLAN_SUMMARY.md +++ b/TEST_PLAN_SUMMARY.md @@ -41,7 +41,7 @@ I've created a comprehensive test plan for the My Weekly To Do List application ## Implementation Notes The test plan assumes: -- Application will run on localhost:3000 during testing +- Application will run on localhost:3001 during testing - All components are properly implemented and accessible - Environment variables are configured appropriately - Network connectivity supports API interactions diff --git a/debug-login-page.js b/debug-login-page.js index 078fbec..6c236a4 100644 --- a/debug-login-page.js +++ b/debug-login-page.js @@ -1,9 +1,9 @@ // Debug script to inspect login page structure and Tailwind CSS -// Run this by visiting http://localhost:3000/auth/login in a browser and using DevTools +// Run this by visiting http://localhost:3001/auth/login in a browser and using DevTools console.log('=== DEBUGGING LOGIN PAGE STRUCTURE ==='); -// This will be run in browser console after navigating to http://localhost:3000/auth/login +// This will be run in browser console after navigating to http://localhost:3001/auth/login // 1. Check if page loads properly console.log('1. Checking page load...'); @@ -23,7 +23,7 @@ console.log('\n3. Checking login elements:'); const h1Elements = document.querySelectorAll('h1'); console.log('H1 elements count:', h1Elements.length); h1Elements.forEach((el, i) => { - console.log(` H1 ${i+1}: "${el.textContent}"`); + console.log(` H1 ${i + 1}: "${el.textContent}"`); }); // 4. Check for form elements @@ -59,7 +59,7 @@ const inputs = document.querySelectorAll('input'); console.log('Inputs found:', inputs.length); inputs.forEach((input, i) => { if (input.type === 'email' || input.type === 'password') { - console.log(` Input ${i+1}: type=${input.type}, name=${input.name}`); + console.log(` Input ${i + 1}: type=${input.type}, name=${input.name}`); console.log(` Classes: ${input.className}`); } }); @@ -67,7 +67,7 @@ inputs.forEach((input, i) => { const buttons = document.querySelectorAll('button'); console.log('Buttons found:', buttons.length); buttons.forEach((btn, i) => { - console.log(` Button ${i+1}: type=${btn.type}, text="${btn.textContent.substring(0, 30)}..."`); + console.log(` Button ${i + 1}: type=${btn.type}, text="${btn.textContent.substring(0, 30)}..."`); }); // 8. Check for expected text content diff --git a/debug-signup.js b/debug-signup.js index 1e12fa9..4d59559 100644 --- a/debug-signup.js +++ b/debug-signup.js @@ -4,7 +4,7 @@ * This script is meant to be run in the browser console to debug signup issues. * * To use: - * 1. Navigate to http://localhost:3000/auth/signup + * 1. Navigate to http://localhost:3001/auth/signup * 2. Open browser developer tools (F12) * 3. Go to Console tab * 4. Paste this entire script and run it @@ -30,7 +30,7 @@ if (!signupForm) { }); } else { console.log('✅ Form found:', signupForm); - + // Check form attributes console.log('Form attributes:', { action: signupForm.action, @@ -38,28 +38,28 @@ if (!signupForm) { id: signupForm.id, className: signupForm.className }); - + // Add temporary event listener to see if submit is being called - signupForm.addEventListener('submit', function(e) { + signupForm.addEventListener('submit', function (e) { console.log('📋 Form submit intercepted'); console.log('Submit event:', e); console.log('Form data will be sent to:', this.action || 'default'); }, { once: true }); - + // Check input fields const emailInput = document.querySelector('input[name="email"]'); const passwordInput = document.querySelector('input[name="password"]'); const submitButton = document.querySelector('button[type="submit"]'); - + console.log('Input fields:'); if (emailInput) console.log('✅ Email input:', emailInput); if (passwordInput) console.log('✅ Password input:', passwordInput); if (submitButton) console.log('✅ Submit button:', submitButton); - + // Monitor network requests console.log('🔍 Monitoring network requests...'); const originalFetch = window.fetch; - window.fetch = function(...args) { + window.fetch = function (...args) { console.log('📡 Fetch called with:', args); const [input, init] = args; console.log(' URL:', input); @@ -69,9 +69,9 @@ if (!signupForm) { } return originalFetch.apply(this, args); }; - + // Monitor for errors - window.addEventListener('error', function(e) { + window.addEventListener('error', function (e) { console.error('🚨 JS Error caught:', e.error); console.error('Error location:', e.filename, e.lineno, e.colno); }); diff --git a/deployment-notes.md b/deployment-notes.md index b590900..f058f56 100644 --- a/deployment-notes.md +++ b/deployment-notes.md @@ -41,7 +41,7 @@ Since this is a Next.js application, you can deploy it with: # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD curl -f http://localhost:3000/health || exit 1 + CMD curl -f http://localhost:3001/health || exit 1 # For Next.js, run in production mode CMD ["npm", "run", "start"] diff --git a/package.json b/package.json index 9d88949..68589ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.0.0", + "version": "1.1.0", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "main": "index.js", "scripts": { diff --git a/playwright.config.ts b/playwright.config.ts index 864efe2..0288d35 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -26,7 +26,7 @@ export default defineConfig({ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('')`. */ - baseURL: 'http://localhost:3000', + baseURL: 'http://localhost:3001', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', @@ -73,7 +73,7 @@ export default defineConfig({ /* Run your local dev server before starting the tests */ // webServer: { // command: 'npm run start', - // url: 'http://localhost:3000', + // url: 'http://localhost:3001', // reuseExistingServer: !process.env.CI, // }, }); diff --git a/src/app/globals.css b/src/app/globals.css index 7701b3b..4845170 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -465,6 +465,8 @@ h3 { --weekly-settings-toggle-active-bg: white; --weekly-settings-toggle-text: #6b7280; --weekly-settings-toggle-active-text: #111827; + --weekly-header-brightness: 1; + --weekly-goal-brightness: 1; } /* Weekly Main Container */ @@ -709,7 +711,9 @@ h3 { position: relative; transition: all 0.2s ease; border-bottom: 1px solid var(--weekly-border); /* Restore lines */ - line-height: initial; + line-height: 1.4; + height: auto; + min-height: 32px; } /* Remove border from last item to look cleaner, or keep for paper look */ @@ -737,6 +741,9 @@ h3 { font-weight: var(--weekly-task-weight, 400); line-height: var(--weekly-task-line-height, 1.5); color: var(--weekly-task-color, var(--weekly-text)); + white-space: normal; + word-break: break-word; + overflow: visible; border: none; outline: none; background: transparent; @@ -1264,19 +1271,20 @@ h3 { .weekly-someday-list .weekly-task-item { border-bottom: 1px solid transparent; - height: 32px; - padding: 0 1rem; + min-height: 32px; + height: auto; + padding: 4px 1rem; display: flex; - align-items: center; + align-items: flex-start; } .weekly-someday-list .weekly-task-text { font-size: var(--base-font-size); - line-height: 32px; - max-height: 32px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; + line-height: 1.4; + word-break: break-word; + overflow: visible; + white-space: normal; + text-overflow: clip; padding: 0; } @@ -1607,6 +1615,8 @@ h3 { --weekly-settings-text: #ffffff; --weekly-settings-toggle-bg: #222222; --weekly-settings-toggle-active-bg: #333333; + --weekly-header-brightness: 1.5; + --weekly-goal-brightness: 1.5; --weekly-settings-toggle-text: #888888; --weekly-settings-toggle-active-text: #ffffff; } diff --git a/src/components/WeeklyView.tsx b/src/components/WeeklyView.tsx index 3c84773..504c045 100644 --- a/src/components/WeeklyView.tsx +++ b/src/components/WeeklyView.tsx @@ -39,6 +39,7 @@ import { Palette, Sparkles, Info, + Trash2, } from "lucide-react"; // Types @@ -418,6 +419,29 @@ function invertColor(hex: string): string { } } +// Helper to lighten color for dark mode +function adjustColorForDarkMode(hex: string, isDarkMode: boolean): string { + if (!isDarkMode || !hex || !hex.startsWith("#")) return hex; + + // Simple hex to RGB + let r = parseInt(hex.slice(1, 3), 16); + let g = parseInt(hex.slice(3, 5), 16); + let b = parseInt(hex.slice(5, 7), 16); + + // Calculate brightness (0-255) + const brightness = (r * 299 + g * 587 + b * 114) / 1000; + + // If it's too dark for dark mode, lighten it + if (brightness < 120) { + r = Math.min(255, r + 100); + g = Math.min(255, g + 100); + b = Math.min(255, b + 100); + return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; + } + + return hex; +} + // Main Component export default function WeeklyView() { const { data: session } = useSession(); @@ -558,6 +582,7 @@ export default function WeeklyView() { yearFontSize?: string; yearFontWeight?: string; yearColor?: string; + quoteSourceUrls?: string[]; }>({ name: session?.user?.name || "", email: session?.user?.email || "", @@ -609,6 +634,7 @@ export default function WeeklyView() { weekendColorSun: "#dc2626", pastDayColor: "#a6a6a7", quoteSourceUrl: "https://recite.vercel.app/api/random", + quoteSourceUrls: ["https://recite.vercel.app/api/random"], }); const [motivationalQuote, setMotivationalQuote] = useState(""); const [showSummary, setShowSummary] = useState(false); @@ -945,31 +971,42 @@ export default function WeeklyView() { const fetchMotivationalQuote = useCallback(async () => { if (profile.goalFallbackType !== "quote") return; - try { - const url = profile.quoteSourceUrl || "https://recite.vercel.app/api/random"; - const res = await fetch(url); - if (!res.ok) throw new Error("Failed to fetch quote"); - const data = await res.json(); - // Handle different JSON formats (array or object) - let quoteText = ""; - if (Array.isArray(data) && data.length > 0) { - const item = data[0]; - quoteText = item.quote || item.text || item.content || (typeof item === 'string' ? item : ""); - if (item.author) quoteText += ` - ${item.author}`; - } else if (data && typeof data === 'object') { - quoteText = data.quote || data.text || data.content || ""; - if (data.author) quoteText += ` - ${data.author}`; - } else if (typeof data === 'string') { - quoteText = data; + const urls = profile.quoteSourceUrls && profile.quoteSourceUrls.length > 0 + ? profile.quoteSourceUrls + : [profile.quoteSourceUrl || "https://recite.vercel.app/api/random"]; + + // Strategy: try sources until one works + for (const url of urls) { + try { + const res = await fetch(url); + if (!res.ok) continue; + const data = await res.json(); + + let quoteText = ""; + if (Array.isArray(data) && data.length > 0) { + const item = data[0]; + quoteText = item.quote || item.text || item.content || (typeof item === 'string' ? item : ""); + if (item.author) quoteText += ` - ${item.author}`; + } else if (data && typeof data === 'object') { + quoteText = data.quote || data.text || data.content || ""; + if (data.author) quoteText += ` - ${data.author}`; + } else if (typeof data === 'string') { + quoteText = data; + } + + if (quoteText) { + setMotivationalQuote(quoteText); + return; // Success! + } + } catch (error) { + console.error(`Error fetching quote from ${url}:`, error); } - - if (quoteText) setMotivationalQuote(quoteText); - } catch (error) { - console.error("Error fetching motivational quote:", error); - setMotivationalQuote("Stay focused and productive."); } - }, [profile.goalFallbackType, profile.quoteSourceUrl]); + + // Final fallback if all failed + setMotivationalQuote("Stay focused and productive."); + }, [profile.goalFallbackType, profile.quoteSourceUrl, profile.quoteSourceUrls]); // Fetch tasks on mount useEffect(() => { @@ -1457,15 +1494,17 @@ export default function WeeklyView() { const dateStr = formatDateToISO(date); // Use local date formatting return tasks .filter((task) => { - if (!task.scheduledDate) return false; - // Exclude sub-tasks from top-level list (they render inside their parent) - if (task.parentTaskId) return false; - // Use string comparison to avoid timezone shifts - const taskDateStr = - typeof task.scheduledDate === "string" - ? task.scheduledDate.substring(0, 10) - : formatDateToISO(new Date(task.scheduledDate)); - return taskDateStr === dateStr; + if (!task.completed && task.scheduledDate) { + // Exclude sub-tasks from top-level list (they render inside their parent) + if (task.parentTaskId) return false; + // Use string comparison to avoid timezone shifts + const taskDateStr = + typeof task.scheduledDate === "string" + ? task.scheduledDate.substring(0, 10) + : formatDateToISO(new Date(task.scheduledDate)); + return taskDateStr === dateStr; + } + return false; }) .sort((a, b) => { // Sort by time if available @@ -1798,6 +1837,9 @@ export default function WeeklyView() { const taskDuration = task.duration || 15; const taskEnd = taskStart + taskDuration; + // Skip completed tasks (they don't render in the grid) + if (task.completed) return false; + // Overlap condition: task starts before slot ends AND task ends after slot starts return taskStart < slotEnd && taskEnd > slotStart; }); @@ -3270,27 +3312,39 @@ export default function WeeklyView() { {/* CENTER SECTION: Week/Year, Goal, Focus Mode - Reveal on Hover */} -
+
{/* Week & Year */}
- {(isLoading || isSyncing || syncStatus === "syncing") && ( + {(isLoading || isSyncing || syncStatus === "syncing") ? (
+ ) : ( + )} KW {getWeekNumber(currentWeekStart).toString().padStart(2, "0")} | - + {currentWeekStart.getFullYear()}
@@ -3330,6 +3384,8 @@ export default function WeeklyView() { : undefined, fontSize: profile.goalFontSize || undefined, fontWeight: profile.goalFontWeight || undefined, + color: adjustColorForDarkMode((profile.goalFallbackType === "quote" ? profile.taskColor : undefined) || "#333333", darkMode), + filter: "brightness(var(--weekly-goal-brightness, 1))" }} > {showNextTask @@ -5640,7 +5696,15 @@ function TaskItem({ )} - {task.title} + + {task.title} +
@@ -6062,7 +6126,9 @@ interface SettingsSidebarProps { yearFontSize?: string; yearFontWeight?: string; yearColor?: string; + quoteSourceUrls: string[]; }) => void; + quoteSourceUrls?: string[]; goal: string; setGoal: (goal: string) => void; saveGoal: (goal: string) => void; @@ -6288,6 +6354,7 @@ function SettingsSidebar({ yearFontSize?: string; yearFontWeight?: string; yearColor?: string; + quoteSourceUrls?: string[]; }>({ name: "", email: "", @@ -8995,7 +9062,7 @@ function SettingsSidebar({ flex: 1, padding: "10px", borderRadius: "6px", - border: "none", + border: profile.goalScope === "week" ? "2px solid var(--weekly-teal)" : "none", background: profile.goalScope === "week" ? "var(--weekly-settings-toggle-active-bg)" @@ -9004,7 +9071,8 @@ function SettingsSidebar({ profile.goalScope === "week" ? "var(--weekly-settings-toggle-active-text)" : "var(--weekly-settings-text)", - fontWeight: 600, + fontWeight: 700, + boxShadow: profile.goalScope === "week" ? "0 2px 4px rgba(0,0,154,0.1)" : "none", cursor: "pointer", transition: "all 0.2s", }} @@ -9020,7 +9088,7 @@ function SettingsSidebar({ flex: 1, padding: "10px", borderRadius: "6px", - border: "none", + border: profile.goalScope === "day" ? "2px solid var(--weekly-teal)" : "none", background: profile.goalScope === "day" ? "var(--weekly-settings-toggle-active-bg)" @@ -9029,7 +9097,8 @@ function SettingsSidebar({ profile.goalScope === "day" ? "var(--weekly-settings-toggle-active-text)" : "var(--weekly-settings-text)", - fontWeight: 600, + fontWeight: 700, + boxShadow: profile.goalScope === "day" ? "0 2px 4px rgba(0,154,154,0.1)" : "none", cursor: "pointer", transition: "all 0.2s", }} @@ -9104,28 +9173,73 @@ function SettingsSidebar({ color: "var(--weekly-settings-label)", }} > - API-Datenquelle (URL) + API-Datenquellen (URLs) - - setProfile((p) => ({ - ...p, - quoteSourceUrl: e.target.value, - })) - } - className="weekly-input" - placeholder="https://recite.vercel.app/api/random" - style={{ - width: "100%", - padding: "10px", - fontSize: "0.95rem", - borderRadius: "6px", - border: "1px solid var(--weekly-border)", - background: "var(--weekly-bg)", - }} - /> +
+ {(profile.quoteSourceUrls || [profile.quoteSourceUrl || "https://recite.vercel.app/api/random"]).map((url: string, idx: number) => ( +
+ { + const newUrls = [...(profile.quoteSourceUrls || [profile.quoteSourceUrl || "https://recite.vercel.app/api/random"])]; + newUrls[idx] = e.target.value; + setProfile((p) => ({ ...p, quoteSourceUrls: newUrls })); + }} + className="weekly-input" + placeholder="https://..." + style={{ + flex: 1, + padding: "10px", + fontSize: "0.95rem", + borderRadius: "6px", + border: "1px solid var(--weekly-border)", + background: "var(--weekly-bg)", + }} + /> + +
+ ))} + +

URL that returns a JSON list or object of quotes (e.g. {'[{"quote":"...","author":"..."}]'} or {'{"quote":"..."}'}).

diff --git a/test-account-setup.js b/test-account-setup.js index 686094c..64b6111 100644 --- a/test-account-setup.js +++ b/test-account-setup.js @@ -4,18 +4,18 @@ * * Usage: * 1. Start the development server: npm run dev - * 2. Visit http://localhost:3000/auth/signup in your browser + * 2. Visit http://localhost:3001/auth/signup in your browser * 3. Fill in the form with: * - Email: mail@martin-bierschenk.de * - Password: My-Weekly-ToDo-List * 4. Or use the following curl command: * - * curl -X POST http://localhost:3000/api/auth/signup \ + * curl -X POST http://localhost:3001/api/auth/signup \ * -H "Content-Type: application/json" \ * -d '{"email":"mail@martin-bierschenk.de","password":"My-Weekly-ToDo-List"}' * * For login after signup: - * curl -X POST http://localhost:3000/api/auth/login \ + * curl -X POST http://localhost:3001/api/auth/login \ * -H "Content-Type: application/json" \ * -d '{"email":"mail@martin-bierschenk.de","password":"My-Weekly-ToDo-List"}' */ @@ -32,18 +32,18 @@ console.log('Email: mail@martin-bierschenk.de'); console.log('Password: My-Weekly-ToDo-List'); console.log(''); console.log('Method 1: Using Browser Interface'); -console.log('1. Navigate to http://localhost:3000/auth/signup'); +console.log('1. Navigate to http://localhost:3001/auth/signup'); console.log('2. Fill in the form with the credentials above'); console.log('3. Submit the form'); console.log(''); console.log('Method 2: Using curl commands (in terminal)'); console.log('First, create the account:'); -console.log('curl -X POST http://localhost:3000/api/auth/signup \\'); +console.log('curl -X POST http://localhost:3001/api/auth/signup \\'); console.log(' -H "Content-Type: application/json" \\'); console.log(' -d \'{"email":"mail@martin-bierschenk.de","password":"My-Weekly-ToDo-List"}\''); console.log(''); console.log('Then, authenticate with the account:'); -console.log('curl -X POST http://localhost:3000/api/auth/login \\'); +console.log('curl -X POST http://localhost:3001/api/auth/login \\'); console.log(' -H "Content-Type: application/json" \\'); console.log(' -d \'{"email":"mail@martin-bierschenk.de","password":"My-Weekly-ToDo-List"}\''); console.log(''); diff --git a/test-api.js b/test-api.js index f8e396b..6071d2c 100644 --- a/test-api.js +++ b/test-api.js @@ -5,9 +5,9 @@ const fetch = require('node-fetch'); async function testSignupApi() { console.log('Testing signup API endpoint...'); - + try { - const response = await fetch('http://localhost:3000/api/auth/signup', { + const response = await fetch('http://localhost:3001/api/auth/signup', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -17,13 +17,13 @@ async function testSignupApi() { password: 'testpassword' }) }); - + console.log('Status:', response.status); console.log('Headers:', response.headers.raw()); - + const contentType = response.headers.get('content-type'); console.log('Content-Type:', contentType); - + if (contentType && contentType.includes('application/json')) { const data = await response.json(); console.log('JSON Response:', data); @@ -32,7 +32,7 @@ async function testSignupApi() { console.log('Non-JSON Response (likely HTML):'); console.log(text.substring(0, 500) + '...'); } - + } catch (error) { console.error('Error testing API:', error); } diff --git a/tests/auth-flow-test.spec.ts b/tests/auth-flow-test.spec.ts index e44e414..2300a42 100644 --- a/tests/auth-flow-test.spec.ts +++ b/tests/auth-flow-test.spec.ts @@ -3,11 +3,11 @@ import { test, expect } from '@playwright/test'; // Test authentication flow against your running application test.describe('Authentication Flow Tests', () => { test('should access login page', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Check that we're on the login page await expect(page).toHaveTitle(/Login/); - + // Check for login form elements await expect(page.getByText('Login')).toBeVisible(); await expect(page.getByPlaceholder('Email')).toBeVisible(); @@ -16,11 +16,11 @@ test.describe('Authentication Flow Tests', () => { }); test('should access signup page', async ({ page }) => { - await page.goto('http://localhost:3000/auth/signup'); - + await page.goto('http://localhost:3001/auth/signup'); + // Check that we're on the signup page await expect(page).toHaveTitle(/Sign Up/); - + // Check for signup form elements await expect(page.getByText('Sign Up')).toBeVisible(); await expect(page.getByPlaceholder('Email')).toBeVisible(); diff --git a/tests/css-debug-test.spec.ts b/tests/css-debug-test.spec.ts index bfd9950..3a4f446 100644 --- a/tests/css-debug-test.spec.ts +++ b/tests/css-debug-test.spec.ts @@ -3,16 +3,16 @@ import { test, expect } from '@playwright/test'; // Test to verify CSS styling is working in your application test.describe('CSS Debugging Tests', () => { test('should load with proper CSS styling', async ({ page }) => { - await page.goto('http://localhost:3000'); - + await page.goto('http://localhost:3001'); + // Verify main page elements have proper styling const mainHeader = page.getByText('My Weekly To Do List'); await expect(mainHeader).toBeVisible(); - + // Check for CSS classes that indicate styling is applied const taskItem = page.locator('.task-item'); await expect(taskItem).toBeVisible(); - + // Verify basic CSS properties are applied const taskItemStyles = await taskItem.evaluate(element => { const computedStyle = window.getComputedStyle(element); @@ -22,18 +22,18 @@ test.describe('CSS Debugging Tests', () => { boxShadow: computedStyle.boxShadow }; }); - + console.log('Task item CSS properties:', taskItemStyles); expect(taskItemStyles.backgroundColor).toBeTruthy(); }); test('should have proper auth form styling', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Check that auth form has proper styling const authForm = page.locator('.auth-form'); await expect(authForm).toBeVisible(); - + // Verify CSS properties on auth form const formStyles = await authForm.evaluate(element => { const computedStyle = window.getComputedStyle(element); @@ -43,7 +43,7 @@ test.describe('CSS Debugging Tests', () => { boxShadow: computedStyle.boxShadow }; }); - + console.log('Auth form CSS properties:', formStyles); expect(formStyles.backgroundColor).toBeTruthy(); }); diff --git a/tests/local-deployment-test.spec.ts b/tests/local-deployment-test.spec.ts index 2dec03d..7f38fdc 100644 --- a/tests/local-deployment-test.spec.ts +++ b/tests/local-deployment-test.spec.ts @@ -3,19 +3,19 @@ import { test, expect } from '@playwright/test'; // Simple test to verify the app is running and accessible test('My Weekly To Do List should be accessible', async ({ page }) => { // Navigate to the running application - await page.goto('http://localhost:3000'); - + await page.goto('http://localhost:3001'); + // Check that the main elements are present await expect(page).toHaveTitle(/My Weekly To Do List/); - + // Check for main UI elements await expect(page.getByText('My Weekly To Do List')).toBeVisible(); await expect(page.getByText('Weekly View')).toBeVisible(); }); test('Should be able to access tasks page', async ({ page }) => { - await page.goto('http://localhost:3000'); - + await page.goto('http://localhost:3001'); + // Try to navigate to tasks page const tasksLink = page.getByText('Tasks'); await expect(tasksLink).toBeVisible(); diff --git a/tests/login-basic-debug.spec.ts b/tests/login-basic-debug.spec.ts index f865774..33c0d36 100644 --- a/tests/login-basic-debug.spec.ts +++ b/tests/login-basic-debug.spec.ts @@ -2,50 +2,50 @@ import { test, expect } from '@playwright/test'; test.describe('Login Page Core Functionality', () => { test('should load the login page with core elements', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Basic page verification await expect(page).toHaveURL(/login/); await expect(page).toHaveTitle(/Weekly To Do List/); - + // Check that the page has essential structure const h1Element = page.locator('h1'); await expect(h1Element).toBeVisible(); - + // Check for a login-related paragraph const loginParagraph = page.getByText(/Sign in to your account|Welcome back!/); await expect(loginParagraph).toBeVisible(); - + // Check for form elements const emailInput = page.locator('input[name="email"]'); const passwordInput = page.locator('input[name="password"]'); const submitButton = page.locator('button[type="submit"]'); - + await expect(emailInput).toBeVisible(); await expect(passwordInput).toBeVisible(); await expect(submitButton).toBeVisible(); - + // Check for links const signupLink = page.getByRole('link', { name: /Don't have an account|Create account/ }); const forgotPasswordLink = page.getByRole('link', { name: /Forgot your password/ }); - + await expect(signupLink).toBeVisible(); await expect(forgotPasswordLink).toBeVisible(); - + console.log('✅ Login page loaded successfully with core elements'); }); test('should have basic CSS structure', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Check that the page has basic styling structure const body = page.locator('body'); await expect(body).toBeVisible(); - + // Check for main wrapper div that should have Tailwind classes const mainWrapper = page.locator('[class*="min-h-screen"]'); await expect(mainWrapper).toBeVisible(); - + console.log('✅ Basic CSS structure verified'); }); }); \ No newline at end of file diff --git a/tests/login-debug-simplified.spec.ts b/tests/login-debug-simplified.spec.ts index 851f546..c7dd539 100644 --- a/tests/login-debug-simplified.spec.ts +++ b/tests/login-debug-simplified.spec.ts @@ -2,37 +2,37 @@ import { test, expect } from '@playwright/test'; test.describe('Login Page Debug - Simplified', () => { test('should load login page successfully', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Basic page load verification await expect(page).toHaveURL(/login/); await expect(page).toHaveTitle(/Weekly To Do List/); - + // Check for key elements that should be present await expect(page.locator('h1')).toContainText('Weekly To Do List'); await expect(page.locator('p')).toContainText('Sign in to your account'); - + // Check for form elements await expect(page.locator('input[name="email"]')).toBeVisible(); await expect(page.locator('input[name="password"]')).toBeVisible(); await expect(page.locator('button[type="submit"]')).toBeVisible(); - + // Check for navigation links await expect(page.getByRole('link', { name: 'Don\'t have an account?' })).toBeVisible(); await expect(page.getByRole('link', { name: 'Forgot your password?' })).toBeVisible(); }); test('should have Tailwind CSS classes in structure', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Check main container classes const container = page.locator('div.min-h-screen.bg-gray-50'); await expect(container).toBeVisible(); - + // Check the form container const formContainer = page.locator('div.bg-white.rounded-xl.shadow-lg.p-8'); await expect(formContainer).toBeVisible(); - + // Check for basic Tailwind classes that should be present const bodyClass = await page.evaluate(() => document.body.className); expect(bodyClass).toContain('min-h-screen'); @@ -40,19 +40,19 @@ test.describe('Login Page Debug - Simplified', () => { }); test('should have functional form elements', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Test that the form exists const form = page.locator('form'); await expect(form).toBeVisible(); - + // Test that we can interact with inputs const emailInput = page.locator('input[name="email"]'); const passwordInput = page.locator('input[name="password"]'); - + await expect(emailInput).toBeVisible(); await expect(passwordInput).toBeVisible(); - + // Test that submit button exists const submitButton = page.locator('button[type="submit"]'); await expect(submitButton).toBeVisible(); diff --git a/tests/login-debug.spec.ts b/tests/login-debug.spec.ts index 6c7216c..8ffb99c 100644 --- a/tests/login-debug.spec.ts +++ b/tests/login-debug.spec.ts @@ -2,50 +2,50 @@ import { test, expect } from '@playwright/test'; test.describe('Login Page Debug Test', () => { test('should load the login page correctly', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Check page title and heading await expect(page).toHaveTitle(/Weekly To Do List/); await expect(page.locator('h1')).toContainText('Weekly To Do List'); await expect(page.locator('p:has-text("Sign in to your account")')).toBeVisible(); - + // Check for Tailwind CSS classes in main container const mainContainer = page.locator('div.min-h-screen.bg-gray-50'); await expect(mainContainer).toBeVisible(); - + // Check for form elements const form = page.locator('form'); await expect(form).toBeVisible(); - + // Check for email input field const emailInput = page.locator('#email-address'); await expect(emailInput).toBeVisible(); await expect(emailInput).toHaveAttribute('type', 'email'); await expect(emailInput).toHaveAttribute('placeholder', 'Email address'); - + // Check for password input field const passwordInput = page.locator('#password'); await expect(passwordInput).toBeVisible(); await expect(passwordInput).toHaveAttribute('type', 'password'); await expect(passwordInput).toHaveAttribute('placeholder', 'Password'); - + // Check for submit button const submitButton = page.locator('button[type="submit"]'); await expect(submitButton).toBeVisible(); await expect(submitButton).toContainText('Sign in'); - + // Check for links await expect(page.locator('a:has-text("Don\'t have an account?")')).toBeVisible(); await expect(page.locator('a:has-text("Forgot your password?")')).toBeVisible(); - + // Check for Tailwind classes in form container const formContainer = page.locator('div.max-w-md.w-full.space-y-8.bg-white.rounded-xl.shadow-lg.p-8'); await expect(formContainer).toBeVisible(); }); test('should have Tailwind CSS applied correctly', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Check for various Tailwind utility classes const expectedClasses = [ 'min-h-screen', @@ -174,21 +174,21 @@ test.describe('Login Page Debug Test', () => { 'text-sm', 'text-gray-600' ]; - + // Check that the page contains at least one element with Tailwind class const bodyElement = page.locator('body'); const bodyClasses = await bodyElement.getAttribute('class'); expect(bodyClasses).toBeTruthy(); - + // Check for some specific Tailwind classes that should be present expect(bodyClasses).toContain('min-h-screen'); expect(bodyClasses).toContain('bg-gray-50'); - + // Check that the form has appropriate Tailwind classes const formElement = page.locator('form'); const formClasses = await formElement.getAttribute('class'); expect(formClasses).toContain('space-y-6'); - + // Check that inputs have Tailwind classes const inputElement = page.locator('#email-address'); const inputClasses = await inputElement.getAttribute('class'); @@ -198,33 +198,33 @@ test.describe('Login Page Debug Test', () => { }); test('should validate form submission with empty fields', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Try submitting with empty fields await page.click('button[type="submit"]'); - + // Should still be on the same page and show validation error - await expect(page).toHaveURL('http://localhost:3000/auth/login'); + await expect(page).toHaveURL('http://localhost:3001/auth/login'); }); test('should have working password visibility toggle', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + const passwordInput = page.locator('#password'); const toggleButton = page.locator('button[aria-label*="toggle"]'); - + // Initially should be password type await expect(passwordInput).toHaveAttribute('type', 'password'); - + // Click toggle button await toggleButton.click(); - + // Should switch to text type await expect(passwordInput).toHaveAttribute('type', 'text'); - + // Click again to toggle back await toggleButton.click(); - + // Should switch back to password type await expect(passwordInput).toHaveAttribute('type', 'password'); }); diff --git a/tests/login-simple-debug.spec.ts b/tests/login-simple-debug.spec.ts index 1f756ea..1a82010 100644 --- a/tests/login-simple-debug.spec.ts +++ b/tests/login-simple-debug.spec.ts @@ -2,51 +2,51 @@ import { test, expect } from '@playwright/test'; test.describe('Login Page Structure and Tailwind Debug', () => { test('should display login page with proper structure', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Verify main layout structure await expect(page.locator('div.min-h-screen.bg-gray-50')).toBeVisible(); await expect(page.locator('div.flex.flex-col.items-center.justify-center.min-h-screen.py-12.px-4.sm:px-6.lg:px-8')).toBeVisible(); - + // Check header elements await expect(page.locator('h1.text-3xl.font-bold.text-gray-900')).toBeVisible(); await expect(page.locator('p.mt-2.text-gray-600')).toContainText('Sign in to your account'); - + // Check form structure await expect(page.locator('form')).toBeVisible(); await expect(page.locator('div.max-w-md.w-full.space-y-8.bg-white.rounded-xl.shadow-lg.p-8')).toBeVisible(); - + // Check input fields await expect(page.locator('#email-address')).toBeVisible(); await expect(page.locator('#password')).toBeVisible(); - + // Check for submit button await expect(page.locator('button[type="submit"]')).toBeVisible(); - + // Check links await expect(page.locator('a[href="/auth/signup"]')).toBeVisible(); await expect(page.locator('a[href="/auth/forgot-password"]')).toBeVisible(); }); test('should have proper Tailwind CSS classes applied', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Test that the page uses Tailwind utility classes appropriately const pageBody = await page.evaluate(() => { const body = document.querySelector('body'); return body?.className || ''; }); - + // Check for essential Tailwind classes expect(pageBody).toContain('min-h-screen'); expect(pageBody).toContain('bg-gray-50'); - + // Check form container classes const formContainer = await page.evaluate(() => { const container = document.querySelector('.max-w-md.w-full.space-y-8.bg-white.rounded-xl.shadow-lg.p-8'); return container?.className || ''; }); - + expect(formContainer).toContain('max-w-md'); expect(formContainer).toContain('bg-white'); expect(formContainer).toContain('rounded-xl'); @@ -55,23 +55,23 @@ test.describe('Login Page Structure and Tailwind Debug', () => { }); test('should have interactive elements working', async ({ page }) => { - await page.goto('http://localhost:3000/auth/login'); - + await page.goto('http://localhost:3001/auth/login'); + // Test password visibility toggle const passwordField = page.locator('#password'); const toggleButton = page.locator('button:has-text("Toggle password visibility")'); - + // Check initial state const initialType = await passwordField.getAttribute('type'); expect(initialType).toBe('password'); - + // If toggle exists, test it if (await toggleButton.isVisible()) { await toggleButton.click(); - + const toggledType = await passwordField.getAttribute('type'); expect(toggledType).toBe('text'); - + await toggleButton.click(); const finalType = await passwordField.getAttribute('type'); expect(finalType).toBe('password');