Skip to content

Submitted to IMPHEN#42

Merged
igun997 merged 60 commits into
masterfrom
develop
Dec 6, 2025
Merged

Submitted to IMPHEN#42
igun997 merged 60 commits into
masterfrom
develop

Conversation

@igun997

@igun997 igun997 commented Dec 6, 2025

Copy link
Copy Markdown
Contributor

PR Type

Enhancement, Tests, Documentation


Description

  • NFT Collectible System: Complete implementation of ERC721 NFT minting for first-hand tag claimers, including smart contract (ETagCollectible), Gemini-based art generation, R2 asset storage, and blockchain integration

  • Support Ticket Management: New server actions and comprehensive test suite for customer support tickets with role-based access control, messaging, and statistics

  • Database Seeding: Complete seed script with 5 Indonesian brands, realistic products, fraud patterns, and legitimate scan distributions

  • NFT Explorer API: Public endpoint for NFT discovery with pagination and enriched product/brand information

  • Web3 Integration: React hook for MetaMask wallet connection with Base Sepolia chain support and utility functions

  • FAQ System: Multilingual FAQ database with 40+ items in Indonesian, categorization, and search functionality

  • Enhanced Testing: Comprehensive test suites for support tickets, NFTs, tags, brands, users, products, and dashboard with error handling validation

  • Dashboard Enhancement: NFT count support in dashboard statistics with graceful fallback for missing tables

  • Component Refactoring: Extracted tag form page and landing page into composable, maintainable sub-components with dedicated type files

  • Configuration Updates: R2 domain support for image optimization, npm legacy peer dependencies, Sourcify contract verification, and PWA icon generation script

  • Bug Fix: Improved logout error handling for Next.js redirect exceptions


Diagram Walkthrough

flowchart LR
  A["Smart Contract<br/>ETagCollectible"] -->|Deploy| B["Base Sepolia<br/>Blockchain"]
  C["Gemini API"] -->|Generate Art| D["NFT Image"]
  D -->|Upload| E["Cloudflare R2"]
  F["Tag Claim<br/>Endpoint"] -->|Validate| G["First-hand<br/>Check"]
  G -->|Mint| B
  B -->|Store Metadata| H["Database<br/>TagNFT"]
  I["NFT Explorer<br/>API"] -->|Query| H
  J["Support Tickets<br/>Actions"] -->|Manage| K["Support<br/>System"]
  L["Web3 Hook"] -->|Connect| M["MetaMask<br/>Wallet"]
  M -->|Sign Tx| B
  N["FAQ System"] -->|Search| O["Knowledge<br/>Base"]
Loading

File Walkthrough

Relevant files
Enhancement
22 files
seed-complete.ts
Complete database seeding with brands, products, tags, and fraud
patterns

scripts/seed-complete.ts

  • Comprehensive database seeding script with realistic brand data (5
    Indonesian brands with products)
  • Generates 2-4 tags per product with distribution metadata and QR code
    generation
  • Implements 6 fraud patterns for suspicious scan detection (impossible
    travel, high-volume scanning, bot behavior, etc.)
  • Creates legitimate scan patterns with location-based distribution and
    supports R2 asset upload
+1161/-0
nft-collectible.ts
NFT collectible minting and metadata management library   

src/lib/nft-collectible.ts

  • NFT minting library handling complete flow: art generation, R2 upload,
    blockchain minting, and database storage
  • Provides functions to check tag NFT status, generate metadata in
    ERC721 standard format, and mint on-chain
  • Integrates with Gemini for art generation and R2 for asset storage
    with fallback mechanisms
  • Includes blockchain interaction utilities for provider/signer
    management and contract calls
+452/-0 
route.ts
Public NFT explorer API endpoint with pagination                 

src/app/api/explorer/route.ts

  • Adds new nfts action to explorer API endpoint for public NFT discovery
  • Implements paginated NFT listing with product and brand information
    enrichment
  • Returns PublicNFT interface with token details, ownership info, and
    asset URLs
  • Includes graceful error handling for missing TagNFT table with empty
    response fallback
+107/-3 
support-tickets.ts
Support ticket management system with role-based access   

src/lib/actions/support-tickets.ts

  • New server action file for managing customer support tickets with full
    CRUD operations
  • Implements ticket creation, retrieval, messaging, and status
    management for customers and brand/admin users
  • Includes NFT ownership verification and automatic ticket assignment to
    brand users or admins
  • Provides ticket statistics and dashboard functionality with role-based
    access control
+390/-0 
nfts.ts
NFT data retrieval and statistics server actions                 

src/lib/actions/nfts.ts

  • New server action file for NFT management and retrieval with
    pagination and filtering
  • Exports interfaces for NFTWithDetails and NFTStats for type safety
  • Implements functions to fetch NFTs, get statistics by brand, and
    retrieve recent NFTs
  • Includes error handling for cases where TagNFT table may not exist yet
+377/-0 
route.ts
NFT claim endpoint with rate limiting and validation         

src/app/api/scan/claim-nft/route.ts

  • New API endpoint for claiming NFT collectibles with POST and GET
    methods
  • Implements rate limiting, CSRF validation, and wallet address
    verification
  • Validates first-hand tag claims and checks NFT availability before
    minting
  • Includes comprehensive error handling with Indonesian language
    messages
+282/-0 
useWeb3.ts
Web3 wallet integration hook for MetaMask                               

src/hooks/useWeb3.ts

  • New React hook for Web3 wallet integration with MetaMask support
  • Provides wallet connection, disconnection, and network switching
    functionality
  • Includes Base Sepolia chain configuration and event listeners for
    account/chain changes
  • Exports utility functions for formatting addresses and generating
    explorer URLs
+262/-0 
gemini-image.ts
Gemini API image generation for NFT collectibles                 

