diff --git a/pickleglass_web/README.md b/pickleglass_web/README.md
new file mode 100644
index 00000000..c418be82
--- /dev/null
+++ b/pickleglass_web/README.md
@@ -0,0 +1,59 @@
+# PickleGlass Web
+
+PickleGlass is a personalized AI assistant platform designed to help users in various contexts such as learning, meetings, sales, recruiting, and customer support. This is the Next.js frontend for PickleGlass, providing a modern, responsive, and accessible user interface.
+
+## Project Overview
+
+- **Framework:** Next.js (React, TypeScript)
+- **Styling:** Tailwind CSS
+- **State & Data:** Firebase, REST API, Local Storage
+- **UI Components:** Custom and Lucide React icons
+
+## Main Features
+
+- **Personalized AI Contexts:**
+ - Choose or create custom AI assistant presets for different scenarios (school, meetings, sales, recruiting, customer support, etc.).
+- **User Authentication:**
+ - Supports both Firebase (Google sign-in) and local mode for privacy.
+- **Activity Tracking:**
+ - View, manage, and delete your past AI sessions and conversations.
+- **Settings & Personalization:**
+ - Manage your profile, privacy settings, billing (coming soon), and API keys.
+- **Search:**
+ - Quickly search through your conversations and activities.
+- **Responsive Sidebar Navigation:**
+ - Collapsible sidebar with quick access to main sections and external resources.
+- **Download Center:**
+ - Download PickleGlass for desktop, mobile, or tablet platforms.
+- **Help Center:**
+ - Access guides and support resources.
+
+## Recent Improvements
+
+- **Reusable Loading Spinner:**
+ - All loading states now use a consistent, accessible `` component for better UX and maintainability.
+- **UI Consistency:**
+ - Loading spinners and empty states are now visually unified across all pages.
+- **Code Maintainability:**
+ - Reduced code duplication and improved readability by refactoring repeated UI patterns.
+
+## Getting Started
+
+1. **Install dependencies:**
+ ```bash
+ npm install
+ ```
+2. **Run the development server:**
+ ```bash
+ npm run dev
+ ```
+3. **Open your browser:**
+ Visit [http://localhost:3000](http://localhost:3000)
+
+## Contributing
+
+Contributions are welcome! Please fork the repository, create a feature branch, and submit a pull request. For major changes, open an issue first to discuss your ideas.
+
+## License
+
+This project is licensed under the MIT License.
diff --git a/pickleglass_web/app/activity/details/page.tsx b/pickleglass_web/app/activity/details/page.tsx
index 78b022cf..e00c6989 100644
--- a/pickleglass_web/app/activity/details/page.tsx
+++ b/pickleglass_web/app/activity/details/page.tsx
@@ -1,201 +1,208 @@
-'use client'
-
-import { useState, useEffect, Suspense } from 'react'
-import { useRedirectIfNotAuth } from '@/utils/auth'
-import { useSearchParams, useRouter } from 'next/navigation'
-import Link from 'next/link'
-import {
- UserProfile,
- SessionDetails,
- Transcript,
- AiMessage,
- getSessionDetails,
- deleteSession,
-} from '@/utils/api'
+'use client';
+
+import { useState, useEffect, Suspense } from 'react';
+import { useRedirectIfNotAuth } from '@/utils/auth';
+import { useSearchParams, useRouter } from 'next/navigation';
+import Link from 'next/link';
+import { UserProfile, SessionDetails, Transcript, AiMessage, getSessionDetails, deleteSession } from '@/utils/api';
+import LoadingSpinner from '@/components/LoadingSpinner';
type ConversationItem = (Transcript & { type: 'transcript' }) | (AiMessage & { type: 'ai_message' });
-const Section = ({ title, children }: { title: string, children: React.ReactNode }) => (
+const Section = ({ title, children }: { title: string; children: React.ReactNode }) => (
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/pickleglass_web/app/help/page.tsx b/pickleglass_web/app/help/page.tsx
index 5bc91d17..ce532b4d 100644
--- a/pickleglass_web/app/help/page.tsx
+++ b/pickleglass_web/app/help/page.tsx
@@ -1,110 +1,89 @@
-'use client'
+'use client';
-import { HelpCircle, Book, MessageCircle, Mail } from 'lucide-react'
-import { useRedirectIfNotAuth } from '@/utils/auth'
+import { HelpCircle, Book, MessageCircle, Mail } from 'lucide-react';
+import { useRedirectIfNotAuth } from '@/utils/auth';
+import LoadingSpinner from '@/components/LoadingSpinner';
export default function HelpPage() {
- const userInfo = useRedirectIfNotAuth()
+ const userInfo = useRedirectIfNotAuth();
+
+ if (!userInfo) {
+ return (
+
+
+
+ );
+ }
- if (!userInfo) {
return (
-
-
-
-
Loading...
-
-
- )
- }
+
+
+
Help Center
- return (
-
-
-
Help Center
-
-
-
-
-
-
Getting Started
-
-
- New to pickleglass? Learn about basic features and setup methods.
-
-
-
• Setting up personalized contexts
-
• Selecting presets and creating custom contexts
-
• Checking activity records
-
• Changing settings
-
-
+
+
+
+
+
Getting Started
+
+
New to pickleglass? Learn about basic features and setup methods.
+
+
• Setting up personalized contexts
+
• Selecting presets and creating custom contexts
+
• Checking activity records
+
• Changing settings
+
+
-
-
-
-
Frequently Asked Questions
-
-
- Check out frequently asked questions and answers from other users.
-
-
-
-
- How do I change the context?
-
-
- On the Personalize page, select a preset or enter a custom context, then click the Save button.
-
-
-
-
- Where can I check my activity history?
-
-
- You can check your past activity records on the My Activity page.
-
-
-
-
-
+
+
+
+
Frequently Asked Questions
+
+
Check out frequently asked questions and answers from other users.
+
+
+ How do I change the context?
+
+ On the Personalize page, select a preset or enter a custom context, then click the Save button.
+
+
+
+ Where can I check my activity history?
+
You can check your past activity records on the My Activity page.
+
+
+
+
-
-
-
-
-
Community
-
-
- Connect with other users and share tips.
-
-
-
+
+
+
+
+
Community
+
+
Connect with other users and share tips.
+
+
-
-
-
-
Contact Us
-
-
- Couldn't find a solution? Contact us directly.
-
-
-
-
+
+
+
+
Contact Us
+
+
Couldn't find a solution? Contact us directly.
+
+
+
-
-
💡 Tip
-
- Each context is optimized for different situations.
- Choose the appropriate preset for your work environment,
- or create your own custom context!
-
+
+
💡 Tip
+
+ Each context is optimized for different situations. Choose the appropriate preset for your work environment, or create your
+ own custom context!
+
+
+
-
-
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/pickleglass_web/app/login/page.tsx b/pickleglass_web/app/login/page.tsx
index b9148fdd..3df6dcd0 100644
--- a/pickleglass_web/app/login/page.tsx
+++ b/pickleglass_web/app/login/page.tsx
@@ -1,133 +1,140 @@
-'use client'
+'use client';
-import { useRouter } from 'next/navigation'
-import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth'
-import { auth } from '@/utils/firebase'
-import { Chrome } from 'lucide-react'
-import { useState, useEffect } from 'react'
+import { useRouter } from 'next/navigation';
+import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
+import { auth } from '@/utils/firebase';
+import { Chrome } from 'lucide-react';
+import { useState, useEffect } from 'react';
+import LoadingSpinner from '@/components/LoadingSpinner';
export default function LoginPage() {
- const router = useRouter()
- const [isLoading, setIsLoading] = useState(false)
- const [isElectronMode, setIsElectronMode] = useState(false)
-
- useEffect(() => {
- const urlParams = new URLSearchParams(window.location.search)
- const mode = urlParams.get('mode')
- setIsElectronMode(mode === 'electron')
- }, [])
-
- const handleGoogleSignIn = async () => {
- const provider = new GoogleAuthProvider()
- setIsLoading(true)
-
- try {
- const result = await signInWithPopup(auth, provider)
- const user = result.user
-
- if (user) {
- console.log('✅ Google login successful:', user.uid)
-
- if (isElectronMode) {
- try {
- const idToken = await user.getIdToken()
-
- const deepLinkUrl = `pickleglass://auth-success?` + new URLSearchParams({
- uid: user.uid,
- email: user.email || '',
- displayName: user.displayName || '',
- token: idToken
- }).toString()
-
- console.log('🔗 Return to electron app via deep link:', deepLinkUrl)
-
- window.location.href = deepLinkUrl
-
- // Maybe we don't need this
+ const router = useRouter();
+ const [isLoading, setIsLoading] = useState(false);
+ const [isElectronMode, setIsElectronMode] = useState(false);
+
+ useEffect(() => {
+ const urlParams = new URLSearchParams(window.location.search);
+ const mode = urlParams.get('mode');
+ setIsElectronMode(mode === 'electron');
+ }, []);
+
+ const handleGoogleSignIn = async () => {
+ const provider = new GoogleAuthProvider();
+ setIsLoading(true);
+
+ try {
+ const result = await signInWithPopup(auth, provider);
+ const user = result.user;
+
+ if (user) {
+ console.log('✅ Google login successful:', user.uid);
+
+ if (isElectronMode) {
+ try {
+ const idToken = await user.getIdToken();
+
+ const deepLinkUrl =
+ `pickleglass://auth-success?` +
+ new URLSearchParams({
+ uid: user.uid,
+ email: user.email || '',
+ displayName: user.displayName || '',
+ token: idToken,
+ }).toString();
+
+ console.log('🔗 Return to electron app via deep link:', deepLinkUrl);
+
+ window.location.href = deepLinkUrl;
+
+ // Maybe we don't need this
// setTimeout(() => {
// alert('Login completed. Please return to Pickle Glass app.')
// }, 1000)
-
- } catch (error) {
- console.error('❌ Deep link processing failed:', error)
- alert('Login was successful but failed to return to app. Please check the app.')
- }
- }
- else if (typeof window !== 'undefined' && window.require) {
- try {
- const { ipcRenderer } = window.require('electron')
- const idToken = await user.getIdToken()
-
- ipcRenderer.send('firebase-auth-success', {
- uid: user.uid,
- displayName: user.displayName,
- email: user.email,
- idToken
- })
-
- console.log('📡 Auth info sent to electron successfully')
- } catch (error) {
- console.error('❌ Electron communication failed:', error)
- }
- }
- else {
- router.push('/settings')
- }
- }
- } catch (error: any) {
- console.error('❌ Google login failed:', error)
-
- if (error.code !== 'auth/popup-closed-by-user') {
- alert('An error occurred during login. Please try again.')
- }
- } finally {
- setIsLoading(false)
- }
- }
-
- return (
-
-
-
Welcome to Pickle Glass
-
Sign in with your Google account to sync your data across all devices.
- {isFirebaseMode
- ? 'All data is safely stored and synchronized in Firebase Cloud.'
- : 'Data is stored in local database and you can use personal API keys.'
- }
-
-
-
-
-
-
-
-
-
-
-
-
Free
-
- $0/month
-
-
-
-
- Experience how Pickle Glass works with unlimited responses.
-
-
-
-
-
- Daily unlimited responses
-
-
-
- Unlimited access to free models
-
-
-
- Unlimited text output
-
-
-
- Screen viewing, audio listening
-
-
-
- Custom system prompts
-
-
-
- Community support only
-
-
-
-
-
+ const isFirebaseMode = mode === 'firebase';
-
-
-
Pro
-
- $25/month
-
-
-
-
- Use latest models, get full response output, and work with custom prompts.
-
- Specially crafted for teams that need complete customization.
-
-
-
-
-
- Custom integrations
-
-
-
- User provisioning & role-based access
-
-
-
- Advanced post-call analytics
-
-
-
- Single sign-on
-
-
-
- Advanced security features
-
-
-
- Centralized billing
-
-
-
- Usage analytics & reporting dashboard
-
-
-
-
-
-
-
-
-
-
-
-
All features are currently free!
-
- {isFirebaseMode
- ? 'Enjoy all Pickle Glass features for free in Firebase hosting mode. Pro and Enterprise plans will be released soon with additional premium features.'
- : 'Enjoy all Pickle Glass features for free in local mode. You can use personal API keys or continue using the free system.'
- }
-
- {isFirebaseMode
- ? `Logged in with Google account (${userInfo.email})`
- : 'Running as local user'
- }
-
-
- {isFirebaseMode && (
-
- )}
-
+
+ {isFirebaseMode
+ ? 'All data is safely stored and synchronized in Firebase Cloud.'
+ : 'Data is stored in local database and you can use personal API keys.'}
+
-
-
Display Name
-
Enter your full name or a display name you're comfortable using.
- {isFirebaseMode
- ? 'Permanently remove your Firebase account and all content. This action cannot be undone, so please proceed carefully.'
- : 'Permanently remove your personal account and all content from the Pickle Glass platform. This action cannot be undone, so please proceed carefully.'
- }
-
Specially crafted for teams that need complete customization.
+
+
+
+
+ Custom integrations
+
+
+
+ User provisioning & role-based access
+
+
+
+ Advanced post-call analytics
+
+
+
+ Single sign-on
+
+
+
+ Advanced security features
+
+
+
+ Centralized billing
+
+
+
+ Usage analytics & reporting dashboard
+
+
+
+
+
+
+
+
+
+
+
+
All features are currently free!
+
+ {isFirebaseMode
+ ? 'Enjoy all Pickle Glass features for free in Firebase hosting mode. Pro and Enterprise plans will be released soon with additional premium features.'
+ : 'Enjoy all Pickle Glass features for free in local mode. You can use personal API keys or continue using the free system.'}
+
+ {isFirebaseMode
+ ? 'Permanently remove your Firebase account and all content. This action cannot be undone, so please proceed carefully.'
+ : 'Permanently remove your personal account and all content from the Pickle Glass platform. This action cannot be undone, so please proceed carefully.'}
+