--- a/kivicare-clinic-management-system/app/abstracts/KCAbstractPaymentGateway.php
+++ b/kivicare-clinic-management-system/app/abstracts/KCAbstractPaymentGateway.php
@@ -0,0 +1,207 @@
+<?php
+namespace Appabstracts;
+
+use AppbaseClassesKCErrorLogger;
+if (!defined('ABSPATH')) {
+ exit; // Exit if accessed directly
+}
+/**
+ * Abstract Payment Gateway Class
+ * Base class for all payment gateway implementations
+ */
+abstract class KCAbstractPaymentGateway {
+
+ protected $gateway_id;
+ protected $gateway_name;
+ protected $gateway_description;
+
+ protected $gateway_logo;
+ protected $is_enabled;
+ protected $test_mode;
+ protected $settings;
+ protected $appointment_data;
+
+ protected $settings_fields = [];
+
+ protected $settings_keys;
+
+ /**
+ * Constructor
+ * @param string $setting_key Gateway specific settings
+ */
+ public function __construct($setting_key) {
+ $this->settings_keys = $setting_key;
+ $settings = get_option($this->settings_keys, []);
+
+ // If settings is a string, try to decode it as JSON
+ if (is_string($settings)) {
+ $decoded = json_decode($settings, true);
+
+ if(!empty($decoded)){
+ // Only use decoded value if it's valid JSON and results in an array
+ $settings = json_last_error() === JSON_ERROR_NONE && is_array($decoded) ? $decoded : [];
+ }
+ }
+
+ $this->settings = $settings;
+ $this->init();
+ }
+
+ /**
+ * Initialize gateway specific settings
+ */
+ abstract protected function init();
+
+ /**
+ * Process the payment
+ * @param array $appointment_data Appointment booking data
+ * @return array Payment response
+ */
+ abstract public function process_payment($appointment_data);
+
+ /**
+ * Validate payment data
+ * @param array $payment_data Payment form data
+ * @return bool|WP_Error
+ */
+ abstract public function validate_payment_data($payment_data);
+
+ /**
+ * Handle payment callback/webhook
+ * @param array $callback_data Callback data from payment provider
+ * @return array Response data
+ */
+ abstract public function handle_payment_callback($callback_data);
+
+ /**
+ * Get gateway ID
+ * @return string
+ */
+ public function get_gateway_id() {
+ return $this->gateway_id;
+ }
+
+ /**
+ * Get gateway name
+ * @return string
+ */
+ public function get_gateway_name() {
+ return $this->gateway_name;
+ }
+
+ public function get_gateway_logo() {
+ return $this->gateway_logo;
+ }
+
+ public function get_gateway_description() {
+ return $this->gateway_description;
+ }
+
+ /**
+ * Check if gateway is enabled
+ * @return bool
+ */
+ public function is_enabled() {
+ return $this->is_enabled;
+ }
+
+ /**
+ * Check if in test mode
+ * @return bool
+ */
+ public function is_test_mode() {
+ return $this->test_mode;
+ }
+
+ /**
+ * Get setting value
+ * @param string $key Setting key
+ * @param mixed $default Default value
+ * @return mixed
+ */
+ protected function get_setting($key, $default = '') {
+ return isset($this->settings[$key]) ? $this->settings[$key] : $default;
+ }
+ /**
+ * Set setting value
+ * @param string $key Setting key
+ * @param mixed $value Setting value
+ */
+ public function get_settings(){
+ return $this->settings;
+ }
+
+ /**
+ * Log gateway activity
+ * @param string $message Log message
+ * @param string $level Log level (info, error, debug)
+ */
+ protected function log($message, $level = 'info') {
+ KCErrorLogger::instance()->error("[{$this->gateway_id}] {$level}: {$message}");
+ }
+
+ /**
+ * Format amount for payment processing
+ * @param float $amount Amount to format
+ * @return float
+ */
+ protected function format_amount($amount) {
+ return round($amount, 2);
+ }
+
+ /**
+ * Generate transaction reference
+ * @param int $appointment_id Appointment ID
+ * @return string
+ */
+ protected function generate_transaction_ref($appointment_id) {
+ return $this->gateway_id . '_' . $appointment_id . '_' . time();
+ }
+
+ /**
+ * Create payment response array
+ * @param string $status Payment status (success, failed, pending)
+ * @param string $message Response message
+ * @param array $data Additional response data
+ * @return array
+ */
+ protected function create_payment_response($status, $message, $data = []) {
+ return array(
+ 'status' => $status,
+ 'message' => $message,
+ 'gateway' => $this->gateway_id,
+ 'data' => $data,
+ 'timestamp' => current_time('timestamp')
+ );
+ }
+ /**
+ * Get return URL after payment
+ * @param int $appointment_id Appointment ID
+ * @return string Return URL
+ */
+ protected function get_return_url($appointment_id) {
+ return rest_url('kivicare/v1/appointments/payment-success?appointment_id=' . $appointment_id . '&gateway='.$this->gateway_id);
+ }
+
+ /**
+ * Get cancel URL
+ * @param int $appointment_id Appointment ID
+ * @return string Cancel URL
+ */
+ protected function get_cancel_url($appointment_id) {
+ return rest_url('kivicare/v1/appointments/payment-cancel?appointment_id=' . $appointment_id . '&gateway='.$this->gateway_id);
+ }
+
+ public function init_hook(){}
+
+ public function get_fields(): array {
+ return $this->settings_fields;
+ }
+ public abstract function update_settings($settings);
+
+ protected function save_settings()
+ {
+ $json_settings = json_encode($this->settings);
+ update_option($this->settings_keys, $json_settings);
+ }
+}
No newline at end of file
--- a/kivicare-clinic-management-system/app/abstracts/KCAbstractTelemedProvider.php
+++ b/kivicare-clinic-management-system/app/abstracts/KCAbstractTelemedProvider.php
@@ -0,0 +1,555 @@
+<?php
+namespace Appabstracts;
+
+use AppbaseClassesKCErrorLogger;
+use KCTAppmodelsKCTAppointmentZoomMapping;
+use KCGMAppmodelsKCGMAppointmentGoogleMeetMapping;
+
+
+if (!defined('ABSPATH')) {
+ exit; // Exit if accessed directly
+}
+
+/**
+ * Abstract Telemed Provider
+ * Base class for all telemedical service providers
+ */
+abstract class KCAbstractTelemedProvider
+{
+ /**
+ * Provider settings key
+ * @var string
+ */
+ protected $settings_key;
+
+ /**
+ * Provider configuration
+ * @var array
+ */
+ protected $config;
+
+ /**
+ * Provider ID
+ * @var string
+ */
+ protected $provider_id;
+
+ /**
+ * Provider name
+ * @var string
+ */
+ protected $provider_name;
+
+ /**
+ * Constructor
+ * @param string $settings_key Settings key for this provider
+ */
+ public function __construct($settings_key)
+ {
+ $this->settings_key = $settings_key;
+ $this->config = $this->get_config();
+ $this->init();
+ }
+
+ /**
+ * Initialize provider
+ * Called after construction, can be overridden by child classes
+ */
+ protected function init()
+ {
+ // Override in child classes for specific initialization
+ }
+
+ /**
+ * Initialize hooks
+ * Called by factory to set up WordPress hooks
+ */
+ public function init_hook()
+ {
+ // Add any WordPress hooks here
+ add_action('wp_ajax_kc_test_' . $this->get_provider_id() . '_connection', array($this, 'test_connection_ajax'));
+ add_action('wp_ajax_kc_' . $this->get_provider_id() . '_webhook', array($this, 'handle_webhook'));
+ add_action('wp_ajax_nopriv_kc_' . $this->get_provider_id() . '_webhook', array($this, 'handle_webhook'));
+ }
+
+ /**
+ * Get provider configuration from database
+ * @return array Provider configuration
+ */
+ public function get_config()
+ {
+ $default_config = $this->get_default_config();
+ $saved_config = get_option($this->settings_key, array());
+
+ return wp_parse_args($saved_config, $default_config);
+ }
+
+ /**
+ * Get default configuration
+ * Must be implemented by child classes
+ * @return array Default configuration
+ */
+ abstract protected function get_default_config();
+
+ /**
+ * Get provider ID
+ * Must be implemented by child classes
+ * @return string Provider identifier
+ */
+ abstract public function get_provider_id();
+
+ /**
+ * Get provider name
+ * Must be implemented by child classes
+ * @return string Provider display name
+ */
+ abstract public function get_provider_name();
+
+ /**
+ * Check if provider is enabled
+ * @return bool True if provider is enabled
+ */
+ public function is_enabled()
+ {
+ return !empty($this->config['enabled']) && $this->is_configured();
+ }
+
+ /**
+ * Check if provider is properly configured
+ * Must be implemented by child classes
+ * @return bool True if provider has required configuration
+ */
+ abstract public function is_configured();
+
+ /**
+ * Create a meeting
+ * Must be implemented by child classes
+ * @param array $meeting_data Meeting configuration
+ * @return array|WP_Error Meeting details or WP_Error on failure
+ */
+ abstract public function create_meeting($meeting_data = array());
+
+ /**
+ * Update a meeting
+ * Must be implemented by child classes
+ * @param string $meeting_id Meeting ID
+ * @param array $meeting_data Updated meeting configuration
+ * @return array|false Updated meeting details or false on failure
+ */
+ abstract public function update_meeting($meeting_id, $meeting_data = array());
+
+ /**
+ * Delete a meeting
+ * Must be implemented by child classes
+ * @param string $meeting_id Meeting ID
+ * @return bool True on success, false on failure
+ */
+ abstract public function delete_meeting($meeting_id);
+
+ /**
+ * Get meeting details
+ * Must be implemented by child classes
+ * @param string $meeting_id Meeting ID
+ * @return array|false Meeting details or false if not found
+ */
+ abstract public function get_meeting($meeting_id);
+
+ /**
+ * Test connection to provider API
+ * Must be implemented by child classes
+ * @param array $data Connection data
+ * @param int $doctor_id Doctor ID
+ * @return array Connection test result with success status and message
+ */
+ abstract public function test_connection($data = [], $doctor_id = 0);
+
+ /**
+ * Update doctor configuration
+ * @param int $doctor_id Doctor ID
+ * @param array $config Configuration data
+ * @return bool True on success
+ */
+ abstract public function update_doctor_config(int $doctor_id, array $config): bool;
+
+ /**
+ * Get authorization URL for doctor
+ * @param int $doctor_id Doctor ID
+ * @return string Authorization URL
+ */
+ abstract public function get_authorization_url($doctor_id): string;
+
+ /**
+ * Disconnect doctor from provider
+ * @param int $doctor_id Doctor ID
+ * @return bool True on success
+ */
+ public function disconnect_doctor(int $doctor_id): bool
+ {
+ return false;
+ }
+
+ /**
+ * Check if provider supports recording
+ * @return bool True if recording is supported
+ */
+ public function supports_recording()
+ {
+ return false; // Override in child classes if recording is supported
+ }
+
+ /**
+ * Check if provider supports waiting room
+ * @return bool True if waiting room is supported
+ */
+ public function supports_waiting_room()
+ {
+ return false; // Override in child classes if waiting room is supported
+ }
+
+ /**
+ * Check if provider supports password protection
+ * @return bool True if password protection is supported
+ */
+ public function supports_password()
+ {
+ return false; // Override in child classes if password protection is supported
+ }
+
+ /**
+ * Get supported meeting types
+ * @return array Array of supported meeting types
+ */
+ public function get_supported_meeting_types()
+ {
+ return array('instant', 'scheduled'); // Override in child classes for specific types
+ }
+
+ /**
+ * Get maximum meeting duration (in minutes)
+ * @return int Maximum duration in minutes, 0 for unlimited
+ */
+ public function get_max_duration()
+ {
+ return 0; // Override in child classes for specific limits
+ }
+
+ /**
+ * Get maximum participants
+ * @return int Maximum participants, 0 for unlimited
+ */
+ public function get_max_participants()
+ {
+ return 0; // Override in child classes for specific limits
+ }
+
+ /**
+ * Format meeting data for API
+ * @param array $meeting_data Raw meeting data
+ * @return array Formatted meeting data
+ */
+ protected function format_meeting_data($meeting_data)
+ {
+ $defaults = array(
+ 'topic' => 'Telemedical Consultation',
+ 'type' => 'scheduled',
+ 'start_time' => current_time('mysql'),
+ 'duration' => 30,
+ 'timezone' => wp_timezone_string(),
+ 'password' => '',
+ 'waiting_room' => false,
+ 'auto_recording' => false,
+ 'host_video' => true,
+ 'participant_video' => true,
+ 'mute_upon_entry' => true,
+ 'patient_id' => '',
+ 'doctor_id' => '',
+ 'appointment_id' => ''
+ );
+
+ return wp_parse_args($meeting_data, $defaults);
+ }
+
+ /**
+ * Save meeting record using appropriate model
+ * @param array $meeting_data Meeting data to save
+ * @return int|false Meeting record ID or false on failure
+ */
+ protected function save_meeting_record($meeting_data)
+ {
+ try {
+ $provider_id = $this->get_provider_id();
+ KCErrorLogger::instance()->error($provider_id );
+
+ switch ($provider_id) {
+ case 'zoom':
+ return $this->save_zoom_meeting_record($meeting_data);
+
+ case 'googlemeet':
+ return $this->save_google_meet_record($meeting_data);
+
+ default:
+ // For other providers, you can extend this or create generic models
+ $this->log('warning', "No specific model found for provider: {$provider_id}");
+ return false;
+ }
+ } catch (Exception $e) {
+ $this->log('error', 'Failed to save meeting record: ' . $e->getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Save Zoom meeting record
+ * @param array $meeting_data Meeting data
+ * @return int|false
+ */
+ abstract protected function save_zoom_meeting_record($meeting_data);
+
+ /**
+ * Save Google Meet record
+ * @param array $meeting_data Meeting data
+ * @return int|false
+ */
+ protected function save_google_meet_record($meeting_data)
+ {
+ try {
+
+ $google_meet_mapping = KCGMAppointmentGoogleMeetMapping::create([
+ 'eventId' => $meeting_data['id'] ?? $meeting_data['event_id'] ?? null,
+ 'appointmentId' => $meeting_data['appointment_id'] ?? 0,
+ 'url' => $meeting_data['join_url'] ?? $meeting_data['url'] ?? null,
+ 'password' => $meeting_data['password'] ?? null,
+ 'eventUrl' => $meeting_data['event_url'] ?? null,
+ ]);
+
+ if (!$google_meet_mapping) {
+ $this->log('error', __('Failed to save Google Meet record', 'kivicare-clinic-management-system'), [
+ 'meeting_data' => $meeting_data
+ ]);
+ return false;
+ }
+
+ $this->log('info', __('Google Meet record saved', 'kivicare-clinic-management-system'), [
+ 'meeting_data' => $meeting_data,
+ 'record_id' => $google_meet_mapping
+ ]);
+ return $google_meet_mapping;
+ } catch (Exception $e) {
+ $this->log('error', __('Failed to save Google Meet record: ', 'kivicare-clinic-management-system') . $e->getMessage(), [
+ 'meeting_data' => $meeting_data
+ ]);
+ return false;
+ }
+ }
+
+ /**
+ * Update meeting record using appropriate model
+ * @param string $meeting_id Meeting ID
+ * @param array $meeting_data Updated meeting data
+ * @return bool True on success, false on failure
+ */
+ protected function update_meeting_record($meeting_id, $meeting_data)
+ {
+ try {
+ $provider_id = $this->get_provider_id();
+ KCErrorLogger::instance()->error('update_meeting_record'.print_r($meeting_data));
+ switch ($provider_id) {
+ case 'zoom':
+ return $this->update_zoom_meeting_record($meeting_id, $meeting_data);
+
+ case 'googlemeet':
+ return $this->update_google_meet_record($meeting_id, $meeting_data);
+
+ default:
+ $this->log('warning', "No specific model found for provider: {$provider_id}");
+ return false;
+ }
+ } catch (Exception $e) {
+ $this->log('error', 'Failed to update meeting record: ' . $e->getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Update Zoom meeting record
+ * @param string $meeting_id Zoom meeting ID
+ * @param array $meeting_data Updated data
+ * @return bool
+ */
+ protected function update_zoom_meeting_record($meeting_id, $meeting_data)
+ {
+ $zoom_mapping = KCTAppointmentZoomMapping::query()
+ ->where('zoomId', $meeting_id)
+ ->first();
+
+ if (!$zoom_mapping) {
+ return false;
+ }
+
+ $update_data = [];
+ if (isset($meeting_data['start_url'])) {
+ $update_data['startUrl'] = $meeting_data['start_url'];
+ }
+ if (isset($meeting_data['join_url'])) {
+ $update_data['joinUrl'] = $meeting_data['join_url'];
+ }
+ if (isset($meeting_data['password'])) {
+ $update_data['password'] = $meeting_data['password'];
+ }
+
+ return !empty($update_data) ? $zoom_mapping->update($update_data) : true;
+ }
+
+ /**
+ * Update Google Meet record
+ * @param string $meeting_id Event ID
+ * @param array $meeting_data Updated data
+ * @return bool
+ */
+ protected function update_google_meet_record($meeting_id, $meeting_data)
+ {
+ $google_meet_mapping = KCGMAppointmentGoogleMeetMapping::query()
+ ->where('eventId', $meeting_id)
+ ->first();
+
+ if (!$google_meet_mapping) {
+ return false;
+ }
+
+ $update_data = [];
+ if (isset($meeting_data['url']) || isset($meeting_data['join_url'])) {
+ $update_data['url'] = $meeting_data['url'] ?? $meeting_data['join_url'];
+ }
+ if (isset($meeting_data['password'])) {
+ $update_data['password'] = $meeting_data['password'];
+ }
+ if (isset($meeting_data['event_url'])) {
+ $update_data['eventUrl'] = $meeting_data['event_url'];
+ }
+
+ return !empty($update_data) ? $google_meet_mapping->update($update_data) : true;
+ }
+
+ /**
+ * Delete meeting record using appropriate model
+ * @param string $meeting_id Meeting ID
+ * @return bool True on success, false on failure
+ */
+ abstract protected function delete_meeting_record($meeting_id);
+
+
+ /**
+ * Get meeting records by appointment ID
+ * @param int $appointment_id Appointment ID
+ * @return object|null Meeting record or null if not found
+ */
+ abstract public function get_meeting_by_appointment($appointment_id);
+
+
+ /**
+ * Handle AJAX connection test
+ */
+ public function test_connection_ajax()
+ {
+ // Verify nonce for security
+ $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
+
+ if ( ! wp_verify_nonce( $nonce, 'kc_telemed_test_connection' ) ) {
+ wp_die( esc_html__( 'Security check failed', 'kivicare-clinic-management-system' ) );
+ }
+
+ // Check user permissions
+ if (!current_user_can('manage_options')) {
+ wp_die('Insufficient permissions');
+ }
+
+ $result = $this->test_connection();
+ wp_send_json($result);
+ }
+
+ /**
+ * Handle webhook from provider
+ * Override in child classes for specific webhook handling
+ */
+ public function handle_webhook()
+ {
+ // Override in child classes for specific webhook handling
+ wp_send_json_success();
+ }
+
+ /**
+ * Log provider activity
+ * @param string $level Log level (info, warning, error)
+ * @param string $message Log message
+ * @param array $context Additional context data
+ */
+ protected function log($level, $message, $context = array())
+ {
+ if (defined('WP_DEBUG') && WP_DEBUG) {
+ $log_message = sprintf(
+ '[%s] %s: %s',
+ $this->get_provider_name(),
+ strtoupper($level),
+ $message
+ );
+
+ if (!empty($context)) {
+ $log_message .= ' Context: ' . wp_json_encode($context);
+ }
+
+ KCErrorLogger::instance()->error($log_message);
+ }
+ }
+
+ /**
+ * Get configuration field value
+ * @param string $key Configuration key
+ * @param mixed $default Default value
+ * @return mixed Configuration value
+ */
+ protected function get_config_value($key, $default = null)
+ {
+ return isset($this->config[$key]) ? $this->config[$key] : $default;
+ }
+
+ /**
+ * Update configuration
+ * @param array $new_config New configuration data
+ * @return bool True on success, false on failure
+ */
+ public function update_config($new_config)
+ {
+ $this->config = wp_parse_args($new_config, $this->get_default_config());
+ return update_option($this->settings_key, $this->config);
+ }
+
+ /**
+ * Get provider capabilities
+ * @return array Array of provider capabilities
+ */
+ public function get_capabilities()
+ {
+ return array(
+ 'recording' => $this->supports_recording(),
+ 'waiting_room' => $this->supports_waiting_room(),
+ 'password' => $this->supports_password(),
+ 'meeting_types' => $this->get_supported_meeting_types(),
+ 'max_duration' => $this->get_max_duration(),
+ 'max_participants' => $this->get_max_participants()
+ );
+ }
+ /**
+ * Get provider ID
+ * @return bool True Appointment Meeting is cancle is set, false otherwise
+ */
+ public abstract function cancel_meeting_by_appointment($appointment_id);
+
+ /**
+ * Get doctor access token
+ * @param int $doctor_id Doctor ID
+ * @return string|null Access token or null if not found
+ */
+ public abstract function get_doctor_access_token($doctor_id);
+
+ public abstract function is_doctor_telemed_connected(): bool;
+}
No newline at end of file
--- a/kivicare-clinic-management-system/app/abstracts/KCElementorWidgetAbstract.php
+++ b/kivicare-clinic-management-system/app/abstracts/KCElementorWidgetAbstract.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace Appabstracts;
+
+use AppbaseClassesKCBase;
+use ElementorWidget_Base;
+
+use KucrutVite;
+use function IqonicViteiqonic_enqueue_asset;
+
+abstract class KCElementorWidgetAbstract extends Widget_Base
+{
+ /**
+ * Asset handle prefix
+ *
+ * @var string
+ */
+ protected $handle_prefix = KIVI_CARE_NAME;
+
+ /**
+ * Directory path where Vite assets are located
+ *
+ * @var string
+ */
+ protected $assets_dir;
+
+ /**
+ * Entry point for the widget's JavaScript
+ *
+ * @var string
+ */
+ protected $js_entry;
+
+ /**
+ * Entry point for the widget's CSS
+ *
+ * @var string
+ */
+ protected $css_entry;
+
+ /**
+ * Script dependencies
+ *
+ * @var array
+ */
+ protected $script_dependencies = ['jquery', 'elementor-frontend'];
+
+ /**
+ * CSS dependencies
+ *
+ * @var array
+ */
+ protected $css_dependencies = [];
+
+ /**
+ * Load scripts in footer
+ *
+ * @var bool
+ */
+ protected $in_footer = true;
+
+ /**
+ * CSS media type
+ *
+ * @var string
+ */
+ protected $css_media = 'all';
+
+ /**
+ * KCBase instance
+ */
+ protected KCBase $kcbase;
+
+ /**
+ * Static flag to prevent duplicate localization
+ */
+ protected static $is_localize_enqueued = false;
+
+ /**
+ * KCElementorWidgetAbstract constructor.
+ */
+ public function __construct($data = [], $args = null)
+ {
+ parent::__construct($data, $args);
+
+ // Set the default assets directory if not overridden
+ if (empty($this->assets_dir)) {
+ $this->assets_dir = KIVI_CARE_DIR . '/dist';
+ }
+
+ $this->kcbase = KCBase::get_instance();
+ }
+
+ /**
+ * Register assets for the widget using Vite
+ *
+ * @return void
+ */
+ public function registerAssets()
+ {
+ if (empty($this->js_entry) && empty($this->css_entry)) {
+ return;
+ }
+
+ // Enqueue JS if available
+ if (!empty($this->js_entry)) {
+ iqonic_enqueue_asset(
+ $this->assets_dir,
+ $this->js_entry,
+ [
+ 'handle' => $this->handle_prefix . $this->getWidgetName(),
+ 'dependencies' => $this->script_dependencies,
+ 'css-dependencies' => $this->css_dependencies,
+ 'css-media' => $this->css_media,
+ 'css-only' => false,
+ 'in-footer' => $this->in_footer,
+ ]
+ );
+ }
+
+
+ // Get JED locale data and add it inline
+ $locale_data_kc = kc_get_jed_locale_data('kivicare-clinic-management-system');
+
+ if (self::$is_localize_enqueued) {
+ return;
+ }
+
+ wp_localize_script($this->handle_prefix . $this->getWidgetName(), 'kc_frontend', [
+ 'rest_url' => rest_url(),
+ 'home_url' => home_url(),
+ 'nonce' => wp_create_nonce('wp_rest'),
+ 'locale_data' => $locale_data_kc,
+ 'prefix' => KIVI_CARE_PREFIX,
+ 'loader_image' => KIVI_CARE_DIR_URI . 'assets/images/loader.gif',
+ 'place_holder_img' => KIVI_CARE_DIR_URI . 'assets/images/demo-img.png',
+ 'current_user_id' => get_current_user_id(),
+ ]);
+ self::$is_localize_enqueued = true;
+ }
+
+ /**
+ * Get widget name for asset handles
+ * This method should be implemented by child classes
+ *
+ * @return string
+ */
+ abstract protected function getWidgetName();
+}
No newline at end of file
--- a/kivicare-clinic-management-system/app/abstracts/KCShortcodeAbstract.php
+++ b/kivicare-clinic-management-system/app/abstracts/KCShortcodeAbstract.php
@@ -0,0 +1,220 @@
+<?php
+
+namespace Appabstracts;
+
+use AppbaseClassesKCBase;
+use KucrutVite;
+use function IqonicViteiqonic_enqueue_asset;
+
+abstract class KCShortcodeAbstract
+{
+ /**
+ * Shortcode tag
+ *
+ * @var string
+ */
+ protected $tag;
+
+ /**
+ * Default shortcode attributes
+ *
+ * @var array
+ */
+ protected $default_attrs = [];
+
+ /**
+ * Asset handle prefix
+ *
+ * @var string
+ */
+ protected $handle_prefix = KIVI_CARE_NAME;
+
+ /**
+ * Directory path where Vite assets are located
+ *
+ * @var string
+ */
+ protected $assets_dir;
+
+ /**
+ * Entry point for the shortcode's JavaScript
+ *
+ * @var string
+ */
+ protected $js_entry;
+
+ /**
+ * Entry point for the shortcode's CSS
+ *
+ * @var string
+ */
+ protected $css_entry;
+
+ /**
+ * Script dependencies
+ *
+ * @var array
+ */
+ protected $script_dependencies = [];
+
+ /**
+ * CSS dependencies
+ *
+ * @var array
+ */
+ protected $css_dependencies = [];
+
+ /**
+ * Load scripts in footer
+ *
+ * @var bool
+ */
+ protected $in_footer = false;
+
+
+ /**
+ * KCShortcodeAbstract constructor.
+ */
+
+ protected KCBase $kcbase;
+
+ protected static $is_localize_enqueued = false;
+ public function __construct()
+ {
+ // Set the default assets directory if not overridden
+ if (empty($this->assets_dir)) {
+ $this->assets_dir = KIVI_CARE_DIR . '/dist';
+ }
+ $this->kcbase = KCBase::get_instance();
+
+ // Register shortcode
+ add_shortcode($this->tag, [$this, 'renderShortcode']);
+
+ // Register assets
+ add_action('wp_enqueue_scripts', [$this, 'registerAssets']);
+ }
+
+ /**
+ * Register assets for the shortcode using Vite
+ *
+ * @return void
+ */
+ public function registerAssets()
+ {
+ if (empty($this->js_entry) && empty($this->css_entry)) {
+ return;
+ }
+
+ // Only register assets if the shortcode is used on the page
+ if (!$this->isShortcodePresent()) {
+ return;
+ }
+
+ // Enqueue JS if available
+ if (!empty($this->js_entry)) {
+ iqonic_enqueue_asset(
+ $this->assets_dir,
+ $this->js_entry,
+ [
+ 'handle' => $this->handle_prefix . $this->tag,
+ 'dependencies' => $this->script_dependencies ?? [], // Use shortcode dependencies
+ 'css-dependencies' => $this->css_dependencies ?? [], // Use shortcode CSS dependencies
+ 'css-media' => 'all', // Optional.
+ 'css-only' => false, // Optional. Set to true to only load style assets in production mode.
+ 'in-footer' => $this->in_footer ?? false, // Use shortcode footer setting
+ ]
+ );
+ }
+
+
+ // Get JED locale data and add it inline
+ $locale_data_kc = kc_get_jed_locale_data('kivicare-clinic-management-system');
+
+ if (self::$is_localize_enqueued) {
+ return;
+ }
+ wp_localize_script($this->handle_prefix . $this->tag, 'kc_frontend', [
+ 'rest_url' => rest_url(),
+ 'home_url' => home_url(),
+ 'nonce' => wp_create_nonce('wp_rest'),
+ 'locale_data' => $locale_data_kc,
+ 'prefix' => KIVI_CARE_PREFIX,
+ 'loader_image' => KIVI_CARE_DIR_URI . 'assets/images/loader.gif',
+ 'place_holder_img' => KIVI_CARE_DIR_URI . 'assets/images/demo-img.png',
+ 'current_user_id' => get_current_user_id(),
+ ]);
+ self::$is_localize_enqueued = true;
+ }
+
+ /**
+ * Check if the shortcode is present in the current page content
+ *
+ * @return bool
+ */
+ protected function isShortcodePresent()
+ {
+ global $post;
+
+ if (!is_a($post, 'WP_Post')) {
+ return false;
+ }
+
+ // Check if shortcode is in post content
+ if (has_shortcode($post->post_content, $this->tag)) {
+ return true;
+ }
+
+ // Also check if shortcode is in widgets or other areas
+ if (is_active_widget(false, false, 'text') || is_active_widget(false, false, 'custom_html')) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Render the shortcode HTML
+ *
+ * @param array|string $atts Shortcode attributes
+ * @param string|null $content Shortcode content
+ * @return string
+ */
+ public function renderShortcode($atts, $content = null)
+ {
+ // Parse attributes with defaults
+ $atts = shortcode_atts($this->default_attrs, $atts, $this->tag);
+
+ // Get unique ID for this instance
+ $id = 'kc-' . $this->tag . '-' . uniqid();
+
+ // Start output buffering
+ ob_start();
+
+ // Render the shortcode content
+ $this->render($id, $atts, $content);
+
+ // Return the buffered output
+ return ob_get_clean();
+ }
+
+ /**
+ * Render the shortcode content
+ * This method must be implemented by child classes
+ *
+ * @param string $id Unique ID for this shortcode instance
+ * @param array $atts Shortcode attributes
+ * @param string|null $content Shortcode content
+ * @return void
+ */
+ abstract protected function render($id, $atts, $content = null);
+
+ /**
+ * Get shortcode tag
+ *
+ * @return string
+ */
+ public function getTag()
+ {
+ return $this->tag;
+ }
+}
No newline at end of file
--- a/kivicare-clinic-management-system/app/admin/AdminMenu.php
+++ b/kivicare-clinic-management-system/app/admin/AdminMenu.php
@@ -0,0 +1,108 @@
+<?php
+namespace Appadmin;
+
+use AppbaseClassesKCBase;
+use function IqonicViteiqonic_enqueue_asset;
+
+defined('ABSPATH') or die('Something went wrong');
+
+class AdminMenu
+{
+ /**
+ * Dashboard permalink handler instance
+ */
+ private $permalink_handler;
+
+ public function register()
+ {
+ add_action('admin_menu', [$this, 'addMenuItems']);
+
+ // Initialize permalink handler
+ $this->permalink_handler = KCDashboardPermalinkHandler::instance();
+ }
+
+ public function addMenuItems()
+ {
+ $dashboard_url = $this->permalink_handler->get_menu_kivicare_url(KCBase::get_instance()->getLoginUserRole());
+ if (empty($dashboard_url)) {
+ add_menu_page(
+ 'KiviCare Dashboard',
+ 'KiviCare',
+ 'read',
+ 'dashboard',
+ [$this, 'renderDashboard'],
+ KIVI_CARE_DIR_URI . 'assets/images/sidebar-logo.svg',
+ 99
+ );
+ } else {
+ add_menu_page(
+ 'KiviCare Dashboard',
+ 'KiviCare',
+ 'read',
+ $dashboard_url,
+ '',
+ KIVI_CARE_DIR_URI . 'assets/images/sidebar-logo.svg',
+ 99
+ );
+ }
+ }
+
+ public function renderDashboard()
+ {
+ // Initialize permalink handler if not already done
+ if (!$this->permalink_handler) {
+ $this->permalink_handler = KCDashboardPermalinkHandler::instance();
+ }
+
+ // Check if this is a permalink-based dashboard request
+ if ($this->permalink_handler->is_dashboard_request()) {
+ // Let the permalink handler manage the template
+ return;
+ }
+
+ // Legacy admin dashboard rendering
+ $this->enqueue_dashboard_assets();
+ echo '<div id="kc-dashboard"></div>';
+ }
+ /**
+ * Enqueue dashboard assets
+ */
+ private function enqueue_dashboard_assets()
+ {
+ // Enqueue the script and style for Media Uploader
+ wp_enqueue_media();
+
+ iqonic_enqueue_asset(
+ KIVI_CARE_DIR . '/dist',
+ 'app/dashboard/main.jsx',
+ [
+ 'handle' => 'kc-dashboard',
+ 'dependencies' => apply_filters('kc_dashboard_script_dependencies', ['wp-i18n', 'wp-hooks']), // Optional script dependencies. Defaults to empty array.
+ 'css-dependencies' => [], // Optional style dependencies. Defaults to empty array.
+ 'css-media' => 'all', // Optional.
+ 'css-only' => false, // Optional. Set to true to only load style assets in production mode.
+ 'in-footer' => false, // Optional. Defaults to false.
+ ]
+ );
+
+ // Get JED locale data and add it inline
+ $locale_data_kc = kc_get_jed_locale_data('kivicare-clinic-management-system');
+
+ add_action('admin_footer', function () {
+ echo '<style>
+ #wpcontent, #footer { margin-left: 0px !important;padding-left: 0px !important; }
+ html.wp-toolbar { padding-top: 0px !important; }
+ #adminmenuback, #adminmenuwrap, #wpadminbar, #wpfooter,#adminmenumain, #screen-meta { display: none !important; }
+ #wpcontent .notice { display:none; }
+ </style>';
+ });
+
+ wp_localize_script('kc-dashboard', 'kc_frontend', [
+ 'rest_url' => rest_url(),
+ 'nonce' => wp_create_nonce('wp_rest'),
+ 'locale_data' => $locale_data_kc,
+ 'prefix' => KIVI_CARE_PREFIX,
+ 'loader_image' => KIVI_CARE_DIR_URI . 'assets/images/loader.gif',
+ ]);
+ }
+}
No newline at end of file
--- a/kivicare-clinic-management-system/app/admin/KCDashboardPermalinkHandler.php
+++ b/kivicare-clinic-management-system/app/admin/KCDashboardPermalinkHandler.php
@@ -0,0 +1,458 @@
+<?php
+
+namespace Appadmin;
+
+use AppbaseClassesKCBase;
+use AppbaseClassesKCPermissions;
+use AppmodelsKCOption;
+use function IqonicViteiqonic_enqueue_asset;
+
+defined('ABSPATH') || exit;
+
+/**
+ * KiviCare Dashboard Permalink Handler
+ *
+ * Handles custom dashboard routes for different user roles
+ *
+ * @package KiviCare
+ * @version 3.0.0
+ */
+class KCDashboardPermalinkHandler
+{
+ /**
+ * The single instance of the class.
+ *
+ * @var KCDashboardPermalinkHandler
+ */
+ protected static $instance = null;
+
+ /**
+ * Dashboard routes configuration
+ */
+ private $dashboard_routes = [];
+
+ public function __construct()
+ {
+ add_filter('rewrite_rules_array', [$this, 'add_dashboard_rewrite_rules']);
+ add_filter('query_vars', [$this, 'register_query_vars']);
+ add_filter('template_include', [$this, 'handle_template_include'], 99);
+ add_action('init', [$this, 'add_permalink_tags']);
+
+
+ // Get roles from KCBase and transform into dashboard routes
+ $roles = array_merge(KCBase::get_instance()->KCGetRoles(), [
+ 'administrator'
+ ]);
+ $this->dashboard_routes = [];
+
+ $pro_slugs = KCOption::getMultiple([
+ 'dashboard_slug_admin',
+ 'dashboard_slug_clinic_admin',
+ 'dashboard_slug_doctor',
+ 'dashboard_slug_receptionist',
+ 'dashboard_slug_patient',
+ ]);
+ foreach ($roles as $role) {
+ // Remove 'kiviCare_' prefix and use as both key and value
+ $clean_role = str_replace(KIVI_CARE_PREFIX, '', $role);
+
+ // Map the clean role to pro slug option key
+ $pro_slug_key = match ($clean_role) {
+ 'administrator' => 'dashboard_slug_admin',
+ 'clinic_admin' => 'dashboard_slug_clinic_admin',
+ 'doctor' => 'dashboard_slug_doctor',
+ 'receptionist' => 'dashboard_slug_receptionist',
+ 'patient' => 'dashboard_slug_patient',
+ default => ''
+ };
+
+ // Use pro slug if available, otherwise use default
+ if (!empty($pro_slug_key) && !empty($pro_slugs[$pro_slug_key])) {
+ $this->dashboard_routes[$role] = $pro_slugs[$pro_slug_key];
+ } else {
+ $this->dashboard_routes[$role] = 'kivicare-' . $clean_role . '-dashboard';
+ }
+ }
+
+ }
+
+ /**
+ * Get singleton instance
+ */
+ public static function instance()
+ {
+ if (is_null(self::$instance)) {
+ self::$instance = new self();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Add dashboard rewrite rules
+ */
+ public function add_dashboard_rewrite_rules($rules)
+ {
+ $roles = array_merge(KCBase::get_instance()->KCGetRoles(), ['administrator']);
+ $dashboard_routes = [];
+ $pro_slugs = KCOption::getMultiple([
+ 'dashboard_slug_admin',
+ 'dashboard_slug_doctor',
+ 'dashboard_slug_receptionist',
+ 'dashboard_slug_patient',
+ ]);
+
+ foreach ($roles as $role) {
+ $clean_role = str_replace(KIVI_CARE_PREFIX, '', $role);
+ $pro_slug_key = match ($clean_role) {
+ 'clinic_admin', 'administrator' => 'dashboard_slug_admin',
+ 'doctor' => 'dashboard_slug_doctor',
+ 'receptionist' => 'dashboard_slug_receptionist',
+ 'patient' => 'dashboard_slug_patient',
+ default => ''
+ };
+
+ if (!empty($pro_slug_key) && !empty($pro_slugs[$pro_slug_key])) {
+ $dashboard_routes[$role] = $pro_slugs[$pro_slug_key];
+ } else {
+ $dashboard_routes[$role] = 'kivicare-' . $clean_role . '-dashboard';
+ }
+ }
+
+ $new_rules = [];
+ foreach ($dashboard_routes as $route => $slug) {
+ if (empty($slug))
+ continue;
+
+ $new_rules[$slug . '(?:/(.*))?/?$'] = 'index.php?kc_dashboard_type=' . $route . '&kc_dashboard_path=$matches[1]';
+ }
+
+ return $new_rules + $rules; // Prepend our rules
+ }
+
+ /**
+ * Add permalink tags
+ */
+ public function add_permalink_tags()
+ {
+ add_rewrite_tag('%kc_dashboard_type%', '([^&]+)');
+ add_rewrite_tag('%kc_dashboard_action%', '([^&]+)');
+ add_rewrite_tag('%kc_dashboard_path%', '(.+?)');
+ }
+
+ /**
+ * Register custom query variables
+ */
+ public function register_query_vars($vars)
+ {
+ $vars[] = 'kc_dashboard_type';
+ $vars[] = 'kc_dashboard_action';
+ $vars[] = 'kc_dashboard_path'; // Add this new query var
+ return $vars;
+ }
+
+ /**
+ * Handle template include
+ */
+ public function handle_template_include($template)
+ {
+ global $wp, $wp_query;
+
+ $dashboard_type = get_query_var('kc_dashboard_type');
+ $dashboard_action = get_query_var('kc_dashboard_action');
+ $dashboard_path = get_query_var('kc_dashboard_path'); // Get the full path
+
+ // Check if this is a KiviCare dashboard request
+ if (!empty($dashboard_type) && in_array($dashboard_type, array_keys($this->dashboard_routes))) {
+
+ // Verify user permissions for the requested dashboard type
+ if (!$this->verify_dashboard_access($dashboard_type)) {
+ // Redirect to login or show access denied
+ if (!is_user_logged_in()) {
+ wp_safe_redirect(wp_login_url(home_url($wp->request)));
+ exit;
+ } else {
+ wp_die(esc_html__('You do not have sufficient permissions to access this dashboard.', 'kivicare-clinic-management-system'), esc_html__('Access Denied', 'kivicare-clinic-management-system'), ['response' => 403]);
+ }
+ }
+
+ // Set up global variables for the template
+ $GLOBALS['kc_dashboard_type'] = $dashboard_type;
+ $GLOBALS['kc_dashboard_action'] = $dashboard_action;
+ $GLOBALS['kc_dashboard_path'] = $dashboard_path; // Make path available to template
+ $GLOBALS['kc_current_user_role'] = KCBase::get_instance()->KCGetRoles();
+
+ // Prevent 404
+ $wp_query->is_404 = false;
+ status_header(200);
+
+ // Remove admin bar inline CSS
+ add_filter('show_admin_bar', '__return_false');
+ remove_action('wp_head', '_admin_bar_bump_cb');
+
+ // Enqueue dashboard assets
+ add_action('wp_enqueue_scripts', function () use ($dashboard_type) {
+ global $wp_styles, $wp_scripts;
+
+ $allowed_styles = apply_filters('kc_allowed_styles', ['kc-dashboard-style']);
+ $allowed_scripts = apply_filters('kc_allowed_scripts', ['kc-dashboard-script']);
+
+ foreach ($wp_styles->queue as $handle) {
+ if (
+ !in_array($handle, $allowed_styles) &&
+ strpos($handle, 'wp-') !== 0 &&
+ strpos($handle, 'media-') !== 0
+ ) {
+ wp_dequeue_style($handle);
+ wp_deregister_style($handle);
+ }
+ }
+
+ foreach ($wp_scripts->queue as $handle) {
+
+ if (
+ !in_array($handle, $allowed_scripts) &&
+ strpos($handle, 'wp-') !== 0 &&
+ strpos($handle, 'media-') !== 0
+ ) {
+ wp_dequeue_script($handle);
+ wp_deregister_script($handle);
+ }
+ }
+
+
+ // Now enqueue only your needed assets
+ $this->enqueue_dashboard_assets($dashboard_type);
+ }, 999); // Priority 999 ensures this runs after everyone else
+
+
+ // Return the KiviCare dashboard template
+ $dashboard_template = KIVI_CARE_DIR . '/templates/html-kc-dashboard.php';
+
+ if (file_exists($dashboard_template)) {
+ add_filter('heartbeat_settings', function ($settings) {
+ $settings['interval'] = 5; // seconds
+ return $settings;
+ });
+
+ return $dashboard_template;
+ }
+ }
+
+ return $template;
+ }
+
+ /**
+ * Verify if current user has access to the requested dashboard
+ */
+ private function verify_dashboard_access($dashboard_type)
+ {
+ if (!is_user_logged_in()) {
+ return false;
+ }
+ // Check role-based access
+ return match ($dashboard_type) {
+ 'administrator' => current_user_can('administrator') || KCPermissions::has_permission('administrator_dashboard'),
+ KCBase::get_instance()->getClinicAdminRole() => KCPermissions::has_permission('clinic_admin_dashboard'),
+ KCBase::get_instance()->getReceptionistRole() => KCPermissions::has_permission('receptionist_dashboard'),
+ KCBase::get_instance()->getDoctorRole() => KCPermissions::has_permission('doctor_dashboard'),
+ KCBase::get_instance()->getPatientRole() => KCPermissions::has_permission('patient_dashboard'),
+ default => false,
+ };
+ }
+
+ /**
+ * Get current user's KiviCare role
+ */
+ private function get_user_role()
+ {
+ if (!is_user_logged_in()) {
+ return null;
+ }
+
+ $current_user = wp_get_current_user();
+
+ // Check KiviCare specific role first
+ $kc_role = get_user_meta($current_user->ID, 'kivicare_user_role', true);
+ if (!empty($kc_role)) {
+ return $kc_role;
+ }
+
+ // Fallback to WordPress roles
+ if (current_user_can('administrator')) {
+ return 'administrator';
+ }
+
+ // Default role based on capabilities
+ if (KCPermissions::has_permission('clinic_admin_dashboard')) {
+ return 'clinic_admin';
+ } elseif (KCPermissions::has_permission('doctor_dashboard')) {
+ return 'doctor';
+ } elseif (KCPermissions::has_permission('receptionist_dashboard')) {
+ return 'receptionist';
+ } elseif (KCPermissions::has_permission('patient_dashboard')) {
+ return 'patient';
+ }
+
+ return 'patient'; // Default role
+ }
+
+ /**
+ * Enqueue dashboard assets based on dashboard type
+ */
+ private function enqueue_dashboard_assets($dashboard_type)
+ {
+ // Enqueue media uploader
+ wp_enqueue_media();
+
+
+
+ // Use Vite to enqueue the main dashboard script
+ if (function_exists(function: 'IqonicViteiqonic_enqueue_asset')) {
+ iqonic_enqueue_asset(
+ KIVI_CARE_DIR . '/dist',
+ 'app/dashboard/main.jsx',
+ [
+ 'handle' => 'kc-dashboard-' . $dashboard_type,
+ 'dependencies' => apply_filters('kc_dashboard_script_dependencies', ['wp-i18n', 'wp-hooks', 'heartbeat']),
+ 'css-dependencies' => [],
+ 'css-media' => 'all',
+ 'css-only' => false,
+ 'in-footer' => false,
+ ]
+ );
+ }
+
+ // Get JED locale data
+ $locale_data_kc = function_exists('kc_get_jed_locale_data') ? kc_get_jed_locale_data('kivicare-clinic-management-system') : [];
+
+ // Prepare dashboard data array
+ $dashboard_data = [
+ 'rest_url' => rest_url(),
+ 'nonce' => wp_create_nonce('wp_rest'),
+ 'locale_data' => $locale_data_kc,
+ 'prefix' => defined('KIVI_CARE_PREFIX') ? KIVI_CARE_PREFIX : 'kc_',
+ 'loader_image' => (defined('KIVI_CARE_DIR_URI') ? KIVI_CARE_DIR_URI : '') . 'assets/images/loader.gif',
+ 'dashboard_type' => $dashboard_type,
+ 'user_role' => KCBase::get_instance()->KCGetRoles(),
+ 'dashboard_url' => wp_make_link_relative($this->get_dashboard_url($dashboard_type)),
+ 'dashboard_uri' => $this->get_dashboard_url($dashboard_type),
+ 'api_url' => rest_url('kivicare/v1/'),
+ 'admin_url' => KCBase::get_instance()->getLoginUserRole() === 'administrator' ? admin_url() : ''
+ ];
+ // Apply filter to dashboard data
+ $dashboard_data = apply_filters('kivicare_dashboard_data', $dashboard_data);
+
+ // Localize script with dashboard data
+ wp_localize_script('kc-dashboard-' . $dashboard_type, 'kc_frontend', $dashboard_data);
+ }
+
+ /**
+ * Get the menu URL for a specific dashboard type
+ */
+ public function get_menu_kivicare_url($dashboard_type)
+ {
+ $home_url = $this->get_dashboard_url($dashboard_type);
+ $path = isset($_SERVER['REQUEST_URI']) ? wp_parse_url(sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])), PHP_URL_PATH) : '';
+ $segments = explode('/', trim($path, '/'));
+ if (
+ function_exists('kcGetSetupWizardOptions') &&
+ is_array(kcGetSetupWizardOptions()) &&
+ KCBase::get_instance()->getLoginUserRole() === 'administrator' &&
+ !in_array('clinic-setup', $segments)
+ ) {
+ $setupClinicStep = [
+ "getting_started" => 'clinic-setup',
+ "clinic" => 'clinic-setup/basic-info',
+ "clinic_admin" => 'clinic-setup/admin-info',
+ ];
+ $setup_config = kcGetSetupWizardOptions();
+ foreach ($setup_config as $step) {
+ if (isset($step['completed']) && $step['completed'] === false) {
+ if (isset($setupClinicStep[$step['name']])) {
+ return $home_url . '/' . $setupClinicStep[$step['name']];
+ }
+ }
+ }
+ }
+ return $home_url;
+ }
+
+ /**
+ * Get dashboard URL for a specific type with optional path
+ */
+ public function get_dashboard_url($dashboard_type)
+ {
+ $route = $this->dashboard_routes[$dashboard_type] ?? '';
+ if (!$route) {
+ return home_url();
+ }
+
+ $permalink_structure = get_option('permalink_structure');
+
+ if (empty($permalink_structure)) {
+ // Plain permalinks
+ return add_query_arg('kc_dashboard_type', $dashboard_type, home_url('/'));
+ }
+
+ $has_index_php = strpos($permalink_structure, '/index.php') !== false;
+
+ if ($has_index_php) {
+ return home_url('/index.php/' . $route);
+ }
+
+ return home_url('/' . $route);
+ }
+
+
+ /**
+ * Get all dashboard routes
+ */
+ public function get_dashboard_routes()
+ {
+ return $this->dashboard_routes;
+ }
+
+ /**
+ * Check if current request is for a KiviCare dashboard
+ */
+ public function is_dashboard_request()
+ {
+ return !empty(get_query_var('kc_dashboard_type'));
+ }
+
+ /**
+ * Get current dashboard type
+ */
+ public function get_current_dashboard_type()
+ {
+ return get_query_var('kc_dashboard_type');
+ }
+
+ /**
+ * Redirect user to their appropriate dashboard
+ */
+ public function redirect_to_user_dashboard()
+ {
+ $user_role = KCBase::get_instance()->KCGetRoles();
+ $dashboard_url = $this->get_dashboard_url($user_role);
+ if ($dashboard_url) {
+ wp_safe_redirect($dashboard_url);
+ exit;
+ }
+ }
+ /**
+ * Flushes the WordPress rewrite rules.
+ *
+ * This static method accesses the global $wp_rewrite object and calls its
+ * flush_rules() method to regenerate the rewrite rules. This is typically
+ * used when custom permalinks or rewrite rules have been added or modified,
+ * ensuring that WordPress recognizes the changes.
+ *
+ * @return void
+ */
+ public static function flush_rewrite_rules()
+ {
+ global $wp_rewrite;
+ $wp_rewrite->flush_rules(false); // Do not reset permalink structure
+ }
+}
--- a/kivicare-clinic-management-system/app/baseClasses/KCActivate.php
+++ b/kivicare-clinic-management-system/app/baseClasses/KCActivate.php
@@ -2,761 +2,42 @@
namespace AppbaseClasses;
-use AppcontrollersKCPaymentController;
-use AppmodelsKCAppointment;
-use AppmodelsKCClinic;
-use AppmodelsKCDoctorClinicMapping;
-use AppmodelsKCServiceDoctorMapping;
-use WP_Error;
-use WP_Query;
-use function ClueStreamFilterfun;
-
-
-class KCActivate extends KCBase {
-
- private $request;
- private $db;
-
- public static function activate() {
-
- // Migrate database and other data...
- self::migrateDatabase();
-
- // following function call is only for development purpose remove in production mode.
- (new self())->migratePermissions();
-// (new self())->addDefaultPosts();
- (new self())->addDefaultOptions();
- (new self())->addDefaultModuleConfig();
- (new self())->addAdministratorPermission();
- (new self())->tableAlterFiled();
- (new self())->createShortcodePage();
- (new self())->migrateSidebar();
- (new self())->addNewPosts();
- }
- public function init() {
-
- $version = KIVI_CARE_VERSION;
- $prefix = KIVI_CARE_PREFIX;
- $config_options = kc_get_multiple_option("
- '{$prefix}telemed_enable_added_to_service_table',
- '{$prefix}doctor_service_duration_column',
- '{$prefix}new-permissions-migrate{$version}',
- '{$prefix}permissions-migrate-1.0{$version}',
- '{$prefix}lang_option',
- '{$prefix}widgetSetting',
- '{$prefix}widget_order_list',
- '{$prefix}local_payment_status',
- '{$prefix}patient_mapping_table_1',
- '{$prefix}patient_review_table_1',
- '{$prefix}patient_mapping_table_1',
- '{$prefix}service_mapping_new_column',
- '{$prefix}payment_appointment_table_insert_1',
- '{$prefix}copyrightText_save',
- 'is_lang_version_2.3.7',
- 'kivicare_version_2_3_0',
- '{$prefix}logout_redirect',
- 'is_kivicarepro_upgrade_lang',
- '{$prefix}email_appointment_reminder',
- '{$prefix}showServiceImageFirst',
- '{$prefix}tax_table_migrate',
- '{$prefix}custom_notification_dynamic_keys_update',
- '{$prefix}custom_form_table',
- '{$prefix}is_appointment_widget_migrated'
- ");
-
- add_action( 'set_logged_in_cookie', function( $logged_in_cookie ){
- $_COOKIE[LOGGED_IN_COOKIE] = $logged_in_cookie;
- } );
-
- //hook call after all plugins loaded
- add_action( 'init', function (){
- //translate language
- load_plugin_textdomain( 'kc-lang', false, dirname( KIVI_CARE_BASE_NAME ) . '/languages' );
- });
-
- //security hook
- add_filter( 'xmlrpc_methods', function ( $methods ) {
- return array();
- } );
-
- //add user_status parameter in get_user function
- add_action( 'pre_user_query', function ( $query ) {
- if ( isset( $query->query_vars['user_status'] ) ) {
- $query->query_where .= " AND user_status = " . (int) $query->query_vars['user_status'];
- }
- } );
-
- add_filter( 'user_search_columns', function( $search_columns ) {
- $search_columns[] = 'display_name';
- $search_columns[] = 'user_status';
- return $search_columns;
- } );
-
- add_action('pre_get_posts', array($this, 'wpb_remove_products_from_shop_listing'), 90, 1);
-
- // Override WooCommerce pay_action for KiviCare
- add_action('init', array($this, 'override_woocommerce_form_handler'), 20);
-
- add_action('init',function () use ($config_options){
- if(empty($config_options[KIVI_CARE_PREFIX.'telemed_enable_added_to_service_table'])){
- global $wpdb;
- kcUpdateFields($wpdb->prefix.'kc_service_doctor_mapping',['telemed_service' => 'varchar(10)']);
- $telemed_id = $wpdb->get_var("SELECT id FROM {$wpdb->prefix}kc_services WHERE LOWER(name) = 'telemed'");
- $all_service = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}kc_service_doctor_mapping ");
- foreach ($all_service as $service){
- $wpdb->update($wpdb->prefix.'kc_service_doctor_mapping',['telemed_service' => (int)$service->service_id == (int)$telemed_id ? 'yes' : 'no'],['id' => $service->id]);
- }
- update_option(KIVI_CARE_PREFIX.'telemed_enable_added_to_service_table','yes');
- }
- $this->migrateDoctorServiceTableData();
- if(empty($config_options[KIVI_CARE_PREFIX.'doctor_service_duration_column'])){
- global $wpdb;
- kcUpdateFields($wpdb->prefix.'kc_service_doctor_mapping',['duration' => 'int(5)']);
- update_option(KIVI_CARE_PREFIX.'doctor_service_duration_column','yes');
- }
- });
-
- add_action('admin_init', function () use ($config_options){
- $allRole = ['administrator',$this->getPatientRole(),
- $this->getDoctorRole(),
- $this->getReceptionistRole(),
- $this->getClinicAdminRole()];
-
-
- if (empty($config_options[KIVI_CARE_PREFIX . 'new-permissions-migrate' . KIVI_CARE_VERSION])) {
-
- $editable_roles = get_editable_roles();
-
- if(!empty($editable_roles) && is_array($editable_roles)){
-
- $editable_roles = array_keys($editable_roles);
- $containsSearch = count(array_intersect($allRole, $editable_roles)) === count($allRole);
-
- if($containsSearch){
-