Appearance
Listings Management β
Overview β
The listings system is the core of AcqMarketplace, enabling sellers to create comprehensive business profiles and buyers to discover opportunities. This document covers the complete listing lifecycle, admin moderation, AI-powered creation assistance, and integration points.
Listing Lifecycle β
mermaid
graph LR
A[Draft] --> B[Pending Review]
B --> C{Admin Review}
C -->|Approved| D[Active]
C -->|Rejected| E[Needs Changes]
E --> B
D --> F[Under Offer]
F --> G[Sold]
D --> H[Archived]Search & Filters β
- Filter model (public Explore):
- Keys:
searchTerm,category,techStack[],businessAge,priceRange,profitRange,revenueRange,clientsRange,sortBy. - Source:
src/hooks/useExploreFilters.ts:4and:16-27.
- Keys:
- Query building and filter application:
- Active only:
.eq('status','active')βsrc/hooks/useEnhancedExploreQuery.ts:29. - Text search (title/description ilike):
:32-34. - Category:
:36-38. - Tech stack (array contains):
:40-48with tech name mapping viasrc/hooks/useTechStack.ts:17-21. - Business age (date range over
established_date)::50-70. - Price range:
:73-78. - Revenue range:
:81-85. - Profit range:
:87-92. - Customers range:
:94-99.
- Active only:
Sorting β
- Sponsored first:
order('is_sponsored', { ascending: false })βsrc/hooks/useEnhancedExploreQuery.ts:101-103. - Then by selected sort:
price-lowβpriceasc β:104-106price-highβpricedesc β:106-108monthly_revenueβ desc β:108-110monthly_profitβ desc β:110-112- default
created_atdesc β:112-114
- UI sort labels/toggles:
src/components/explore/ExploreFilters.tsx:61-71, 139-178.
Pagination modes β
- Pagination (9 items/page):
ITEMS_PER_PAGE=9βsrc/hooks/useEnhancedExploreQuery.ts:8,:164-195. - Infinite scroll (Load More):
useInfiniteQueryβ:141-159. - Mode selection:
Explore/ExploreInfiniteβExploreCoreβExploreMainβsrc/pages/Explore.tsx:5-7,src/pages/ExploreInfinite.tsx:5-7,src/components/explore/ExploreMain.tsx:13-16, 61-82.
Status Definitions β
| Status | Description | User Actions | Admin Actions |
|---|---|---|---|
| Draft | Being created by seller | Edit, Submit for Review | View Only |
| Pending | Awaiting admin approval | View Only | Approve, Reject, Comment |
| Active | Live on marketplace | Edit (limited), Manage Offers | Archive, Feature |
| Under Offer | Deal in progress | View Only, Manage Current Deal | Monitor, Mediate |
| Sold | Successfully transferred | View Only | Archive |
| Archived | Removed from marketplace | Reactivate Request | Restore |
Core Components β
Listing Creation Flow β
File: src/pages/CreateListing.tsxFeatures:
- Multi-step form with validation
- Image upload and management
- AI-Powered Project Analysis: Auto-populate fields from project URLs
- Mistral AI integration for intelligent data extraction
- Robust error handling and JSON parsing recovery
- Financial defaults generation for missing data
- Category normalization and structured description mapping
- Rich text descriptions with i18n
- Technology stack selection
- Financial metrics input
Create Listing Fields (Required vs Optional) β
Grouped by form sections. Fields marked Required reflect the UI (RequiredFieldIndicator) and must be provided to submit.
Basic Information (required)
- title / title_ro / title_en β Required
- category β Required
- website_url β Required
- screenshot_url β Required (general listing screenshot)
- images[] β Required (at least 1)
Overview (required)
- business_model / business_model_ro / business_model_en β Required
- established_date β Required
- number_of_customers β Required
Financial Data (required)
- monthly_revenue β Required
- monthly_profit β Required
- price β Required
- allow_negotiation β Optional (boolean)
- price_reasoning / price_reasoning_ro / price_reasoning_en β Optional
- revenue_metrics_screenshot β Optional
Traffic & Audience
- monthly_traffic β Required
- target_audience / target_audience_ro / target_audience_en β Optional
- traffic_metrics_screenshot β Optional
Business Details
- competitors / competitors_ro / competitors_en β Optional
- growth_opportunities / growth_opportunities_ro / growth_opportunities_en β Optional
- reason_for_selling / reason_for_selling_ro / reason_for_selling_en β Required
Resources & Technologies
- technologies[] β Optional
- included_assets[] β Optional
Additional Terms
- terms_conditions / terms_conditions_ro / terms_conditions_en β Optional
Administrator (evidence & docs)
- files (financial documents) β Admin-only UI
Notes:
- Many text fields are multilingual; when present, use the localized variant (e.g.,
*_ro,*_en). - Sensitive fields may be blurred for certain users; see Data Visibility below.
Field Validation Matrix (key rules) β
| Field | Type | Rules |
|---|---|---|
| title | text | 3β120 chars |
| category | text | must match listing_categories.slug |
| website_url | URL | valid http(s) URL |
| images | array[text] | min length 1, max 10 |
| screenshot_url | text | optional if imagesβ₯1, else required |
| business_model | text | 50β1500 chars |
| established_date | date | YYYY-MM-DD, <= today |
| number_of_customers | integer | >= 0 |
| monthly_revenue | numeric | >= 0 |
| monthly_profit | numeric | can be negative, recommended β₯ 0 |
| price | numeric | > 0 |
| monthly_traffic | integer | >= 0 |
| reason_for_selling | text | 50β1500 chars |
| price_reasoning | text | optional |
| target_audience | text | optional |
| competitors | text | optional |
| growth_opportunities | text | optional |
| terms_conditions | text | optional |
Listing Detail View β
File: src/pages/ListingDetail.tsxFeatures:
- Comprehensive business information
- Image gallery with lightbox
- Financial metrics (subscription-gated)
- Contact seller functionality
- Offer submission
Listings Browse/Search β
Files: src/pages/Explore.tsx, src/pages/ExploreInfinite.tsx, src/pages/ExploreCore.tsxFeatures:
- Advanced filtering (price, category, metrics)
- Sorting options (newest, price, revenue)
- Pagination and infinite scroll
- Saved searches and watchlist
- Featured listings priority
Data Model β
Core Table: listings β
sql
-- Key columns from migration analysis
id uuid PRIMARY KEY
seller_id uuid REFERENCES profiles(id)
title text NOT NULL
price numeric NOT NULL
status listing_status DEFAULT 'draft'
monthly_revenue numeric
monthly_profit numeric
monthly_traffic integer
category text NOT NULL
verification_status text DEFAULT 'pending'
featured boolean DEFAULT false
created_at timestamptz DEFAULT now()Related Tables β
- listing_views: Track visitor engagement
- admin_listing_comments: Admin feedback system
- offers: Purchase offers from buyers
- saved_searches: User search preferences
- favorites / watchlist: User watchlist items
Categories & Q&A β
- listing_categories: Used for category selection in the create-listing form (public SELECT; admin-managed)
- listing_questions (Q&A): Buyers ask questions; sellers/admin answer; visible on approved listings
Favorites & Watchlist β
- Hook:
src/hooks/useUserFavorites.ts:12-22readsfavorites.listing_idfor the current user, exposed in Explore βsrc/components/explore/ExploreMain.tsx:23-24, 38-71.
Admin Listings Management β
- Filters and status/category selectors:
src/components/admin/listings/ListingFilters.tsx:33-59, 61-71. - Sorting tabs with counts:
src/components/admin/listings/ListingSortTabs.tsx:28-39, 44-77. - Admin pages:
src/pages/admin/AdminListings.tsxandsrc/pages/admin/AdminListingsDetail.tsx.
Admin Moderation System β
Complete Listing Approval Flow β
The listing approval process is a critical part of maintaining platform quality and ensuring all listings meet our standards before becoming visible to users.
Flow Overview β
mermaid
graph TD
A[Seller Submits Listing] --> B[Status: Pending]
B --> C[Admin Reviews Listing]
C --> D{Review Decision}
D -->|Approve| E[Check Seller Verification]
E --> F{Seller Verified?}
F -->|Yes| G[Update Both Status & Verification]
F -->|No| H[Update Only Status]
G --> I[Listing Visible to All Users]
H --> J[Listing Visible to Verified Users Only]
D -->|Reject| K[Add Admin Comments]
K --> L[Notify Seller]
L --> M[Seller Makes Changes]
M --> B
D -->|Request Changes| N[Add Field-Specific Comments]
N --> LDetailed Approval Process β
1. Initial Review Phase
- Location:
/admin/listings(AdminListings.tsx) - Actions Available:
- View complete listing details
- Add field-specific comments
- Approve listing
- Reject listing
- Request specific changes
2. Approval Logic (Updated) When admin clicks "Approve":
typescript
// New approval logic in AdminListings.tsx
const approveListingMutation = useMutation({
mutationFn: async (listingId: string) => {
// 1. Check if seller is verified
const { data: listing } = await supabase
.from('listings')
.select(`
*,
seller:profiles!listings_seller_id_fkey (
seller_verification_status,
seller_verified
)
`)
.eq('id', listingId)
.single();
// 2. Determine verification status
const isSellerVerified = listing.seller?.seller_verified === true ||
listing.seller?.seller_verification_status === 'approved';
// 3. Update both status and verification_status if seller is verified
const updateData = { status: 'active' };
if (isSellerVerified) {
updateData.verification_status = 'approved';
}
// 4. Apply updates
const { data, error } = await supabase
.from('listings')
.update(updateData)
.eq('id', listingId)
.select()
.single();
return data;
}
});3. Visibility Rules After Approval
| Seller Status | Listing Status | Verification Status | Visibility |
|---|---|---|---|
| Verified | Active | Approved | β All Users (Public) |
| Not Verified | Active | Pending | β Only Owner & Admin |
4. RLS Policy Requirements
sql
-- Listings are visible to public only if:
-- 1. status = 'active' AND
-- 2. verification_status = 'approved' OR 'verified'
CREATE POLICY "listings_consolidated" ON listings
FOR ALL TO public
USING (
(seller_id = auth.uid()) OR
is_admin_user() OR
((status = 'active') AND (verification_status = ANY (ARRAY['approved', 'verified']))) OR
(auth.role() = 'service_role')
);Review Process Steps β
1. Initial Review
- Verify business information completeness
- Check financial document authenticity
- Validate contact information
- Review for policy compliance
2. Feedback System
- Field-specific comments using
admin_listing_comments - Resolution tracking per comment
- Email notifications to sellers
3. Approval Criteria
- Complete business description
- Valid financial metrics
- Appropriate categorization
- No policy violations
Admin Actions β
Approve Listing
typescript
// Automatic dual-update for verified sellers
const approveListing = async (listingId: string) => {
// Updates both status='active' AND verification_status='approved'
// Only if seller is already verified
}Reject Listing
typescript
const rejectListing = async (listingId: string, reason: string) => {
// Update status to 'rejected'
// Add rejection reason
// Notify seller with feedback
}Request Changes
typescript
const requestChanges = async (listingId: string, comments: AdminComment[]) => {
// Add field-specific comments
// Keep status as 'pending'
// Notify seller of required changes
}Common Issues & Solutions β
Issue: Listing approved but not visible to users Cause: Seller not verified, so only status was updated Solution: Verify seller first, then approve listing
Issue: Admin approval not working Cause: Missing seller verification check in approval logic Solution: Use updated approval mutation that checks seller verification
Issue: Listing visible to owner but not public Cause: verification_status still 'pending' Solution: Check seller verification status and update accordingly
Testing the Approval Flow β
Create Test Listing:
sqlINSERT INTO listings (title, status, verification_status, seller_id) VALUES ('Test Listing', 'pending', 'pending', 'seller-uuid');Test Approval as Admin:
- Go to
/admin/listings - Find pending listing
- Click "Approve"
- Verify both status and verification_status updated
- Go to
Verify Visibility:
- Check as unauthenticated user on
/explore - Should be visible if seller is verified
- Should be hidden if seller not verified
- Check as unauthenticated user on
Email Notifications β
Approval Notification:
- Sent to seller when listing approved
- Includes listing title and link
- Confirms visibility status
Rejection Notification:
- Sent to seller when listing rejected
- Includes admin comments and feedback
- Explains next steps for resubmission
Change Request Notification:
- Sent to seller when changes requested
- Includes field-specific comments
- Links to edit listing page
Subscription-Based Access Control β
Data Visibility (Blur Policy) β
Implemented via: blurred_fields (config), can_view_field() (verification), listing_view_secure (server-side masking projection)
- Full documentation and real field matrix: see
docs/architecture/blur-policy.md.
Implementation Pattern β
typescript
// Check field visibility based on user subscription
const canViewField = await supabase.rpc('can_view_field', {
p_listing: listingId,
p_field_key: 'monthly_profit',
p_user: userId
})
if (canViewField) {
// Show actual value
} else {
// Show blurred placeholder
}Search and Filtering β
See βSearch & Filtersβ above for the authoritative list and implementation references.
Featured Listings System β
Promotion Features β
- Priority Placement: Top of search results
- Enhanced Visibility: Special styling and badges
- Analytics Tracking: Views, clicks, conversion rates
- Scheduling: Start/end date management
Implementation β
sql
-- Featured listing promotion
UPDATE listings
SET
featured = true,
sponsored_start_date = NOW(),
sponsored_end_date = NOW() + INTERVAL '30 days'
WHERE id = $1Image Management β
Upload System β
Storage: Supabase Storage with CDN Allowed Formats: JPG, PNG, WebP Size Limits: 5MB per image, 10 images per listing
Security & Access
- Private bucket with signed URLs (see Security & Privacy β Storage & Media Security)
- Store only keys in DB; resolve signed URLs at render-time
typescript
// Image upload implementation
const uploadListingImage = async (file: File, listingId: string) => {
const fileExt = file.name.split('.').pop()
const filePath = `listings/${listingId}/${Date.now()}.${fileExt}`
const { data, error } = await supabase.storage
.from('listing-images')
.upload(filePath, file)
return data?.path
}Internationalization β
Multi-language Support β
All listing content supports Romanian and English:
title/title_ro/title_endescription/description_ro/description_enbusiness_model/business_model_ro/business_model_en
Content Management β
typescript
// Language-aware content retrieval
const getLocalizedContent = (listing: Listing, field: string, locale: string) => {
const localizedField = `${field}_${locale}`
return listing[localizedField] || listing[field] || ''
}Performance Optimizations β
Database Indexing β
sql
-- Key indexes for listing queries
CREATE INDEX idx_listings_status ON listings(status);
CREATE INDEX idx_listings_category ON listings(category);
CREATE INDEX idx_listings_price ON listings(price);
CREATE INDEX idx_listings_featured ON listings(featured, created_at);Caching Strategy β
- Listing Cards: React Query with 5-minute cache
- Listing Details: Fresh data on page load
- Search Results: 2-minute cache with invalidation
- Images: CDN caching with 1-year expiry
Integration Points β
With Offers System β
- Automatic offer notifications
- Seller dashboard integration
- Deal creation workflow
With Messaging System β
- Contact seller functionality
- Conversation initiation
- Notification delivery
With Analytics β
- View tracking and analytics
- Conversion funnel analysis
- Performance metrics
API Endpoints β
Core Listing Operations β
typescript
// Fetch public listings
GET /rest/v1/listings?status=eq.active
// Create new listing (authenticated)
POST /rest/v1/listings
// Update listing (owner only)
PATCH /rest/v1/listings?id=eq.{listing_id}
// Admin moderation
POST /rest/v1/rpc/admin_moderate_listingREST API Contracts (examples) β
http
POST /rest/v1/listings
Content-Type: application/json
{
"title": "AI SEO SaaS",
"category": "saas",
"website_url": "https://example.com",
"price": 9900,
"monthly_revenue": 800,
"monthly_profit": 500,
"monthly_traffic": 12000,
"business_model": "SaaS subscription",
"established_date": "2023-04-01",
"number_of_customers": 42,
"images": ["listings/{id}/main.webp"]
}
PATCH /rest/v1/listings?id=eq.{listing_id}
Content-Type: application/json
{
"price": 8900,
"price_reasoning": "Promo price for quick sale"
}
GET /rest/v1/listing_view_secure?id=eq.{listing_id}JSON Schema: Create Listing Payload β
json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://acqm/docs/schemas/create-listing.schema.json",
"title": "CreateListingPayload",
"type": "object",
"additionalProperties": false,
"properties": {
"title": { "type": "string", "minLength": 3, "maxLength": 120 },
"category": { "type": "string", "minLength": 1 },
"website_url": { "type": "string", "format": "uri" },
"price": { "type": "number", "exclusiveMinimum": 0 },
"monthly_revenue": { "type": "number", "minimum": 0 },
"monthly_profit": { "type": "number" },
"monthly_traffic": { "type": "integer", "minimum": 0 },
"business_model": { "type": "string", "minLength": 50, "maxLength": 1500 },
"established_date": { "type": "string", "format": "date" },
"number_of_customers": { "type": "integer", "minimum": 0 },
"images": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"maxItems": 10
},
"screenshot_url": { "type": "string" },
"allow_negotiation": { "type": "boolean" },
"price_reasoning": { "type": "string" },
"revenue_metrics_screenshot": { "type": "string" },
"target_audience": { "type": "string" },
"traffic_metrics_screenshot": { "type": "string" },
"competitors": { "type": "string" },
"growth_opportunities": { "type": "string" },
"reason_for_selling": { "type": "string", "minLength": 50, "maxLength": 1500 }
},
"required": [
"title",
"category",
"website_url",
"price",
"monthly_revenue",
"monthly_profit",
"monthly_traffic",
"business_model",
"established_date",
"number_of_customers",
"images",
"screenshot_url",
"reason_for_selling"
]
}listing_view_secure β Example Responses β
Blurred (public/free)
json
{
"id": "c1b6c5f4-1a2b-4b2e-9a0a-111111111111",
"title": "AI SEO SaaS",
"category": "saas",
"price": 9900,
"monthly_revenue": null,
"monthly_profit": null,
"monthly_traffic": null,
"number_of_customers": null,
"website_url": null,
"screenshot_url": "listings/..../cover.webp",
"traffic_metrics_screenshot": null,
"revenue_metrics_screenshot": null,
"main_image": "listings/..../main.webp",
"featured": false,
"verification_status": "approved",
"created_at": "2025-09-01T12:00:00.000Z"
}Visible (starter/pro/owner/admin)
json
{
"id": "c1b6c5f4-1a2b-4b2e-9a0a-111111111111",
"title": "AI SEO SaaS",
"category": "saas",
"price": 9900,
"monthly_revenue": 800,
"monthly_profit": 500,
"monthly_traffic": 12000,
"number_of_customers": 42,
"website_url": "https://example.com",
"screenshot_url": "listings/..../cover.webp",
"traffic_metrics_screenshot": "listings/..../traffic.webp",
"revenue_metrics_screenshot": "listings/..../revenue.webp",
"main_image": "listings/..../main.webp",
"featured": false,
"verification_status": "approved",
"created_at": "2025-09-01T12:00:00.000Z"
}Notes:
- Null values indicate masked (blurred) fields by the secure view.
- Owners and admins always receive fully visible values.
Custom RPC Functions β
get_admin_listing_comments(listing_id): Retrieve admin feedbacksend_admin_comments_to_user(listing_id): Notify seller of feedbackcan_view_field(listing_id, field_key, user_id): Check field visibility
Security Considerations β
Row Level Security (RLS) β
sql
-- Listings visibility policy
CREATE POLICY "Public can view active listings"
ON listings FOR SELECT
TO public
USING (status = 'active' AND verification_status = 'approved');
-- Sellers can manage own listings
CREATE POLICY "Sellers manage own listings"
ON listings FOR ALL
TO authenticated
USING (seller_id = auth.uid());Data Validation β
- Server-side validation for all financial data
- Image content scanning for inappropriate material
- Text content moderation for spam/scams
- Business verification document validation
Testing Strategy β
Unit Tests β
- Listing creation workflow
- Filter and search logic
- Data validation functions
- Permission checking
Integration Tests β
- Admin moderation flow
- Subscription access control
- Image upload and processing
- Email notification delivery
E2E Tests β
- Complete listing creation
- Buyer browsing experience
- Admin review process
- Mobile responsiveness
Troubleshooting β
Common Issues β
Listing Not Appearing in Search
- Check listing status (must be 'active')
- Verify verification_status is 'approved'
- Confirm category assignment
- Review RLS policies
Image Upload Failures
- Verify file size (<5MB)
- Check file format (JPG/PNG/WebP)
- Confirm Supabase Storage permissions
- Review bucket policy configuration
Admin Comments Not Saving
- Verify admin role permissions
- Check admin_listing_comments table access
- Confirm notification triggers are working
- Review RPC function permissions
Next Steps β
- Enhanced Search: Implement Elasticsearch for advanced search
- AI Features: Auto-categorization and valuation assistance
- Video Content: Support for business demo videos
- Verification: Enhanced business verification process
- Analytics: Advanced listing performance metrics
Related Documentation:
- Offers System - Purchase offer workflow
- Admin Guide - Moderation tools and processes
- Data Model - Complete database schema