Files
telegram-shop/worklog.md
2026-08-05 12:00:27 +00:00

22 KiB
Raw Blame History

Worklog

Task 5 — Dashboard API Route & Dashboard Page Component

Date: 2025-06-24

What was done

  1. Created Dashboard API route at /src/app/api/stats/dashboard/route.ts

    • GET handler with auth verification via getAuth
    • Returns comprehensive dashboard data:
      • stats: totalUsers, totalProducts, totalPurchases, totalRevenue, totalSubcategories, aov, conversionRate, completedPurchases, pendingPurchases, cancelledPurchases
      • chartData: 7-day and 30-day revenue + 7-day new user data
      • topProducts: top 5 by quantity from completed purchases (name, qty, revenue)
      • topSpenders: top 5 users by total spent (username, spent)
      • revenueByCategory: revenue grouped by category name using $queryRaw
      • topCountries: top 5 countries with product counts using $queryRaw
      • activities: merged last 10 completed purchases + last 10 audit logs, sorted by date
      • walletSummary: per wallet type (BTC/LTC/ETH/USDT/USDC) with count, total balance, mock USD balance
    • Uses parallel Promise.all for basic counts, $queryRaw for complex aggregations (revenue by category, top countries)
    • Wrapped in try/catch returning 500 on error
  2. Created Dashboard Page component at /src/components/dashboard/dashboard-page.tsx

    • Named export DashboardPage (not default)
    • "use client" directive, fetches data from /api/stats/dashboard via useState/useEffect
    • Loading skeleton state, error state handling
    • 10 KPI Cards in a responsive grid (2/3/5 cols): Total Users, Total Products, Total Purchases, Revenue, AOV, Conversion Rate, Completed, Pending, Cancelled, Subcategories — each with lucide-react icon and color accent
    • 7 Charts in a 2-col grid (1-col mobile):
      1. Revenue 7 days (AreaChart, orange gradient)
      2. Revenue 30 days (AreaChart, cyan gradient)
      3. New Users 7 days (BarChart, violet)
      4. Top 5 Products (horizontal BarChart, yellow)
      5. Top 5 Spenders (horizontal BarChart, pink)
      6. Revenue by Category (donut PieChart, 5 colors)
      7. Top 5 Countries (BarChart, orange)
    • Uses recharts with ResponsiveContainer, proper tooltips styled with shadcn CSS variables
    • Activity Feed: merged list with purchase/audit icons, scrollable max-h-96
    • Wallet Summary Table: type, count, crypto balance (8 decimal), USD mock balance
    • Empty states shown when no data
    • Chart colors: orange=#f97316, cyan=#06b6d4, violet=#8b5cf6, yellow=#eab308, pink=#ec4899

Files created

  • src/app/api/stats/dashboard/route.ts
  • src/components/dashboard/dashboard-page.tsx

Notes

  • Pre-existing errors in dev.log (admin-header.tsx missing @/ import prefix, LoginPage not named export, other missing page components) are unrelated to this task
  • ESLint passes cleanly with no errors

Task 7 — Users API Routes & Users Page Components

Date: 2025-06-24