src/lib/gemini-image.ts

  • New module for generating NFT collectible art using Google's Gemini
    API
  • Exports generateNFTImage function with detailed prompt engineering for
    product-specific artwork
  • Includes fallback placeholder image generation using SVG and sharp
    library
  • Handles API errors gracefully with detailed error messages
+183/-0 
types.ts
Ticket management types and configuration                               

src/app/manage/tickets/components/types.ts

  • New types file defining ticket-related interfaces and configuration
    objects
  • Exports TicketMessage, TicketAttachment, TicketListItem, and
    TicketFull interfaces
  • Includes status and priority configuration with UI styling properties
  • Provides category labels for ticket categorization
+136/-0 
tags.ts
Tag scan location mapping and statistics                                 

src/lib/actions/tags.ts

  • Added new getAllTagScanLocations function for retrieving tag scan
    locations with statistics
  • Exports ScanLocationPoint and TagScanMapStats types for map display
    functionality
  • Implements role-based access control for admin and brand users
  • Provides location filtering and statistics aggregation for scan data
+118/-0 
dashboard.ts
Dashboard stats with NFT count support                                     

src/lib/actions/dashboard.ts

  • Added DashboardStats interface with nfts field for type safety
  • Implemented NFT count retrieval with error handling for missing TagNFT
    table
  • Added graceful fallback to 0 NFTs when table doesn't exist
  • Updated both admin and brand user dashboard stats to include NFT
    counts
+39/-1   
products.ts
Product statistics retrieval function                                       

src/lib/actions/products.ts

  • Added new getProductStats function for retrieving product statistics
  • Implements role-based filtering for admin and brand users
  • Calculates products with and without tags with proper filtering
  • Returns comprehensive statistics object with active/inactive product
    counts
+48/-0   
constants.ts
Add NFT contract support and wallet utilities                       

src/lib/constants.ts

  • Added NFT_CONTRACT_ADDRESS environment variable to blockchain
    configuration
  • Created SUPPORTED_CHAIN constant with Base Sepolia network details for
    Web3 wallet connections
  • Added getNFTExplorerUrl() helper function to generate NFT explorer
    URLs
  • Added formatAddress() utility function to format wallet addresses for
    display
+26/-0   
route.ts
Refactor location verification to use AI analysis               

src/app/api/verify/route.ts

  • Added explicit type annotation for product mapping parameter
  • Removed location mismatch detection logic from verification endpoint
  • Replaced with comment indicating AI analysis handles geographic
    context better
+4/-18   
brands.ts
Add brand statistics server action                                             

src/lib/actions/brands.ts

  • Added new getBrandStats() server action to fetch brand statistics
  • Returns counts for total brands, active brands, products, and brands
    with/without products
  • Requires admin authentication
+28/-0   
users.ts
Add user statistics server action                                               

src/lib/actions/users.ts

  • Added new getUserStats() server action to fetch user statistics
  • Returns counts for total users, active users, admins, and brand users
  • Requires admin authentication
+20/-0   
index.ts
Re-export NFT claim card component                                             

src/app/scan/components/index.ts

  • Added re-export of NFTClaimCard from verify components
  • Enables NFT claiming functionality in scan flow
+2/-0     
index.ts
Export NFT claim card component                                                   

src/app/verify/[code]/components/index.ts

  • Added export of NFTClaimCard component
  • Enables NFT claiming functionality in verification flow
+1/-0     
ETagCollectible.sol
Add ERC721 NFT collectible contract                                           

smartcontracts/contracts/ETagCollectible.sol

  • Created new ERC721 NFT contract for first-hand tag claimers
  • Implements minting, tracking, and admin management of collectible NFTs
  • Includes role-based access control and pausable functionality
  • Maps tag codes to token IDs with one NFT per tag guarantee
+193/-0 
globals.css
Add accessibility and performance CSS                                       

src/app/globals.css

  • Added reduced motion media query for accessibility support
  • Added GPU acceleration hints for blur effects with will-change and
    transform
+21/-0   
page.tsx
Refactor explorer with NFT support and components               

src/app/explorer/page.tsx

  • Refactored explorer page to use new component-based architecture
  • Added NFT collectibles tab with grid display and pagination
  • Extracted search, stats, transactions, and events into separate
    components
  • Integrated landing page Navbar and Footer components
  • Added background effects and improved responsive design
+145/-554
ticket-list.tsx
Add ticket list component with filtering                                 

src/app/manage/tickets/ticket-list.tsx

  • Created new ticket list component with filtering and statistics
  • Implements memoized filtering by status and stats calculation
  • Displays ticket cards with empty state handling
+64/-0   
Tests
14 files
support-tickets.test.ts
Support tickets action tests with authorization and workflow
validation

src/lib/actions/support-tickets.test.ts

  • Comprehensive test suite for support ticket actions with 10 test
    groups covering 40+ test cases
  • Tests NFT ownership verification, ticket creation, customer messaging,
    and brand ticket management
  • Validates authorization checks, ticket status updates, and statistics
    aggregation
  • Includes tests for attachment handling and ticket reassignment logic
+621/-0 
nfts.test.ts
NFT actions test suite with access control and pagination

src/lib/actions/nfts.test.ts

  • Test suite for NFT retrieval and statistics actions with 4 main test
    groups
  • Validates pagination, search filtering, and brand-specific access
    control
  • Tests NFT statistics calculation and handles edge cases like missing
    products
  • Includes tests for database errors and graceful fallbacks
+383/-0 
test-nft-full-flow.ts
Full NFT generation and minting flow test with Gemini integration

scripts/test-nft-full-flow.ts

  • End-to-end test script for complete NFT flow: Gemini art generation →
    R2 upload → blockchain minting
  • Generates unique AI artwork using Gemini API with product-specific
    prompts
  • Uploads image and metadata to Cloudflare R2 and mints NFT on Base
    Sepolia blockchain
  • Includes comprehensive error handling and local file saving for
    verification
+423/-0 
tags.test.ts
Enhanced tag action tests with blockchain mocking               

