From f661f909925e74ed904524f3c0b822eb756e497d Mon Sep 17 00:00:00 2001 From: xavidop Date: Wed, 4 Jun 2025 16:08:46 +0200 Subject: [PATCH] feat: updating some fixes --- README.md | 176 +++++++++++++++++- src/ai/flows/scan-pokemon-card.ts | 6 +- .../collection/[cardId]/edit/page.tsx | 16 +- 3 files changed, 188 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index cc730c7..3dca50a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,175 @@ -# Firebase Studio +# Cardex - PokΓ©mon Card Collection Manager -This is a NextJS starter in Firebase Studio. +A modern web application for managing your PokΓ©mon card collection with AI-powered card scanning and identification. -To get started, take a look at src/app/page.tsx. +## Features + +- πŸ€– **AI Card Scanning**: Use Gemini Vision to automatically identify PokΓ©mon cards from photos +- πŸ” **Secure Authentication**: Firebase Authentication with email and Google login +- πŸ“± **Responsive Design**: Modern UI built with Next.js and Tailwind CSS +- πŸ’Ύ **Cloud Storage**: Real-time data synchronization with Firestore +- ✏️ **CRUD Operations**: Create, read, update, and delete cards in your collection +- 🎯 **Type Safety**: Full TypeScript implementation for better development experience + +## Technology Stack + +```mermaid +graph TB + subgraph "Frontend" + A[Next.js 15] --> B[React 18] + B --> C[TypeScript] + C --> D[Tailwind CSS] + D --> E[Radix UI Components] + B --> F[React Hook Form] + F --> G[Zod Validation] + end + + subgraph "Backend Services" + H[Firebase Auth] --> I[Google Authentication] + J[Firestore Database] --> K[Real-time Updates] + L[Gemini AI] --> M[Vision API] + M --> N[Card Recognition] + end + + subgraph "Development Tools" + O[Genkit AI Framework] --> P[AI Flow Management] + Q[ESLint] --> R[Code Quality] + S[PostCSS] --> T[CSS Processing] + end + + A --> H + A --> J + A --> L + O --> L +``` + +## Project Structure + +``` +src/ +β”œβ”€β”€ ai/ +β”‚ └── flows/ +β”‚ └── scan-pokemon-card.ts # AI card scanning logic +β”œβ”€β”€ app/ +β”‚ β”œβ”€β”€ dashboard/ +β”‚ β”‚ β”œβ”€β”€ collection/ # Card collection pages +β”‚ β”‚ └── scan/ # Card scanning page +β”‚ └── page.tsx # Root page with auth routing +β”œβ”€β”€ components/ +β”‚ β”œβ”€β”€ cards/ +β”‚ β”‚ β”œβ”€β”€ CardForm.tsx # Reusable card form +β”‚ β”‚ β”œβ”€β”€ CardItem.tsx # Individual card display +β”‚ β”‚ └── CardScanner.tsx # AI scanning component +β”‚ └── ui/ # Reusable UI components +β”œβ”€β”€ hooks/ +β”‚ └── useAuth.tsx # Authentication hook +β”œβ”€β”€ lib/ +β”‚ β”œβ”€β”€ firebase.ts # Firebase configuration +β”‚ └── firestore.ts # Database operations +└── types/ + └── index.ts # TypeScript type definitions +``` + +## Getting Started + +### Prerequisites + +- Node.js 18+ +- Firebase project with Firestore and Authentication enabled +- Google AI API key for Gemini Vision + +### Installation + +1. Clone the repository: +```bash +git clone +cd cardex +``` + +2. Install dependencies: +```bash +npm install +``` + +3. Set up environment variables: +```bash +# Create .env.local file with: +NEXT_PUBLIC_FIREBASE_API_KEY=your_firebase_api_key +NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com +NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id +NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.com +NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id +NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id +GOOGLE_GENAI_API_KEY=your_gemini_api_key +``` + +4. Run the development server: +```bash +npm run dev +``` + +5. (Optional) Start the Genkit AI development server: +```bash +npm run genkit:dev +``` + +## Usage + +### Card Scanning +1. Navigate to the scan page +2. Upload a photo of your PokΓ©mon card +3. Click "Scan Card" to identify the card using AI +4. Review and edit the detected information +5. Save the card to your collection + +### Collection Management +- View all your cards in a responsive grid layout +- Edit card details by clicking on any card +- Delete cards from your collection +- Cards are automatically sorted by last updated date + +## Database Structure + +``` +Firestore Collection: users/{userId}/pokemon_cards/{cardId} +β”œβ”€β”€ name: string +β”œβ”€β”€ set: string +β”œβ”€β”€ rarity: string +β”œβ”€β”€ imageDataUrl: string (base64 encoded image) +β”œβ”€β”€ userId: string +β”œβ”€β”€ createdAt: timestamp +└── updatedAt: timestamp +``` + +## AI Integration + +The app uses Google's Gemini Vision API through the Genkit framework to: +- Analyze uploaded card images +- Extract card name, set, and rarity information +- Provide structured data for user review and confirmation + +## Contributing + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +## Scripts + +- `npm run dev` - Start development server with Turbopack +- `npm run build` - Build for production +- `npm run start` - Start production server +- `npm run lint` - Run ESLint +- `npm run typecheck` - Run TypeScript type checking +- `npm run genkit:dev` - Start Genkit AI development server +- `npm run genkit:watch` - Start Genkit with file watching + +## License + +This project is private and proprietary. + +## Support + +For support and questions, please open an issue in the repository. diff --git a/src/ai/flows/scan-pokemon-card.ts b/src/ai/flows/scan-pokemon-card.ts index 06f27f9..cb1bcb5 100644 --- a/src/ai/flows/scan-pokemon-card.ts +++ b/src/ai/flows/scan-pokemon-card.ts @@ -24,7 +24,7 @@ export type ScanPokemonCardInput = z.infer; const ScanPokemonCardOutputSchema = z.object({ cardDetails: z.object({ name: z.string().describe("The name of the Pokemon card.").optional(), - set: z.string().describe("The set the Pokemon card belongs to.").optional(), + set: z.string().describe("The first set the Pokemon card belongs to.").optional(), rarity: z.string().describe("The rarity of the Pokemon card.").optional(), }).describe("Details about the Pokemon card.").optional(), error: z.string().describe("Error message if the card could not be identified.").optional() @@ -40,14 +40,14 @@ const scanPokemonCardPrompt = ai.definePrompt({ name: 'scanPokemonCardPrompt', input: {schema: ScanPokemonCardInputSchema}, output: {schema: ScanPokemonCardOutputSchema}, - prompt: `You are an expert Pokemon card appraiser. Use the following image to identify the card's name, set, and rarity. If you are unable to determine any of these values, leave them blank.\n + prompt: `You are an expert Pokemon card appraiser. Use the following image to identify the card's name, first set it was introduced in, and rarity. If you are unable to determine any of these values, leave them blank.\n Photo: {{media url=photoDataUri}} \n Respond using the following format: { "cardDetails": { "name": "Pokemon card name", - "set": "Pokemon card set", + "set": "Pokemon first card set it was introduced in", "rarity": "Pokemon card rarity" } } diff --git a/src/app/dashboard/collection/[cardId]/edit/page.tsx b/src/app/dashboard/collection/[cardId]/edit/page.tsx index b0d63e8..655b8d6 100644 --- a/src/app/dashboard/collection/[cardId]/edit/page.tsx +++ b/src/app/dashboard/collection/[cardId]/edit/page.tsx @@ -10,7 +10,7 @@ import { useToast } from '@/hooks/use-toast'; import { Loader2, AlertTriangle } from 'lucide-react'; export default function EditCardPage() { - const { user } = useAuth(); + const { user, loading: authLoading } = useAuth(); const router = useRouter(); const params = useParams(); const cardId = params.cardId as string; @@ -22,6 +22,16 @@ export default function EditCardPage() { const { toast } = useToast(); useEffect(() => { + // Don't do anything while auth is still loading + if (authLoading) return; + + // Redirect if no user + if (!user) { + router.replace('/login'); + return; + } + + // Only fetch card if we have user and cardId if (user && cardId) { setLoading(true); setError(null); @@ -40,10 +50,8 @@ export default function EditCardPage() { toast({ title: "Error", description: "Failed to load card details.", variant: "destructive" }); }) .finally(() => setLoading(false)); - } else if (!user && !loading) { // if auth is loaded and no user - router.replace('/login'); } - }, [user, cardId, router, toast, loading]); + }, [user, cardId, router, toast, authLoading]); // Removed 'loading' from dependencies const handleUpdateCard = async (data: CardFormInputs) => { if (!user || !card) return;