What was done

  1. Created Users Bulk API route at src/app/api/users/bulk/route.ts

    • GET: Returns paginated users with search (username/telegramId), page, limit (default 50, max 100) query params
    • Uses Prisma findMany with include: { _count: { select: { wallets: true, purchases: true } } }
    • Orders by id DESC, returns { data, total, page, limit }
    • Auth check via getAuth
  2. Created User Detail API route at src/app/api/users/[id]/route.ts

    • GET: Returns single user with _count of wallets/purchases, wallets array, and purchases (last 20 with product name)
    • POST: Toggles user status (0↔2). Uses $transaction to update user and create AuditLog entry with action status_toggle. Returns { ok: true, user }
    • Next.js 16 async params pattern (params: Promise<{ id: string }>)
  3. Created Adjust Balance API route at src/app/api/users/[id]/adjust-balance/route.ts

    • POST: Accepts { amount: number, currency: "total_balance" | "bonus_balance" }
    • Validates amount is number and currency is one of the two allowed values
    • Uses $transaction to update balance and create AuditLog with action balance_adjust (includes old/new balance in details)
    • Returns { ok: true, newBalance }
  4. Created UsersPage component at src/components/users/users-page.tsx

    • Named export UsersPage (not default), "use client"
    • Fetches from /api/users/bulk?limit=50 with useState/useEffect
    • Search input with 300ms debounce, filters via ?search= API param
    • Table columns: ID, Telegram ID, Username, Country, City, Status (Badge: green=Active, red=Blocked, gray=Deleted), Balance ($X.XX), Wallets count, Purchases count, Actions (View button)
    • View button navigates via window.location.hash = '/users/${id}'
    • Loading skeleton table, empty state "No users found", error state
    • Max height scroll: max-h-[calc(100vh-12rem)] overflow-y-auto
    • Pagination with Previous/Next buttons, page X of Y display
    • Uses shadcn Table, Input, Badge, Button; lucide-react Search, Eye icons
  5. Created UserDetailPage component at src/components/users/user-detail-page.tsx

    • Named export UserDetailPage with prop { userId: string } (not default), "use client"
    • Fetches from /api/users/${userId}
    • Back button navigates to window.location.hash = '/users'
    • Profile card: ID, Telegram ID, Username, Status badge, Country, City, District, Language, Registered date, Main Balance, Bonus Balance (with icons)
    • Purchases table (last 20): ID, Product name, Qty, Total Price, Status badge (green=Completed, yellow=Pending, red=Cancelled), Date
    • Wallets table: Type (badge), Address (truncated mono), Balance (8 decimals)
    • Balance Adjustment form: Amount input (number, step 0.01), currency Select (Main/Bonus), Submit button. Calls /api/users/${userId}/adjust-balance. Refetches user on success
    • Ban/Unban button with AlertDialog confirmation. Dynamic title/description based on current status. Calls POST /api/users/${userId}. Refetches on success
    • Toast notifications (sonner) on success/error for all actions
    • Loading skeleton, error states, responsive grid layout (1-col mobile, 3-col desktop)

Files created

  • src/app/api/users/bulk/route.ts
  • src/app/api/users/[id]/route.ts
  • src/app/api/users/[id]/adjust-balance/route.ts
  • src/components/users/users-page.tsx
  • src/components/users/user-detail-page.tsx

Notes

  • ESLint passes cleanly with 0 errors, 0 warnings
  • Pre-existing dev.log error (admin-header.tsx missing @/ import prefix) is unrelated to this task
  • All API routes follow the established pattern: getAuth check, try/catch with 500 fallback, Prisma ORM

Task 6 — Catalog API Routes & Catalog Page Component

Date: 2025-06-24