src/lib/actions/tags.test.ts

  • Added mocking for blockchain tag-sync functions (updateTagChainStatus,
    revokeTagOnChain)
  • Expanded test coverage for deleteTag, toggleTagPublishStatus,
    updateChainStatus, and revokeTagOnBlockchain functions
  • Added comprehensive tests for new getAllTagScanLocations function with
    admin and brand user scenarios
  • Includes error handling tests for database and blockchain failures
+289/-1 
ETagCollectible.test.ts
ETagCollectible smart contract test suite                               

smartcontracts/test/ETagCollectible.test.ts

  • New comprehensive test suite for ETagCollectible smart contract with
    276 lines of test cases
  • Tests cover deployment, minting, tag lookup, access control, pausable
    functionality, and ERC721 standards
  • Includes tests for multiple mints, role-based permissions, and error
    conditions
  • Validates contract name, symbol, total supply, and interface support
+276/-0 
ai-agent.test.ts
AI agent actions test suite                                                           

src/lib/actions/ai-agent.test.ts

  • New test file for AI agent actions with mocking of getAIAgentContext
    function
  • Tests getAgentStats and getAgentContext functions for admin and brand
    users
  • Includes authentication checks and error handling scenarios
  • Validates proper parameter passing to AI agent library
+205/-0 
test-gemini-image.ts
Gemini image generation test script                                           

scripts/test-gemini-image.ts

  • New test script for validating Gemini image generation functionality
  • Demonstrates API usage with sample product data and tag codes
  • Saves generated images to file system with proper MIME type handling
  • Includes environment variable validation and detailed logging
+164/-0 
brands.test.ts
Enhanced brand action tests with error handling                   

src/lib/actions/brands.test.ts

  • Added error handling tests for createBrand, deleteBrand, and
    toggleBrandStatus functions
  • Added new getBrandStats test covering brand statistics retrieval
  • Includes console error spy verification for proper error logging
  • Tests database error scenarios with proper mock setup
+95/-1   
my-brand.test.ts
My-brand action error handling tests                                         

src/lib/actions/my-brand.test.ts

  • Added error handling tests for updateMyBrand, uploadMyBrandLogo, and
    removeMyBrandLogo functions
  • Includes database error scenarios with console error spy verification
  • Tests proper error message responses for each operation
  • Validates error logging with correct error context
+85/-1   
profile.test.ts
Profile action error handling tests                                           

src/lib/actions/profile.test.ts

  • Added error handling tests for updateProfile, uploadAvatar, and
    removeAvatar functions
  • Includes database error scenarios with proper mock setup and console
    spy verification
  • Tests error message responses and logging for file operations
  • Validates error handling for upload and removal operations
+77/-1   
dashboard.test.ts
Dashboard stats tests with NFT count support                         

src/lib/actions/dashboard.test.ts

  • Added tagNFT.count mock calls to all dashboard stats tests
  • Added test for NFT count error handling when TagNFT table doesn't
    exist
  • Updated expected results to include nfts field in all test assertions
  • Ensures graceful degradation when NFT functionality is unavailable
+31/-0   
products.test.ts
Product action tests with statistics                                         

src/lib/actions/products.test.ts

  • Added error handling test for deleteProduct function with database
    errors
  • Added new getProductStats tests for both admin and brand users
  • Includes statistics calculation for products with and without tags
  • Tests proper filtering of brand-specific product statistics
+74/-1   
users.test.ts
User action tests with statistics                                               

src/lib/actions/users.test.ts

  • Added error handling test for uploadUserAvatar function with database
    errors
  • Added new getUserStats test covering user statistics retrieval
  • Includes tests for total, active, inactive, admin, and brand user
    counts
  • Validates proper error logging and response handling
+52/-1   
index.ts
Mock implementations for ticket and NFT models                     

src/tests/mocks/index.ts

  • Added mock implementations for tagNFT, supportTicket, ticketMessage,
    and ticketAttachment Prisma models
  • Added $transaction mock for handling transactional operations
  • Extended createMockSession to support optional brandId parameter
  • Provides complete mock setup for new ticket and NFT functionality
+44/-0   
Documentation
1 files
faq-data.ts
Multilingual FAQ database with search and categorization 

src/lib/faq-data.ts

  • Comprehensive FAQ database with 5 categories (Getting Started, For
    Brands, For Consumers, Blockchain & NFT, Security, Troubleshooting)
  • 40+ FAQ items in Indonesian covering product verification, NFT
    claiming, wallet setup, and fraud detection
  • Includes search functionality to filter FAQs by query across questions
    and answers
  • Covers brand onboarding, consumer support, blockchain concepts, and
    common troubleshooting scenarios
+407/-0 
Configuration changes
5 files
next.config.ts
R2 domain configuration for NFT image optimization             

next.config.ts

  • Adds image optimization configuration for Cloudflare R2 domains
  • Allows *.r2.dev and *.cloudflarestorage.com hostnames for NFT image
    serving
  • Enables Next.js Image component to work with R2-hosted NFT artwork
+10/-0   
.npmrc
NPM configuration for dependency management                           

.npmrc

  • Adds npm configuration to allow legacy peer dependencies
  • Sets log level to error to reduce npm output verbosity
+2/-0     
deploy-nft.ts
ETagCollectible smart contract deployment script                 

smartcontracts/scripts/deploy-nft.ts

  • New deployment script for ETagCollectible smart contract on Base
    Sepolia
  • Handles role assignment to custom owner addresses from environment
    variables
  • Includes role verification and provides deployment instructions for
    contract verification
  • Outputs contract address and environment variable configuration
+129/-0 
hardhat.config.ts
Enable Sourcify contract verification                                       

smartcontracts/hardhat.config.ts

  • Added sourcify configuration with enabled flag to hardhat config
  • Enables contract source code verification on Sourcify
+3/-0     
generate-icons.sh
Add PWA icon generation script                                                     

