Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions assets/apps/dashboard/src/Components/Content/AvailableModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/* global neveDash */
import { __ } from '@wordpress/i18n';
import {
NEVE_AVAIABLE_MODULES_ICON_MAP,
NEVE_STORE,
} from '../../utils/constants';
import { ArrowRight, LoaderCircle, LucideSettings } from 'lucide-react';
import Card from '../../Layout/Card';
import { useState } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import Toggle from '../Common/Toggle';
import { send } from '../../utils/rest';

const Toast = ({ message }) => {
return (
<div className="fixed flex items-center gap-2 z-[99999] top-10 right-5 bg-white text-black text-base font-medium px-4 py-2 rounded shadow border border-solid border-blue-600">
{message}
</div>
);
};

const ModuleToggle = ({
slug,
moduleData,
setMessage,
isActive,
setIsActive,
isInstalled,
setIsInstalled,
}) => {
const [loading, setLoading] = useState(false);
const { changeModuleStatus, setObfxModuleStatus, setToast } =
useDispatch(NEVE_STORE);
const { moduleStatus } = useSelect((select) => {
const { getObfxModuleStatus } = select(NEVE_STORE);

return {
moduleStatus: getObfxModuleStatus(slug) || false,
};
});

const { api } = neveDash;
const { title } = moduleData;
const toastMessage = {
installing: __('Installing Orbix Fox Plugin', 'neve'),
activating: __('Activating Orbix Fox Plugin', 'neve'),
};

const handleToggle = async (value) => {
try {
setLoading(true);
changeModuleStatus(slug, value);

let isPluginActive = true;
// Handle plugin installation or activation
if (!isInstalled) {
setMessage(toastMessage.installing);
isPluginActive = false;
} else if (!isActive) {
setMessage(toastMessage.activating);
isPluginActive = false;
}

if (!isPluginActive) {
await send(api + 'activate-plugin', {
slug: 'themeisle-companion',
}).then((res) => {
if (res.success) {
setIsInstalled(true);
setIsActive(true);
}
});
}

// Fire the send method after install/activate or immediately if both are done
const response = await send(api + 'activate-module', {
slug,
value,
});

setObfxModuleStatus(slug, response.success ? value : !value);
setToast(
response.success
? (value
? __('Module Activated', 'neve')
: __('Module Deactivated.', 'neve')) + ` (${title})`
: response.data
);
} catch (error) {
setToast(
__('Something went wrong while activating the module.', 'neve')
);
} finally {
setLoading(false);
setMessage('');
}
};

return (
<div className="flex gap-2 items-center">
<Toggle
checked={moduleStatus}
onToggle={handleToggle}
disabled={loading}
/>
</div>
);
};

const AvailableModuleCard = ({
moduleData,
slug,
setMessage,
isActive,
setIsActive,
isInstalled,
setIsInstalled,
}) => {
const { title, description } = moduleData;
const CardIcon = NEVE_AVAIABLE_MODULES_ICON_MAP[slug] || LucideSettings;

return (
<Card
icon={<CardIcon size={18} />}
title={title}
className="bg-white p-6 rounded-lg shadow-sm"
afterTitle={
<ModuleToggle
slug={slug}
moduleData={moduleData}
setMessage={setMessage}
isActive={isActive}
setIsActive={setIsActive}
isInstalled={isInstalled}
setIsInstalled={setIsInstalled}
/>
}
id={`module-${slug}`}
>
<p className="text-gray-600 text-sm leading-relaxed">
{description}
</p>
{!isActive ? (
<p className="mt-2 italic text-sm text-gray-500">
{__(
'This feature is part of OrbitFox plugin, built by the Neve team. Enabling the toggle will automatically install and activate the plugin.',
'neve'
)}
</p>
) : (
<a
className="flex mt-2 text-blue-600 gap-2 align-middle"
href="admin.php?page=obfx_companion"
>
{__('Go to Settings to Edit')}
<ArrowRight size={18} />
</a>
)}
</Card>
);
};

