## AI Master Prompt for SandSeeker MVP Development
**1. PROJECT OVERVIEW:**
SandSeeker is a web application designed to explore and document the incredible geological and ecological diversity hidden beneath ordinary sand. The platform allows users to submit, discover, and learn about sand samples from various geographical locations. Each submission can include detailed geological information, high-quality images, and videos, along with precise location data. SandSeeker aims to be a comprehensive resource for science enthusiasts, travelers, educators, and anyone curious about the microscopic world of sand, solving the problem of fragmented and inaccessible information about global sand compositions and their unique environments.
**Value Proposition:** Discover the unseen world of sand, from its geological origins to the ecosystems it supports, all in one interactive platform.
**2. TECH STACK:**
* **Frontend Framework:** React with Next.js (App Router)
* **Styling:** Tailwind CSS
* **ORM:** Drizzle ORM (for PostgreSQL)
* **Database:** PostgreSQL
* **UI Component Library:** shadcn/ui
* **Authentication:** NextAuth.js (e.g., with Google, GitHub providers)
* **State Management:** React Context API / Zustand for global state, local component state for UI elements.
* **Form Handling:** React Hook Form + Zod for validation
* **Mapping:** React Leaflet or similar mapping library
* **Image/Video Handling:** Cloudinary or AWS S3 for storage and CDN
* **Charting (Future/Optional):** Recharts or Chart.js for potential future data visualization features.
* **Deployment:** Vercel
**3. DATABASE SCHEMA (Drizzle ORM for PostgreSQL):**
```typescript
// schema.ts
import { pgTable, serial, varchar, text, timestamp, float8, uuid, pgEnum, boolean, integer, index } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
import { createId } from '@paralleldrive/cuid2'; // For unique IDs
// User Roles Enum
export const userRoleEnum = pgEnum('user_role', ['user', 'admin']);
// Users Table
export const users = pgTable('users', {
id: uuid('id').primaryKey().$default(() => createId()),
name: varchar('name'),
email: varchar('email').notNull().unique(),
emailVerified: timestamp('emailVerified', { mode: 'date' }),
image: varchar('image'),
role: userRoleEnum('role').default('user'),
createdAt: timestamp('createdAt').defaultNow(),
updatedAt: timestamp('updatedAt').defaultNow(),
}, (users) => ({
emailIndex: index('idx_users_email').on(users.email),
}));
// Sand Sample Table
export const sandSamples = pgTable('sand_samples', {
id: uuid('id').primaryKey().$default(() => createId()),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
title: varchar('title', { length: 255 }).notNull(),
description: text('description'),
locationName: varchar('location_name', { length: 255 }).notNull(),
country: varchar('country', { length: 100 }),
continent: varchar('continent', { length: 100 }),
latitude: float8('latitude').notNull(),
longitude: float8('longitude').notNull(),
geologicalInfo: text('geological_info'),
ecosystemDetails: text('ecosystem_details'),
primaryImage: varchar('primary_image', { length: 512 }), // URL to primary image
createdAt: timestamp('createdAt').defaultNow(),
updatedAt: timestamp('updatedAt').defaultNow(),
}, (sandSamples) => ({
locationIndex: index('idx_sand_samples_location').on(sandSamples.latitude, sandSamples.longitude),
titleIndex: index('idx_sand_samples_title').on(sandSamples.title),
}));
// Media Table (for additional images/videos)
export const media = pgTable('media', {
id: uuid('id').primaryKey().$default(() => createId()),
sandSampleId: uuid('sand_sample_id').notNull().references(() => sandSamples.id, { onDelete: 'cascade' }),
url: varchar('url', { length: 512 }).notNull(),
type: varchar('type', { length: 20 }).notNull(), // 'image' or 'video'
caption: varchar('caption', { length: 255 }),
createdAt: timestamp('createdAt').defaultNow(),
}, (media) => ({
sandSampleMediaIndex: index('idx_media_sand_sample').on(media.sandSampleId),
}));
// Favorites Table
export const favorites = pgTable('favorites', {
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
sandSampleId: uuid('sand_sample_id').notNull().references(() => sandSamples.id, { onDelete: 'cascade' }),
createdAt: timestamp('createdAt').defaultNow(),
}, (favorites) => ({
pk: primaryKey(favorites.userId, favorites.sandSampleId),
userFavIndex: index('idx_favorites_user').on(favorites.userId),
sampleFavIndex: index('idx_favorites_sample').on(favorites.sandSampleId),
}));
// User Relations
export const userRelations = relations(users, ({ many }) => ({
sandSamples: many(sandSamples),
favorites: many(favorites),
}));
// Sand Sample Relations
export const sandSampleRelations = relations(sandSamples, ({ one, many }) => ({
user: one(users, {
fields: [sandSamples.userId],
references: [users.id],
}),
media: many(media),
favorites: many(favorites),
}));
// Media Relations
export const mediaRelations = relations(media, ({ one }) => ({
sandSample: one(sandSamples, {
fields: [media.sandSampleId],
references: [sandSamples.id],
}),
}));
// Favorite Relations
export const favoriteRelations = relations(favorites, ({ one }) => ({
user: one(users, {
fields: [favorites.userId],
references: [users.id],
}),
sandSample: one(sandSamples, {
fields: [favorites.sandSampleId],
references: [sandSamples.id],
}),
}));
```
**4. CORE FEATURES & USER FLOW:**
* **User Authentication (NextAuth.js):**
* **Flow:** User lands on the homepage -> Clicks 'Sign In' -> Presented with options (Google, GitHub, Email/Password) -> Selects a provider -> Redirected to provider for authentication -> Redirected back to SandSeeker -> User session created/retrieved -> User dashboard or homepage displayed with authenticated state.
* **Edge Cases:** Invalid credentials, provider errors, session expiration.
* **Sand Sample Submission:**
* **Flow:** Authenticated user navigates to 'Submit Sample' page -> Fills out a form including: Title, Description, Location (interactive map selection or manual input for Lat/Lon), Country, Continent, Geological Info, Ecosystem Details -> Uploads at least one primary image -> Optionally uploads additional images/videos -> Clicks 'Submit' -> Form data and media are validated -> Data saved to `sand_samples` and `media` tables via API -> User is redirected to their new sample's detail page.
* **Components:** Multi-step form (or single long form), interactive map component, file upload component, input validation.
* **Edge Cases:** Missing required fields, invalid location data, failed media upload, network errors during submission.
* **View Sand Samples (List & Detail):**
* **List View (Homepage / Browse):**
* **Flow:** User (authenticated or guest) visits homepage/browse page -> A list/grid of recent or featured sand samples is displayed, showing thumbnail, title, location -> User can scroll/paginate for more samples.
* **Components:** Sample Card component, infinite scroll/pagination component.
* **Detail View:**
* **Flow:** User clicks on a sample card or via search results -> Navigates to the sample's detail page -> Displays: Title, primary image/gallery, detailed description, location (on a map), geological info, ecosystem details, uploader's username (linked to profile), 'Add to Favorites' button (if authenticated).
* **Components:** Image gallery/carousel, map display, detailed text sections, Favorite button.
* **Edge Cases:** Sample not found (404), unauthorized access to private samples (if implemented later).
* **Map Exploration:**
* **Flow:** User navigates to the 'Explore Map' page -> A world map is displayed with markers indicating sand sample locations -> User can pan/zoom the map -> Clicking a marker shows a small pop-up with sample title and link to detail page -> Optionally, filters can be applied (e.g., by country, geological type) to update map markers dynamically.
* **Components:** Map container, marker clusterer (for performance with many markers), filter controls.
* **Edge Cases:** No samples in the current map view, map loading errors.
* **Favorites:**
* **Flow:** Authenticated user views a sand sample detail page -> Clicks 'Add to Favorites' button -> Button state updates, sample added to `favorites` table -> User navigates to their 'My Favorites' dashboard page -> Displays a list of their favorited samples.
* **Components:** Favorite toggle button, list display on dashboard.
* **Edge Cases:** Unauthenticated user clicks favorite, removing a favorite.
**5. API & DATA FETCHING (Next.js App Router - Server Actions & Route Handlers):**
* **Authentication:** Handled by NextAuth.js, provides session object available in Server Components and Client Components via hooks.
* **Sand Sample Submission:** Primarily using Server Actions (`'use server'`).
* `POST /api/sand-samples`: Server Action to handle form submission, validation, DB insertion, and media upload to Cloudinary/S3. Returns success/error message.
* **Fetching Sand Samples:**
* **List (Homepage/Browse):** Server Component fetching recent/paginated samples directly from DB using Drizzle ORM. `fetch('/api/sand-samples?limit=10&page=1', { cache: 'force-cache' })` or direct DB query in RSC.
* **Detail Page:** Server Component fetching single sample data by ID. `fetch('/api/sand-samples/[id]')` or direct DB query.
* **Map Data:** Route Handler or Server Component fetching sample locations for map display. `fetch('/api/sand-samples/locations?bbox=<bounds>')` - optimized to return only necessary fields (id, title, lat, lon).
* **Favorites:**
* **Add/Remove Favorite:** Server Action. `POST /api/favorites` (body: { sandSampleId }), `DELETE /api/favorites` (body: { sandSampleId }).
* **Fetch User Favorites:** Server Component fetching favorites for the logged-in user.
**Data Structure Examples:**
* **`SandSampleDetail` (for detail page):** `{ id, title, description, locationName, country, continent, latitude, longitude, geologicalInfo, ecosystemDetails, primaryImage, createdAt, user: { id, name, image }, media: [{ id, url, type, caption }], isFavorite: boolean }`
* **`SandSampleListItem` (for list/map):** `{ id, title, locationName, latitude, longitude, primaryImage }`
**6. COMPONENT BREAKDOWN (Next.js App Router Structure):**
```
app/
├── api/
│ ├── auth/[...nextauth]/route.ts // NextAuth.js handler
│ ├── sand-samples/
│ │ ├── route.ts // Route Handler for locations/general fetch
│ │ └── [id]/route.ts // Route Handler for single sample fetch (alternative to RSC query)
│ └── favorites/
│ └── route.ts // Route Handler for favorite actions (alternative to Server Actions)
├── (app)/ // Parallel routes or group for authenticated pages
│ ├── dashboard/
│ │ ├── page.tsx // User Dashboard (My Samples, Favorites)
│ │ └── loading.tsx
│ ├── submit/
│ │ └── page.tsx // Sand Sample Submission Form
│ │ └── loading.tsx
│ ├── favorites/
│ │ └── page.tsx // List of user's favorite samples
│ │ └── loading.tsx
│ ├── samples/
│ │ └── [id]/
│ │ ├── page.tsx // Sand Sample Detail Page
│ │ └── loading.tsx
│ └── settings/
│ └── page.tsx // User Settings
│ └── loading.tsx
├── (marketing)/ // Marketing pages group
│ ├── layout.tsx // Marketing layout
│ ├── page.tsx // Homepage / Browse Samples
│ ├── explore/
│ │ └── page.tsx // Interactive Map Exploration
│ │ └── loading.tsx
│ └── about/
│ └── page.tsx // About Page
├── layout.tsx // Root Layout (Providers, Head)
├── globals.css // Tailwind CSS base styles
└── page.tsx // Temporary root page if needed, or redirect
components/
├── ui/
│ ├── * shadcn/ui components *
│ ├── AuthButton.tsx
│ ├── Footer.tsx
│ ├── Header.tsx
│ ├── ImageUpload.tsx
│ ├── MapDisplay.tsx
│ ├── SampleCard.tsx
│ ├── SampleForm.tsx
│ ├── UserAvatar.tsx
│ └── ...
```
**State Management:**
* **Global State (Context/Zustand):** User authentication status, possibly UI theme settings.
* **Server State:** Data fetched via Server Components or Server Actions (managed by Next.js cache).
* **Client State (useState, useReducer):** UI state like form inputs, modal visibility, toggle states, map interactions.
**7. UI/UX DESIGN & VISUAL IDENTITY:**
* **Design Style:** "Modern Minimalist with Natural Elements"
* **Color Palette:**
* Primary: `hsl(210, 40%, 20%)` (Deep Blue/Navy - for backgrounds, text)
* Secondary: `hsl(35, 80%, 50%)` (Warm Sand/Ochre - for accents, buttons, highlights)
* Accent: `hsl(160, 60%, 40%)` (Teal/Aqua - for interactive elements, map markers)
* Backgrounds: `hsl(210, 40%, 10%)` (Dark Navy), `hsl(210, 40%, 15%)` (Slightly Lighter Dark)
* Text: `hsl(0, 0%, 90%)` (Light Gray/Off-white)
* Borders/Dividers: `hsl(210, 40%, 25%)` (Darker Gray)
* **Typography:**
* Headings: A clean, slightly condensed sans-serif like Inter or Poppins (e.g., Poppins Bold).
* Body Text: A highly readable sans-serif like Inter or Lato (e.g., Inter Regular).
* **Layout:** Clean, spacious layouts with ample whitespace. Card-based design for sample listings. Sidebar navigation within authenticated sections. Full-width map view for exploration.
* **Responsiveness:** Mobile-first approach. Use Tailwind's responsive prefixes (sm:, md:, lg:, xl:) extensively. Ensure navigation adapts well, forms are usable on small screens, and map interactions are touch-friendly.
* **Visual Elements:** Subtle use of gradient backgrounds (e.g., subtle dark blue to slightly lighter dark blue). High-quality imagery is key. Icons should be clean and line-based.
**8. SAMPLE/MOCK DATA:**
* **Sand Sample 1:**
* Title: "Red Sands of Santorini"
* Location: "Red Beach, Santorini, Greece"
* Coords: `36.3949° N, 25.4185° E`
* Geology: "Volcanic sand, rich in iron oxides giving it a deep red color, composed of fragments of red rocks and lava."
* Ecosystem: "Sparse coastal vegetation adapted to arid, volcanic soil. Limited marine life near shore due to sharp rocks."
* Primary Image: `https://example.com/images/santorini_red_sand.jpg`
* **Sand Sample 2:**
* Title: "Oolitic Sand from The Bahamas"
* Location: "Cable Beach, Nassau, The Bahamas"
* Coords: `25.0714° N, 77.4732° W`
* Geology: "Composed of ooids - small spherical grains formed by concentric layers of calcium carbonate precipitated around a nucleus, typical of warm, shallow marine environments."
* Ecosystem: "Clear, shallow turquoise waters, home to coral reefs, seagrass beds, and diverse marine life. Coastal vegetation includes palms."
* Primary Image: `https://example.com/images/bahamas_oolitic.jpg`
* **Sand Sample 3:**
* Title: "Black Volcanic Sand, Iceland"
* Location: "Reynisfjara Black Sand Beach, Iceland"
* Coords: `63.4039° N, 19.0163° W`
* Geology: "Basalt sand and pebbles, ground down from volcanic rock over millennia. Contains high concentrations of dark minerals like olivine and magnetite."
* Ecosystem: "Cold North Atlantic waters, dramatic basalt columns, puffin colonies on nearby cliffs. Very sparse beach vegetation."
* Primary Image: `https://example.com/images/iceland_black_sand.jpg`
* **Sand Sample 4:**
* Title: "Quartz Sand Dunes, Namibia"
* Location: "Sossusvlei, Namib Desert, Namibia"
* Coords: `24.7500° S, 15.7500° E`
* Geology: "Predominantly well-rounded quartz grains, transported by wind over vast distances. Iron oxides contribute to the orange/red hues of the dunes."
* Ecosystem: "Extremely arid desert environment. Specialized flora (e.g., Welwitschia mirabilis) and fauna (e.g., desert beetles, geckos) adapted to survive with minimal water."
* Primary Image: `https://example.com/images/namibia_dunes.jpg`
* **Sand Sample 5:**
* Title: "White Coral Sand, Maldives"
* Location: "Bikini Atoll, Marshall Islands"
* Coords: `11.5900° N, 165.4100° E`
* Geology: "Finely ground fragments of coral skeletons (calcium carbonate) and shells, giving it a bright white appearance. Washed ashore by tropical currents."
* Ecosystem: "Warm, shallow tropical waters. Rich coral reef ecosystems teeming with colorful fish, sea turtles, and invertebrates."
* Primary Image: `https://example.com/images/maldives_coral.jpg`
* **Additional Media Example (Video):**
* Sand Sample ID: `sample_id_123`
* URL: `https://example.com/videos/santorini_waves.mp4`
* Type: `video`
* Caption: "Waves crashing on the red sand beach."
**9. TURKISH TRANSLATIONS (for UI Elements):**
* **App Title:** SandSeeker -> Kum Avcısı
* **Navigation:** Home -> Ana Sayfa, Explore -> Keşfet, Submit Sample -> Numune Gönder, Dashboard -> Pano, Settings -> Ayarlar, Sign In -> Giriş Yap, Sign Out -> Çıkış Yap
* **Sample Detail Page:** Title -> Başlık, Description -> Açıklama, Location -> Konum, Geological Info -> Jeolojik Bilgi, Ecosystem Details -> Ekosistem Detayları, Uploaded by -> Yükleyen, Add to Favorites -> Favorilere Ekle, Remove from Favorites -> Favorilerden Çıkar
* **Submission Form:** Submit Sample -> Numune Gönder, Sample Title -> Numune Başlığı, Location Name -> Konum Adı, Upload Primary Image -> Ana Görseli Yükle, Upload More Media -> Ek Medya Yükle, Save Draft -> Taslağı Kaydet, Publish -> Yayınla
* **Buttons:** Submit -> Gönder, Cancel -> İptal, Save -> Kaydet, Edit -> Düzenle, Delete -> Sil, View Map -> Haritada Gör, Add Photo -> Fotoğraf Ekle
* **Labels/Placeholders:** Search samples... -> Örnekleri ara..., Enter title... -> Başlık girin..., Enter description... -> Açıklama girin...
* **Messages:** Sample submitted successfully -> Numune başarıyla gönderildi., Added to favorites -> Favorilere eklendi., Error saving sample -> Numune kaydedilirken hata oluştu.
**10. ANIMATIONS:**
* **Page Transitions:** Subtle fade or slide transitions between pages using Next.js `AnimatePresence` and Framer Motion.
* **Button Hovers:** Slight scale increase or background color change on hover.
* **Card Hovers:** Subtle shadow lift or slight scale-up for sample cards on hover.
* **Loading States:** Use shadcn/ui's `Skeleton` component or spinners (`lucide-react` icons) for data loading placeholders.
* **Map Markers:** Subtle bounce or pulse animation for selected/hovered markers.
* **Form Feedback:** Smooth transitions for validation error messages appearing/disappearing.
**11. EDGE CASES & VALIDATIONS:**
* **Authentication:** Redirect unauthenticated users from protected routes (submit, dashboard) to login. Handle expired sessions gracefully.
* **Empty States:** Design specific UI states for when a user has no samples, no favorites, or search returns no results. Provide clear calls to action (e.g., "Submit your first sample!").
* **Form Validation:** Implement robust client-side (using Zod with React Hook Form) and server-side validation for all user inputs (required fields, data formats, geo-coordinate ranges).
* **Media Uploads:** Handle different file types (images, videos), size limits, and potential upload failures. Provide clear feedback to the user. Consider image compression/resizing on upload.
* **Map Interactions:** Ensure map remains usable on touch devices. Handle cases where location permissions might be denied or unavailable.
* **Data Integrity:** Use database constraints and transactions where necessary to maintain data consistency (e.g., ensuring a favorite entry links valid user and sample IDs).
* **Error Handling:** Implement global error handling for API requests and display user-friendly error messages. Log detailed errors to a monitoring service.
* **Rate Limiting:** Implement rate limiting on API endpoints (especially for submissions and auth) to prevent abuse.
* **SEO:** Use Next.js metadata API for proper SEO tagging on public pages (homepage, sample details).