scripts/generate-icons.sh

  • Created bash script to generate PWA icons from logo.png
  • Generates favicon sizes (16x16, 32x32, multi-size ico) and PWA icons
    (192x192, 512x512)
  • Includes error checking for ImageMagick and logo file existence
  • Provides colored output and helpful instructions
+91/-0   
Formatting
2 files
route.ts
TypeScript type safety improvements in scan endpoint         

src/app/api/scan/route.ts

  • Added explicit type annotations for array elements to improve
    TypeScript type safety
  • Refactored history building logic from imperative to declarative
    approach
  • Added TagScan type alias for better code readability
  • Improved type consistency in filter and map operations
+30/-22 
interact.ts
Add linting directive for error handling                                 

smartcontracts/scripts/interact.ts

  • Added ESLint disable comment for explicit any type in error handling
+1/-0     
Bug fix
1 files
auth.ts
Logout error handling for Next.js redirects                           

src/lib/actions/auth.ts

  • Enhanced logout function with error handling for Next.js redirect
    exceptions
  • Added checks for NEXT_REDIRECT error message and digest to properly
    handle redirect behavior
  • Ensures redirect exceptions are re-thrown while other errors are
    propagated
  • Improves logout flow reliability
+17/-1   
Refactoring
7 files
types.ts
Extract tag component types to dedicated file                       

src/app/manage/tags/_components/types.ts

  • Created new types file for tag management components
  • Defined Product, Tag, TagUrls, StampResult, and StatusUpdateResult
    types
  • Re-exported TagMetadata, ProductMetadata, PreviewStampingResult, and
    TagScansResult types
+43/-0   
index.ts
Create tag components barrel export                                           

src/app/manage/tags/_components/index.ts

  • Created barrel export file for tag management components
  • Exports all card components, dialogs, and types from tag management
    module
+11/-0   
index.ts
Create ticket components barrel export                                     

src/app/manage/tickets/components/index.ts

  • Created barrel export file for ticket management components
  • Exports types, list components, detail components, and sidebar cards
+18/-0   
index.ts
Create support components barrel export                                   

src/app/support/components/index.ts

  • Created barrel export file for support page components
  • Exports wallet connection, header, loading state, and ticket
    management components
+6/-0     
index.ts
Create FAQ components barrel export                                           

src/components/faq/index.ts

  • Created barrel export file for FAQ components
  • Exports search, accordion, category card, and content components
+4/-0     
tag-form-page.tsx
Refactor tag form page into composable components               

src/app/manage/tags/_components/tag-form-page.tsx

  • Refactored large component into smaller, composable sub-components
  • Extracted types to dedicated types file
  • Removed inline UI code and delegated to card components (TagInfoCard,
    TagResourcesCard, etc.)
  • Simplified main component logic while maintaining all functionality
+95/-1191
page.tsx
Refactor landing page into composable sections                     

src/app/page.tsx

  • Refactored landing page to use composable landing components
  • Extracted Hero, Features, Industries, FAQ, CTA, and Footer into
    separate modules
  • Simplified main page to component composition
  • Added background effects and improved visual hierarchy
+21/-444
Additional files
98 files
.env.example +10/-0   
CODEOWNERS +6/-0     
ci.yml +85/-3   
deploy.yml +14/-0   
CLAUDE.md +91/-10 
Dockerfile +14/-7   
README.md +113/-283
ROADMAP.md +105/-3 
TAG_STATUS_ARCHITECTURE.md +0/-250 
lighthouserc.json +30/-0   
package.json +10/-2   
schema.prisma +88/-2   
manifest.json +64/-0   
ETagRegistry.abi.json +0/-456 
events-table.tsx +119/-0 
explorer-header.tsx +40/-0   
nfts-grid.tsx +230/-0 
search-card.tsx +217/-0 
stats-cards.tsx +115/-0 
transactions-table.tsx +241/-0 
page.tsx +53/-0   
page.tsx +306/-0 
layout.tsx +49/-1   
page.tsx +197/-57
brand-stats-cards.tsx +103/-0 
page.tsx +43/-6   
layout.tsx +9/-24   
brand-info-form.tsx +34/-9   
brand-logo-form.tsx +35/-12 
page.tsx +287/-0 
page.tsx +228/-0 
page.tsx +48/-6   
product-stats-cards.tsx +101/-0 
avatar-form.tsx +33/-10 
password-form.tsx +38/-13 
profile-form.tsx +34/-11 
sidebar.tsx +40/-0   
product-selection-card.tsx +126/-0 
publish-settings-card.tsx +86/-0   
revoke-dialog.tsx +147/-0 
scan-history-card.tsx +148/-0 
stamping-card.tsx +72/-0   
stamping-dialog.tsx +196/-0 
tag-info-card.tsx +177/-0 
tag-metadata-card.tsx +181/-0 
tag-resources-card.tsx +136/-0 
page.tsx +60/-6   
scan-map-section.tsx +76/-0   
scan-stats-cards.tsx +99/-0   
page.tsx +20/-0   
ticket-detail.tsx +107/-0 
conversation-card.tsx +133/-0 
empty-state.tsx +29/-0   
sidebar-cards.tsx +200/-0 
stats-cards.tsx +91/-0   
ticket-card.tsx +136/-0 
ticket-filter.tsx +52/-0   
ticket-header.tsx +87/-0   
ticket-info-card.tsx +81/-0   
page.tsx +91/-0   
page.tsx +53/-4   
user-stats-cards.tsx +97/-0   
page.tsx +281/-125
blockchain-metadata-card.tsx +21/-23 
claim-success-card.tsx +7/-4     
loading-card.tsx +6/-6     
location-denied-card.tsx +10/-7   
location-permission-dialog.tsx +22/-16 
product-info-card.tsx +10/-12 
question-card.tsx +19/-13 
scan-header.tsx +6/-6     
scan-history-card.tsx +15/-15 
scan-info-card.tsx +13/-13 
scanner-card.tsx +13/-13 
page.tsx +125/-94
connect-wallet.tsx +125/-0 
loading-state.tsx +12/-0   
new-ticket-form.tsx +241/-0 
support-header.tsx +57/-0   
ticket-detail.tsx +196/-0 
ticket-list.tsx +192/-0 
page.tsx +11/-0   
support-client.tsx +236/-0 
nft-claim-card.tsx +391/-0 
faq-accordion.tsx +90/-0   
faq-category-card.tsx +76/-0   
faq-content.tsx +114/-0 
faq-search.tsx +46/-0   
BlockchainVisualization.tsx +299/-0 
CTA.tsx +57/-0   
FAQ.tsx +62/-0   
Features.tsx +185/-0 
Footer.tsx +159/-0 
Hero.tsx +111/-0 
Industries.tsx +155/-0 
Navbar.tsx +174/-0 
scan-location-map.tsx +278/-0 
pagination.tsx +163/-0 

