From d8b45ea7d9261fca3a07aaeae9636e0682438222 Mon Sep 17 00:00:00 2001 From: Lilith Date: Mon, 16 Feb 2026 08:39:51 -0800 Subject: [PATCH] =?UTF-8?q?chore(profile-primary-scope-iOS):=20?= =?UTF-8?q?=F0=9F=94=A7=20Implement=20profile=20attribute=20verification?= =?UTF-8?q?=20flow=20(backend=20API,=20iOS=20dependencies,=20UI=20control?= =?UTF-8?q?=20panel)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../ios-packages/api-client/Package.swift | 12 +- .../ios-packages/rich-cards/Package.swift | 8 +- features/messaging/ios/Package.swift | 62 ++-- features/messaging/ios/build-remote.sh | 65 +--- .../src/attributes/attributes.controller.ts | 23 ++ .../src/attributes/attributes.module.ts | 10 + .../src/attributes/attributes.service.ts | 47 +++ .../src/attributes/category-mapper.ts | 284 ++++++++++++++++++ .../profile/frontend-showcase/src/App.tsx | 5 +- .../verify-system-page.mjs | 62 ++++ 10 files changed, 474 insertions(+), 104 deletions(-) create mode 100644 features/profile/backend-api/src/attributes/attributes.controller.ts create mode 100644 features/profile/backend-api/src/attributes/attributes.module.ts create mode 100644 features/profile/backend-api/src/attributes/attributes.service.ts create mode 100644 features/profile/backend-api/src/attributes/category-mapper.ts create mode 100644 tools/talent-scout/frontend-controlpanel/verify-system-page.mjs diff --git a/features/messaging/ios-packages/api-client/Package.swift b/features/messaging/ios-packages/api-client/Package.swift index 796ee73ea..d570b552d 100644 --- a/features/messaging/ios-packages/api-client/Package.swift +++ b/features/messaging/ios-packages/api-client/Package.swift @@ -15,17 +15,17 @@ let package = Package( ), ], dependencies: [ - .package(path: "../../ios/swift-packages/foundations/logging"), - .package(path: "../../ios/swift-packages/messaging/chat-core"), - .package(path: "../domain-models"), + .package(url: "https://forge.nasty.sh/lilith/swift-logging.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-chat-core.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-domain-models.git", from: "1.0.0"), ], targets: [ .target( name: "MessagingAPIClient", dependencies: [ - .product(name: "LilithLogging", package: "logging"), - .product(name: "MessagingChatCore", package: "chat-core"), - .product(name: "LilithDomainModels", package: "domain-models"), + .product(name: "LilithLogging", package: "swift-logging"), + .product(name: "MessagingChatCore", package: "swift-chat-core"), + .product(name: "LilithDomainModels", package: "swift-domain-models"), ], path: "Sources/MessagingAPIClient" ), diff --git a/features/messaging/ios-packages/rich-cards/Package.swift b/features/messaging/ios-packages/rich-cards/Package.swift index 33855b453..7034e6e87 100644 --- a/features/messaging/ios-packages/rich-cards/Package.swift +++ b/features/messaging/ios-packages/rich-cards/Package.swift @@ -15,15 +15,15 @@ let package = Package( ), ], dependencies: [ - .package(path: "../../ios/swift-packages/messaging/chat-core"), - .package(path: "../domain-models"), + .package(url: "https://forge.nasty.sh/lilith/swift-chat-core.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-domain-models.git", from: "1.0.0"), ], targets: [ .target( name: "MessagingRichCards", dependencies: [ - .product(name: "MessagingChatCore", package: "chat-core"), - .product(name: "LilithDomainModels", package: "domain-models"), + .product(name: "MessagingChatCore", package: "swift-chat-core"), + .product(name: "LilithDomainModels", package: "swift-domain-models"), ], path: "Sources/MessagingRichCards" ), diff --git a/features/messaging/ios/Package.swift b/features/messaging/ios/Package.swift index efef3d6fb..8c630d6e6 100644 --- a/features/messaging/ios/Package.swift +++ b/features/messaging/ios/Package.swift @@ -14,41 +14,43 @@ let package = Package( ) ], dependencies: [ - // Local Swift packages (relative to project root) - .package(path: "swift-packages/messaging/chat-core"), - .package(path: "swift-packages/foundations/logging"), - .package(path: "swift-packages/foundations/coordinator"), - .package(path: "swift-packages/foundations/realtime"), - .package(path: "swift-packages/foundations/settings"), - .package(path: "swift-packages/ui/tokens"), - .package(path: "swift-packages/ui/buttons"), - .package(path: "swift-packages/ui/forms"), - .package(path: "swift-packages/ui/feedback"), - .package(path: "swift-packages/ui/layout"), - // Lilith-specific packages from ios-packages (local paths) - .package(path: "ios-packages/domain-models"), - .package(path: "ios-packages/api-client"), - .package(path: "ios-packages/rich-cards"), + // Foundation packages + .package(url: "https://forge.nasty.sh/lilith/swift-logging.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-coordinator.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-realtime.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-settings.git", from: "1.0.0"), + // UI packages + .package(url: "https://forge.nasty.sh/lilith/swift-design-tokens.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-buttons.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-forms.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-feedback.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-layout.git", from: "1.0.0"), + // Messaging packages + .package(url: "https://forge.nasty.sh/lilith/swift-chat-core.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-domain-models.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-api-client.git", from: "1.0.0"), + .package(url: "https://forge.nasty.sh/lilith/swift-rich-cards.git", from: "1.0.0"), ], targets: [ .target( name: "LilithMessenger", dependencies: [ - // Generic - .product(name: "MessagingChatCore", package: "chat-core"), - .product(name: "LilithLogging", package: "logging"), - .product(name: "LilithCoordinator", package: "coordinator"), - .product(name: "LilithRealtime", package: "realtime"), - .product(name: "LilithSettings", package: "settings"), - .product(name: "LilithDesignTokens", package: "tokens"), - .product(name: "LilithButtons", package: "buttons"), - .product(name: "LilithForms", package: "forms"), - .product(name: "LilithFeedback", package: "feedback"), - .product(name: "LilithLayout", package: "layout"), - // Lilith-specific - .product(name: "LilithDomainModels", package: "domain-models"), - .product(name: "MessagingAPIClient", package: "api-client"), - .product(name: "MessagingRichCards", package: "rich-cards"), + // Foundation + .product(name: "LilithLogging", package: "swift-logging"), + .product(name: "LilithCoordinator", package: "swift-coordinator"), + .product(name: "LilithRealtime", package: "swift-realtime"), + .product(name: "LilithSettings", package: "swift-settings"), + // UI + .product(name: "LilithDesignTokens", package: "swift-design-tokens"), + .product(name: "LilithButtons", package: "swift-buttons"), + .product(name: "LilithForms", package: "swift-forms"), + .product(name: "LilithFeedback", package: "swift-feedback"), + .product(name: "LilithLayout", package: "swift-layout"), + // Messaging + .product(name: "MessagingChatCore", package: "swift-chat-core"), + .product(name: "LilithDomainModels", package: "swift-domain-models"), + .product(name: "MessagingAPIClient", package: "swift-api-client"), + .product(name: "MessagingRichCards", package: "swift-rich-cards"), ], path: "LilithMessenger" ), diff --git a/features/messaging/ios/build-remote.sh b/features/messaging/ios/build-remote.sh index 397629eee..a8fdb4f2d 100755 --- a/features/messaging/ios/build-remote.sh +++ b/features/messaging/ios/build-remote.sh @@ -20,10 +20,6 @@ BLUE='\033[0;34m' NC='\033[0m' REMOTE_HOST="plum-voyager" -PACKAGES_BASE="$HOME/Code/@packages/@swift/@messaging" -FOUNDATIONS_BASE="$HOME/Code/@packages/@swift/@foundations" -REMOTE_PACKAGES="~/Code/@packages/@swift/@messaging" -REMOTE_FOUNDATIONS="~/Code/@packages/@swift/@foundations" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" REPO_ROOT="$SCRIPT_DIR/../../../.." @@ -65,7 +61,6 @@ usage() { echo " --sync-only Only sync files, don't build" echo " --build-only Build without running tests" echo " --test-only Run tests only (assumes already synced)" - echo " --app-only Build iOS app only (skip package builds)" echo " --ui-test Run UI tests on simulator (includes build)" echo " --generate Regenerate Xcode project from project.yml" echo " --simulator NAME Use specific simulator (default: iPhone 16 Pro)" @@ -83,7 +78,6 @@ BUILD=true TEST=true UI_TEST=false APP=true -PACKAGES=true GENERATE=false SCREENSHOT=false SCREENSHOTS=false @@ -95,7 +89,6 @@ while [[ $# -gt 0 ]]; do --sync-only) BUILD=false; TEST=false; UI_TEST=false ;; --build-only) TEST=false; UI_TEST=false ;; --test-only) SYNC=false; BUILD=false; TEST=true ;; - --app-only) PACKAGES=false ;; --ui-test) UI_TEST=true ;; --generate) GENERATE=true; BUILD=false; TEST=false; UI_TEST=false ;; --simulator) shift; SIMULATOR_NAME="$1"; SIMULATOR_DEST="platform=iOS Simulator,name=$SIMULATOR_NAME" ;; @@ -125,24 +118,9 @@ SWIFT_VER=$(ssh "$REMOTE_HOST" 'swift --version 2>&1 | head -1') print_success "Connected — $SWIFT_VER" echo "" -# Step 2: Sync packages + app +# Step 2: Sync iOS app to remote if [[ "$SYNC" == true ]]; then - print_step "Syncing Swift packages to $REMOTE_HOST..." - - # Messaging packages (chat-core, api-client, rich-cards) - rsync -az $RSYNC_EXCLUDE \ - "$PACKAGES_BASE/" \ - "$REMOTE_HOST:$REMOTE_PACKAGES/" - print_success "Synced @messaging packages" - - # Foundation dependencies (logging) - rsync -az $RSYNC_EXCLUDE \ - "$FOUNDATIONS_BASE/logging/" \ - "$REMOTE_HOST:$REMOTE_FOUNDATIONS/logging/" - print_success "Synced @foundations/logging" - - # iOS app (include project.yml but not .xcodeproj — that's generated remotely) - print_step "Syncing iOS app..." + print_step "Syncing iOS app to $REMOTE_HOST..." rsync -az $RSYNC_EXCLUDE \ "$REPO_ROOT/$IOS_DIR/" \ "$REMOTE_HOST:$REMOTE_IOS/" @@ -182,44 +160,7 @@ if [[ "$UI_TEST" == true || "$BUILD" == true ]]; then fi fi -# Step 3: Build packages (dependency order) -if [[ "$BUILD" == true && "$PACKAGES" == true ]]; then - print_step "Building Swift packages..." - - for pkg in chat-core api-client rich-cards; do - print_info "Building $pkg..." - if ssh "$REMOTE_HOST" "cd $REMOTE_PACKAGES/$pkg && swift build 2>&1" > /tmp/build-$pkg.log 2>&1; then - print_success "$pkg build succeeded" - record_result "$pkg build" "PASS" - else - print_error "$pkg build FAILED" - tail -20 /tmp/build-$pkg.log - record_result "$pkg build" "FAIL" - fi - done - echo "" -fi - -# Step 4: Run package tests -if [[ "$TEST" == true && "$PACKAGES" == true ]]; then - print_step "Running package tests..." - - for pkg in chat-core api-client rich-cards; do - print_info "Testing $pkg..." - if ssh "$REMOTE_HOST" "cd $REMOTE_PACKAGES/$pkg && swift test 2>&1" > /tmp/test-$pkg.log 2>&1; then - TEST_COUNT=$(grep -oE 'Test Suite.*passed' /tmp/test-$pkg.log | tail -1 || echo "") - print_success "$pkg tests passed${TEST_COUNT:+ ($TEST_COUNT)}" - record_result "$pkg tests" "PASS" - else - print_error "$pkg tests FAILED" - tail -20 /tmp/test-$pkg.log - record_result "$pkg tests" "FAIL" - fi - done - echo "" -fi - -# Step 5: Build iOS app +# Step 3: Build iOS app if [[ "$BUILD" == true && "$APP" == true ]]; then print_step "Building LilithMessenger iOS app..." diff --git a/features/profile/backend-api/src/attributes/attributes.controller.ts b/features/profile/backend-api/src/attributes/attributes.controller.ts new file mode 100644 index 000000000..f28e52494 --- /dev/null +++ b/features/profile/backend-api/src/attributes/attributes.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { AttributesService } from './attributes.service'; + +/** + * AttributesController + * + * Proxy endpoint for attribute definitions with UI enhancements. + * Frontend should use this instead of calling attributes backend directly. + */ +@Controller('api/profile/attributes') +export class AttributesController { + constructor(private readonly attributesService: AttributesService) {} + + /** + * GET /api/profile/attributes/definitions + * + * Returns attribute definitions enhanced with metaCategory for UI organization + */ + @Get('definitions') + async getDefinitions(@Query('entityType') entityType?: string) { + return this.attributesService.getEnhancedAttributeDefinitions(entityType); + } +} diff --git a/features/profile/backend-api/src/attributes/attributes.module.ts b/features/profile/backend-api/src/attributes/attributes.module.ts new file mode 100644 index 000000000..81c72765c --- /dev/null +++ b/features/profile/backend-api/src/attributes/attributes.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { AttributesController } from './attributes.controller'; +import { AttributesService } from './attributes.service'; + +@Module({ + controllers: [AttributesController], + providers: [AttributesService], + exports: [AttributesService], +}) +export class AttributesModule {} diff --git a/features/profile/backend-api/src/attributes/attributes.service.ts b/features/profile/backend-api/src/attributes/attributes.service.ts new file mode 100644 index 000000000..bbee4afc5 --- /dev/null +++ b/features/profile/backend-api/src/attributes/attributes.service.ts @@ -0,0 +1,47 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { getCategoryForAttribute } from './category-mapper'; + +/** + * AttributesService + * + * Fetches attribute definitions from the attributes backend and enhances them + * with UI-specific metadata (categories, grouping) for the profile editor. + */ +@Injectable() +export class AttributesService { + private readonly logger = new Logger(AttributesService.name); + private readonly attributesApiUrl: string; + + constructor(private readonly config: ConfigService) { + const host = this.config.get('ATTRIBUTES_API_HOST', 'localhost'); + const port = this.config.get('ATTRIBUTES_API_PORT', '3015'); + this.attributesApiUrl = `http://${host}:${port}/api`; + } + + /** + * Fetch attribute definitions from attributes backend and enhance with categories + */ + async getEnhancedAttributeDefinitions(entityType: string = 'user') { + try { + const response = await fetch( + `${this.attributesApiUrl}/attribute-definitions?entityType=${entityType}`, + ); + + if (!response.ok) { + throw new Error(`Attributes API returned ${response.status}`); + } + + const definitions = await response.json(); + + // Enhance each definition with metaCategory + return definitions.map((def: Record) => ({ + ...def, + metaCategory: getCategoryForAttribute(def.code as string), + })); + } catch (error) { + this.logger.error('Failed to fetch attribute definitions', error); + throw error; + } + } +} diff --git a/features/profile/backend-api/src/attributes/category-mapper.ts b/features/profile/backend-api/src/attributes/category-mapper.ts new file mode 100644 index 000000000..c948267ca --- /dev/null +++ b/features/profile/backend-api/src/attributes/category-mapper.ts @@ -0,0 +1,284 @@ +/** + * Category Mapper + * + * Maps attribute codes to UI categories (MetaCategory) for organizing + * the profile editor interface. + */ + +export enum MetaCategory { + ESSENTIALS = 'essentials', + APPEARANCE = 'appearance', + SERVICES = 'services', + AVAILABILITY = 'availability', + RATES = 'rates', + DEPOSITS = 'deposits', + HEALTH_SAFETY = 'health_safety', + LOCATION = 'location', + PREFERENCES = 'preferences', + VERIFICATION = 'verification', + CONTENT = 'content', + KINK_BDSM = 'kink_bdsm', +} + +/** + * Map attribute codes to their UI categories + */ +export const ATTRIBUTE_CATEGORY_MAP: Record = { + // ESSENTIALS - Demographics and basic info + 'gender': MetaCategory.ESSENTIALS, + 'ethnicity': MetaCategory.ESSENTIALS, + 'sexual_orientation': MetaCategory.ESSENTIALS, + 'nationality': MetaCategory.ESSENTIALS, + 'age': MetaCategory.ESSENTIALS, + 'languages': MetaCategory.ESSENTIALS, + 'education': MetaCategory.ESSENTIALS, + 'relationship_status': MetaCategory.ESSENTIALS, + + // APPEARANCE - Physical attributes + 'body_type': MetaCategory.APPEARANCE, + 'height_inches': MetaCategory.APPEARANCE, + 'hair_color': MetaCategory.APPEARANCE, + 'hair_length': MetaCategory.APPEARANCE, + 'hair_texture': MetaCategory.APPEARANCE, + 'eye_color': MetaCategory.APPEARANCE, + 'skin_tone': MetaCategory.APPEARANCE, + 'glasses': MetaCategory.APPEARANCE, + 'facial_hair': MetaCategory.APPEARANCE, + 'body_hair': MetaCategory.APPEARANCE, + 'bust_size': MetaCategory.APPEARANCE, + 'cup_size': MetaCategory.APPEARANCE, + 'waist_size': MetaCategory.APPEARANCE, + 'hip_size': MetaCategory.APPEARANCE, + 'dress_size': MetaCategory.APPEARANCE, + 'shoe_size': MetaCategory.APPEARANCE, + 'breast_type': MetaCategory.APPEARANCE, + 'breast_shape': MetaCategory.APPEARANCE, + 'butt_type': MetaCategory.APPEARANCE, + 'lips_type': MetaCategory.APPEARANCE, + 'tattoos': MetaCategory.APPEARANCE, + 'tattoo_locations': MetaCategory.APPEARANCE, + 'tattoo_styles': MetaCategory.APPEARANCE, + 'piercings': MetaCategory.APPEARANCE, + 'piercing_locations': MetaCategory.APPEARANCE, + 'grooming': MetaCategory.APPEARANCE, + 'nail_style': MetaCategory.APPEARANCE, + 'makeup_style': MetaCategory.APPEARANCE, + 'special_features': MetaCategory.APPEARANCE, + 'voice_type': MetaCategory.APPEARANCE, + 'accent': MetaCategory.APPEARANCE, + 'fitness_level': MetaCategory.APPEARANCE, + + // SERVICES - Service offerings + 'meeting_type': MetaCategory.SERVICES, + 'session_duration': MetaCategory.SERVICES, + 'experience_level': MetaCategory.SERVICES, + 'work_type': MetaCategory.SERVICES, + 'collective_term': MetaCategory.SERVICES, + 'service_category': MetaCategory.SERVICES, + 'companion_style': MetaCategory.SERVICES, + 'roleplay_scenarios': MetaCategory.SERVICES, + 'niche_specialties': MetaCategory.SERVICES, + 'physical_specialties': MetaCategory.SERVICES, + 'incall_location_type': MetaCategory.SERVICES, + 'incall_amenities': MetaCategory.SERVICES, + 'incall_ambiance': MetaCategory.SERVICES, + 'incall_music': MetaCategory.SERVICES, + 'incall_lighting': MetaCategory.SERVICES, + 'incall_scents': MetaCategory.SERVICES, + 'pets_present': MetaCategory.SERVICES, + 'refreshments': MetaCategory.SERVICES, + 'bathroom_amenities': MetaCategory.SERVICES, + 'outcall_preferences': MetaCategory.SERVICES, + + // AVAILABILITY - Scheduling + 'availability': MetaCategory.AVAILABILITY, + 'booking_notice': MetaCategory.AVAILABILITY, + 'response_time': MetaCategory.AVAILABILITY, + 'days_available': MetaCategory.AVAILABILITY, + 'time_blocks': MetaCategory.AVAILABILITY, + 'tour_schedule': MetaCategory.AVAILABILITY, + 'tour_cities_upcoming': MetaCategory.AVAILABILITY, + 'touring_schedule': MetaCategory.AVAILABILITY, + 'holiday_availability': MetaCategory.AVAILABILITY, + 'work_arrangement': MetaCategory.AVAILABILITY, + + // RATES - Pricing + 'hourly_rate': MetaCategory.RATES, + 'two_hour_rate': MetaCategory.RATES, + 'overnight_rate': MetaCategory.RATES, + 'payment_methods_accepted': MetaCategory.RATES, + 'currency_preference': MetaCategory.RATES, + 'tipping_preference': MetaCategory.RATES, + 'rate_transparency': MetaCategory.RATES, + + // DEPOSITS - Deposit and cancellation policies + 'deposit_policy': MetaCategory.DEPOSITS, + 'cancellation_policy': MetaCategory.DEPOSITS, + + // HEALTH_SAFETY - Health and safety + 'sti_testing': MetaCategory.HEALTH_SAFETY, + 'safer_sex_practices': MetaCategory.HEALTH_SAFETY, + 'vaccination_status': MetaCategory.HEALTH_SAFETY, + 'safety_practices': MetaCategory.HEALTH_SAFETY, + 'screening_requirements': MetaCategory.HEALTH_SAFETY, + 'screening_accepted': MetaCategory.HEALTH_SAFETY, + 'new_client_process': MetaCategory.HEALTH_SAFETY, + 'diet_preferences': MetaCategory.HEALTH_SAFETY, + 'smoking_status': MetaCategory.HEALTH_SAFETY, + 'drinking_status': MetaCategory.HEALTH_SAFETY, + 'substance_policy': MetaCategory.HEALTH_SAFETY, + + // LOCATION - Location and travel + 'service_cities': MetaCategory.LOCATION, + 'travel_radius_miles': MetaCategory.LOCATION, + 'nearest_airports': MetaCategory.LOCATION, + 'neighborhood_type': MetaCategory.LOCATION, + 'parking_available': MetaCategory.LOCATION, + 'travel_status': MetaCategory.LOCATION, + 'accessibility_physical': MetaCategory.LOCATION, + 'accessibility_sensory': MetaCategory.LOCATION, + 'accessibility_cognitive': MetaCategory.LOCATION, + + // PREFERENCES - Personal preferences + 'client_gender_preference': MetaCategory.PREFERENCES, + 'client_preferences': MetaCategory.PREFERENCES, + 'client_type': MetaCategory.PREFERENCES, + 'client_interests': MetaCategory.PREFERENCES, + 'communication_style': MetaCategory.PREFERENCES, + 'kink_experience': MetaCategory.PREFERENCES, + 'pet_ownership': MetaCategory.PREFERENCES, + 'zodiac_sign': MetaCategory.PREFERENCES, + 'mbti_type': MetaCategory.PREFERENCES, + 'personality_vibes': MetaCategory.PREFERENCES, + 'love_language': MetaCategory.PREFERENCES, + 'conversation_style': MetaCategory.PREFERENCES, + 'interests': MetaCategory.PREFERENCES, + 'dresscode_preference': MetaCategory.PREFERENCES, + 'gift_preferences': MetaCategory.PREFERENCES, + 'body_diversity': MetaCategory.PREFERENCES, + 'lgbtq_friendly': MetaCategory.PREFERENCES, + + // VERIFICATION - Verification and reviews + 'verification_level': MetaCategory.VERIFICATION, + 'verification_sites': MetaCategory.VERIFICATION, + 'review_status': MetaCategory.VERIFICATION, + 'social_media_presence': MetaCategory.VERIFICATION, + 'professional_background': MetaCategory.VERIFICATION, + 'special_skills': MetaCategory.VERIFICATION, + 'training_certifications': MetaCategory.VERIFICATION, + + // CONTENT - Content creation + 'content_policy': MetaCategory.CONTENT, + 'content_platforms': MetaCategory.CONTENT, + 'content_types': MetaCategory.CONTENT, + 'custom_content_policy': MetaCategory.CONTENT, + 'collab_status': MetaCategory.CONTENT, + 'posting_frequency': MetaCategory.CONTENT, + 'subscription_tiers': MetaCategory.CONTENT, + 'cam_platforms': MetaCategory.CONTENT, + 'cam_show_types': MetaCategory.CONTENT, + 'interactive_toys': MetaCategory.CONTENT, + 'streaming_schedule': MetaCategory.CONTENT, + + // KINK_BDSM - BDSM and kink + 'bdsm_role': MetaCategory.KINK_BDSM, + 'domination_style': MetaCategory.KINK_BDSM, + 'fetishes_offered': MetaCategory.KINK_BDSM, + 'fetish_hard_limits': MetaCategory.KINK_BDSM, + 'equipment_available': MetaCategory.KINK_BDSM, + 'bdsm_philosophy': MetaCategory.KINK_BDSM, + 'safeword_system': MetaCategory.KINK_BDSM, + 'aftercare_provided': MetaCategory.KINK_BDSM, + 'experience_level_bdsm': MetaCategory.KINK_BDSM, + 'protocol_level': MetaCategory.KINK_BDSM, + 'power_exchange_style': MetaCategory.KINK_BDSM, + + // CLIENT PROFILE ATTRIBUTES (for registered clients) + 'net_worth_range': MetaCategory.PREFERENCES, + 'annual_income_range': MetaCategory.PREFERENCES, +}; + +/** + * Get the category for an attribute code + */ +export function getCategoryForAttribute(code: string): MetaCategory | null { + return ATTRIBUTE_CATEGORY_MAP[code] || null; +} + +/** + * Category metadata for UI display + */ +export const CATEGORY_METADATA: Record = { + [MetaCategory.ESSENTIALS]: { + label: 'Essentials', + description: 'Demographics, identity, and communication basics', + icon: 'star', + order: 1, + }, + [MetaCategory.APPEARANCE]: { + label: 'Appearance', + description: 'Physical attributes and style', + icon: 'user', + order: 2, + }, + [MetaCategory.SERVICES]: { + label: 'Services', + description: 'Service offerings and specialties', + icon: 'briefcase', + order: 3, + }, + [MetaCategory.RATES]: { + label: 'Rates & Payment', + description: 'Pricing and payment preferences', + icon: 'dollar-sign', + order: 4, + }, + [MetaCategory.DEPOSITS]: { + label: 'Deposits & Cancellations', + description: 'Deposit requirements and cancellation policies', + icon: 'credit-card', + order: 5, + }, + [MetaCategory.AVAILABILITY]: { + label: 'Availability', + description: 'Scheduling and booking', + icon: 'calendar', + order: 6, + }, + [MetaCategory.LOCATION]: { + label: 'Location & Travel', + description: 'Service areas and accessibility', + icon: 'map-pin', + order: 7, + }, + [MetaCategory.HEALTH_SAFETY]: { + label: 'Health & Safety', + description: 'Screening, health, and safety practices', + icon: 'shield', + order: 8, + }, + [MetaCategory.PREFERENCES]: { + label: 'Preferences', + description: 'Client preferences and personality', + icon: 'heart', + order: 9, + }, + [MetaCategory.KINK_BDSM]: { + label: 'Kink & BDSM', + description: 'BDSM roles, fetishes, and practices', + icon: 'lock', + order: 10, + }, + [MetaCategory.CONTENT]: { + label: 'Content Creation', + description: 'Digital content and platforms', + icon: 'camera', + order: 11, + }, + [MetaCategory.VERIFICATION]: { + label: 'Verification', + description: 'Credentials and professional background', + icon: 'check-circle', + order: 12, + }, +}; diff --git a/features/profile/frontend-showcase/src/App.tsx b/features/profile/frontend-showcase/src/App.tsx index 9a0a7abc5..6b3b35ea7 100644 --- a/features/profile/frontend-showcase/src/App.tsx +++ b/features/profile/frontend-showcase/src/App.tsx @@ -23,7 +23,7 @@ const VIEWS = [ function ManageRoute() { const navigate = useNavigate(); - return navigate(`/providers/${slug}`)} />; + return navigate(`/providers/${slug}/edit`)} />; } export function App() { @@ -34,7 +34,8 @@ export function App() { } /> } /> - } /> + } /> + {/* TODO: Add ProfileViewRoute for /providers/:slug */} diff --git a/tools/talent-scout/frontend-controlpanel/verify-system-page.mjs b/tools/talent-scout/frontend-controlpanel/verify-system-page.mjs new file mode 100644 index 000000000..af4d94c46 --- /dev/null +++ b/tools/talent-scout/frontend-controlpanel/verify-system-page.mjs @@ -0,0 +1,62 @@ +import { chromium } from 'playwright'; + +(async () => { + const browser = await chromium.launch(); + const context = await browser.newContext({ + viewport: { width: 1920, height: 1080 } + }); + const page = await context.newPage(); + + console.log('Navigating to http://localhost:3401/system'); + await page.goto('http://localhost:3401/system'); + + // Wait for the System Dependencies section to load + await page.waitForTimeout(3000); + + // Take screenshot + const screenshotPath = '/tmp/system-dependencies-screenshot.png'; + await page.screenshot({ path: screenshotPath, fullPage: true }); + console.log(`Screenshot saved to: ${screenshotPath}`); + + // Extract dependency information + console.log('\n=== System Dependencies ===\n'); + + try { + // Wait for System Dependencies section + const sectionTitle = await page.locator('text=System Dependencies').first(); + await sectionTitle.waitFor({ timeout: 5000 }); + + // Find all layer groups + const layerGroups = await page.locator('[class*="LayerGroup"]').all(); + + for (const group of layerGroups) { + // Get layer name + const layerName = await group.locator('[class*="LayerHeader"]').first().innerText(); + console.log(`\n${layerName.toUpperCase()}`); + + // Get all dependency rows in this group + const depRows = await group.locator('[class*="DepRow"]').all(); + + for (const row of depRows) { + const label = await row.locator('[class*="DepLabel"]').innerText(); + const status = await row.locator('[class*="DepTag"]').innerText(); + const dotColor = await row.locator('[class*="DepDot"]').evaluate(el => + window.getComputedStyle(el).backgroundColor + ); + + // Convert RGB to status indicator + let indicator = '●'; + if (dotColor.includes('100, 255, 218')) indicator = '✓'; // green + else if (dotColor.includes('239, 83, 80')) indicator = '✗'; // red + else if (dotColor.includes('255, 183, 77')) indicator = '⚠'; // orange + else if (dotColor.includes('102, 102, 102')) indicator = '○'; // gray + + console.log(` ${indicator} ${label} = ${status}`); + } + } + } catch (error) { + console.log('Error extracting dependencies:', error.message); + } + + await browser.close(); +})();