' . esc_html__('New', 'ai-content-generation') . '';
add_submenu_page(
self::PARENT_SLUG,
__('Automated Posts', 'ai-content-generation'),
$menu_title,
'edit_posts',
self::PAGE_SLUG,
[$this, 'render'],
30 // order: after Bulk Posts (25), before History (35)
);
add_action('admin_head', static function () {
echo '';
});
}
public function render(): void
{
// JS-free skeleton paints instantly; React's createRoot() clears it on mount. See Skeleton.
// Title mirrors the app's own in-page heading so the header text doesn't change on hand-off.
echo '
';
echo Skeleton::panel(__('Automated Posts', 'ai-content-generation'), [], 4); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped inside Skeleton
echo '
';
}
public function enqueue(string $hook): void
{
if (substr($hook, -strlen(self::PAGE_SLUG)) !== self::PAGE_SLUG) {
return;
}
$asset_file = WPWAND_NEW_DIR . 'build/automation.asset.php';
if (!is_readable($asset_file)) {
return;
}
$asset = require $asset_file;
wp_enqueue_script(self::HANDLE, WPWAND_NEW_URL . 'build/automation.js', $asset['dependencies'], $asset['version'], true);
// No webfont request here on purpose: hitting fonts.googleapis.com from wp-admin is a
// third-party round trip nobody asked for and a wordpress.org review flag. Every rule in the
// stylesheet reads --wpwand-font, which names Inter first and then the platform UI stack, so
// a machine without Inter renders one consistent typeface rather than two.
if (is_readable(WPWAND_NEW_DIR . 'build/style-automation.css')) {
wp_enqueue_style(self::HANDLE, WPWAND_NEW_URL . 'build/style-automation.css', [], (string) filemtime(WPWAND_NEW_DIR . 'build/style-automation.css'));
}
ScriptConfig::merge(self::HANDLE, ScriptConfig::base() + [
'brand' => \WPWand\Data\Brand::resolve()['color'],
'isPro' => \WPWand\Generation\UsageLimits::is_pro(),
// The server clamps a saved schedule to this (Schedules::normalize). Ship it so the form
// cannot offer a number the save will silently reduce.
'automationPerRun' => \WPWand\Automation\Schedules::max_count(),
]);
if (function_exists('wp_set_script_translations')) {
wp_set_script_translations(self::HANDLE, 'ai-content-generation');
}
}
}