0, 'accent' => '', ], $atts, 'cleannetic_booking' ); $this->enqueue_frontend_assets(); $default_service_id = absint( $atts['service'] ); $accent_color = sanitize_hex_color( $atts['accent'] ) ?: Settings::get( 'accent_color', '#0D9488' ); $services = ServicePostType::get_active_services(); $settings = Settings::all(); ob_start(); include CLEANNETIC_DIR . 'templates/booking-form/wizard.php'; return (string) ob_get_clean(); } public function render_customer_portal(): string { $this->enqueue_frontend_assets(); $template = (string) apply_filters( 'cleannetic_customer_portal_template', CLEANNETIC_DIR . 'templates/customer-portal.php' ); ob_start(); if ( file_exists( $template ) ) { include $template; } return (string) ob_get_clean(); } public function enqueue_frontend_assets(): void { wp_enqueue_style( 'cleannetic-frontend', CLEANNETIC_URL . 'assets/css/frontend.css', [], CLEANNETIC_VERSION ); $print_js = CLEANNETIC_DIR . 'assets/js/print.js'; wp_enqueue_script( 'cleannetic-print', CLEANNETIC_URL . 'assets/js/print.js', [], file_exists( $print_js ) ? filemtime( $print_js ) : CLEANNETIC_VERSION, true ); wp_enqueue_script( 'cleannetic-booking-wizard', CLEANNETIC_URL . 'assets/js/booking-wizard.js', [ 'cleannetic-print' ], CLEANNETIC_VERSION, true ); $services = ServicePostType::get_active_services(); $settings = Settings::all(); $localized = [ 'rest_url' => esc_url_raw( rest_url( 'cleannetic/v1/' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'currency' => Formatter::currency_config(), 'services' => $services, 'working_hours' => $settings['working_hours'] ?? [], 'slot_interval' => (int) ( $settings['slot_interval'] ?? 60 ), 'buffer_minutes' => (int) ( $settings['buffer_minutes'] ?? 30 ), 'tax_rate' => (float) ( $settings['tax_rate'] ?? 0 ), 'tax_label' => (string) ( $settings['tax_label'] ?? __( 'Tax', 'cleannetic' ) ), 'accent_color' => (string) ( $settings['accent_color'] ?? '#0D9488' ), 'blocked_dates' => (array) ( $settings['blocked_dates'] ?? [] ), 'min_notice' => (int) ( $settings['min_notice_hours'] ?? 24 ), 'max_advance' => (int) ( $settings['max_advance_days'] ?? 60 ), 'cash_title' => (string) ( $settings['cash_title'] ?? __( 'Pay on site', 'cleannetic' ) ), 'cash_desc' => (string) ( $settings['cash_description'] ?? '' ), 'terms_text' => (string) ( $settings['terms_text'] ?? '' ), 'require_terms' => (bool) ( $settings['require_terms'] ?? false ), 'frequencies' => PricingEngine::frequency_discounts(), 'business' => [ 'name' => (string) ( $settings['business_name'] ?? get_bloginfo( 'name' ) ), 'email' => (string) ( $settings['business_email'] ?? get_option( 'admin_email' ) ), 'phone' => (string) ( $settings['business_phone'] ?? '' ), 'address' => (string) ( $settings['business_address'] ?? '' ), ], 'i18n' => [ 'confirm_btn' => __( 'Confirm & Book Cleaning', 'cleannetic' ), 'request_quote' => __( 'Request My Quote', 'cleannetic' ), 'agree_terms' => __( 'Please agree to the service terms to complete your booking.', 'cleannetic' ), 'no_period_slots' => __( 'No arrival times available in this part of the day.', 'cleannetic' ), 'to_be_scheduled' => __( 'To be scheduled', 'cleannetic' ), 'at' => __( 'at', 'cleannetic' ), 'one_time' => __( 'One-Time', 'cleannetic' ), 'quote_received' => __( 'Quote Request Received', 'cleannetic' ), 'quote_title' => __( 'Thanks — we will be in touch!', 'cleannetic' ), 'quote_estimate' => __( 'Estimated Amount:', 'cleannetic' ), 'blocked_day' => __( 'Closed / unavailable', 'cleannetic' ), 'invalid_email' => __( 'Please enter a valid email address', 'cleannetic' ), 'invalid_phone' => __( 'Please enter a valid phone number.', 'cleannetic' ), 'required_first' => __( 'Please enter your first name', 'cleannetic' ), 'required_phone' => __( 'Please enter your phone number', 'cleannetic' ), 'required_address' => __( 'Please enter the street address', 'cleannetic' ), 'month_names' => array_values( array_map( static fn( $m ) => $GLOBALS['wp_locale']->get_month( $m ), range( 1, 12 ) ) ), 'select_service' => __( 'Please select a service first.', 'cleannetic' ), 'select_date' => __( 'Please select an appointment date.', 'cleannetic' ), 'select_slot' => __( 'Please choose an available time slot.', 'cleannetic' ), 'submitting' => __( 'Securing your booking...', 'cleannetic' ), 'confirmed' => __( 'Booking confirmed!', 'cleannetic' ), 'error_generic' => __( 'Something went wrong. Please check your inputs.', 'cleannetic' ), 'no_slots' => __( 'No available time slots on this date.', 'cleannetic' ), 'loading_slots' => __( 'Checking available times...', 'cleannetic' ), 'print_confirmation_kicker' => __( 'Booking confirmation', 'cleannetic' ), 'print_confirmation_title' => __( 'You are all set!', 'cleannetic' ), 'print_confirmation_intro' => __( 'Keep this confirmation for your records. We have also emailed these details.', 'cleannetic' ), 'print_quote_kicker' => __( 'Quote request', 'cleannetic' ), 'print_job_kicker' => __( 'Job sheet', 'cleannetic' ), 'print_job_title' => __( 'Cleaning job sheet', 'cleannetic' ), 'print_job_intro' => __( 'Give this sheet to the assigned cleaner. It includes the customer, address and job details.', 'cleannetic' ), 'print_booking_ref' => __( 'Booking reference', 'cleannetic' ), 'print_service' => __( 'Service', 'cleannetic' ), 'print_datetime' => __( 'Date & time', 'cleannetic' ), 'print_preferred_datetime' => __( 'Preferred date & time', 'cleannetic' ), 'print_location' => __( 'Service location', 'cleannetic' ), 'print_customer' => __( 'Customer', 'cleannetic' ), 'print_phone' => __( 'Phone', 'cleannetic' ), 'print_email' => __( 'Email', 'cleannetic' ), 'print_total' => __( 'Total amount', 'cleannetic' ), 'print_payment' => __( 'Payment status', 'cleannetic' ), 'print_payment_method' => __( 'Payment', 'cleannetic' ), 'print_notes' => __( 'Special instructions', 'cleannetic' ), 'print_staff' => __( 'Assigned staff', 'cleannetic' ), 'print_status' => __( 'Status', 'cleannetic' ), 'print_gate' => __( 'Gate / access code', 'cleannetic' ), 'print_printed' => __( 'Printed', 'cleannetic' ), 'print_thank_you' => __( 'Thank you for booking with', 'cleannetic' ), 'print_pricing' => __( 'Pricing', 'cleannetic' ), 'print_amount' => __( 'Amount', 'cleannetic' ), 'print_unassigned' => __( 'Unassigned', 'cleannetic' ), ], 'icons' => Icons::svg_map( [ 'check', 'clock', 'bed', 'bath', 'sparkles', 'calendar', 'map-pin', 'user', 'shield-check', 'chevron-right', 'chevron-left', 'arrow-right' ] ), ]; $localized = (array) apply_filters( 'cleannetic_frontend_localized_config', $localized ); wp_localize_script( 'cleannetic-booking-wizard', 'cleannetic_config', $localized ); do_action( 'cleannetic_enqueue_frontend_assets', $localized ); } }