What was done

  1. Created Catalog Tree API route at src/app/api/catalog/tree/route.ts

    • GET: Returns full catalog tree as 3 flat arrays: locations, categories, subcategories
    • Each location includes categoryCount and productCount
    • Each category includes subcategoryCount, productCount, and nested location info
    • Each subcategory includes productCount and nested category info
    • Uses parallel Promise.all to fetch all three tables simultaneously
    • Auth check via getAuth
  2. Created Products Bulk API route at src/app/api/products/bulk/route.ts

    • GET: Paginated products with filters: loc (locationId), cat (categoryId), sub (subcategoryId), search (name contains), page, limit
    • Includes category (id, name), subcategory (id, name), location (id, country, city, district)
    • Returns { data, total, page, limit }
    • Auth check via getAuth
  3. Created Products Add API route at src/app/api/products/add/route.ts

    • POST: Creates product with all fields from JSON body
    • isMono=1 (or true) forces quantityInStock=999999
    • Validates required fields: locationId, categoryId, name, price
    • Returns created product with relations included (201)
    • Auth check via getAuth
  4. Created Product [id] API route at src/app/api/products/[id]/route.ts

    • GET: Single product with full category, subcategory, location relations
    • PUT: Updates product fields, same mono logic (isMono=1 → stock=999999)
    • DELETE: Deletes product, returns { ok: true }
    • Uses Next.js 16 async params pattern
    • Auth check via getAuth
  5. Created Locations CRUD API routes:

    • src/app/api/locations/bulk/route.ts: GET (all with _count categories/products), POST (create with unique constraint handling, 409 on duplicate)
    • src/app/api/locations/[id]/route.ts: PUT (update name fields), PATCH (toggle is_active 0↔1), DELETE (only if no categories/products, 400 otherwise)
  6. Created Categories CRUD API routes:

    • src/app/api/categories/bulk/route.ts: GET (all with location info, _count), POST (create, 409 on duplicate within location)
    • src/app/api/categories/[id]/route.ts: PUT (update name/locationId), PATCH (toggle is_active), DELETE (only if no products, cascades subcategories)
  7. Created Subcategories CRUD API routes:

    • src/app/api/subcategories/bulk/route.ts: GET (all with category info, _count), POST (create, 409 on duplicate within category)
    • src/app/api/subcategories/[id]/route.ts: PUT (update name), PATCH (toggle is_active), DELETE (only if no products)
  8. Created CatalogPage component at src/components/catalog/catalog-page.tsx

    • Named export CatalogPage (not default), "use client"
    • Two-column layout using ResizablePanelGroup (30% left / 70% right)
    • Left Panel — Tree View:
      • Collapsible accordion: Country → City → District (location) → Category → Subcategory
      • Each node shows name, active/inactive badge, item count
      • Hover actions on each node: inline rename (Enter/blur save), toggle active (Switch), delete (AlertDialog), add child
      • Click a node to filter products on the right
      • Add forms inline at each level (location: country+city+district inputs; category/subcategory: name input)
      • Scrollable: overflow-y-auto max-h-[calc(100vh-14rem)]
      • Uses shadcn Accordion, Switch, Button, Badge, AlertDialog, Input
    • Right Panel — Products Table:
      • Columns: ID, Photo (40x40 img or placeholder), Name, Category, Subcategory, Price ($X.XX), Stock (∞ for mono), Actions (Edit, Delete)
      • Search input with 300ms debounce
      • Clear filter button when filter active
      • Product count display, pagination (Previous/Next)
      • Max height scroll with overflow-auto
    • Product Modal (Dialog):
      • Three cascading Select components: Location (grouped as "Country > City > District"), Category (filtered by location), Subcategory (filtered by category, with "None" option)
      • Changing location/category reloads dependent selects
      • Checkbox: "Digital Product (infinite stock)" → disables quantity field, shows ∞
      • Price input (number, min 0.01)
      • Description textarea
      • Photo URL and Hidden Photo URL text inputs
      • Collapsible "Hidden Content Fields" section: hidden_description, hidden_coordinates, private_data
      • Save and Cancel buttons, loading state during save
      • Edit mode pre-fills all fields from existing product
    • Delete Confirmation: AlertDialog for all destructive actions (locations, categories, subcategories, products)
    • Toast notifications (sonner) on all success/error actions
    • Loading skeleton state, error state with retry button
    • Responsive: category/subcategory columns hidden on smaller screens
    • Colors: orange (MapPin), amber (FolderOpen), emerald (Tag)

Files created

  • src/app/api/catalog/tree/route.ts
  • src/app/api/products/bulk/route.ts
  • src/app/api/products/add/route.ts
  • src/app/api/products/[id]/route.ts
  • src/app/api/locations/bulk/route.ts
  • src/app/api/locations/[id]/route.ts
  • src/app/api/categories/bulk/route.ts
  • src/app/api/categories/[id]/route.ts
  • src/app/api/subcategories/bulk/route.ts
  • src/app/api/subcategories/[id]/route.ts
  • src/components/catalog/catalog-page.tsx

Notes

  • ESLint passes cleanly with 0 errors, 0 warnings
  • Pre-existing dev.log error (admin-header.tsx missing @/ import prefix) is unrelated to this task
  • All 10 API routes follow the established pattern: getAuth check, try/catch with 500 fallback, Prisma ORM
  • Unique constraint violations return 409 status code
  • DELETE endpoints enforce referential integrity (no delete if children exist)
  • All [id] routes use Next.js 16 async params pattern (params: Promise<{ id: string }>)

Task 8 — Wallets API Routes & Wallets Page Component

Date: 2025-06-24