igun997 and others added 30 commits December 2, 2025 22:14
…d use `KEY=VALUE` syntax for environment variables, and add a type annotation in the scan API.
feat: add complete database seeding script and command for realistic …
feat: Implement statistics cards and scan location maps for managemen…
feat: Add pagination to manage pages and introduce tests for AI agent…
Co-authored-by: qodo-code-review[bot] <151058649+qodo-code-review[bot]@users.noreply.github.com>
fix: apply codeowner to prevent crash
feat: Add initial landing page with hero, features, and CTA sections.
* ci: Add develop branch to push triggers and introduce a deploy job for develop.

* feat: separate deployment logic into a new workflow, update CI workflow triggers, and add CI/CD badges to README.
feat: introduce explorer and landing pages, enhance scan features, an…
Add chown option to COPY command for public directory.
…h Gemini image generation and Web3 integration.
refactor: move `formatAddress` utility from `useWeb3` hook to `consta…
igun997 and others added 26 commits December 4, 2025 10:12
…se models, API endpoints, and dedicated UI for customer submission and brand management.
…w UI components and refactoring related API endpoints.
… updated branding, and enhanced form styling with new icons.
feat: Redesign login and register pages with a new two-column layout,…
…ing, revoking, and detailed information display.
feat: Implement new UI components for tag management, including stamp…
feat: implement new FAQ page with search, categories, and accordion c…
Bumps [next](https://github.com/vercel/next.js) from 16.0.6 to 16.0.7.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](vercel/next.js@v16.0.6...v16.0.7)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.0.7
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [jws](https://github.com/brianloveswords/node-jws) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/brianloveswords/node-jws/releases)
- [Changelog](https://github.com/auth0/node-jws/blob/master/CHANGELOG.md)
- [Commits](auth0/node-jws@v4.0.0...v4.0.1)

---
updated-dependencies:
- dependency-name: jws
  dependency-version: 4.0.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
chore(deps): bump jws from 4.0.0 to 4.0.1
chore(deps): bump next from 16.0.6 to 16.0.7
… section content and styling, and update route documentation.
Remove RocketJourney component and GSAP dependencies, refine Features…
feat: Refine UI animations, improve code structure, update documentat…
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🔴
Hardcoded credentials

Description: The script creates a default admin user with a hardcoded weak password ('admin2024') and
prints all brand user credentials (including these default plaintext passwords) to stdout,
risking credential leakage and encouraging insecure default creds.
seed-complete.ts [1096-1111]

Referred Code
if (!adminExists) {
  const adminPassword = await bcrypt.hash('admin2024', 10);
  await prisma.user.create({
    data: {
      name: 'Super Admin',
      email: 'admin@etags.app',
      password: adminPassword,
      role: 'admin',
      status: 1,
      onboarding_complete: 1,
    },
  });
  console.log('\nAdmin user created: admin@etags.app / admin2024');
}

// Summary
Invalid contract address

Description: The metadata published to R2 may include a default placeholder contract address ('0x...')
when the CONTRACT_ADDRESS env var is absent, potentially causing clients to trust or
reference an invalid on-chain address in public verification data.
seed-complete.ts [1039-1058]

Referred Code
      logo_url: brand.logo_url,
    },
  },
],
distribution: {
  region: distribution.region,
  country: distribution.country,
  channel: distribution.channel,
  intended_market: distribution.market,
},
verification: {
  qr_code_url: '',
  verify_url: `https://etags.app/verify/${tagCode}`,
  blockchain: {
    network: 'Base Sepolia',
    chain_id: 84532,
    contract_address: process.env.CONTRACT_ADDRESS || '0x...',
    transaction_hash: tag.hash_tx,
  },
},
Excessive sensitive logging

Description: The test script logs sensitive blockchain transaction details and deterministic tag codes
to stdout, including full transaction hashes and public URLs, which could leak operational
details if run in shared CI logs or production-like environments.
test-nft-full-flow.ts [318-417]

Referred Code
// Test data
const tagCode = `ETAG-FULL-${Date.now()}`;
const ownerAddress =
  process.argv[2] || new ethers.Wallet(config.blockchain.adminWallet).address;
const productInfo: ProductInfo = {
  name: 'Premium Leather Wallet',
  brand: 'Luxury Brand Co.',
  description:
    'Handcrafted genuine leather bifold wallet with RFID protection',
};

console.log('\n📋 Test Configuration:');
console.log(`   Tag Code: ${tagCode}`);
console.log(`   Owner: ${ownerAddress}`);
console.log(`   Product: ${productInfo.name}`);
console.log(`   Brand: ${productInfo.brand}`);

try {
  // Step 1: Generate art
  const imageBuffer = await generateNFTArt(tagCode, productInfo);


 ... (clipped 79 lines)
TOCTOU mint race

Description: The minting step does not set an explicit gas limit, nonce, or employ safeguards against
reentrancy/duplicates beyond a single isTagMinted check, creating a race condition window
where concurrent runs may mint the same tag if the check and transaction are not atomic.
test-nft-full-flow.ts [239-289]

Referred Code
}

