dependencies = $dependencies; $this->settings = $settings; $this->product_draft_input = $product_draft_input; $this->batch_create = $batch_create; } /** * Initialize admin hooks. * * @return void */ public function init() { add_action( 'admin_menu', array( $this, 'register_menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); add_action( 'admin_head', array( $this, 'print_sign_in_menu_styles' ) ); } /** * Register admin pages. * * @return void */ public function register_menu() { add_menu_page( __( 'AI Product Studio', 'ai-product-studio-for-woocommerce' ), __( 'AI Product Studio', 'ai-product-studio-for-woocommerce' ), 'manage_options', 'apsw-dashboard', array( $this, 'render_dashboard_page' ), 'dashicons-welcome-add-page', 56 ); add_submenu_page( 'apsw-dashboard', __( 'Dashboard', 'ai-product-studio-for-woocommerce' ), __( 'Dashboard', 'ai-product-studio-for-woocommerce' ), 'manage_options', 'apsw-dashboard', array( $this, 'render_dashboard_page' ) ); add_submenu_page( 'apsw-dashboard', __( 'Create Product', 'ai-product-studio-for-woocommerce' ), __( 'Create Product', 'ai-product-studio-for-woocommerce' ), 'manage_options', 'apsw-create-product', array( $this, 'render_create_product_page' ) ); $this->add_create_product_workflow_submenu( __( 'Text to Product', 'ai-product-studio-for-woocommerce' ), 'apsw-create-product-text' ); $this->add_create_product_workflow_submenu( __( 'Image to Product', 'ai-product-studio-for-woocommerce' ), 'apsw-create-product-image' ); $this->add_create_product_workflow_submenu( __( 'Advanced Product', 'ai-product-studio-for-woocommerce' ), 'apsw-create-product-advanced' ); $this->add_create_product_workflow_submenu( __( 'Bulk Image to Product', 'ai-product-studio-for-woocommerce' ), 'apsw-create-product-bulk-image' ); add_submenu_page( 'apsw-dashboard', __( 'Batch Create', 'ai-product-studio-for-woocommerce' ), __( 'Batch Create', 'ai-product-studio-for-woocommerce' ), 'manage_options', 'apsw-batch-create', array( $this, 'render_batch_create_page' ) ); add_submenu_page( 'apsw-dashboard', __( 'Settings', 'ai-product-studio-for-woocommerce' ), __( 'Settings', 'ai-product-studio-for-woocommerce' ), 'manage_options', 'apsw-settings', array( $this, 'render_settings_page' ) ); if ( ! $this->is_clugins_connected() ) { add_submenu_page( 'apsw-dashboard', __( 'Sign in', 'ai-product-studio-for-woocommerce' ), __( 'Sign in', 'ai-product-studio-for-woocommerce' ), 'manage_options', 'apsw-connect', array( $this, 'render_settings_page' ) ); } } /** * Add a workflow link that routes to the existing Create Product page. * * @param string $label Workflow label. * @param string $slug Registered submenu slug. * @return void */ private function add_create_product_workflow_submenu( $label, $slug ) { add_submenu_page( 'apsw-dashboard', $label, $label, 'manage_options', sanitize_key( $slug ), array( $this, 'render_create_product_page' ) ); } /** * Load admin styles for plugin pages. * * @param string $hook Current admin hook. * @return void */ public function enqueue_assets( $hook ) { $apsw_hook_pages = array( 'toplevel_page_apsw-dashboard' => 'apsw-dashboard', 'ai-product-studio_page_apsw-dashboard' => 'apsw-dashboard', 'ai-product-studio_page_apsw-create-product' => 'apsw-create-product', 'ai-product-studio_page_apsw-create-product-text' => 'apsw-create-product', 'ai-product-studio_page_apsw-create-product-image' => 'apsw-create-product', 'ai-product-studio_page_apsw-create-product-advanced' => 'apsw-create-product', 'ai-product-studio_page_apsw-create-product-bulk-image' => 'apsw-create-product', 'ai-product-studio_page_apsw-batch-create' => 'apsw-batch-create', 'ai-product-studio_page_apsw-settings' => 'apsw-settings', 'ai-product-studio_page_apsw-connect' => 'apsw-settings', ); $current_page = isset( $apsw_hook_pages[ $hook ] ) ? $apsw_hook_pages[ $hook ] : ''; if ( '' === $current_page ) { return; } $css_file = APSW_PLUGIN_DIR . 'assets/css/admin.css'; $js_file = APSW_PLUGIN_DIR . 'assets/js/admin.js'; wp_enqueue_style( 'apsw-admin', APSW_PLUGIN_URL . 'assets/css/admin.css', array(), file_exists( $css_file ) ? filemtime( $css_file ) : APSW_VERSION ); if ( in_array( $current_page, array( 'apsw-create-product', 'apsw-batch-create' ), true ) ) { wp_enqueue_media(); } wp_enqueue_script( 'apsw-admin', APSW_PLUGIN_URL . 'assets/js/admin.js', array( 'jquery' ), file_exists( $js_file ) ? filemtime( $js_file ) : APSW_VERSION, true ); wp_localize_script( 'apsw-admin', 'apswAdmin', array( 'mediaTitle' => __( 'Select Images', 'ai-product-studio-for-woocommerce' ), 'mediaButton' => __( 'Use images', 'ai-product-studio-for-woocommerce' ), 'strings' => array( 'mediaLibraryUnavailable' => __( 'WordPress Media Library is not available on this page.', 'ai-product-studio-for-woocommerce' ), 'selectImages' => __( 'Select Images', 'ai-product-studio-for-woocommerce' ), 'useSelectedImages' => __( 'Use selected images', 'ai-product-studio-for-woocommerce' ), 'selectMainImageForAiAnalysis' => __( 'Select Main Image for AI Analysis', 'ai-product-studio-for-woocommerce' ), 'useImageForAiAnalysis' => __( 'Use image for AI analysis', 'ai-product-studio-for-woocommerce' ), 'selectImagesForAiAnalysis' => __( 'Select Images for AI Analysis', 'ai-product-studio-for-woocommerce' ), 'selectGalleryImagesOptional' => __( 'Select Gallery Images (Optional)', 'ai-product-studio-for-woocommerce' ), 'selectLocalImages' => __( 'Select Local Images', 'ai-product-studio-for-woocommerce' ), 'useImagesForAiAnalysis' => __( 'Use images for AI analysis', 'ai-product-studio-for-woocommerce' ), 'useGalleryImages' => __( 'Use gallery images', 'ai-product-studio-for-woocommerce' ), 'useLocalImages' => __( 'Use local images', 'ai-product-studio-for-woocommerce' ), 'bulkImageSelectionLimitNotice' => __( 'You can select up to 10 images. Only the first 10 selected images will be used.', 'ai-product-studio-for-woocommerce' ), 'advancedNotesHelper' => __( 'Add enough product detail to guide the generated product content.', 'ai-product-studio-for-woocommerce' ), 'basicNotesHelper' => __( 'Write product details. AI will organize them.', 'ai-product-studio-for-woocommerce' ), 'bulkImageHelper' => __( 'Each selected image is analyzed by AI and creates one product. Images are processed one by one. You can select up to 10 images.', 'ai-product-studio-for-woocommerce' ), 'advancedImageHelper' => __( 'AI analyzes the main image together with your product details. Gallery images are optional and stay local.', 'ai-product-studio-for-woocommerce' ), 'imageModeHelper' => __( 'AI analyzes the main image. Gallery images are optional and stay local.', 'ai-product-studio-for-woocommerce' ), 'localImagesHelper' => __( 'Selected images stay local and are not analyzed by AI.', 'ai-product-studio-for-woocommerce' ), 'selectedImages' => __( 'Selected images', 'ai-product-studio-for-woocommerce' ), 'galleryImages' => __( 'Gallery images', 'ai-product-studio-for-woocommerce' ), 'localImages' => __( 'Local images', 'ai-product-studio-for-woocommerce' ), 'generateProductWithAi' => __( 'Generate Product with AI', 'ai-product-studio-for-woocommerce' ), 'progressPreparingProductDetails' => __( 'Preparing product details...', 'ai-product-studio-for-woocommerce' ), 'progressSendingDetailsToCluginsManagedAi' => __( 'Sending details to Clugins Managed AI...', 'ai-product-studio-for-woocommerce' ), 'progressWritingProductContent' => __( 'Writing product content...', 'ai-product-studio-for-woocommerce' ), 'progressCreatingWooProduct' => __( 'Creating the WooCommerce product...', 'ai-product-studio-for-woocommerce' ), 'progressSavingProductDetails' => __( 'Saving images, pricing, and product details...', 'ai-product-studio-for-woocommerce' ), 'progressAlmostDone' => __( 'Almost done...', 'ai-product-studio-for-woocommerce' ), 'generating' => __( 'Generating...', 'ai-product-studio-for-woocommerce' ), 'selectAtLeastOneImage' => __( 'Select at least one image before generating products from images.', 'ai-product-studio-for-woocommerce' ), 'bulkImageLimit' => __( 'You can select up to 10 images for Bulk Image to Product.', 'ai-product-studio-for-woocommerce' ), 'advancedDetailsRequired' => __( 'Add a few more product details before generating an advanced product.', 'ai-product-studio-for-woocommerce' ), 'advancedMainImageRequired' => __( 'Select a main product image before generating an advanced product.', 'ai-product-studio-for-woocommerce' ), 'imageMainImageRequired' => __( 'Select a main product image before generating from an image.', 'ai-product-studio-for-woocommerce' ), /* translators: {current}: current product number, {total}: total product count. */ 'batchProductsStepLabel' => __( '2. Product {current} of {total}', 'ai-product-studio-for-woocommerce' ), /* translators: {current}: current product number, {total}: total product count. */ 'batchProductStepTitle' => __( 'Product {current} of {total}', 'ai-product-studio-for-woocommerce' ), 'batchProductStepCopy' => __( 'Product details are sent to AI. Selected images stay local and are attached after creation.', 'ai-product-studio-for-woocommerce' ), 'productBriefsStepLabel' => __( '2. Product briefs', 'ai-product-studio-for-woocommerce' ), 'textToProduct' => __( 'Text to Product', 'ai-product-studio-for-woocommerce' ), 'addBatchItemDetails' => __( 'Add product details for this batch item.', 'ai-product-studio-for-woocommerce' ), 'noCategorySelected' => __( 'No category selected', 'ai-product-studio-for-woocommerce' ), /* translators: {number}: product item number. */ 'productReviewTitle' => __( 'Product {number}', 'ai-product-studio-for-woocommerce' ), 'localImageSelected' => __( 'Local image selected', 'ai-product-studio-for-woocommerce' ), 'noLocalImageSelected' => __( 'No local image selected', 'ai-product-studio-for-woocommerce' ), 'productKind' => __( 'Product kind', 'ai-product-studio-for-woocommerce' ), 'virtual' => __( 'Virtual', 'ai-product-studio-for-woocommerce' ), 'physical' => __( 'Physical', 'ai-product-studio-for-woocommerce' ), 'category' => __( 'Category', 'ai-product-studio-for-woocommerce' ), 'notSelected' => __( 'Not selected', 'ai-product-studio-for-woocommerce' ), 'regularPrice' => __( 'Regular price', 'ai-product-studio-for-woocommerce' ), 'notEntered' => __( 'Not entered', 'ai-product-studio-for-woocommerce' ), 'salePrice' => __( 'Sale price', 'ai-product-studio-for-woocommerce' ), 'stockQuantity' => __( 'Stock quantity', 'ai-product-studio-for-woocommerce' ), 'selectLocalProductImage' => __( 'Select Local Product Image', 'ai-product-studio-for-woocommerce' ), 'useLocalProductImage' => __( 'Use local product image', 'ai-product-studio-for-woocommerce' ), 'setupBatchItems' => __( 'Set up batch items before creating the batch.', 'ai-product-studio-for-woocommerce' ), 'addAllBatchItemDetails' => __( 'Add product details for every batch item.', 'ai-product-studio-for-woocommerce' ), ), ) ); } /** * Style the conditional Sign in submenu item. * * @return void */ public function print_sign_in_menu_styles() { if ( $this->is_clugins_connected() ) { return; } ?> dependencies->is_woocommerce_active(); $settings = $this->settings->get_settings(); $selected_provider_key = $this->settings->normalize_provider_key( $settings[ APSW_Settings::FIELD_PROVIDER ] ); $provider_ready = $this->is_selected_provider_configured( $settings, $selected_provider_key ); $selected_provider_name = $this->settings->get_provider_label( $selected_provider_key ); include APSW_PLUGIN_DIR . 'admin/views/dashboard.php'; } /** * Render settings page. * * @return void */ public function render_settings_page() { $settings = $this->settings->get_settings(); $selected_provider_key = $this->settings->normalize_provider_key( $settings[ APSW_Settings::FIELD_PROVIDER ] ); $selected_provider_name = $this->settings->get_provider_label( $selected_provider_key ); $description_length_options = $this->settings->get_description_length_options(); $generation_option_fields = $this->settings->get_generation_option_fields(); include APSW_PLUGIN_DIR . 'admin/views/settings.php'; } /** * Render Create Product Draft page. * * @return void */ public function render_create_product_page() { $contexts = $this->product_draft_input->get_allowed_contexts(); $status_options = $this->product_draft_input->get_status_options(); $product_categories = $this->get_product_categories(); $last_prepared_data = $this->product_draft_input->get_last_prepared_input( get_current_user_id() ); $creation_notice = $this->product_draft_input->consume_creation_notice( get_current_user_id() ); $ai_creation_notice = $this->product_draft_input->consume_ai_creation_notice( get_current_user_id() ); $ai_generation_notice = $this->product_draft_input->consume_ai_generation_notice( get_current_user_id() ); $generate_create_notice = $this->product_draft_input->consume_generate_create_notice( get_current_user_id() ); $settings = $this->settings->get_settings(); $selected_provider_key = $this->settings->normalize_provider_key( $settings[ APSW_Settings::FIELD_PROVIDER ] ); $provider_ready = $this->is_selected_provider_configured( $settings, $selected_provider_key ); $selected_provider_name = $this->settings->get_provider_label( $selected_provider_key ); $woocommerce_active = $this->dependencies->is_woocommerce_active(); $show_review_prompt = $this->product_draft_input->should_show_review_prompt( get_current_user_id() ); $review_url = $this->product_draft_input->get_review_url(); include APSW_PLUGIN_DIR . 'admin/views/create-product.php'; } /** * Render Batch Create page. * * @return void */ public function render_batch_create_page() { $product_categories = $this->get_product_categories(); $batch_notice = $this->batch_create->consume_notice( get_current_user_id() ); $batch_limit = $this->batch_create->get_batch_limit(); $settings = $this->settings->get_settings(); $selected_provider_key = $this->settings->normalize_provider_key( $settings[ APSW_Settings::FIELD_PROVIDER ] ); $provider_ready = $this->is_selected_provider_configured( $settings, $selected_provider_key ); $selected_provider_name = $this->settings->get_provider_label( $selected_provider_key ); $woocommerce_active = $this->dependencies->is_woocommerce_active(); include APSW_PLUGIN_DIR . 'admin/views/batch-create.php'; } /** * Return available WooCommerce product categories. * * @return WP_Term[] */ private function get_product_categories() { if ( ! taxonomy_exists( 'product_cat' ) ) { return array(); } $terms = get_terms( array( 'taxonomy' => 'product_cat', 'hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC', ) ); return is_wp_error( $terms ) || ! is_array( $terms ) ? array() : $terms; } /** * Check whether the selected provider has the required local credential. * * @param array $settings Settings array. * @param string $provider Selected provider key. * @return bool */ private function is_selected_provider_configured( array $settings, $provider ) { if ( 'clugins_managed' === $provider ) { return ! empty( $settings[ APSW_Settings::FIELD_CLUGINS_ACCESS_KEY ] ); } return false; } /** * Check whether Clugins has a stored Access Key. * * @return bool */ private function is_clugins_connected() { $settings = $this->settings->get_settings(); $provider = $this->settings->normalize_provider_key( $settings[ APSW_Settings::FIELD_PROVIDER ] ); return $this->is_selected_provider_configured( $settings, $provider ); } }