What was done

  1. Created Wallets Overview API route at src/app/api/wallets/overview/route.ts

    • GET: Returns owner summary data with auth check
    • Aggregates all CryptoWallet records to compute: totals (balance per walletType), walletCounts, totalUsd, totalWallets, totalUsers (unique users with wallets)
    • Commission data: commissionEnabled (true), commissionRate (0.05/5%), currentCommission (totalUsd × rate), payments (last 20 CommissionPayment records), lastPaidAmount, commissionDue (max(0, current - paid))
  2. Created Wallets Bulk API route at src/app/api/wallets/bulk/route.ts

    • GET: Returns all users who have wallets, with wallet counts
    • Query param ?search= filters by username or telegramId (contains)
    • Each user includes: id, username, telegramId, status, totalBalance, bonusBalance, walletCount, country, city
  3. Created Wallets [userId] API route at src/app/api/wallets/[userId]/route.ts

    • GET: Returns user profile + wallets array for a specific user
    • Wallets include: id, walletType, address, balance, createdAt
    • Uses Next.js 16 async params pattern
    • 400 on invalid userId, 404 if user not found
  4. Created Record Payment API route at src/app/api/wallets/record-payment/route.ts

    • POST: Accepts {paidAmount: number, note?: string}
    • Validates paidAmount is positive number
    • Creates CommissionPayment record with computed fields
    • Returns {ok: true}
  5. Created Seeds API route at src/app/api/wallets/seeds/route.ts

    • GET: Returns all seed phrases (mnemonic field) — super admin only via requireSuperAuth
    • Filters to wallets where mnemonic is not null
    • Includes user username in response
    • Creates AuditLog entry with action seed_phrase_viewed
  6. Created Export Seeds API route at src/app/api/wallets/export-seeds/route.ts

    • GET: Returns CSV download — super admin only
    • CSV columns: WalletId, UserId, Username, WalletType, Address, DerivationPath, Mnemonic
    • Content-Type: text/csv, Content-Disposition: attachment
    • Creates AuditLog entry with action csv_seed_export
  7. Created WalletsPage component at src/components/wallets/wallets-page.tsx

    • Named export WalletsPage (not default), "use client"
    • Three-section layout with shadcn Tabs:
    • Tab 1: User Wallets — Left panel: searchable user list with status badges and wallet counts; Right panel: selected user profile card (balances, location) + wallets table (type badge, clickable truncated address with copy-to-clipboard, 8-decimal balance, created date). "Refresh Balances" button (mock toast). Debounced search, loading skeletons, empty states.
    • Tab 2: Owner Summary — 4 KPI cards (Total USD Value, Total Users, Active Wallets, Commission Due). "Balances by Currency" table (BTC/LTC/ETH/USDT/USDC with wallet count and total balance). Commission section with stats (Rate, Total Balance, Commission Amount, Last Paid), commission-due warning banner, Record Payment form (amount + note inputs), Payment History table (last 20 with date, amount, note).
    • Tab 3: Seed Phrases (super_admin only) — Commission-due warning disables unlock. AlertDialog confirmation before loading seeds. Seeds table: User, Type, Address (click to copy), Derivation, Mnemonic (masked by default with eye toggle reveal, click to copy). "Export All Seeds as CSV" button with AlertDialog confirmation.
    • All copy-to-clipboard uses sonner toast "Copied!"
    • Responsive design, max-height scrollable tables
    • Uses shadcn: Tabs, Card, Table, Badge, Button, Input, Label, AlertDialog, Skeleton

Files created

  • src/app/api/wallets/overview/route.ts
  • src/app/api/wallets/bulk/route.ts
  • src/app/api/wallets/[userId]/route.ts
  • src/app/api/wallets/record-payment/route.ts
  • src/app/api/wallets/seeds/route.ts
  • src/app/api/wallets/export-seeds/route.ts
  • src/components/wallets/wallets-page.tsx

Notes

  • ESLint passes cleanly with 0 errors, 0 warnings
  • Pre-existing dev.log error (admin-header.tsx missing @/ import prefix) is unrelated to this task
  • All 6 API routes follow the established pattern: getAuth/requireSuperAuth check, try/catch with 500 fallback, Prisma ORM
  • Seed/export routes create AuditLog entries for security tracking
  • [userId] route uses Next.js 16 async params pattern (params: Promise<{ userId: string }>)

Task 9-11 — All Remaining API Routes & Page Components

Date: 2025-06-24

What was done

  1. Purchases API at src/app/api/purchases/bulk/route.ts

    • GET: Paginated purchases with ?status=&page=&limit=50
    • Includes user (username, telegramId) and product (name)
    • Returns {data, total, page, limit}
  2. PurchasesPage at src/components/purchases/purchases-page.tsx

    • Status filter tabs: All, Pending, Completed, Cancelled
    • Table: ID, User (link to #/users/id), Product, Qty, Total Price ($X.XX), Currency, TX Hash (truncated, click to copy), Date, Status Badge
    • Pagination, loading skeleton, empty state, max-h scroll
  3. Audit Log API at src/app/api/audit/bulk/route.ts

    • GET: Paginated audit log with ?page=&limit=100, ordered id DESC
    • Returns {data, total, page, limit}
  4. AuditPage at src/components/audit/audit-page.tsx

    • Table: ID, Action (color-coded Badge), Admin ID, Details (Collapsible JSON), Date
    • Action badge colors: login=blue, balance_adjust=orange, status_toggle=red, seed_phrase_viewed/csv_seed_export=purple, seed_demo=amber, clear_all=dark-red
    • Pagination, loading skeleton, empty state
  5. CategoriesPage at src/components/categories/categories-page.tsx

    • Table: ID, Name, Location (country > city > district), Subcategories count, Products count, Active (Switch), Actions (Edit, Delete)
    • Add/Edit Dialog with name input + location Select (grouped by country/city/district)
    • Delete AlertDialog with product-count protection
    • Uses existing categories/bulk and categories/[id] APIs
  6. LocationsPage at src/components/locations/locations-page.tsx

    • Table: ID, Country, City, District, Categories count, Products count, Active (Switch), Actions (Edit, Delete)
    • Add/Edit Dialog with country, city, district inputs
    • Delete AlertDialog with category/product count protection
    • Uses existing locations/bulk and locations/[id] APIs
  7. Settings API at src/app/api/settings/route.ts

    • GET: Hardcoded settings object with _masked array
    • PUT: Accepts {key, value}, returns {ok: true, message} (no persistence)
  8. SettingsPage at src/components/settings/settings-page.tsx

    • Sections: Bot, WireGuard, Admin — each setting with Label, Input, per-field Save button
    • Masked secrets shown as disabled inputs with bullet placeholders
    • WG_ENABLED as Switch, warning banner about restart requirement
  9. Locales API at src/app/api/locales/route.ts

    • GET: Hardcoded 3-language (en, es, de) demo data
    • PUT: Accepts {lang, key, value}, returns {ok: true} (no persistence)
  10. LocalesPage at src/components/locales/locales-page.tsx

    • Table: rows = flattened locale keys, columns = en, es, de
    • Each cell is inline-editable Input, blur triggers save
    • Grouped by section (menu, common, products) with rowSpan headers
  11. Seed Data API at src/app/api/seed/data/route.ts

    • GET: Returns {seeded: boolean} based on user count > 0
  12. Seed Demo API at src/app/api/seed/demo/route.ts

    • POST: Verifies reauth token, creates audit log, clears all data, seeds demo:
      • 4 locations (USA/New York/Manhattan, Brooklyn; UK/London; Germany/Berlin)
      • 2-3 categories per location, 1-2 subcategories per category
      • 5-10 products per category with random prices
      • 10-20 users with random usernames and balances
      • 10-15 wallets across users, 5-10 purchases (mixed statuses)
      • 3-5 audit log entries
    • Resets autoincrement via sqlite_sequence
  13. Seed Clear API at src/app/api/seed/clear/route.ts

    • POST: Verifies reauth token, creates audit log BEFORE deleting
    • Deletes all tables in order, resets autoincrement
  14. SeedPage at src/components/seed/seed-page.tsx

    • Role gate: shows "Access Denied" if not super_admin
    • DB status indicator (Contains Data / Empty)
    • Two red danger cards: Seed Demo Data, Clear All Data
    • Each action opens AlertDialog with warning text + reauth token password input
    • Confirm disabled until token entered, loading spinner during operation
    • Toast on success/error

Files created

  • src/app/api/purchases/bulk/route.ts
  • src/app/api/audit/bulk/route.ts
  • src/app/api/settings/route.ts
  • src/app/api/locales/route.ts
  • src/app/api/seed/data/route.ts
  • src/app/api/seed/demo/route.ts
  • src/app/api/seed/clear/route.ts
  • src/components/purchases/purchases-page.tsx
  • src/components/audit/audit-page.tsx
  • src/components/categories/categories-page.tsx
  • src/components/locations/locations-page.tsx
  • src/components/settings/settings-page.tsx
  • src/components/locales/locales-page.tsx
  • src/components/seed/seed-page.tsx

Notes

  • ESLint passes cleanly with 0 errors, 0 warnings
  • Pre-existing dev.log error (admin-header.tsx missing @/ import prefix) is unrelated
  • Categories and Locations APIs were already created by Task 6 (catalog agent)
  • All 7 new API routes use getAuth or verifyReAuth for auth
  • All page components use named exports (not default), "use client" directive
  • Seed demo/clear routes use verifyReAuth from @/lib/auth for double-confirmation security