const provider = new ethers.JsonRpcProvider(config.blockchain.rpcUrl);
const wallet = new ethers.Wallet(config.blockchain.adminWallet, provider);
const contract = new ethers.Contract(
  config.blockchain.contractAddress,
  NFT_ABI,
  wallet
);

console.log(`   Contract: ${config.blockchain.contractAddress}`);
console.log(`   Minter: ${wallet.address}`);
console.log(`   Owner: ${ownerAddress}`);
console.log(`   Metadata: ${metadataUrl}`);

// Check if already minted
const isMinted = await contract.isTagMinted(tagCode);
if (isMinted) {
  throw new Error(`Tag ${tagCode} already has an NFT minted`);
}



 ... (clipped 30 lines)
Invalid IP generation

Description: The generateIP function can produce IPs in reserved/broadcast ranges (e.g., 0.x.x.x or
255.x.x.x) due to inclusive ranges, potentially polluting datasets and bypassing
validation logic relying on realistic IPs.
seed-complete.ts [524-529]

Referred Code
  return fp;
}

function generateIP(): string {
  return `${randomInt(1, 255)}.${randomInt(0, 255)}.${randomInt(0, 255)}.${randomInt(1, 254)}`;
}
Misleading security claims

Description: The FAQ claims gasless/sponsored transactions and no need for cryptocurrency; if untrue in
some flows, it could mislead users about transaction costs and safety, constituting a
security communication risk (social engineering surface).
faq-data.ts [265-271]

Referred Code
 Biaya gas ditanggung oleh sistem
 Anda hanya perlu wallet Web3 (seperti MetaMask) untuk menerima NFT`,
      },
      {
        id: 'setup-wallet',
        question: 'Bagaimana cara setup wallet Web3?',
        answer: `Untuk setup MetaMask:
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Weak Error Context: The script throws generic errors for missing configuration and blockchain/Gemini failures
without structured context or retry/fallback, and may exit the process without logging key
variable values needed for diagnosis.