export default () => {
const [message, setMessage] = useState('');
const [isInstalled, setIsInstalled] = useState(
neveDash.orbitFox.isInstalled
);
const [isActive, setIsActive] = useState(neveDash.orbitFox.isActive);

return (
<>
<div className="mb-6">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold">
{__('Available Modules', 'neve')}
</h2>
</div>
<div className="grid xl:grid-cols-2 gap-6">
{Object.entries(neveDash.availableModules).map(
([slug, moduleData]) => (
<AvailableModuleCard
key={slug}
slug={slug}
moduleData={moduleData}
setMessage={setMessage}
isActive={isActive}
setIsActive={setIsActive}
isInstalled={isInstalled}
setIsInstalled={setIsInstalled}
/>
)
)}
</div>
</div>
{message && (
<Toast
message={
<>
<LoaderCircle
size={18}
className="animate-spin text-blue-800"
/>
{message}
</>
}
/>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import AvailableModule from '../AvailableModule';
import ModuleGrid from '../ModuleGrid';

export default () => {
return <ModuleGrid />;
return (
<>
<AvailableModule />
<ModuleGrid />
</>
);
};
6 changes: 6 additions & 0 deletions assets/apps/dashboard/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,10 @@ export default {
payload: loggerStatus,
};
},
setObfxModuleStatus(slug, value) {
return {
type: 'SET_OBFX_MODULE_STATUS',
payload: { slug, value },
};
},
};
11 changes: 11 additions & 0 deletions assets/apps/dashboard/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const initialState = {
currentTab: 'start',
license: neveDash.pro ? neveDash.license : {},
notifications: neveDash.notifications || {},
obfxModuleStatus: neveDash.orbitFox?.data?.module_status || {},
};

const hash = getTabHash();
Expand Down Expand Up @@ -73,6 +74,16 @@ const reducer = (state = initialState, action) => {
neve_logger_flag: action.payload,
},
};
case 'SET_OBFX_MODULE_STATUS':
return {
...state,
obfxModuleStatus: {
...state.obfxModuleStatus,
[action.payload.slug]: {
active: action.payload.value,
},
},
};
}
return state;
};
Expand Down
11 changes: 11 additions & 0 deletions assets/apps/dashboard/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,15 @@ export default {

return shownNotifications;
},
getObfxModuleStatus: (state, slug) => {
if (!state.obfxModuleStatus) {
return false;
}

if (state.obfxModuleStatus[slug]) {
return state.obfxModuleStatus[slug]?.active || false;
}

return false;
},
};
10 changes: 10 additions & 0 deletions assets/apps/dashboard/src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import {
LucideGraduationCap,
LucideImage,
LucideLayoutTemplate,
LucideLock,
LucideNewspaper,
LucidePalette,
LucidePanelRightDashed,
LucidePanelsTopLeft,
LucidePanelTopDashed,
LucidePin,
LucideRss,
Expand All @@ -22,6 +24,7 @@ import {
LucideShoppingCart,
LucideTimer,
LucideToyBrick,
LucideType,
LucideTypeOutline,
} from 'lucide-react';

Expand Down Expand Up @@ -67,3 +70,10 @@ export const NEVE_PLUGIN_ICON_MAP = {
'hyve-lite': LucideBotMessageSquare,
// 'sparks'
};

export const NEVE_AVAIABLE_MODULES_ICON_MAP = {
'login-customizer': LucideLock,
'custom-fonts': LucideType,
'policy-notice': LucideShield,
'post-duplicator': LucidePanelsTopLeft,
};
36 changes: 36 additions & 0 deletions inc/admin/dashboard/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,14 @@ private function get_localization() {
'canActivatePlugins' => current_user_can( 'activate_plugins' ),
'rootUrl' => get_site_url(),
'sparksActive' => defined( 'SPARKS_WC_VERSION' ) ? 'yes' : 'no',
'api' => esc_url( rest_url( '/nv/v1/dashboard/' ) ),
'availableModules' => $this->get_available_modules(),
'orbitFox' => array(
'isInstalled' => file_exists( ABSPATH . 'wp-content/plugins/themeisle-companion/themeisle-companion.php' ),
'isActive' => class_exists( 'Orbit_Fox' ),
'activationUrl' => $this->plugin_helper->get_plugin_action_link( 'themeisle-companion' ),
'data' => class_exists( 'Orbit_Fox' ) ? get_option( 'obfx_data' ) : array(),
),
];

if ( defined( 'NEVE_PRO_PATH' ) ) {
Expand Down Expand Up @@ -824,6 +832,34 @@ private function get_external_plugins_data() {
return $plugins;
}

/**
* Get available modules.
*
* @return array<mixed>
*/
private function get_available_modules() {
$modules = array(
'login-customizer' => array(
'title' => __( 'Login Customizer', 'neve' ),
'description' => __( 'Customize your WordPress login page with branding and styling options.', 'neve' ),
),
'custom-fonts' => array(
'title' => __( 'Custom Fonts/Scripts', 'neve' ),
'description' => __( 'Add custom fonts and scripts to your website easily.', 'neve' ),
),
'policy-notice' => array(
'title' => __( 'Cookie Notice', 'neve' ),
'description' => __( 'Display a customizable cookie consent notice for GDPR compliance.', 'neve' ),
),
'post-duplicator' => array(
'title' => __( 'Duplicate Page', 'neve' ),
'description' => __( 'Quickly duplicate posts, pages, and custom post types.', 'neve' ),
),
);

return apply_filters( 'neve_available_modules', $modules );
}

/**
* Renders the custom layout header section in the admin dashboard for Custom Layouts
*
Expand Down
Loading
Loading