From 27ea3d7f1eee3259f47319b4a33631b2a602a287 Mon Sep 17 00:00:00 2001 From: Shazahanul Islam Shohag Date: Wed, 12 Feb 2025 11:13:57 +0600 Subject: [PATCH] update: React Admin Dashboard Structure (#2558) * feat: react admin dashboard * refactor: implemented the `Hookable` Interface * refactor: implemented the `Hookable` Interface * refactor: implemented the new servicefor AdminDashboardServiceProvider.php * refactor: implemented the `Hookable` to Dashboard * chore: comment changed --- includes/Admin/Dashboard/Dashboard.php | 220 ++++++++++++++++++ includes/Admin/Dashboard/Pageable.php | 70 ++++++ .../Admin/Dashboard/Pages/AbstractPage.php | 59 +++++ includes/Admin/Dashboard/Pages/Status.php | 79 +++++++ includes/Admin/Status/Status.php | 64 ----- .../AdminDashboardServiceProvider.php | 29 +++ .../Providers/ServiceProvider.php | 1 + src/Status/index.tsx | 22 +- .../admin-dashboard-tailwind.config.js | 9 + src/admin/dashboard/components/Dashboard.tsx | 55 +++++ src/admin/dashboard/components/Header.tsx | 205 ++++++++++++++++ src/admin/dashboard/components/Layout.tsx | 17 ++ src/admin/dashboard/index.tsx | 12 + src/admin/dashboard/style.scss | 2 + webpack-entries.js | 39 ++-- 15 files changed, 791 insertions(+), 92 deletions(-) create mode 100644 includes/Admin/Dashboard/Dashboard.php create mode 100644 includes/Admin/Dashboard/Pageable.php create mode 100644 includes/Admin/Dashboard/Pages/AbstractPage.php create mode 100644 includes/Admin/Dashboard/Pages/Status.php create mode 100644 includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php create mode 100644 src/admin/dashboard/admin-dashboard-tailwind.config.js create mode 100644 src/admin/dashboard/components/Dashboard.tsx create mode 100644 src/admin/dashboard/components/Header.tsx create mode 100644 src/admin/dashboard/components/Layout.tsx create mode 100644 src/admin/dashboard/index.tsx create mode 100644 src/admin/dashboard/style.scss diff --git a/includes/Admin/Dashboard/Dashboard.php b/includes/Admin/Dashboard/Dashboard.php new file mode 100644 index 0000000000..647053c555 --- /dev/null +++ b/includes/Admin/Dashboard/Dashboard.php @@ -0,0 +1,220 @@ + + */ + protected array $pages = []; + + /** + * @var string + */ + protected string $script_key = 'dokan-admin-dashboard'; + + /** + * Register hooks. + */ + public function register_hooks(): void { + add_action( 'dokan_admin_menu', [ $this, 'register_menu' ], 99, 2 ); + add_action( 'dokan_register_scripts', [ $this, 'register_scripts' ] ); + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); + } + + /** + * Get all pages. + * + * @since DOKAN_SINCE + * + * @return array< Pageable > + * + * @throws \InvalidArgumentException If the page is not an instance of Pageable. + */ + public function get_pages(): array { + $pages = apply_filters( 'dokan_admin_dashboard_pages', $this->pages ); + + if ( ! is_array( $pages ) ) { + return $this->pages; + } + + return array_filter( + $pages, function ( $page ) { + if ( ! $page instanceof Pageable ) { + throw new \InvalidArgumentException( esc_html__( 'The page must be an instance of Pageable.', 'dokan-lite' ) ); + } + return true; + } + ); + } + + /** + * Register the submenu menu. + * + * @since DOKAN_SINCE + * + * @param string $capability Menu capability. + * @param string $position Menu position. + * + * @return void + */ + public function register_menu( string $capability, string $position ) { + global $submenu; + + $parent_slug = 'dokan'; + + // TODO: Remove and rewrite this code for registering `dokan-dashboard`. + $menu_slug = 'dokan-dashboard'; + add_submenu_page( + 'dokan', + '', + '', + $capability, + $menu_slug, + [ $this, 'render_dashboard_page' ], + 1 + ); + + foreach ( $this->get_pages() as $page ) { + $menu_args = $page->menu( $capability, $position ); + + if ( ! $menu_args ) { + continue; + } + + $route = $menu_args['route'] ?? $page->get_id(); + $route = trim( $route, ' /' ); + + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $submenu[ $parent_slug ][] = [ $menu_args['menu_title'], $capability, 'admin.php?page=' . $menu_slug . '#/' . $route ]; + } + } + + /** + * Render the dashboard page. + * + * @since DOKAN_SINCE + * + * @return void + */ + public function render_dashboard_page(): void { + $settings = $this->settings(); + + ob_start(); + echo '
' . esc_html__( 'Loading...', 'dokan-lite' ) . '
'; + echo ob_get_clean(); + } + + /** + * Get all settings. + * + * @since DOKAN_SINCE + * + * @return array + */ + public function settings(): array { + $settings = [ + 'nonce' => wp_create_nonce( 'dokan_admin_dashboard' ), + ]; + + foreach ( $this->get_pages() as $page ) { + + /** + * Filter the settings for a specific page. + * + * @since DOKAN_SINCE + * + * @param array $settings The settings. + * @param string $page_id The page ID. + * @param Pageable $page The page. + */ + $settings[ $page->get_id() ] = apply_filters( 'dokan_admin_dashboard_page_settings', $page->settings(), $page->get_id(), $page ); + } + + /** + * Filter the settings. + * + * @since DOKAN_SINCE + * + * @param array $settings The settings. + */ + return apply_filters( 'dokan_admin_dashboard_pages_settings', $settings ); + } + + /** + * Get all scripts ids. + * + * @since DOKAN_SINCE + * + * @return array + */ + public function scripts(): array { + return array_reduce( + $this->get_pages(), fn( $carry, $page ) => array_merge( $carry, $page->scripts() ), [ $this->script_key ] + ); + } + + /** + * Get all styles ids. + * + * @since DOKAN_SINCE + * + * @return array + */ + public function styles(): array { + return array_reduce( + $this->get_pages(), fn( $carry, $page ) => array_merge( $carry, $page->styles() ), [ $this->script_key ] + ); + } + + /** + * Register dashboard scripts. + * + * @since DOKAN_SINCE + * + * @return void + */ + public function register_scripts() { + $script = require DOKAN_DIR . '/assets/js/dokan-admin-dashboard.asset.php'; + + // Register the main script. + wp_register_script( $this->script_key, DOKAN_PLUGIN_ASSEST . '/js/dokan-admin-dashboard.js', $script['dependencies'], $script['version'], true ); + wp_register_style( $this->script_key, DOKAN_PLUGIN_ASSEST . '/css/dokan-admin-dashboard.css', [], $script['version'] ); + + // Register all other scripts. + foreach ( $this->get_pages() as $page ) { + $page->register(); + } + } + + /** + * Enqueue dashboard scripts. + * + * @since DOKAN_SINCE + * + * @return void + */ + public function enqueue_scripts() { + $screen = get_current_screen(); + + if ( $screen->id !== 'toplevel_page_dokan' && $screen->id !== 'dokan_page_dokan-dashboard' ) { + return; + } + + foreach ( $this->scripts() as $handle ) { + wp_enqueue_script( $handle ); + } + + foreach ( $this->styles() as $handle ) { + wp_enqueue_style( $handle ); + } + } +} diff --git a/includes/Admin/Dashboard/Pageable.php b/includes/Admin/Dashboard/Pageable.php new file mode 100644 index 0000000000..5de14dc841 --- /dev/null +++ b/includes/Admin/Dashboard/Pageable.php @@ -0,0 +1,70 @@ + An array of associative arrays with keys 'route', 'page_title', 'menu_title', 'capability', 'position'. + */ + public function menu( string $capability, string $position ): array; + + /** + * Get the settings values. + * + * @since DOKAN_SINCE + * + * @return array An array of settings values. + */ + public function settings(): array; + + /** + * Get the scripts. + * + * @since DOKAN_SINCE + * + * @return array An array of script handles. + */ + public function scripts(): array; + + /** + * Get the styles. + * + * @since DOKAN_SINCE + * + * @return array An array of style handles. + */ + public function styles(): array; + + /** + * Register the page scripts and styles. + * + * @since DOKAN_SINCE + * + * @return void + */ + public function register(): void; +} diff --git a/includes/Admin/Dashboard/Pages/AbstractPage.php b/includes/Admin/Dashboard/Pages/AbstractPage.php new file mode 100644 index 0000000000..61ceba3963 --- /dev/null +++ b/includes/Admin/Dashboard/Pages/AbstractPage.php @@ -0,0 +1,59 @@ + __( 'Dokan Status', 'dokan-lite' ), + 'menu_title' => __( 'Status', 'dokan-lite' ), + 'route' => 'status', + 'capability' => $capability, + 'position' => 99, + ]; + } + + /** + * @inheritDoc + */ + public function settings(): array { + return []; + } + + /** + * @inheritDoc + */ + public function scripts(): array { + return [ 'dokan-status' ]; + } + + /** + * Get the styles. + * + * @since DOKAN_SINCE + * + * @return array An array of style handles. + */ + public function styles(): array { + return [ 'dokan-status' ]; + } + + /** + * Register the page scripts and styles. + * + * @since DOKAN_SINCE + * + * @return void + */ + public function register(): void { + $asset_file = include DOKAN_DIR . '/assets/js/dokan-status.asset.php'; + + wp_register_script( + 'dokan-status', + DOKAN_PLUGIN_ASSEST . '/js/dokan-status.js', + $asset_file['dependencies'], + $asset_file['version'], + [ + 'strategy' => 'defer', + 'in_footer' => true, + ] + ); + + wp_register_style( 'dokan-status', DOKAN_PLUGIN_ASSEST . '/css/dokan-status.css', [], $asset_file['version'] ); + } +} diff --git a/includes/Admin/Status/Status.php b/includes/Admin/Status/Status.php index 6ac27af0c9..2f76823bc5 100644 --- a/includes/Admin/Status/Status.php +++ b/includes/Admin/Status/Status.php @@ -13,10 +13,6 @@ class Status extends StatusElement { public function __construct() { parent::__construct( 'dokan-status' ); - - add_action( 'dokan_admin_menu', [ $this, 'register_menu' ], 99, 2 ); - add_action( 'dokan_register_scripts', [ $this, 'register_scripts' ] ); - add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); } /** @@ -35,66 +31,6 @@ public function render(): array { return parent::render()['children']; } - - /** - * Register the submenu menu. - * - * @since DOKAN_SINCE - * - * @param string $capability Menu capability. - * @param string $position Menu position. - * - * @return void - */ - public function register_menu( string $capability, string $position ) { - add_submenu_page( - 'dokan', - __( 'Dokan Status', 'dokan-lite' ), - __( 'Status', 'dokan-lite' ), - $capability, - 'dokan-status', - [ $this, 'render_status_page' ], - 99 - ); - } - - public function register_scripts() { - $asset_file = include DOKAN_DIR . '/assets/js/dokan-status.asset.php'; - - wp_register_script( - 'dokan-status', - DOKAN_PLUGIN_ASSEST . '/js/dokan-status.js', - $asset_file['dependencies'], - $asset_file['version'], - [ - 'strategy' => 'defer', - 'in_footer' => true, - ] - ); - - wp_register_style( 'dokan-status', DOKAN_PLUGIN_ASSEST . '/css/dokan-status.css', [], $asset_file['version'] ); - } - - public function enqueue_scripts() { - if ( 'dokan_page_dokan-status' !== get_current_screen()->id ) { - return; - } - - wp_enqueue_script( 'dokan-status' ); - wp_enqueue_style( 'dokan-status' ); - } - - /** - * Load Status Page Template - * - * @since DOKAN_SINCE - * - * @return void - */ - public function render_status_page() { - echo '
'; - } - /** * Describe the settings options. * diff --git a/includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php b/includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php new file mode 100644 index 0000000000..daad3fcc02 --- /dev/null +++ b/includes/DependencyManagement/Providers/AdminDashboardServiceProvider.php @@ -0,0 +1,29 @@ +services as $service ) { + $definition = $this->share_with_implements_tags( $service ); + $this->add_tags( $definition, $this->tags ); + } + } +} diff --git a/includes/DependencyManagement/Providers/ServiceProvider.php b/includes/DependencyManagement/Providers/ServiceProvider.php index 9ae5b8e70c..515cb24964 100644 --- a/includes/DependencyManagement/Providers/ServiceProvider.php +++ b/includes/DependencyManagement/Providers/ServiceProvider.php @@ -62,6 +62,7 @@ public function boot(): void { $this->getContainer()->addServiceProvider( new FrontendServiceProvider() ); $this->getContainer()->addServiceProvider( new AjaxServiceProvider() ); $this->getContainer()->addServiceProvider( new AnalyticsServiceProvider() ); + $this->getContainer()->addServiceProvider( new AdminDashboardServiceProvider() ); } /** diff --git a/src/Status/index.tsx b/src/Status/index.tsx index cd06865da6..0f26a363c0 100644 --- a/src/Status/index.tsx +++ b/src/Status/index.tsx @@ -1,13 +1,21 @@ -import { createRoot } from '@wordpress/element'; import domReady from '@wordpress/dom-ready'; import Status from './Status'; import './status.scss'; - -const statusDomNode = document.getElementById( 'dokan-status' ); -const statusRoot = createRoot( statusDomNode! ); +import { DokanAdminRoute } from '../admin/dashboard/components/Dashboard'; domReady( () => { - if ( statusDomNode ) { - statusRoot.render( ); - } + // @ts-ignore + wp.hooks.addFilter( + 'dokan-admin-dashboard-routes', + 'dokan-admin-dashboard-status', + ( routes: Array< DokanAdminRoute > ) => { + routes.push( { + id: 'dokan-status', + element: , + path: '/status', + } satisfies DokanAdminRoute ); + + return routes; + } + ); } ); diff --git a/src/admin/dashboard/admin-dashboard-tailwind.config.js b/src/admin/dashboard/admin-dashboard-tailwind.config.js new file mode 100644 index 0000000000..b1fac8829d --- /dev/null +++ b/src/admin/dashboard/admin-dashboard-tailwind.config.js @@ -0,0 +1,9 @@ +import baseConfig from './../../../base-tailwind.config'; + +/** @type {import('tailwindcss').Config} */ +const updatedConfig = { + ...baseConfig, + content: [ './src/admin/dashboard/**/*.{jsx,ts,tsx}' ], +}; + +export default updatedConfig; diff --git a/src/admin/dashboard/components/Dashboard.tsx b/src/admin/dashboard/components/Dashboard.tsx new file mode 100644 index 0000000000..87cc550af9 --- /dev/null +++ b/src/admin/dashboard/components/Dashboard.tsx @@ -0,0 +1,55 @@ +import { withRouter } from '../../../routing'; +import { createHashRouter, RouterProvider } from 'react-router-dom'; +import Layout from './Layout'; + +export type DokanAdminRoute = { + id: string; + element: JSX.Element | React.ReactNode; + path: string; + parent?: string; +}; + +const getAdminRoutes = () => { + let routes: Array< DokanAdminRoute > = []; + + // @ts-ignore + routes = wp.hooks.applyFilters( + 'dokan-admin-dashboard-routes', + routes + ) as Array< DokanAdminRoute >; + + routes.push( { + id: 'dokan-404', + element:

404

, + path: '*', + } ); + + return routes; +}; + +const Dashboard = () => { + const routes = getAdminRoutes(); + + const mapedRoutes = routes.map( ( route ) => { + const WithRouterComponent = withRouter( route.element ); + + return { + path: route.path, + element: ( + + + + ), + }; + } ); + + const router = createHashRouter( mapedRoutes ); + + return ( + <> + + + ); +}; + +export default Dashboard; diff --git a/src/admin/dashboard/components/Header.tsx b/src/admin/dashboard/components/Header.tsx new file mode 100644 index 0000000000..af631f9a94 --- /dev/null +++ b/src/admin/dashboard/components/Header.tsx @@ -0,0 +1,205 @@ +const Header = () => { + return ( + + ); +}; + +export default Header; diff --git a/src/admin/dashboard/components/Layout.tsx b/src/admin/dashboard/components/Layout.tsx new file mode 100644 index 0000000000..d3dc29b8c0 --- /dev/null +++ b/src/admin/dashboard/components/Layout.tsx @@ -0,0 +1,17 @@ +import { SlotFillProvider } from '@wordpress/components'; +import { PluginArea } from '@wordpress/plugins'; +import { DokanToaster } from '@getdokan/dokan-ui'; +import Header from './Header'; + +const Layout = ( { children, route } ) => { + return ( + +
+ { children } + + + + ); +}; + +export default Layout; diff --git a/src/admin/dashboard/index.tsx b/src/admin/dashboard/index.tsx new file mode 100644 index 0000000000..1238b4db6f --- /dev/null +++ b/src/admin/dashboard/index.tsx @@ -0,0 +1,12 @@ +import { createRoot } from '@wordpress/element'; +import domReady from '@wordpress/dom-ready'; +import Dashboard from './components/Dashboard'; + +const dashboardDomNode = document.getElementById( 'dokan-admin-dashboard' ); +const dashboardRoot = createRoot( dashboardDomNode! ); + +domReady( () => { + if ( dashboardDomNode ) { + dashboardRoot.render( ); + } +} ); diff --git a/src/admin/dashboard/style.scss b/src/admin/dashboard/style.scss new file mode 100644 index 0000000000..83d4a12d7f --- /dev/null +++ b/src/admin/dashboard/style.scss @@ -0,0 +1,2 @@ +@config './admin-dashboard-tailwind.config.js'; +@use '../../base-tailwind'; diff --git a/webpack-entries.js b/webpack-entries.js index fba7c3118b..c5c9d994df 100644 --- a/webpack-entries.js +++ b/webpack-entries.js @@ -2,13 +2,12 @@ const entryPoints = { // Dokan tailwind css 'dokan-tailwind': './src/tailwind.css', - 'frontend': './src/dashboard/index.tsx', + frontend: './src/dashboard/index.tsx', + 'dokan-admin-dashboard': './src/admin/dashboard/index.tsx', 'vue-frontend': './src/frontend/main.js', 'vue-admin': './src/admin/main.js', 'vue-bootstrap': './src/utils/Bootstrap.js', - 'vue-vendor': [ - './src/utils/vue-vendor.js', - ], + 'vue-vendor': [ './src/utils/vue-vendor.js' ], 'dokan-promo-notice': './src/promo-notice/main.js', 'dokan-admin-notice': './src/admin/notice/main.js', 'reverse-withdrawal': './assets/src/js/reverse-withdrawal.js', @@ -18,38 +17,36 @@ const entryPoints = { 'vendor-registration': './assets/src/js/vendor-registration.js', 'customize-controls': './assets/src/js/customize-controls.js', 'customize-preview': './assets/src/js/customize-preview.js', - 'pointers': './assets/src/js/pointers.js', - 'dokan': [ + pointers: './assets/src/js/pointers.js', + dokan: [ './assets/src/js/orders.js', './assets/src/js/product-editor.js', './assets/src/js/script.js', './assets/src/js/store-lists.js', './assets/src/js/withdraw.js', - './assets/src/js/dokan-daterangepicker.js' + './assets/src/js/dokan-daterangepicker.js', ], 'login-form-popup': './assets/src/js/login-form-popup.js', 'dokan-maps-compat': './assets/src/js/dokan-maps-compat.js', 'dokan-admin': './assets/src/js/admin.js', - 'dokan-setup-no-wc': [ - './assets/src/js/setup-no-wc.js' - ], - 'helper': './assets/src/js/helper.js', + 'dokan-setup-no-wc': [ './assets/src/js/setup-no-wc.js' ], + helper: './assets/src/js/helper.js', 'dokan-frontend': './assets/src/js/dokan-frontend.js', - 'style': '/assets/src/less/style.less', - 'rtl': '/assets/src/less/rtl.less', - 'admin': '/assets/src/less/admin.less', - 'plugin': '/assets/src/less/plugin.less', + style: '/assets/src/less/style.less', + rtl: '/assets/src/less/rtl.less', + admin: '/assets/src/less/admin.less', + plugin: '/assets/src/less/plugin.less', 'global-admin': '/assets/src/less/global-admin.less', - 'setup': '/assets/src/less/setup.less', - 'setup-no-wc-style': [ - '/assets/src/less/setup-no-wc.less' - ], + setup: '/assets/src/less/setup.less', + 'setup-no-wc-style': [ '/assets/src/less/setup-no-wc.less' ], 'reverse-withdrawal-style': '/assets/src/less/reverse-withdrawal.less', - 'dokan-product-category-ui': '/assets/src/less/dokan-product-category-ui.less', + 'dokan-product-category-ui': + '/assets/src/less/dokan-product-category-ui.less', 'dokan-admin-product-style': '/assets/src/less/dokan-admin-product.less', 'page-views': './assets/src/js/page-views.js', - 'dokan-setup-wizard-commission': './assets/src/js/setup-wizard/commission/index.js', + 'dokan-setup-wizard-commission': + './assets/src/js/setup-wizard/commission/index.js', // Category commission component styles. 'dokan-category-commission': '/src/admin/components/Commission/index.js', 'dokan-status': '/src/Status/index.tsx',