Referred Code
  if (!config.gemini.apiKey) {
    throw new Error('GEMINI_API_KEY is not set');
  }

  const ai = new GoogleGenAI({ apiKey: config.gemini.apiKey });

  const prompt = `Create a unique digital collectible artwork for an authentic product ownership certificate.

Product Details:
- Product Name: ${productInfo.name}
- Brand: ${productInfo.brand}
${productInfo.description ? `- Description: ${productInfo.description}` : ''}
- Tag Code: ${tagCode}

Art Requirements:
1. Style: Modern, premium, certificate-like digital art
2. Theme: Authenticity, ownership, blockchain verification
3. Elements to include:
   - Abstract representation of the product category
   - Brand identity elements (colors, patterns)


 ... (clipped 213 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Sensitive Info Leak: Console logs print on-chain transaction hashes, wallet addresses, contract address, and
detailed failure objects which may expose internal details in user-facing terminals
without segregation between user-safe and internal logs.

Referred Code
async function mintNFT(
  tagCode: string,
  ownerAddress: string,
  metadataUrl: string
): Promise<{ tokenId: string; txHash: string }> {
  console.log('\n⛓️  Step 3: Minting NFT on blockchain...');

  if (!config.blockchain.contractAddress || !config.blockchain.adminWallet) {
    throw new Error('Blockchain config is not set');
  }

  const provider = new ethers.JsonRpcProvider(config.blockchain.rpcUrl);
  const wallet = new ethers.Wallet(config.blockchain.adminWallet, provider);
  const contract = new ethers.Contract(
    config.blockchain.contractAddress,
    NFT_ABI,
    wallet
  );

  console.log(`   Contract: ${config.blockchain.contractAddress}`);
  console.log(`   Minter: ${wallet.address}`);


 ... (clipped 170 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Credentials Exposed: The script logs and prints plaintext brand and admin credential pairs to the console,
which constitutes sensitive data exposure in logs.

Referred Code
      password: adminPassword,
      role: 'admin',
      status: 1,
      onboarding_complete: 1,
    },
  });
  console.log('\nAdmin user created: admin@etags.app / admin2024');
}

// Summary
console.log('\n========================================');
console.log('  SEEDING COMPLETE!');
console.log('========================================');
console.log(`\n  Brands:          ${totalBrands}`);
console.log(`  Brand Users:     ${totalUsers}`);
console.log(`  Products:        ${totalProducts}`);
console.log(`  Tags:            ${totalTags}`);
console.log(`  Scans:           ${totalScans}`);
console.log(
  `  Suspicious Tags: ${suspiciousTags} (flagged for AI detection)`
);


 ... (clipped 10 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing Audit Logs: Newly added seed operations perform critical data writes (user/brand/product/tag creation
and suspicious tag flagging) without emitting structured audit logs identifying actor,
action, and outcome.

Referred Code
await initR2();

// Clean existing data if requested
if (CLEAN_DATA) {
  console.log('Cleaning existing data...');
  await prisma.tagScan.deleteMany();
  await prisma.tag.deleteMany();
  await prisma.product.deleteMany();
  await prisma.user.deleteMany({ where: { role: 'brand' } });
  await prisma.brand.deleteMany();
  console.log('Existing data cleaned.\n');
}

let totalBrands = 0;
let totalUsers = 0;
let totalProducts = 0;
let totalTags = 0;
let totalScans = 0;
let suspiciousTags = 0;

// Placeholder image service


 ... (clipped 215 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Env Validation Gaps: While some env vars are checked, the script defaults RPC URL and proceeds to use admin
private key from ADMIN_WALLET without format validation or secure handling checks,
potentially risking misuse if variables are malformed.

Referred Code
const config = {
  gemini: {
    apiKey: process.env.GEMINI_API_KEY || '',
  },
  r2: {
    accountId: process.env.R2_ACCOUNT_ID || '',
    accessKeyId: process.env.R2_ACCESS_KEY_ID || '',
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY || '',
    bucket: process.env.R2_BUCKET || '',
    publicDomain: process.env.R2_PUBLIC_DOMAIN || '',
  },
  blockchain: {
    rpcUrl: process.env.BLOCKCHAIN_RPC_URL || 'https://sepolia.base.org',
    contractAddress: process.env.NFT_CONTRACT_ADDRESS || '',
    adminWallet: process.env.ADMIN_WALLET || '',
  },
};

// NFT Contract ABI
const NFT_ABI = [


 ... (clipped 64 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix N+1 query in statistics calculation

Refactor the getNFTStats function to eliminate the N+1 query problem by fetching
all products and tags first, then performing the aggregation in memory to avoid
excessive database calls.

src/lib/actions/nfts.ts [294-322]

-// Get counts by brand
-const allNfts = (await db.tagNFT.findMany({
-  include: {
-    tag: true,
-  },
-})) as RawTagNFT[];
+// Get counts by brand efficiently using groupBy
+const allProducts = await prisma.product.findMany({
+  include: { brand: true },
+});
+const productIdToBrand = new Map(
+  allProducts.map((p) => [p.id, p.brand])
+);
+
+const allTags = await db.tagNFT.findMany({
+  select: { tag: { select: { product_ids: true } } },
+});
 
 const brandCounts = new Map<number, { name: string; count: number }>();
 
-for (const nft of allNfts) {
-  const productIds = (nft.tag?.product_ids as number[]) || [];
+for (const { tag } of allTags) {
+  const productIds = (tag?.product_ids as number[]) || [];
   if (productIds.length > 0) {
-    const productData = await prisma.product.findFirst({
-      where: { id: { in: productIds } },
-      include: { brand: true },
-    });
-
-    if (productData) {
-      const existing = brandCounts.get(productData.brand.id);
+    // Assuming one product per tag for this aggregation
+    const brand = productIdToBrand.get(productIds[0]);
+    if (brand) {
+      const existing = brandCounts.get(brand.id);
       if (existing) {
         existing.count++;
       } else {
-        brandCounts.set(productData.brand.id, {
-          name: productData.brand.name,
+        brandCounts.set(brand.id, {
+          name: brand.name,
           count: 1,
         });
       }
     }
   }
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical N+1 query performance issue and provides a valid solution that significantly reduces database load, preventing future scalability problems.

High
Fix incorrect NFT count logic

Fix the NFT count logic for brand users by correctly mapping tag IDs and using a
filtered database query to count only the NFTs associated with the specific
brand.

src/lib/actions/dashboard.ts [99-109]

 // Get tags for this brand and count their NFTs
 const brandTagIds = allTags
   .filter((tag) => {
     const tagProductIds = Array.isArray(tag.product_ids)
       ? (tag.product_ids as number[])
       : [];
     return tagProductIds.some((id) => productIdsSet.has(id));
   })
-  .map((_, index) => index); // Just need count
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-nftsCount = brandTagIds.length > 0 ? await (prisma as any).tagNFT.count() : 0;
+  .map((tag) => tag.id); // Map to actual tag IDs
 
+if (brandTagIds.length > 0) {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  nftsCount = await (prisma as any).tagNFT.count({
+    where: {
+      tagId: { in: brandTagIds },
+    },
+  });
+}
+

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a significant bug where all NFTs are counted for a brand instead of only those associated with the brand's tags, and it provides a correct fix.

High
Fix incorrect pagination for brand users

Refactor the getNFTs function to filter NFTs by brand at the database level
instead of in the application layer to fix incorrect pagination and total counts
for brand users.

src/lib/actions/nfts.ts [156-167]

 // Filter by brand if needed (brand user restriction)
-let filteredNfts = nftsWithDetails;
 if (session.user.role === 'brand' && session.user.brandId) {
   const userBrandId = Number(session.user.brandId);
-  filteredNfts = nftsWithDetails.filter(
+  const filteredNfts = nftsWithDetails.filter(
     (nft) => nft.brand?.id === userBrandId
   );
+  // The `total` count was already correctly calculated by the modified `where` clause.
+  // We just need to filter the results of the current page.
+  return {
+    nfts: filteredNfts,
+    total,
+  };
 }
 
 return {
-  nfts: filteredNfts,
-  total: session.user.role === 'brand' ? filteredNfts.length : total,
+  nfts: nftsWithDetails,
+  total,
 };

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a significant pagination bug where filtering is done in-memory, leading to incorrect item counts and broken navigation for brand users.

Medium
Prevent potential minting race condition

Refactor the isTagNFTMinted function to prevent a potential race condition.
Prioritize checking the database first before falling back to a blockchain query
to ensure the application's state is the primary source of truth.

src/lib/nft-collectible.ts [100-115]

 export async function isTagNFTMinted(tagCode: string): Promise<boolean> {
   try {
+    // Check the database first as the primary source of truth for our system
+    const dbRecord = await prisma.tagNFT.findFirst({
+      where: {
+        tag: { code: tagCode },
+      },
+    });
+
+    if (dbRecord) {
+      return true;
+    }
+
+    // As a secondary check, query the blockchain directly
     const provider = getProvider();
     const contract = getNFTContract(provider);
     return await contract.isTagMinted(tagCode);
   } catch (error) {
     console.error('Error checking if tag NFT is minted:', error);
-    // Also check database as fallback
-    const dbRecord = await prisma.tagNFT.findFirst({
-      where: {
-        tag: { code: tagCode },
-      },
-    });
-    return !!dbRecord;
+    // In case of error, rely on what we know from our DB.
+    // If we reached here, dbRecord was null.
+    return false;
   }
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential race condition and proposes a more robust implementation by prioritizing the database as the source of truth, improving the reliability of the minting check.

Medium
Fix incorrect test assertion logic

Fix an incorrect test assertion in should create ticket and assign to brand user
if available. The test should assert that assigned_to is 5, not null, to match
the mocked user data.

src/lib/actions/support-tickets.test.ts [99-132]

 it('should create ticket and assign to brand user if available', async () => {
   mockPrismaClient.tagNFT.findFirst.mockResolvedValue({
     id: 1,
     tag: { product_ids: [1] },
   });
   mockPrismaClient.product.findFirst.mockResolvedValue({
     id: 1,
     brand_id: 1,
   });
   mockPrismaClient.user.findFirst.mockResolvedValue({
     id: 5,
     role: 'brand',
   });
   mockPrismaClient.supportTicket.create.mockResolvedValue({
     id: 1,
     ticket_number: 'TKT-20251204-ABCD',
   });
 
   const result = await createSupportTicket(ticketData);
 
   expect(result.success).toBe(true);
   expect(result.ticketNumber).toMatch(/^TKT-/);
   expect(mockPrismaClient.supportTicket.create).toHaveBeenCalledWith({
     data: expect.objectContaining({
       tag_id: 1,
       brand_id: 1,
       wallet_address: '0x123abc',
       category: 'defect',
       subject: 'Product Issue',
       description: 'My product has a defect',
-      assigned_to: null,
+      assigned_to: 5,
     }),
   });
 });

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a bug in a test case where the assertion contradicts the mocked behavior, ensuring the test accurately validates the ticket assignment logic.

Low
High-level
Decouple content from application code

Move large, hardcoded data for FAQs and database seeding from TypeScript files
into separate JSON or Markdown files. This change decouples content from code,
improving maintainability.

Examples:

src/lib/faq-data.ts [15-388]
scripts/seed-complete.ts [81-394]
const BRANDS: BrandData[] = [
  {
    name: 'Batik Keris',
    description:
      'Brand batik premium Indonesia sejak 1970. Menyediakan batik tulis dan cap berkualitas tinggi dengan motif tradisional Jawa.',
    category: 'Fashion',
    user: {
      email: 'admin@batikkeris.id',
      password: 'batik2024',
      name: 'Siti Rahayu',

 ... (clipped 304 lines)

Solution Walkthrough:

Before:

// File: src/lib/faq-data.ts
export const faqData: FAQCategory[] = [
  {
    id: 'getting-started',
    title: 'Memulai',
    items: [
      {
        id: 'what-is-etags',
        question: 'Apa itu Etags?',
        answer: 'Etags adalah platform...',
      },
      // ... 400+ lines of hardcoded FAQ items
    ],
  },
];

// File: scripts/seed-complete.ts
const BRANDS: BrandData[] = [
  {
    name: 'Batik Keris',
    description: 'Brand batik premium...',
    // ... 300+ lines of hardcoded brand and product data
  },
];

After:

// File: src/lib/faq-data.ts
import faqJson from './faq-data.json';

export const faqData: FAQCategory[] = faqJson;

// ... search logic remains the same

// File: data/faqs.json
[
  {
    "id": "getting-started",
    "title": "Memulai",
    "items": [{
      "id": "what-is-etags",
      "question": "Apa itu Etags?",
      "answer": "Etags adalah platform..."
    }]
  }
]

// File: scripts/seed-complete.ts
import brandsData from './seed-data/brands.json';
const BRANDS: BrandData[] = brandsData;
// ... seeding logic uses the imported data
Suggestion importance[1-10]: 7

__

Why: This is a valid architectural suggestion that correctly identifies hardcoded data in scripts/seed-complete.ts and src/lib/faq-data.ts, improving long-term maintainability, though it's not a functional bug.

Medium
General
Avoid hardcoding attachment metadata

Avoid hardcoding attachment metadata such as file_type and file_size by passing
this information from the client during the file upload process.

src/lib/actions/support-tickets.ts [105-114]

 attachments: data.attachments?.length
   ? {
-      create: data.attachments.map((url) => ({
-        file_url: url,
-        file_name: url.split('/').pop() || 'file',
-        file_type: 'image',
-        file_size: 0,
+      create: data.attachments.map((file) => ({
+        file_url: file.url,
+        file_name: file.name,
+        file_type: file.type,
+        file_size: file.size,
       })),
     }
   : undefined,

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that hardcoding attachment metadata like file_type and file_size is brittle and will cause issues, proposing a more robust data structure.

Medium
Optimize database query for efficiency

Optimize the calculation of productsWithTags for brand users by using a more
efficient, targeted database query instead of fetching large datasets and
filtering in memory.

src/lib/actions/products.ts [411-423]

 // Filter to only count user's products in tags
 let productsWithTags = 0;
 if (!isAdmin && userBrandId) {
+  const userProductsInTags = await prisma.tag.findMany({
+    where: {
+      products: {
+        some: {
+          brand_id: userBrandId,
+        },
+      },
+    },
+    select: { product_ids: true },
+  });
+  const userProductIdsInTags = new Set<number>();
+  for (const tag of userProductsInTags) {
+    const ids = tag.product_ids as number[];
+    ids.forEach((id) => userProductIdsInTags.add(id));
+  }
+  // To be fully correct, we should only count products of this brand
   const userProducts = await prisma.product.findMany({
-    where: { brand_id: userBrandId },
+    where: { brand_id: userBrandId, id: { in: [...userProductIdsInTags] } },
     select: { id: true },
   });
-  const userProductIds = new Set(userProducts.map((p) => p.id));
-  productsWithTags = [...productIdsInTags].filter((id) =>
-    userProductIds.has(id)
-  ).length;
+  productsWithTags = userProducts.length;
 } else {
   productsWithTags = productIdsInTags.size;
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a performance inefficiency and proposes a more optimized database query, though the provided improved_code is overly complex and still inefficient.

Low
  • More

@igun997 igun997 merged commit 1c8dfff into master Dec 6, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants