Nano Banana Pro
Agent skill for nano-banana-pro
**Completion Date**: 2025-11-12
Sign in to like and favorite skills
Completion Date: 2025-11-12
Successfully implemented the frontend UI for CSV import functionality with drag-and-drop support, real-time error display, and full authentication integration. Providers can now bulk import offerings directly from the web dashboard.
website/src/lib/services/api.ts)importProviderOfferingsCSV() - Import CSV with authenticated requests (29 lines)CsvImportResult interface - Response type with success count and errorsCsvImportError interface - Individual error detailsCode Stats: ~35 lines for API integration
website/src/lib/components/CSVImportDialog.svelte)Code Stats: ~280 lines for complete dialog component
website/src/routes/dashboard/offerings/+page.svelte)Code Stats: ~35 lines for integration
/dashboard/offerings)Import Button:
<button class="px-6 py-3 bg-white/10 backdrop-blur rounded-lg"> <svg><!-- Upload icon --></svg> Import CSV </button>
Dialog Features:
Manual Testing:
cd website && npm run build # ✅ BUILD SUCCESSFUL (6.90s)
1. Offerings Page with Import Button:
2. CSV Import Dialog:
3. File Selected State:
4. Import Results:
Completion Date: 2025-11-12
Successfully implemented CSV import functionality with full validation, error reporting, and upsert support. Providers can now bulk import/update offerings from CSV files with detailed per-row error feedback.
api/src/database/offerings.rs)import_offerings_csv() - Import offerings from CSV with validation and error tracking (49 lines)parse_csv_record() - Parse CSV row into CreateOfferingParams with type conversion (96 lines)Code Stats: Added ~145 lines for CSV import Tests: 4 new comprehensive tests (success, errors, upsert, unauthorized) Test Results: 105/105 passing (+4 from Phase 2)
api/src/api_handlers.rs)import_provider_offerings_csv() - POST endpoint for CSV uploadCsvImportResult struct with success count and error listCsvImportError struct with row number and messageImportOfferingsQuery struct for upsert parameterCode Stats: ~54 lines for handler + response types
api/src/main.rs)POST /api/v1/providers/{pubkey}/offerings/import?upsert=true - Import CSVCode Stats: ~4 lines for route registration
?upsert=true to update existing offerings by offering_idThe import expects the same 38-column format as the export/template:
Example CSV Row:
off-1,Server Name,Description,,USD,100.0,0.0,public,dedicated,,monthly,in_stock,Intel,2,8,3.5GHz,Xeon,ECC,DDR4,32GB,2,2TB,1,500GB,true,1Gbps,10000,US,NYC,40.7,-74.0,cPanel,RTX3090,1,720,"BTC,ETH","SSD,NVMe","Ubuntu,Debian"
✅ test_csv_import_success - Imports 2 offerings with full validation ✅ test_csv_import_with_errors - Handles 3 errors, 1 success correctly ✅ test_csv_import_upsert - Updates existing + creates new offerings ✅ test_csv_import_unauthorized - Ensures proper ownership boundaries
Success Response:
{ "success": true, "data": { "success_count": 5, "errors": [ {"row": 3, "message": "offering_id is required"}, {"row": 7, "message": "Invalid number at column 5"} ] } }
cargo test --bin api-server # ✅ 105/105 PASSING (+4 from Phase 2) cargo clippy --bin api-server # ✅ CLEAN (only pre-existing warnings) cargo make # ✅ ALL TESTS PASS
Import CSV (Create Only):
POST /api/v1/providers/{pubkey}/offerings/import Content-Type: text/plain offering_id,offer_name,... off-1,Server 1,... off-2,Server 2,...
Import CSV with Upsert:
POST /api/v1/providers/{pubkey}/offerings/import?upsert=true Content-Type: text/plain offering_id,offer_name,... off-1,Updated Name,... # Updates existing off-3,New Server,... # Creates new
Response:
{ "success": true, "data": { "success_count": 2, "errors": [] } }
Phase 4: Frontend UI ✅ COMPLETED (see below)
Completion Date: 2025-11-12
Successfully implemented bulk operations for server offerings, including CSV export/template generation and bulk status updates. All operations are authenticated and fully tested.
api/src/database/offerings.rs)bulk_update_stock_status() - Update stock_status for multiple offerings with ownership verificationCode Stats: Added ~50 lines for bulk operations method Tests: 3 new tests (success, unauthorized, empty array) Test Results: 24/24 passing (including 21 from Phase 1)
api/src/api_handlers.rs)bulk_update_provider_offerings_status() - PUT endpoint for bulk status updatesexport_provider_offerings_csv() - GET endpoint for CSV exportgenerate_csv_template() - GET endpoint for CSV template downloadBulkUpdateStatusRequest struct for request bodyCode Stats: ~150 lines across 3 handlers + request struct
api/src/main.rs)PUT /api/v1/providers/{pubkey}/offerings/bulk-status - Bulk update statusGET /api/v1/providers/{pubkey}/offerings/export - Export offerings to CSVGET /api/v1/offerings/template - Download CSV templateCode Stats: ~12 lines for route registration
✅ test_bulk_update_stock_status_success - Updates 3 offerings ✅ test_bulk_update_stock_status_unauthorized - Rejects unauthorized updates ✅ test_bulk_update_stock_status_empty - Handles empty array gracefully
cargo build --bin api-server --release # ✅ SUCCESS cargo clippy --bin api-server # ✅ CLEAN (only pre-existing warnings) cargo test --bin api-server # ✅ 101/101 PASSING (+3 from Phase 1)
Bulk Update Stock Status:
PUT /api/v1/providers/{pubkey}/offerings/bulk-status { "offering_ids": [1, 2, 3], "stock_status": "out_of_stock" }
Export to CSV:
GET /api/v1/providers/{pubkey}/offerings/export # Returns CSV file with all offerings
Download Template:
GET /api/v1/offerings/template # Returns empty CSV template with headers
Phase 3 (Future): CSV Import
Phase 4 (Future): Frontend UI
Completion Date: 2025-11-12
Successfully implemented full CRUD functionality for server offerings in the API server. All operations are authenticated, authorized, and fully tested.
api/src/database/offerings.rs)CreateOfferingParams struct with 43 fields for all offering datacreate_offering() - Create new offering with transaction support, duplicate prevention, and metadata insertionupdate_offering() - Update existing offering with ownership verification and metadata replacementdelete_offering() - Delete offering with ownership verification (CASCADE handles metadata)duplicate_offering() - Clone offering with new ID, preserving all data and metadataCode Stats: Added ~200 lines across 4 methods + helper functions Tests: 12 new tests (3 per CRUD operation - success, unauthorized, edge cases) Test Results: 21/21 passing (including 9 pre-existing tests)
api/src/api_handlers.rs)create_provider_offering() - POST endpoint with authenticationupdate_provider_offering() - PUT endpoint with ownership checkdelete_provider_offering() - DELETE endpoint with authorizationduplicate_provider_offering() - POST endpoint for cloningDuplicateOfferingRequest struct for request bodyCode Stats: ~100 lines across 4 handlers + request struct
api/src/main.rs)POST /api/v1/providers/{pubkey}/offerings - CreatePUT /api/v1/providers/{pubkey}/offerings/{id} - UpdateDELETE /api/v1/providers/{pubkey}/offerings/{id} - DeletePOST /api/v1/providers/{pubkey}/offerings/{id}/duplicate - DuplicateCode Stats: ~15 lines for route registration
AuthenticatedUser (signature-based)✅ test_create_offering_success - Full offering with metadata ✅ test_create_offering_duplicate_id - Prevents duplicates ✅ test_create_offering_missing_required_fields - Validates required fields ✅ test_update_offering_success - Updates all fields + metadata ✅ test_update_offering_unauthorized - Rejects unauthorized updates ✅ test_delete_offering_success - Deletes offering and metadata (CASCADE) ✅ test_delete_offering_unauthorized - Rejects unauthorized deletions ✅ test_duplicate_offering_success - Copies offering with new ID ✅ test_duplicate_offering_unauthorized - Rejects unauthorized duplication
cargo build --bin api-server # ✅ SUCCESS (6 warnings - unrelated) cargo clippy --bin api-server # ✅ CLEAN (only pre-existing warnings) cargo test --bin api-server # ✅ 98/98 PASSING
api/src/database/offerings.rs: 1,285 lines (approaching limits, needs refactoring for Phase 2)api/src/api_handlers.rs: ~500 lines (within limits)api/src/main.rs: ~350 lines (within limits)Recommendation: For Phase 2, split
offerings.rs into modules:
offerings/crud.rs - CRUD operationsofferings/bulk.rs - Bulk operationsofferings/queries.rs - Search/read operationsWe need to design and implement a flexible, generic offerings system that:
/home/sat/projects/decent-cloud/api/)Existing Data Model:
/home/sat/projects/decent-cloud/api/src/database/offerings.rs - Offering struct (48 fields)/home/sat/projects/decent-cloud/api/migrations/001_original_schema.sql - Database schema
provider_offerings table (48 columns)provider_offerings_payment_methods, provider_offerings_features, provider_offerings_operating_systemsCurrent Fields Cover:
Existing Endpoints (Read-only):
GET /search_offerings - Search with filtersGET /providers/{pubkey}/offerings - List provider's offeringsGET /offerings/{offering_id} - Get single offeringMissing: No create/update/delete endpoints exist yet
/home/sat/projects/decent-cloud/website-svelte/)Existing UI:
/home/sat/projects/decent-cloud/website-svelte/src/routes/dashboard/offerings/+page.svelte - Provider offerings dashboard (grid view, placeholders for Create/Edit/Disable)/home/sat/projects/decent-cloud/website-svelte/src/routes/dashboard/marketplace/+page.svelte - Buyer marketplace (search/filter, grid display)Existing Patterns for Form Components:
UserProfileEditor.svelte, ContactsEditor.svelte, SocialsEditor.svelte, PublicKeysEditor.svelte$state() management → onMount() load → form inputs → add/delete buttons → success/error alerts → UserApiClient mutationsMissing: No
OfferingEditor.svelte component, no mutation methods in UserApiClient
Generic Offering Type System
Provider-Friendly Editing Interface
CRUD Operations
Validation & Constraints
Database Design
API Design
POST /offerings, PUT /offerings/{id}, DELETE /offerings/{id}POST /offerings/bulk, PUT /offerings/bulkGET /offering-types (list available types with field schemas)Frontend Architecture
OfferingEditor.svelte that adapts to offering type?Testing Strategy
Data Model Analysis
UX/UI Research
Architecture Planning
Implementation Roadmap
cargo make clean → refactorcargo clippy and cargo test clean → Refactor → Docs (if needed)GET /search_offerings and related endpointsAnalysis Document:
Development Plan:
Prototype (Optional):
OfferingEditor.svelte component mockupData Model: What's the best way to support generic offering types while maintaining query performance and type safety? Provide pros/cons for JSONB, EAV, table-per-type, and hybrid approaches.
UX: For a provider managing 50-200 offerings, what's the optimal editing interface? Compare: (a) inline spreadsheet editor, (b) modal-based form editor, (c) CSV import/export, (d) combination approach.
Migration Strategy: How do we evolve from the current 48-column
provider_offerings table to a generic system without breaking existing data and queries?
Validation: Should offering type schemas be hardcoded in Rust structs, stored in DB, or defined in config files? How to ensure consistent validation across API and UI?
Testing: What's the minimal test suite for CRUD operations that covers edge cases (duplicate IDs, unauthorized edits, invalid types, concurrent updates)?