Snippets Collections
<?php
/**
 * Create a ZIP of every attachment in a "Documents" CPT,
 * organized into main folders and sub-folders using your exact labels,
 * with "/" replaced by "-" in folder names,
 * and named after the document’s title.
 */
function create_documents_zip( $post_id ) {
    if ( ! $post_id ) {
        return false;
    }

    // Build ZIP filename from post title
    $post_title = get_the_title( $post_id );
    $slug       = sanitize_title( $post_title );
    $zip_name   = "{$slug}.zip";

    // 1) Define main folders (sections) and sub-folders (fields)
    $sections = [
        'Personal Information' => [
            'passport-sized-photo'                     => 'Passport Sized Photo',
            'valid-passport'                           => 'Valid Passport',
            'national-identity-cards'                  => 'National Identity Cards',
            'employment-pass-s-pass-dependent-pass'    => 'Employment Pass/ S Pass/ Dependent Pass',
            'birth-certificates-household-registration' => 'Birth Certificates/ Household Registration',
            'official-marriage-certificate'            => 'Official Marriage Certificate',
        ],
        'Education & Professional Information' => [
            'education-certificates-transcripts'                    => 'Education Certificates/ Transcripts',
            'child039schildren039s-school-admission-letter-results' => "Child's/Children's School Admission Letter/ Results",
            'additional-certifications-courses-workshops'           => 'Additional Certifications/ Courses/ Workshops',
        ],
        'Social Contributions' => [
            'media-article-s-in-company--newsletters-websites--magazines--showcasing-contributions'
                => 'Media article (s) in Company / Newsletters / Websites / Magazines / Showcasing Contributions',
            'membership-in-clubs--societies'                        => 'Membership in Clubs / Societies',
            'charity-contribution-receipts--letters'               => 'Charity Contribution Receipt(s) / Letter(s)',
            'corporate-social-responsibility--community-participated-events'
                => 'Corporate Social Responsibility / Community Participated Events',
        ],
        'Employment Documents' => [
            'latest-6-months-payslips'                              => 'Latest 6 Months Payslips',
            'valid-business-registration-certificate-acrabizfile'    => 'Valid Business Registration Certificate (ACRABizfile)',
            'current-employer039s-letter-stating-date-of-employment-position-held-and-salary-per-month-for-past-6-months'
                => "Current Employer's Letter Stating Date of Employment, Position Held and Salary Per Month for Past 6 Months",
            'up-to-date-resume'                                     => 'Up To Date Resume',
            'salary-increment-letters--promotion-letter-s'          => 'Salary Increment Letter(s) / Promotion Letter (s)',
            'annex-a-of-form-4a'                                    => 'Annex A of Form 4A',
        ],
        'Economic Contributions' => [
            'summary-of-personal-insurance'                         => 'Summary of Personal Insurance',
            'summary-of-portfolio-of-personal-investments'          => 'Summary of Portfolio of Personal Investments',
            'summary-of-property-investments'                       => 'Summary of Property Investments',
        ],
        'Testimonials' => [
            'testimonial-from-current--previous-employer'           => 'Testimonial from Current / Previous Employer',
            'testimonial-from-others'                               => 'Testimonial from Others',
        ],
        // "Additional Documents" repeater handled below only if it has files
    ];

    // Prepare the ZIP archive
    $upload_dir = wp_upload_dir();
    $zip_path   = trailingslashit( $upload_dir['basedir'] ) . $zip_name;
    $zip        = new ZipArchive();
    if ( $zip->open( $zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) !== true ) {
        return false;
    }

    // Helper: add one file into Main/Sub
    $add_file = function( $att_id, $main_label, $sub_label ) use ( $zip ) {
        $file = get_attached_file( $att_id );
        if ( ! $file || ! file_exists( $file ) ) {
            return;
        }
        // Clean up labels, replace "/" with "-"
        $safe_main = sanitize_file_name( str_replace( '/', '-', $main_label ) );
        $safe_sub  = sanitize_file_name( str_replace( '/', '-', $sub_label ) );

        // Create the folders and add the file
        $zip->addEmptyDir( $safe_main );
        $zip->addEmptyDir( "{$safe_main}/{$safe_sub}" );
        $zip->addFile( $file, "{$safe_main}/{$safe_sub}/" . basename( $file ) );
    };

    // 2) Loop each defined section and its fields
    foreach ( $sections as $main_label => $fields ) {
        foreach ( $fields as $meta_key => $sub_label ) {
            $raw = get_post_meta( $post_id, $meta_key, true );
            if ( empty( $raw ) ) {
                continue;
            }

            // Normalize to array of items
            $items = is_array( $raw )
                ? $raw
                : ( strpos( $raw, ',' ) !== false
                    ? array_map( 'trim', explode( ',', $raw ) )
                    : [ $raw ] );

            foreach ( $items as $item ) {
                if ( is_array( $item ) && ! empty( $item['id'] ) ) {
                    $item = $item['id'];
                }
                if ( is_string( $item ) && filter_var( $item, FILTER_VALIDATE_URL ) ) {
                    $item = attachment_url_to_postid( $item );
                }
                $att_id = intval( $item );
                if ( $att_id ) {
                    $add_file( $att_id, $main_label, $sub_label );
                }
            }
        }
    }

    // 3) Handle the repeater field "upload-additional-documents" only if it has items
    $repeater = get_post_meta( $post_id, 'upload-additional-documents', true );
    if ( is_array( $repeater ) && ! empty( $repeater ) ) {
        foreach ( $repeater as $row ) {
            $sub_label_raw = ! empty( $row['document-about'] )
                ? $row['document-about']
                : 'Miscellaneous';

            $raw_items = $row['select-documents'];
            if ( empty( $raw_items ) ) {
                continue;
            }

            // Normalize repeater gallery values
            $items = is_array( $raw_items )
                ? $raw_items
                : ( strpos( $raw_items, ',' ) !== false
                    ? array_map( 'trim', explode( ',', $raw_items ) )
                    : [ $raw_items ] );

            foreach ( $items as $item ) {
                if ( is_array( $item ) && ! empty( $item['id'] ) ) {
                    $item = $item['id'];
                }
                if ( is_string( $item ) && filter_var( $item, FILTER_VALIDATE_URL ) ) {
                    $item = attachment_url_to_postid( $item );
                }
                $att_id = intval( $item );
                if ( $att_id ) {
                    $add_file( $att_id, 'Additional Documents', $sub_label_raw );
                }
            }
        }
    }

    $zip->close();
    return $zip_path;
}

/**
 * Stream the ZIP when ?download_all=1&post_id=… is requested.
 */
add_action( 'template_redirect', function() {
    if ( isset( $_GET['download_all'], $_GET['post_id'] ) ) {
        $post_id = intval( $_GET['post_id'] );
        // Optional: verify nonce or user capability here

        $zip_file = create_documents_zip( $post_id );
        if ( ! $zip_file || ! file_exists( $zip_file ) ) {
            wp_die( 'Unable to generate ZIP.' );
        }

        header( 'Content-Type: application/zip' );
        header( 'Content-Disposition: attachment; filename="' . basename( $zip_file ) . '"' );
        header( 'Content-Length: ' . filesize( $zip_file ) );
        readfile( $zip_file );
        exit;
    }
});
wget -nc https://dl.winehq.org/wine-builds/winehq.key
import React, { useState, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { McButton, McInput, McMultiSelect, McSelect } from '@maersk-global/mds-react-wrapper';
import { McOption } from '@maersk-global/mds-react-wrapper/components-core/mc-option';
import styles from '../styles/CreateRule.module.css';
import data from '../data/PnLGroup.json';

class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError() {
    return { hasError: true };
  }
  render() {
    if (this.state.hasError) {
      return <div>Something went wrong. Please refresh the page.</div>;
    }
    return this.props.children;
  }
}

const CreateRules = () => {
  const navigate = useNavigate();
  const [activeTab, setActiveTab] = useState('ruleInfo');
  const [isLoading, setIsLoading] = useState(false);
  const [ruleData, setRuleData] = useState({
    num: '',
    name: '',
    desc: '',
    custRefID: '',
    ruleGroup: '',
    isActive: 'Y',
    pnlGroup: '',
  });
  const [steps, setSteps] = useState([
    {
      stepNo: '',
      stepName: 'Single Step',
      stepDesc: '',
      stepType: 'S',
      preAggregatorColumns: [],
      sourceTableID: '',
      sourceFilterSets: [{ filters: [], operator: '', values: [] }],
      joinColumns: [],
      allocationColumns: [],
      driverTableID: '',
      driverWeightColumn: '',
      driverFilterSets: [{ filters: [], operator: '', values: [] }],
    },
  ]);
  const [errors, setErrors] = useState({ rule: {}, steps: [{ sourceFilterSets: [{}], driverFilterSets: [{}] }] });

  const pnLGroups = data.PnLGroups && typeof data.PnLGroups === 'object'
    ? Object.keys(data.PnLGroups)
    : [];
  const ruleGroups = ruleData.pnlGroup && data.PnLGroups[ruleData.pnlGroup]
    ? data.PnLGroups[ruleData.pnlGroup].RuleGroups || []
    : [];

  const sourceFilterOptions = [
    { value: 'Source_Filter_1', label: 'Source Filter 1' },
    { value: 'Source_Filter_2', label: 'Source Filter 2' },
    { value: 'Source_Filter_3', label: 'Source Filter 3' },
  ];
  const sourceValueOptions = [
    { value: 'Source_Value_1', label: 'Source Value 1' },
    { value: 'Source_Value_2', label: 'Source Value 2' },
    { value: 'Source_Value_3', label: 'Source Value 3' },
  ];
  const preAggregatorOptions = [
    { value: 'column1', label: 'Column 1' },
    { value: 'column2', label: 'Column 2' },
    { value: 'column3', label: 'Column 3' },
  ];
  const joinColumnsOptions = [
    { value: 'join_col1', label: 'Join Column 1' },
    { value: 'join_col2', label: 'Join Column 2' },
    { value: 'join_col3', label: 'Join Column 3' },
  ];
  const allocationColumnsOptions = [
    { value: 'alloc_col1', label: 'Allocation Column 1' },
    { value: 'alloc_col2', label: 'Allocation Column 2' },
    { value: 'alloc_col3', label: 'Allocation Column 3' },
  ];
  const driverFilterOptions = [
    { value: 'Driver_Type_1', label: 'Driver Type: Type 1' },
    { value: 'Driver_Type_2', label: 'Driver Type: Type 2' },
    { value: 'Driver_Status_Active', label: 'Driver Status: Active' },
  ];
  const driverValueOptions = [
    { value: 'Driver_Value_1', label: 'Driver Value 1' },
    { value: 'Driver_Value_2', label: 'Driver Value 2' },
    { value: 'Driver_Value_3', label: 'Driver Value 3' },
  ];

  const operatorOptions = useMemo(() => [
    { value: 'IN', label: 'IN' },
    { value: 'NOT IN', label: 'NOT IN' },
    { value: 'EQ', label: 'EQ' },
    { value: 'NTEQ', label: 'NTEQ' },
    { value: 'IS NULL', label: 'IS NULL' },
    { value: 'GT', label: 'GT' },
    { value: 'LT', label: 'LT' },
    { value: 'GTEQ', label: 'GTEQ' },
    { value: 'LTEQ', label: 'LTEQ' },
    { value: 'BETWEEN', label: 'BETWEEN' },
    { value: 'NOT BETWEEN', label: 'NOT BETWEEN' },
    { value: 'LIKE', label: 'LIKE' },
  ], []);

  const addStep = useCallback(() => {
    setSteps((prevSteps) => [
      ...prevSteps,
      {
        stepNo: '',
        stepName: 'Single Step',
        stepDesc: '',
        stepType: 'S',
        preAggregatorColumns: [],
        sourceTableID: '',
        sourceFilterSets: [{ filters: [], operator: '', values: [] }],
        joinColumns: [],
        allocationColumns: [],
        driverTableID: '',
        driverWeightColumn: '',
        driverFilterSets: [{ filters: [], operator: '', values: [] }],
      },
    ]);
    setErrors((prevErrors) => ({
      ...prevErrors,
      steps: [...prevErrors.steps, { sourceFilterSets: [{}], driverFilterSets: [{}] }],
    }));
  }, []);

  const removeStep = useCallback((index) => {
    if (steps.length === 1) {
      alert('At least one step is required.');
      return;
    }
    setSteps((prevSteps) => prevSteps.filter((_, i) => i !== index));
    setErrors((prevErrors) => ({
      ...prevErrors,
      steps: prevErrors.steps.filter((_, i) => i !== index),
    }));
  }, [steps.length]);

  const addFilterSet = useCallback((stepIndex, type) => {
    setSteps((prevSteps) => {
      const newSteps = [...prevSteps];
      const filterKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets';
      newSteps[stepIndex] = {
        ...newSteps[stepIndex],
        [filterKey]: [...newSteps[stepIndex][filterKey], { filters: [], operator: '', values: [] }],
      };
      return newSteps;
    });
    setErrors((prevErrors) => {
      const newStepsErrors = [...prevErrors.steps];
      const filterErrorsKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets';
      newStepsErrors[stepIndex] = {
        ...newStepsErrors[stepIndex],
        [filterErrorsKey]: [...newStepsErrors[stepIndex][filterErrorsKey], {}],
      };
      return { ...prevErrors, steps: newStepsErrors };
    });
  }, []);

  const removeFilterSet = useCallback((stepIndex, filterIndex, type) => {
    setSteps((prevSteps) => {
      const newSteps = [...prevSteps];
      const filterKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets';
      newSteps[stepIndex] = {
        ...newSteps[stepIndex],
        [filterKey]: newSteps[stepIndex][filterKey].filter((_, i) => i !== filterIndex),
      };
      return newSteps;
    });
    setErrors((prevErrors) => {
      const newStepsErrors = [...prevErrors.steps];
      const filterErrorsKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets';
      newStepsErrors[stepIndex] = {
        ...newStepsErrors[stepIndex],
        [filterErrorsKey]: newStepsErrors[stepIndex][filterErrorsKey].filter((_, i) => i !== filterIndex),
      };
      return { ...prevErrors, steps: newStepsErrors };
    });
  }, []);

  const validateForm = useCallback(() => {
    try {
      const newErrors = { rule: {}, steps: steps.map(() => ({ sourceFilterSets: [], driverFilterSets: [] })) };
      let isValid = true;

      if (!ruleData.num) {
        newErrors.rule.num = 'Rule Number is required';
        isValid = false;
      } else if (!/^[a-zA-Z0-9]+$/.test(ruleData.num)) {
        newErrors.rule.num = 'Rule Number must be alphanumeric';
        isValid = false;
      }
      if (!ruleData.name) {
        newErrors.rule.name = 'Rule Name is required';
        isValid = false;
      }
      if (!ruleData.desc) {
        newErrors.rule.desc = 'Description is required';
        isValid = false;
      }
      if (!ruleData.custRefID) {
        newErrors.rule.custRefID = 'Customer Reference ID is required';
        isValid = false;
      }
      if (!ruleData.pnlGroup) {
        newErrors.rule.pnlGroup = 'PnL Group is required';
        isValid = false;
      }
      if (!ruleData.ruleGroup) {
        newErrors.ruleGroup = 'Rule Group is required';
        isValid = false;
      }
      if (!ruleData.isActive) {
        newErrors.rule.isActive = 'Active status is required';
        isValid = false;
      }

      const stepNumbers = new Set();
      steps.forEach((step, index) => {
        const stepErrors = { sourceFilterSets: step.sourceFilterSets.map(() => ({})),
                            driverFilterSets: step.driverFilterSets.map(() => ({})) };
        if (!step.stepNo) {
          stepErrors.stepNo = 'Step Number is required';
          isValid = false;
        } else if (stepNumbers.has(step.stepNo)) {
          stepErrors.stepNo = 'Step Number must be unique';
          isValid = false;
        } else {
          stepNumbers.add(step.stepNo);
        }
        if (!step.stepName) {
          stepErrors.stepName = 'Step Name is required';
          isValid = false;
        }
        if (!step.stepDesc) {
          stepErrors.stepDesc = 'Step Description is required';
          isValid = false;
        }
        if (!step.stepType) {
          stepErrors.stepType = 'Step Type is required';
          isValid = false;
        }
        if (!step.preAggregatorColumns.length) {
          stepErrors.preAggregatorColumns = 'Pre-Aggregator Columns is required';
          isValid = false;
        }
        if (!step.sourceTableID) {
          stepErrors.sourceTableID = 'Source Table ID is required';
          isValid = false;
        }
        if (!step.sourceFilterSets.some(set => set.filters.length && set.operator && set.values.length)) {
          stepErrors.sourceFilterSets[0].filters = 'At least one complete Source Filter set is required';
          isValid = false;
        }
        step.sourceFilterSets.forEach((set, filterIndex) => {
          if (set.filters.length || set.operator || set.values.length) {
            if (!set.filters.length) {
              stepErrors.sourceFilterSets[filterIndex].filters = 'Source Filters is required';
              isValid = false;
            }
            if (!set.operator) {
              stepErrors.sourceFilterSets[filterIndex].operator = 'Source Operator is required';
              isValid = false;
            }
            if (!set.values.length) {
              stepErrors.sourceFilterSets[filterIndex].values = 'Source Values is required';
              isValid = false;
            }
          }
        });
        if (!step.joinColumns.length) {
          stepErrors.joinColumns = 'Join Columns is required';
          isValid = false;
        }
        if (!step.allocationColumns.length) {
          stepErrors.allocationColumns = 'Allocation Columns is required';
          isValid = false;
        }
        if (!step.driverTableID) {
          stepErrors.driverTableID = 'Driver Table ID is required';
          isValid = false;
        }
        if (!step.driverWeightColumn) {
          stepErrors.driverWeightColumn = 'Driver Weight Column is required';
          isValid = false;
        }
        if (!step.driverFilterSets.some(set => set.filters.length && set.operator && set.values.length)) {
          stepErrors.driverFilterSets[0].filters = 'At least one complete Driver Filter set is required';
          isValid = false;
        }
        step.driverFilterSets.forEach((set, filterIndex) => {
          if (set.filters.length || set.operator || set.values.length) {
            if (!set.filters.length) {
              stepErrors.driverFilterSets[filterIndex].filters = 'Driver Filters is required';
              isValid = false;
            }
            if (!set.operator) {
              stepErrors.driverFilterSets[filterIndex].operator = 'Driver Operator is required';
              isValid = false;
            }
            if (!set.values.length) {
              stepErrors.driverFilterSets[filterIndex].values = 'Driver Values is required';
              isValid = false;
            }
          }
        });
        newErrors.steps[index] = stepErrors;
      });

      setErrors(newErrors);
      return isValid;
    } catch (error) {
      alert('An error occurred during form validation. Please try again.');
      return false;
    }
  }, [ruleData, steps]);

  const parseColumns = (input) => input;

  const parseFilters = (filterSets) => {
    return filterSets
      .filter(set => set.filters.length && set.operator && set.values.length)
      .map(set => ({
        name: set.filters,
        filterType: set.operator,
        values: set.values,
      }));
  };

  const handleInputChange = useCallback((e, stepIndex = null) => {
    const { name, value } = e.target;
    if (stepIndex !== null) {
      setSteps((prevSteps) => {
        const newSteps = [...prevSteps];
        newSteps[stepIndex] = { ...newSteps[stepIndex], [name]: value };
        return newSteps;
      });
      setErrors((prevErrors) => {
        const newStepsErrors = [...prevErrors.steps];
        newStepsErrors[stepIndex] = {
          ...newStepsErrors[stepIndex],
          [name]: '',
        };
        return { ...prevErrors, steps: newStepsErrors };
      });
    } else {
      setRuleData((prevData) => ({
        ...prevData,
        [name]: value,
        ...(name === 'pnlGroup' ? { ruleGroup: '' } : {}),
      }));
      setErrors((prevErrors) => ({
        ...prevErrors,
        rule: { ...prevErrors.rule, [name]: '' },
      }));
    }
  }, []);

  const handleFilterSetChange = useCallback((e, stepIndex, filterIndex, type, field) => {
    const filterKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets';
    const value = field === 'operator' ? e.target.value : e.detail.map(option => option.value);
    setSteps((prevSteps) => {
      const newSteps = [...prevSteps];
      newSteps[stepIndex] = {
        ...newSteps[stepIndex],
        [filterKey]: newSteps[stepIndex][filterKey].map((set, i) =>
          i === filterIndex ? { ...set, [field]: value } : set
        ),
      };
      return newSteps;
    });
    setErrors((prevErrors) => {
      const newStepsErrors = [...prevErrors.steps];
      const filterErrorsKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets';
      newStepsErrors[stepIndex] = {
        ...newStepsErrors[stepIndex],
        [filterErrorsKey]: newStepsErrors[stepIndex][filterErrorsKey].map((setErrors, i) =>
          i === filterIndex ? { ...setErrors, [field]: '' } : setErrors
        ),
      };
      return { ...prevErrors, steps: newStepsErrors };
    });
  }, []);

  const resetForm = useCallback(() => {
    setRuleData({
      num: '',
      name: '',
      desc: '',
      custRefID: '',
      ruleGroup: '',
      isActive: 'Y',
      pnlGroup: '',
    });
    setSteps([
      {
        stepNo: '',
        stepName: 'Single Step',
        stepDesc: '',
        stepType: 'S',
        preAggregatorColumns: [],
        sourceTableID: '',
        sourceFilterSets: [{ filters: [], operator: '', values: [] }],
        joinColumns: [],
        allocationColumns: [],
        driverTableID: '',
        driverWeightColumn: '',
        driverFilterSets: [{ filters: [], operator: '', values: [] }],
      },
    ]);
    setErrors({ rule: {}, steps: [{ sourceFilterSets: [{}], driverFilterSets: [{}] }] });
    setActiveTab('ruleInfo');
  }, []);

  const handleSave = useCallback(async () => {
    if (!validateForm()) {
      alert('Please fill out all required fields.');
      return;
    }

    setIsLoading(true);

    const ruleJson = {
      rules: {
        rule: [
          {
            num: ruleData.num,
            name: ruleData.name,
            desc: ruleData.desc,
            custRefID: ruleData.custRefID,
            ruleGroup: ruleData.ruleGroup,
            isActive: ruleData.isActive,
            Step: steps.map((step, index) => ({
              stepNo: step.stepNo || `${ruleData.num}.${index + 1}`,
              stepName: step.stepName === 'Single Step' ? 'single' : 'multi',
              stepDesc: step.stepDesc,
              stepType: step.stepType,
              isActive: 'Y',
              SourceTable: {
                id: step.sourceTableID,
                Name: step.sourceTableID,
              },
              sourceFilters: {
                columns: parseFilters(step.sourceFilterSets),
              },
              preAggregator: {
                columns: parseColumns(step.preAggregatorColumns),
              },
              join: {
                columns: parseColumns(step.joinColumns),
              },
              allocation: {
                columns: parseColumns(step.allocationColumns),
              },
              driver: {
                driverTableID: step.driverTableID,
                driverWeightColumn: step.driverWeightColumn,
                driverFilters: {
                  columns: parseFilters(step.driverFilterSets),
                },
              },
            })),
          },
        ],
      },
    };

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000);

    try {
      const response = await fetch('/api/rules', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: JSON.stringify(ruleJson),
        signal: controller.signal,
      });

      clearTimeout(timeoutId);

      if (response.ok) {
        alert('Rule created successfully!');
        resetForm();
        navigate('/');
      } else {
        const errorData = await response.json().catch(() => ({ message: response.statusText }));
        alert(`Failed to create rule: ${errorData.message || response.statusText}`);
      }
    } catch (error) {
      if (error.name === 'AbortError') {
        alert('Request timed out. Please try again.');
      } else {
        alert('An error occurred while saving the rule. Please try again.');
      }
    } finally {
      setIsLoading(false);
    }
  }, [validateForm, ruleData, steps, resetForm, navigate]);

  const handleCancel = useCallback(() => {
    navigate('/');
  }, [navigate]);

  const renderTabContent = () => {
    switch (activeTab) {
      case 'ruleInfo':
        return (
          <div className={styles.tabContent}>
            {Object.values(errors.rule).some((error) => error) && (
              <div className={styles.errorSummary}>
                <h4>Please fix the following errors:</h4>
                <ul>
                  {Object.entries(errors.rule).map(([key, error]) => error && (
                    <li key={key}>{error}</li>
                  ))}
                </ul>
              </div>
            )}
            <h3 className={styles.sectionTitle}>Rule Information</h3>
            <div className={styles.formGrid}>
              <div className={styles.gridItem}>
                <McSelect
                  label="PnL Group"
                  name="pnlGroup"
                  value={ruleData.pnlGroup}
                  input={handleInputChange}
                  placeholder="Select a PnL Group"
                  required
                  invalid={!!errors.rule.pnlGroup}
                  invalidmessage={errors.rule.pnlGroup}
                >
                  {pnLGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
              <div className={styles.gridItem}>
                <McSelect
                  label="Rule Group"
                  name="ruleGroup"
                  value={ruleData.ruleGroup}
                  input={handleInputChange}
                  placeholder={ruleGroups.length ? "Select a Rule Group" : "Select a PnL Group first"}
                  required
                  disabled={!ruleData.pnlGroup || !ruleGroups.length}
                  invalid={!!errors.rule.ruleGroup}
                  invalidmessage={errors.rule.ruleGroup}
                >
                  {ruleGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Number"
                name="num"
                value={ruleData.num}
                input={handleInputChange}
                placeholder="Enter rule number"
                required
                invalid={!!errors.rule.num}
                invalidmessage={errors.rule.num}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Name"
                name="name"
                value={ruleData.name}
                input={handleInputChange}
                placeholder="Enter rule name"
                required
                invalid={!!errors.rule.name}
                invalidmessage={errors.rule.name}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Description"
                name="desc"
                value={ruleData.desc}
                input={handleInputChange}
                placeholder="Enter rule description"
                multiline
                rows={3}
                required
                invalid={!!errors.rule.desc}
                invalidmessage={errors.rule.desc}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Customer Reference ID"
                name="custRefID"
                value={ruleData.custRefID}
                input={handleInputChange}
                placeholder="Enter customer reference ID"
                required
                invalid={!!errors.rule.custRefID}
                invalidmessage={errors.rule.custRefID}
              />
            </div>
          </div>
        );
      case 'step':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Step Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Step Number"
                    name="stepNo"
                    value={step.stepNo}
                    input={(e) => handleInputChange(e, index)}
                    placeholder={`Enter step number (e.g., ${ruleData.num}.${index + 1})`}
                    required
                    invalid={!!errors.steps[index].stepNo}
                    invalidmessage={errors.steps[index].stepNo}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Name"
                    name="stepName"
                    value={step.stepName}
                    input={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select Step Name"
                    invalid={!!errors.steps[index].stepName}
                    invalidmessage={errors.steps[index].stepName}
                  >
                    <McOption value="Single Step">Single Step</McOption>
                    <McOption value="Multi Step">Multi Step</McOption>
                  </McSelect>
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Step Description"
                    name="stepDesc"
                    value={step.stepDesc}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter step description"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].stepDesc}
                    invalidmessage={errors.steps[index].stepDesc}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Type"
                    name="stepType"
                    value={step.stepType}
                    input={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select Step Type"
                    invalid={!!errors.steps[index].stepType}
                    invalidmessage={errors.steps[index].stepType}
                  >
                    <McOption value="S">S</McOption>
                    <McOption value="M">M</McOption>
                  </McSelect>
                </div>
              </div>
            ))}
            <div className={styles.stepButtonContainer}>
              <McButton
                label="Add Step"
                appearance="secondary"
                click={addStep}
                className={styles.actionButton}
              />
              {steps.length > 1 && (
                <McButton
                  label="Remove Step"
                  appearance="neutral"
                  click={() => removeStep(steps.length - 1)}
                  className={styles.actionButton}
                />
              )}
            </div>
          </div>
        );
      case 'source':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Source Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Source Table ID"
                    name="sourceTableID"
                    value={step.sourceTableID}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter source table ID"
                    required
                    invalid={!!errors.steps[index].sourceTableID}
                    invalidmessage={errors.steps[index].sourceTableID}
                  />
                </div>
                {step.sourceFilterSets.map((filterSet, filterIndex) => (
                  <div key={filterIndex} className={styles.filterRow}>
                    <McMultiSelect
                      label="Source Filters"
                      name="sourceFilters"
                      value={filterSet.filters}
                      optionselected={(e) => handleFilterSetChange(e, index, filterIndex, 'source', 'filters')}
                      placeholder="Select source filters"
                      required
                      invalid={!!errors.steps[index].sourceFilterSets[filterIndex]?.filters}
                      invalidmessage={errors.steps[index].sourceFilterSets[filterIndex]?.filters}
                    >
                      {sourceFilterOptions.map((option) => (
                        <McOption key={option.value} value={option.value}>
                          {option.label}
                        </McOption>
                      ))}
                    </McMultiSelect>
                    <McSelect
                      label="Source Operator"
                      name="sourceOperator"
                      value={filterSet.operator}
                      input={(e) => handleFilterSetChange(e, index, filterIndex, 'source', 'operator')}
                      placeholder="Select an operator"
                      required
                      invalid={!!errors.steps[index].sourceFilterSets[filterIndex]?.operator}
                      invalidmessage={errors.steps[index].sourceFilterSets[filterIndex]?.operator}
                    >
                      {operatorOptions.map((option) => (
                        <McOption key={option.value} value={option.value}>
                          {option.label}
                        </McOption>
                      ))}
                    </McSelect>
                    <div className={styles.filterValueContainer}>
                      <McMultiSelect
                        label="Source Values"
                        name="sourceValues"
                        value={filterSet.values}
                        optionselected={(e) => handleFilterSetChange(e, index, filterIndex, 'source', 'values')}
                        placeholder="Select source values"
                        required
                        invalid={!!errors.steps[index].sourceFilterSets[filterIndex]?.values}
                        invalidmessage={errors.steps[index].sourceFilterSets[filterIndex]?.values}
                      >
                        {sourceValueOptions.map((option) => (
                          <McOption key={option.value} value={option.value}>
                            {option.label}
                          </McOption>
                        ))}
                      </McMultiSelect>
                      {step.sourceFilterSets.length > 1 && (
                        <McButton
                          label="Remove"
                          appearance="neutral"
                          click={() => removeFilterSet(index, filterIndex, 'source')}
                          className={styles.removeButton}
                        />
                      )}
                    </div>
                  </div>
                ))}
                <div className={styles.stepButtonContainer}>
                  <McButton
                    label="Add More"
                    appearance="secondary"
                    click={() => addFilterSet(index, 'source')}
                    className={styles.actionButton}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'preAggregate':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Pre-Aggregate Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McMultiSelect
                    label="Pre-Aggregator Columns"
                    name="preAggregatorColumns"
                    value={step.preAggregatorColumns}
                    optionselected={(e) => handleMultiSelectChange(e, index, 'preAggregatorColumns')}
                    placeholder="Select pre-aggregator columns"
                    required
                    invalid={!!errors.steps[index].preAggregatorColumns}
                    invalidmessage={errors.steps[index].preAggregatorColumns}
                  >
                    {preAggregatorOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McMultiSelect>
                </div>
              </div>
            ))}
          </div>
        );
      case 'join':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Join Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McMultiSelect
                    label="Join Columns"
                    name="joinColumns"
                    value={step.joinColumns}
                    optionselected={(e) => handleMultiSelectChange(e, index, 'joinColumns')}
                    placeholder="Select join columns"
                    required
                    invalid={!!errors.steps[index].joinColumns}
                    invalidmessage={errors.steps[index].joinColumns}
                  >
                    {joinColumnsOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McMultiSelect>
                </div>
              </div>
            ))}
          </div>
        );
      case 'allocation':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Allocation Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McMultiSelect
                    label="Allocation Columns"
                    name="allocationColumns"
                    value={step.allocationColumns}
                    optionselected={(e) => handleMultiSelectChange(e, index, 'allocationColumns')}
                    placeholder="Select allocation columns"
                    required
                    invalid={!!errors.steps[index].allocationColumns}
                    invalidmessage={errors.steps[index].allocationColumns}
                  >
                    {allocationColumnsOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McMultiSelect>
                </div>
              </div>
            ))}
          </div>
        );
      case 'driver':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Driver Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Table ID"
                    name="driverTableID"
                    value={step.driverTableID}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver table ID"
                    required
                    invalid={!!errors.steps[index].driverTableID}
                    invalidmessage={errors.steps[index].driverTableID}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Weight Column"
                    name="driverWeightColumn"
                    value={step.driverWeightColumn}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver weight column"
                    required
                    invalid={!!errors.steps[index].driverWeightColumn}
                    invalidmessage={errors.steps[index].driverWeightColumn}
                  />
                </div>
                {step.driverFilterSets.map((filterSet, filterIndex) => (
                  <div key={filterIndex} className={styles.filterRow}>
                    <McMultiSelect
                      label="Driver Filters"
                      name="driverFilters"
                      value={filterSet.filters}
                      optionselected={(e) => handleFilterSetChange(e, index, filterIndex, 'driver', 'filters')}
                      placeholder="Select driver filters"
                      required
                      invalid={!!errors.steps[index].driverFilterSets[filterIndex]?.filters}
                      invalidmessage={errors.steps[index].driverFilterSets[filterIndex]?.filters}
                    >
                      {driverFilterOptions.map((option) => (
                        <McOption key={option.value} value={option.value}>
                          {option.label}
                        </McOption>
                      ))}
                    </McMultiSelect>
                    <McSelect
                      label="Driver Operator"
                      name="driverOperator"
                      value={filterSet.operator}
                      input={(e) => handleFilterSetChange(e, index, filterIndex, 'driver', 'operator')}
                      placeholder="Select an operator"
                      required
                      invalid={!!errors.steps[index].driverFilterSets[filterIndex]?.operator}
                      invalidmessage={errors.steps[index].driverFilterSets[filterIndex]?.operator}
                    >
                      {operatorOptions.map((option) => (
                        <McOption key={option.value} value={option.value}>
                          {option.label}
                        </McOption>
                      ))}
                    </McSelect>
                    <div className={styles.filterValueContainer}>
                      <McMultiSelect
                        label="Driver Values"
                        name="driverValues"
                        value={filterSet.values}
                        optionselected={(e) => handleFilterSetChange(e, index, filterIndex, 'driver', 'values')}
                        placeholder="Select driver values"
                        required
                        invalid={!!errors.steps[index].driverFilterSets[filterIndex]?.values}
                        invalidmessage={errors.steps[index].driverFilterSets[filterIndex]?.values}
                      >
                        {driverValueOptions.map((option) => (
                          <McOption key={option.value} value={option.value}>
                            {option.label}
                          </McOption>
                        ))}
                      </McMultiSelect>
                      {step.driverFilterSets.length > 1 && (
                        <McButton
                          label="Remove"
                          appearance="neutral"
                          click={() => removeFilterSet(index, filterIndex, 'driver')}
                          className={styles.removeButton}
                        />
                      )}
                    </div>
                  </div>
                ))}
                <div className={styles.stepButtonContainer}>
                  <McButton
                    label="Add More"
                    appearance="secondary"
                    click={() => addFilterSet(index, 'driver')}
                    className={styles.actionButton}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      default:
        return <div className={styles.tabContent}>No Tab Selected</div>;
    }
  };

  return (
    <ErrorBoundary>
      <div className={styles.pageWrapper}>
        {isLoading && (
          <div className={styles.loader}>Loading...</div>
        )}
        <div className={styles.container}>
          <div className={styles.card}>
            <div className={styles.buttonContainer}>
              <McButton
                label="Back"
                appearance="neutral"
                click={handleCancel}
                className={styles.actionButton}
              />
              <McButton
                label="Save"
                appearance="primary"
                click={handleSave}
                className={styles.actionButton}
                loading={isLoading}
                disabled={isLoading}
              />
            </div>

            <div className={styles.tabs}>
              <button
                className={`${styles.tabButton} ${activeTab === 'ruleInfo' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('ruleInfo')}
              >
                Rule Info
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'step' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('step')}
              >
                Step
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'source' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('source')}
              >
                Source
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'preAggregate' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('preAggregate')}
              >
                Pre-Aggregate
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'join' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('join')}
              >
                Join
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'allocation' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('allocation')}
              >
                Allocation
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'driver' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('driver')}
              >
                Driver
              </button>
            </div>

            {renderTabContent()}
          </div>
        </div>
      </div>
    </ErrorBoundary>
  );
};

export default CreateRules;
C:\Users\"Benutzerordner"\AppData\Local\Packages\5319275A.WhatsAppDesktop_cv1g1gvanyjgm\LocalState\shared\transfers\
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { McButton, McInput, McMultiSelect } from '@maersk-global/mds-react-wrapper';
import { McSelect } from '@maersk-global/mds-react-wrapper/components-core/mc-select';
import { McOption } from '@maersk-global/mds-react-wrapper/components-core/mc-option';
import styles from '../styles/CreateRule.module.css';
import data from '../data/PnLGroup.json';

const CreateRules = () => {
  const navigate = useNavigate();
  const [activeTab, setActiveTab] = useState('ruleInfo');
  const [isLoading, setIsLoading] = useState(false);
  const [ruleData, setRuleData] = useState({
    num: '',
    name: '',
    desc: '',
    custRefID: '',
    ruleGroup: '',
    isActive: 'Y',
    pnlGroup: '',
  });
  const [steps, setSteps] = useState([
    {
      stepNo: '',
      stepName: 'Single Step',
      StepDesc: '',
      stepType: 'S',
      preAggregatorColumns: '',
      sourceTable: '',
      sourceFilters: '',
      joinColumns: '',
      allocationColumns: '',
      driverTableID: '',
      driverWeightColumn: '',
      driverFilters: '',
    },
  ]);
  const [errors, setErrors] = useState({ rule: {}, steps: [{}] });

  const pnlGroups = data.PnLGroups ? Object.keys(data.PnLGroups) : [];
  const ruleGroups = ruleData.pnlGroup && data.PnLGroups[ruleData.pnlGroup]
    ? data.PnLGroups[ruleData.pnlGroup].RuleGroups || []
    : [];

  console.log('pnlGroups:', pnlGroups);
  console.log('ruleGroups:', ruleGroups);

  const addStep = () => {
    setSteps((prevSteps) => [
      ...prevSteps,
      {
        stepNo: '',
        stepName: 'Single Step',
        StepDesc: '',
        stepType: 'S',
        preAggregatorColumns: '',
        sourceTable: '',
        sourceFilters: '',
        joinColumns: '',
        allocationColumns: '',
        driverTableID: '',
        driverWeightColumn: '',
        driverFilters: '',
      },
    ]);
    setErrors((prevErrors) => ({
      ...prevErrors,
      steps: [...prevErrors.steps, {}],
    }));
  };

  const removeStep = (index) => {
    if (steps.length === 1) {
      alert('At least one step is required.');
      return;
    }
    setSteps((prevSteps) => prevSteps.filter((_, i) => i !== index));
    setErrors((prevErrors) => ({
      ...prevErrors,
      steps: prevErrors.steps.filter((_, i) => i !== index),
    }));
  };

  const validateForm = () => {
    const newErrors = { rule: {}, steps: steps.map(() => ({})) };
    let isValid = true;

    if (!ruleData.num) {
      newErrors.rule.num = 'Rule Number is required';
      isValid = false;
    } else if (!/^[a-zA-Z0-9]+$/.test(ruleData.num)) {
      newErrors.rule.num = 'Rule Number must be alphanumeric';
      isValid = false;
    }
    if (!ruleData.name) {
      newErrors.rule.name = 'Rule Name is required';
      isValid = false;
    }
    if (!ruleData.desc) {
      newErrors.rule.desc = 'Description is required';
      isValid = false;
    }
    if (!ruleData.custRefID) {
      newErrors.rule.custRefID = 'Customer Reference ID is required';
      isValid = false;
    }
    if (!ruleData.pnlGroup) {
      newErrors.rule.pnlGroup = 'PnL Group is required';
      isValid = false;
    }
    if (!ruleData.ruleGroup) {
      newErrors.rule.ruleGroup = 'Rule Group is required';
      isValid = false;
    }
    if (!ruleData.isActive) {
      newErrors.rule.isActive = 'Active status is required';
      isValid = false;
    }

    const stepNumbers = new Set();
    steps.forEach((step, index) => {
      const stepErrors = {};
      if (!step.stepNo) {
        stepErrors.stepNo = 'Step Number is required';
        isValid = false;
      } else if (stepNumbers.has(step.stepNo)) {
        stepErrors.stepNo = 'Step Number must be unique';
        isValid = false;
      } else {
        stepNumbers.add(step.stepNo);
      }
      if (!step.stepName) {
        stepErrors.stepName = 'Step Name is required';
        isValid = false;
      }
      if (!step.StepDesc) {
        stepErrors.StepDesc = 'Step Description is required';
        isValid = false;
      }
      if (!step.stepType) {
        stepErrors.stepType = 'Step Type is required';
        isValid = false;
      }
      if (!step.preAggregatorColumns) {
        stepErrors.preAggregatorColumns = 'Pre-Aggregator Columns are required';
        isValid = false;
      }
      if (!step.sourceTable) {
        stepErrors.sourceTable = 'Source Table is required';
        isValid = false;
      }
      if (!step.sourceFilters) {
        stepErrors.sourceFilters = 'Source Filters are required';
        isValid = false;
      } else {
        try {
          parseFilters(step.sourceFilters);
        } catch (e) {
          stepErrors.sourceFilters = 'Invalid Source Filter format';
          isValid = false;
        }
      }
      if (!step.joinColumns) {
        stepErrors.joinColumns = 'Join Columns are required';
        isValid = false;
      }
      if (!step.allocationColumns) {
        stepErrors.allocationColumns = 'Allocation Columns are required';
        isValid = false;
      }
      if (!step.driverTableID) {
        stepErrors.driverTableID = 'Driver Table ID is required';
        isValid = false;
      }
      if (!step.driverWeightColumn) {
        stepErrors.driverWeightColumn = 'Driver Weight Column is required';
        isValid = false;
      }
      if (!step.driverFilters) {
        stepErrors.driverFilters = 'Driver Filters are required';
        isValid = false;
      } else {
        try {
          parseFilters(step.driverFilters);
        } catch (e) {
          stepErrors.driverFilters = 'Invalid Driver Filter format';
          isValid = false;
        }
      }
      newErrors.steps[index] = stepErrors;
    });

    setErrors(newErrors);
    return isValid;
  };

  const parseColumns = (input) => input.split(',').map((item) => item.trim()).filter((item) => item);

  const parseFilters = (input) => {
    if (!input) return [];
    const filters = input.split(';').map((item) => item.trim()).filter((item) => item);
    return filters.map((filter) => {
      const parts = filter.split(':').map((item) => item.trim());
      if (parts.length !== 3) {
        throw new Error('Invalid filter format');
      }
      const [name, filterType, values] = parts;
      if (!name || !filterType || !values) {
        throw new Error('Invalid filter format');
      }
      return { name, filterType, values };
    });
  };

  const handleInputChange = (e, stepIndex = null) => {
    const { name, value } = e.target;
    console.log(`Input changed: ${name} = ${value}${stepIndex !== null ? ` (Step ${stepIndex + 1})` : ''}`);

    if (stepIndex !== null) {
      setSteps((prevSteps) => {
        const newSteps = [...prevSteps];
        newSteps[stepIndex] = { ...newSteps[stepIndex], [name]: value };
        return newSteps;
      });
      setErrors((prevErrors) => ({
        ...prevErrors,
        steps: prevErrors.steps.map((stepErrors, i) =>
          i === stepIndex ? { ...stepErrors, [name]: '' } : stepErrors
        ),
      }));
    } else {
      setRuleData((prevData) => {
        const newData = {
          ...prevData,
          [name]: value,
          ...(name === 'pnlGroup' ? { ruleGroup: '' } : {}),
        };
        console.log('Updated ruleData:', newData);
        return newData;
      });
      setErrors((prevErrors) => ({
        ...prevErrors,
        rule: { ...prevErrors.rule, [name]: '' },
      }));
    }
  };

  const resetForm = () => {
    setRuleData({
      num: '',
      name: '',
      desc: '',
      custRefID: '',
      ruleGroup: '',
      isActive: 'Y',
      pnlGroup: '',
    });
    setSteps([
      {
        stepNo: '',
        stepName: 'Single Step',
        StepDesc: '',
        stepType: 'S',
        preAggregatorColumns: '',
        sourceTable: '',
        sourceFilters: '',
        joinColumns: '',
        allocationColumns: '',
        driverTableID: '',
        driverWeightColumn: '',
        driverFilters: '',
      },
    ]);
    setErrors({ rule: {}, steps: [{}] });
    setActiveTab('ruleInfo');
  };

  const handleSave = async () => {
    if (!validateForm()) {
      console.log('Validation failed:', JSON.stringify(errors, null, 2));
      alert('Please fill out all required fields.');
      return;
    }

    setIsLoading(true);

    const ruleJson = {
      rules: {
        rule: [
          {
            num: ruleData.num,
            name: ruleData.name,
            desc: ruleData.desc,
            custRefID: ruleData.custRefID,
            ruleGroup: ruleData.ruleGroup,
            isActive: ruleData.isActive,
            Step: steps.map((step, index) => ({
              stepNo: step.stepNo || `${ruleData.num}.${index + 1}`,
              stepName: step.stepName === 'Single Step' ? 'single' : 'multi',
              StepDesc: step.StepDesc,
              stepType: step.stepType,
              isActive: 'Y',
              SourceTable: {
                id: '1',
                Name: step.sourceTable,
              },
              sourceFilters: {
                columns: parseFilters(step.sourceFilters),
              },
              preAggregator: {
                columns: parseColumns(step.preAggregatorColumns),
              },
              join: {
                columns: parseColumns(step.joinColumns),
              },
              allocation: {
                columns: parseColumns(step.allocationColumns),
              },
              driver: {
                driverTableID: step.driverTableID,
                driverWeightColumn: step.driverWeightColumn,
                driverFilters: {
                  columns: parseFilters(step.driverFilters),
                },
              },
            })),
          },
        ],
      },
    };

    console.log('Saving Rule Data:', JSON.stringify(ruleJson, null, 2));

    try {
      const response = await fetch('/api/rules', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: JSON.stringify(ruleJson),
      });

      if (response.ok) {
        console.log('Rule created successfully');
        alert('Rule created successfully!');
        resetForm();
        navigate('/');
      } else {
        const errorData = await response.json().catch(() => ({ message: response.statusText }));
        console.error('Failed to create rule:', response.status, errorData);
        alert(`Failed to create rule: ${errorData.message || response.statusText}`);
      }
    } catch (error) {
      console.error('Error during API call:', error.message);
      alert('An error occurred while saving the rule. Please try again.');
    } finally {
      setIsLoading(false);
    }
  };

  const handleCancel = () => {
    console.log('Cancelling rule creation');
    navigate('/');
  };

  const renderTabContent = () => {
    console.log('Rendering tab:', activeTab);
    switch (activeTab) {
      case 'ruleInfo':
        return (
          <div className={styles.tabContent}>
            {Object.values(errors.rule).some((error) => error) && (
              <div className={styles.errorSummary}>
                <h4>Please fix the following errors:</h4>
                <ul>
                  {Object.entries(errors.rule).map(([key, error]) => error && (
                    <li key={key}>{error}</li>
                  ))}
                </ul>
              </div>
            )}
            <h3 className={styles.sectionTitle}>Rule Information</h3>
            <div className={styles.formGrid}>
              <div className={styles.gridItem}>
                <McSelect
                  label="PnL Group"
                  name="pnlGroup"
                  value={ruleData.pnlGroup}
                  input={handleInputChange}
                  placeholder="Select a PnL Group"
                  required
                  invalid={!!errors.rule.pnlGroup}
                  invalidmessage={errors.rule.pnlGroup}
                >
                  {pnlGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
              <div className={styles.gridItem}>
                <McSelect
                  label="Rule Group"
                  name="ruleGroup"
                  value={ruleData.ruleGroup}
                  input={handleInputChange}
                  placeholder={ruleGroups.length ? "Select a Rule Group" : "Select a PnL Group first"}
                  required
                  disabled={!ruleData.pnlGroup || !ruleGroups.length}
                  invalid={!!errors.rule.ruleGroup}
                  invalidmessage={errors.rule.ruleGroup}
                >
                  {ruleGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Number"
                name="num"
                value={ruleData.num}
                input={handleInputChange}
                placeholder="Enter rule number"
                required
                invalid={!!errors.rule.num}
                invalidmessage={errors.rule.num}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Name"
                name="name"
                value={ruleData.name}
                input={handleInputChange}
                placeholder="Enter rule name"
                required
                invalid={!!errors.rule.name}
                invalidmessage={errors.rule.name}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Description"
                name="desc"
                value={ruleData.desc}
                input={handleInputChange}
                placeholder="Enter rule description"
                multiline
                rows={3}
                required
                invalid={!!errors.rule.desc}
                invalidmessage={errors.rule.desc}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Customer Reference ID"
                name="custRefID"
                value={ruleData.custRefID}
                input={handleInputChange}
                placeholder="Enter customer reference ID"
                required
                invalid={!!errors.rule.custRefID}
                invalidmessage={errors.rule.custRefID}
              />
            </div>
          </div>
        );
      case 'step':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Step Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Step Number"
                    name="stepNo"
                    value={step.stepNo}
                    input={(e) => handleInputChange(e, index)}
                    placeholder={`Enter step number (e.g., ${ruleData.num}.${index + 1})`}
                    required
                    invalid={!!errors.steps[index].stepNo}
                    invalidmessage={errors.steps[index].stepNo}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Name"
                    name="stepName"
                    value={step.stepName}
                    input={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select Step Name"
                    invalid={!!errors.steps[index].stepName}
                    invalidmessage={errors.steps[index].stepName}
                  >
                    <McOption value="Single Step">Single Step</McOption>
                    <McOption value="Multi Step">Multi Step</McOption>
                  </McSelect>
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Step Description"
                    name="StepDesc"
                    value={step.StepDesc}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter step description"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].StepDesc}
                    invalidmessage={errors.steps[index].StepDesc}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Type"
                    name="stepType"
                    value={step.stepType}
                    input={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select Step Type"
                    invalid={!!errors.steps[index].stepType}
                    invalidmessage={errors.steps[index].stepType}
                  >
                    <McOption value="S">S</McOption>
                    <McOption value="M">M</McOption>
                  </McSelect>
                </div>
              </div>
            ))}
            <div className={styles.stepButtonContainer}>
              <McButton
                label="Add Step"
                appearance="secondary"
                click={addStep}
                className={styles.actionButton}
              />
              {steps.length > 1 && (
                <McButton
                  label="Remove Step"
                  appearance="neutral"
                  click={() => removeStep(steps.length - 1)}
                  className={styles.actionButton}
                />
              )}
            </div>
          </div>
        );
      case 'source':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Source Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Source Table"
                    name="sourceTable"
                    value={step.sourceTable}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter source table name"
                    required
                    invalid={!!errors.steps[index].sourceTable}
                    invalidmessage={errors.steps[index].sourceTable}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Source Filters"
                    name="sourceFilters"
                    value={step.sourceFilters}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter filters (e.g., PNL_LINE:IN:PnL.DVC.214,PnL.DVC.215;MOVE_TYPE:EQ:EX)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].sourceFilters}
                    invalidmessage={errors.steps[index].sourceFilters}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'preAggregate':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Pre-Aggregate Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Pre-Aggregator Columns"
                    name="preAggregatorColumns"
                    value={step.preAggregatorColumns}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter columns (comma-separated)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].preAggregatorColumns}
                    invalidmessage={errors.steps[index].preAggregatorColumns}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'join':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Join Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Join Columns"
                    name="joinColumns"
                    value={step.joinColumns}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter columns (comma-separated)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].joinColumns}
                    invalidmessage={errors.steps[index].joinColumns}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'allocation':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Allocation Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Allocation Columns"
                    name="allocationColumns"
                    value={step.allocationColumns}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter columns (comma-separated)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].allocationColumns}
                    invalidmessage={errors.steps[index].allocationColumns}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'driver':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Driver Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Table ID"
                    name="driverTableID"
                    value={step.driverTableID}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver table ID"
                    required
                    invalid={!!errors.steps[index].driverTableID}
                    invalidmessage={errors.steps[index].driverTableID}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Weight Column"
                    name="driverWeightColumn"
                    value={step.driverWeightColumn}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver weight column"
                    required
                    invalid={!!errors.steps[index].driverWeightColumn}
                    invalidmessage={errors.steps[index].driverWeightColumn}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Filters"
                    name="driverFilters"
                    value={step.driverFilters}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter filters"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].driverFilters}
                    invalidmessage={errors.steps[index].driverFilters}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      default:
        return <div className={styles.tabContent}>No Tab Selected</div>;
    }
  };

  return (
    <div className={styles.pageWrapper}>
      <div className={styles.container}>
        <div className={styles.card}>
          <div className={styles.buttonContainer}>
            <McButton
              label="Back"
              appearance="neutral"
              click={handleCancel}
              className={styles.actionButton}
            />
            <McButton
              label="Save"
              appearance="primary"
              click={handleSave}
              className={styles.actionButton}
              loading={isLoading}
              disabled={isLoading}
            />
          </div>

          <div className={styles.tabs}>
            <button
              className={`${styles.tabButton} ${activeTab === 'ruleInfo' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('ruleInfo')}
            >
              Rule Info
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'step' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('step')}
            >
              Step
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'source' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('source')}
            >
              Source
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'preAggregate' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('preAggregate')}
            >
              Pre-Aggregate
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'join' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('join')}
            >
              Join
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'allocation' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('allocation')}
            >
              Allocation
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'driver' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('driver')}
            >
              Driver
            </button>
          </div>

          {renderTabContent()}
        </div>
      </div>
    </div>
  );
};

export default CreateRules;

import React, { useState, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { McButton, McInput, McMultiSelect, McSelect } from '@maersk-global/mds-react-wrapper';
import { McOption } from '@maersk-global/mds-react-wrapper/components-core/mc-option';
import styles from '../styles/CreateRule.module.css';
import data from '../data/PnLGroup.json';

class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError() {
    return { hasError: true };
  }
  render() {
    if (this.state.hasError) {
      return <div>Something went wrong. Please refresh the page.</div>;
    }
    return this.props.children;
  }
}

const CreateRules = () => {
  const navigate = useNavigate();
  const [activeTab, setActiveTab] = useState('ruleInfo');
  const [isLoading, setIsLoading] = useState(false);
  const [ruleData, setRuleData] = useState({
    num: '',
    name: '',
    desc: '',
    custRefID: '',
    ruleGroup: '',
    isActive: 'Y',
    pnlGroup: '',
  });
  const [steps, setSteps] = useState([
    {
      stepNo: '',
      stepName: 'Single Step',
      stepDesc: '',
      stepType: 'S',
      preAggregatorColumns: [],
      sourceTableID: '',
      sourceFilters: [],
      sourceOperator: '',
      joinColumns: [],
      allocationColumns: [],
      driverTableID: '',
      driverWeightColumn: '',
      driverFilters: [],
      driverOperator: '',
    },
  ]);
  const [errors, setErrors] = useState({ rule: {}, steps: [{}] });

  const pnLGroups = data.PnLGroups && typeof data.PnLGroups === 'object'
    ? Object.keys(data.PnLGroups)
    : [];
  const ruleGroups = ruleData.pnlGroup && data.PnLGroups[ruleData.pnlGroup]
    ? data.PnLGroups[ruleData.pnlGroup].RuleGroups || []
    : [];

  const sourceFilterOptions = [
    { value: 'Source_Filter_1', label: 'Source Filter 1' },
    { value: 'Source_Filter_2', label: 'Source Filter 2' },
    { value: 'Source_Filter_3', label: 'Source Filter 3' },
  ];
  const preAggregatorOptions = [
    { value: 'column1', label: 'Column 1' },
    { value: 'column2', label: 'Column 2' },
    { value: 'column3', label: 'Column 3' },
  ];
  const joinColumnsOptions = [
    { value: 'join_col1', label: 'Join Column 1' },
    { value: 'join_col2', label: 'Join Column 2' },
    { value: 'join_col3', label: 'Join Column 3' },
  ];
  const allocationColumnsOptions = [
    { value: 'alloc_col1', label: 'Allocation Column 1' },
    { value: 'alloc_col2', label: 'Allocation Column 2' },
    { value: 'alloc_col3', label: 'Allocation Column 3' },
  ];
  const driverFilterOptions = [
    { value: 'Driver_Type_1', label: 'Driver Type: Type 1' },
    { value: 'Driver_Type_2', label: 'Driver Type: Type 2' },
    { value: 'Driver_Status_Active', label: 'Driver Status: Active' },
  ];

  const operatorOptions = useMemo(() => [
    { value: 'IN', label: 'IN' },
    { value: 'NOT IN', label: 'NOT IN' },
    { value: 'EQ', label: 'EQ' },
    { value: 'NTEQ', label: 'NTEQ' },
    { value: 'IS NULL', label: 'IS NULL' },
    { value: 'GT', label: 'GT' },
    { value: 'LT', label: 'LT' },
    { value: 'GTEQ', label: 'GTEQ' },
    { value: 'LTEQ', label: 'LTEQ' },
    { value: 'BETWEEN', label: 'BETWEEN' },
    { value: 'NOT BETWEEN', label: 'NOT BETWEEN' },
    { value: 'LIKE', label: 'LIKE' },
  ], []);

  const addStep = useCallback(() => {
    setSteps((prevSteps) => [
      ...prevSteps,
      {
        stepNo: '',
        stepName: 'Single Step',
        stepDesc: '',
        stepType: 'S',
        preAggregatorColumns: [],
        sourceTableID: '',
        sourceFilters: [],
        sourceOperator: '',
        joinColumns: [],
        allocationColumns: [],
        driverTableID: '',
        driverWeightColumn: '',
        driverFilters: [],
        driverOperator: '',
      },
    ]);
    setErrors((prevErrors) => ({
      ...prevErrors,
      steps: [...prevErrors.steps, {}],
    }));
  }, []);

  const removeStep = useCallback((index) => {
    if (steps.length === 1) {
      alert('At least one step is required.');
      return;
    }
    setSteps((prevSteps) => prevSteps.filter((_, i) => i !== index));
    setErrors((prevErrors) => ({
      ...prevErrors,
      steps: prevErrors.steps.filter((_, i) => i !== index),
    }));
  }, [steps.length]);

  const validateForm = useCallback(() => {
    try {
      const newErrors = { rule: {}, steps: steps.map(() => ({})) };
      let isValid = true;

      if (!ruleData.num) {
        newErrors.rule.num = 'Rule Number is required';
        isValid = false;
      } else if (!/^[a-zA-Z0-9]+$/.test(ruleData.num)) {
        newErrors.rule.num = 'Rule Number must be alphanumeric';
        isValid = false;
      }
      if (!ruleData.name) {
        newErrors.rule.name = 'Rule Name is required';
        isValid = false;
      }
      if (!ruleData.desc) {
        newErrors.rule.desc = 'Description is required';
        isValid = false;
      }
      if (!ruleData.custRefID) {
        newErrors.rule.custRefID = 'Customer Reference ID is required';
        isValid = false;
      }
      if (!ruleData.pnlGroup) {
        newErrors.rule.pnlGroup = 'PnL Group is required';
        isValid = false;
      }
      if (!ruleData.ruleGroup) {
        newErrors.rule.ruleGroup = 'Rule Group is required';
        isValid = false;
      }
      if (!ruleData.isActive) {
        newErrors.rule.isActive = 'Active status is required';
        isValid = false;
      }

      const stepNumbers = new Set();
      steps.forEach((step, index) => {
        const stepErrors = {};
        if (!step.stepNo) {
          stepErrors.stepNo = 'Step Number is required';
          isValid = false;
        } else if (stepNumbers.has(step.stepNo)) {
          stepErrors.stepNo = 'Step Number must be unique';
          isValid = false;
        } else {
          stepNumbers.add(step.stepNo);
        }
        if (!step.stepName) {
          stepErrors.stepName = 'Step Name is required';
          isValid = false;
        }
        if (!step.stepDesc) {
          stepErrors.stepDesc = 'Step Description is required';
          isValid = false;
        }
        if (!step.stepType) {
          stepErrors.stepType = 'Step Type is required';
          isValid = false;
        }
        if (!step.preAggregatorColumns.length) {
          stepErrors.preAggregatorColumns = 'Pre-Aggregator Columns are required';
          isValid = false;
        }
        if (!step.sourceTableID) {
          stepErrors.sourceTableID = 'Source Table ID is required';
          isValid = false;
        }
        if (!step.sourceFilters.length) {
          stepErrors.sourceFilters = 'Source Filters are required';
          isValid = false;
        }
        if (!step.sourceOperator) {
          stepErrors.sourceOperator = 'Source Operator is required';
          isValid = false;
        }
        if (!step.joinColumns.length) {
          stepErrors.joinColumns = 'Join Columns are required';
          isValid = false;
        }
        if (!step.allocationColumns.length) {
          stepErrors.allocationColumns = 'Allocation Columns are required';
          isValid = false;
        }
        if (!step.driverTableID) {
          stepErrors.driverTableID = 'Driver Table ID is required';
          isValid = false;
        }
        if (!step.driverWeightColumn) {
          stepErrors.driverWeightColumn = 'Driver Weight Column is required';
          isValid = false;
        }
        if (!step.driverFilters.length) {
          stepErrors.driverFilters = 'Driver Filters are required';
          isValid = false;
        }
        if (!step.driverOperator) {
          stepErrors.driverOperator = 'Driver Operator is required';
          isValid = false;
        }
        newErrors.steps[index] = stepErrors;
      });

      setErrors(newErrors);
      return isValid;
    } catch (error) {
      alert('An error occurred during form validation. Please try again.');
      return false;
    }
  }, [ruleData, steps]);

  const parseColumns = (input) => input;

  const parseFilters = (filters, operator) => {
    if (!filters.length || !operator) return [];
    return filters.map((filter) => ({
      name: filter,
      filterType: operator,
      values: filter,
    }));
  };

  const handleInputChange = useCallback((e, stepIndex = null) => {
    const { name, value } = e.target;
    if (stepIndex !== null) {
      setSteps((prevSteps) => {
        const newSteps = [...prevSteps];
        newSteps[stepIndex] = { ...newSteps[stepIndex], [name]: value };
        return newSteps;
      });
      setErrors((prevErrors) => ({
        ...prevErrors,
        steps: prevErrors.steps.map((stepErrors, i) =>
          i === stepIndex ? { ...stepErrors, [name]: '' } : stepErrors
        ),
      }));
    } else {
      setRuleData((prevData) => ({
        ...prevData,
        [name]: value,
        ...(name === 'pnlGroup' ? { ruleGroup: '' } : {}),
      }));
      setErrors((prevErrors) => ({
        ...prevErrors,
        rule: { ...prevErrors.rule, [name]: '' },
      }));
    }
  }, []);

  const handleMultiSelectChange = useCallback((e, stepIndex, fieldName) => {
    const selectedValues = e.detail.map((option) => option.value);
    setSteps((prevSteps) => {
      const newSteps = [...prevSteps];
      newSteps[stepIndex] = { ...newSteps[stepIndex], [fieldName]: selectedValues };
      return newSteps;
    });
    setErrors((prevErrors) => ({
      ...prevErrors,
      steps: prevErrors.steps.map((stepErrors, i) =>
        i === stepIndex ? { ...stepErrors, [fieldName]: '' } : stepErrors
      ),
    }));
  }, []);

  const resetForm = useCallback(() => {
    setRuleData({
      num: '',
      name: '',
      desc: '',
      custRefID: '',
      ruleGroup: '',
      isActive: 'Y',
      pnlGroup: '',
    });
    setSteps([
      {
        stepNo: '',
        stepName: 'Single Step',
        stepDesc: '',
        stepType: 'S',
        preAggregatorColumns: [],
        sourceTableID: '',
        sourceFilters: [],
        sourceOperator: '',
        joinColumns: [],
        allocationColumns: [],
        driverTableID: '',
        driverWeightColumn: '',
        driverFilters: [],
        driverOperator: '',
      },
    ]);
    setErrors({ rule: {}, steps: [{}] });
    setActiveTab('ruleInfo');
  }, []);

  const handleSave = useCallback(async () => {
    if (!validateForm()) {
      alert('Please fill out all required fields.');
      return;
    }

    setIsLoading(true);

    const ruleJson = {
      rules: {
        rule: [
          {
            num: ruleData.num,
            name: ruleData.name,
            desc: ruleData.desc,
            custRefID: ruleData.custRefID,
            ruleGroup: ruleData.ruleGroup,
            isActive: ruleData.isActive,
            Step: steps.map((step, index) => ({
              stepNo: step.stepNo || `${ruleData.num}.${index + 1}`,
              stepName: step.stepName === 'Single Step' ? 'single' : 'multi',
              stepDesc: step.stepDesc,
              stepType: step.stepType,
              isActive: 'Y',
              SourceTable: {
                id: step.sourceTableID,
                Name: step.sourceTableID,
              },
              sourceFilters: {
                columns: parseFilters(step.sourceFilters, step.sourceOperator),
                operator: step.sourceOperator,
              },
              preAggregator: {
                columns: parseColumns(step.preAggregatorColumns),
              },
              join: {
                columns: parseColumns(step.joinColumns),
              },
              allocation: {
                columns: parseColumns(step.allocationColumns),
              },
              driver: {
                driverTableID: step.driverTableID,
                driverWeightColumn: step.driverWeightColumn,
                driverFilters: {
                  columns: parseFilters(step.driverFilters, step.driverOperator),
                  operator: step.driverOperator,
                },
              },
            })),
          },
        ],
      },
    };

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000);

    try {
      const response = await fetch('/api/rules', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: JSON.stringify(ruleJson),
        signal: controller.signal,
      });

      clearTimeout(timeoutId);

      if (response.ok) {
        alert('Rule created successfully!');
        resetForm();
        navigate('/');
      } else {
        const errorData = await response.json().catch(() => ({ message: response.statusText }));
        alert(`Failed to create rule: ${errorData.message || response.statusText}`);
      }
    } catch (error) {
      if (error.name === 'AbortError') {
        alert('Request timed out. Please try again.');
      } else {
        alert('An error occurred while saving the rule. Please try again.');
      }
    } finally {
      setIsLoading(false);
    }
  }, [validateForm, ruleData, steps, resetForm, navigate]);

  const handleCancel = useCallback(() => {
    navigate('/');
  }, [navigate]);

  const renderTabContent = () => {
    switch (activeTab) {
      case 'ruleInfo':
        return (
          <div className={styles.tabContent}>
            {Object.values(errors.rule).some((error) => error) && (
              <div className={styles.errorSummary}>
                <h4>Please fix the following errors:</h4>
                <ul>
                  {Object.entries(errors.rule).map(([key, error]) => error && (
                    <li key={key}>{error}</li>
                  ))}
                </ul>
              </div>
            )}
            <h3 className={styles.sectionTitle}>Rule Information</h3>
            <div className={styles.formGrid}>
              <div className={styles.gridItem}>
                <McSelect
                  label="PnL Group"
                  name="pnlGroup"
                  value={ruleData.pnlGroup}
                  input={handleInputChange}
                  placeholder="Select a PnL Group"
                  required
                  invalid={!!errors.rule.pnlGroup}
                  invalidmessage={errors.rule.pnlGroup}
                >
                  {pnLGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
              <div className={styles.gridItem}>
                <McSelect
                  label="Rule Group"
                  name="ruleGroup"
                  value={ruleData.ruleGroup}
                  input={handleInputChange}
                  placeholder={ruleGroups.length ? "Select a Rule Group" : "Select a PnL Group first"}
                  required
                  disabled={!ruleData.pnlGroup || !ruleGroups.length}
                  invalid={!!errors.rule.ruleGroup}
                  invalidmessage={errors.rule.ruleGroup}
                >
                  {ruleGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Number"
                name="num"
                value={ruleData.num}
                input={handleInputChange}
                placeholder="Enter rule number"
                required
                invalid={!!errors.rule.num}
                invalidmessage={errors.rule.num}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Name"
                name="name"
                value={ruleData.name}
                input={handleInputChange}
                placeholder="Enter rule name"
                required
                invalid={!!errors.rule.name}
                invalidmessage={errors.rule.name}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Description"
                name="desc"
                value={ruleData.desc}
                input={handleInputChange}
                placeholder="Enter rule description"
                multiline
                rows={3}
                required
                invalid={!!errors.rule.desc}
                invalidmessage={errors.rule.desc}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Customer Reference ID"
                name="custRefID"
                value={ruleData.custRefID}
                input={handleInputChange}
                placeholder="Enter customer reference ID"
                required
                invalid={!!errors.rule.custRefID}
                invalidmessage={errors.rule.custRefID}
              />
            </div>
          </div>
        );
      case 'step':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Step Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Step Number"
                    name="stepNo"
                    value={step.stepNo}
                    input={(e) => handleInputChange(e, index)}
                    placeholder={`Enter step number (e.g., ${ruleData.num}.${index + 1})`}
                    required
                    invalid={!!errors.steps[index].stepNo}
                    invalidmessage={errors.steps[index].stepNo}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Name"
                    name="stepName"
                    value={step.stepName}
                    input={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select Step Name"
                    invalid={!!errors.steps[index].stepName}
                    invalidmessage={errors.steps[index].stepName}
                  >
                    <McOption value="Single Step">Single Step</McOption>
                    <McOption value="Multi Step">Multi Step</McOption>
                  </McSelect>
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Step Description"
                    name="stepDesc"
                    value={step.stepDesc}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter step description"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].stepDesc}
                    invalidmessage={errors.steps[index].stepDesc}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Type"
                    name="stepType"
                    value={step.stepType}
                    input={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select Step Type"
                    invalid={!!errors.steps[index].stepType}
                    invalidmessage={errors.steps[index].stepType}
                  >
                    <McOption value="S">S</McOption>
                    <McOption value="M">M</McOption>
                  </McSelect>
                </div>
              </div>
            ))}
            <div className={styles.stepButtonContainer}>
              <McButton
                label="Add Step"
                appearance="secondary"
                click={addStep}
                className={styles.actionButton}
              />
              {steps.length > 1 && (
                <McButton
                  label="Remove Step"
                  appearance="neutral"
                  click={() => removeStep(steps.length - 1)}
                  className={styles.actionButton}
                />
              )}
            </div>
          </div>
        );
      case 'source':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Source Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Source Table ID"
                    name="sourceTableID"
                    value={step.sourceTableID}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter source table ID"
                    required
                    invalid={!!errors.steps[index].sourceTableID}
                    invalidmessage={errors.steps[index].sourceTableID}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McMultiSelect
                    label="Source Filters"
                    name="sourceFilters"
                    value={step.sourceFilters}
                    optionselected={(e) => handleMultiSelectChange(e, index, 'sourceFilters')}
                    placeholder="Select source filters"
                    required
                    invalid={!!errors.steps[index].sourceFilters}
                    invalidmessage={errors.steps[index].sourceFilters}
                  >
                    {sourceFilterOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McMultiSelect>
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Source Operator"
                    name="sourceOperator"
                    value={step.sourceOperator}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Select an operator"
                    required
                    invalid={!!errors.steps[index].sourceOperator}
                    invalidmessage={errors.steps[index].sourceOperator}
                  >
                    {operatorOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McSelect>
                </div>
              </div>
            ))}
          </div>
        );
      case 'preAggregate':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Pre-Aggregate Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McMultiSelect
                    label="Pre-Aggregator Columns"
                    name="preAggregatorColumns"
                    value={step.preAggregatorColumns}
                    optionselected={(e) => handleMultiSelectChange(e, index, 'preAggregatorColumns')}
                    placeholder="Select pre-aggregator columns"
                    required
                    invalid={!!errors.steps[index].preAggregatorColumns}
                    invalidmessage={errors.steps[index].preAggregatorColumns}
                  >
                    {preAggregatorOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McMultiSelect>
                </div>
              </div>
            ))}
          </div>
        );
      case 'join':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Join Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McMultiSelect
                    label="Join Columns"
                    name="joinColumns"
                    value={step.joinColumns}
                    optionselected={(e) => handleMultiSelectChange(e, index, 'joinColumns')}
                    placeholder="Select join columns"
                    required
                    invalid={!!errors.steps[index].joinColumns}
                    invalidmessage={errors.steps[index].joinColumns}
                  >
                    {joinColumnsOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McMultiSelect>
                </div>
              </div>
            ))}
          </div>
        );
      case 'allocation':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Allocation Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McMultiSelect
                    label="Allocation Columns"
                    name="allocationColumns"
                    value={step.allocationColumns}
                    optionselected={(e) => handleMultiSelectChange(e, index, 'allocationColumns')}
                    placeholder="Select allocation columns"
                    required
                    invalid={!!errors.steps[index].allocationColumns}
                    invalidmessage={errors.steps[index].allocationColumns}
                  >
                    {allocationColumnsOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McMultiSelect>
                </div>
              </div>
            ))}
          </div>
        );
      case 'driver':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Driver Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Table ID"
                    name="driverTableID"
                    value={step.driverTableID}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver table ID"
                    required
                    invalid={!!errors.steps[index].driverTableID}
                    invalidmessage={errors.steps[index].driverTableID}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Weight Column"
                    name="driverWeightColumn"
                    value={step.driverWeightColumn}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver weight column"
                    required
                    invalid={!!errors.steps[index].driverWeightColumn}
                    invalidmessage={errors.steps[index].driverWeightColumn}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McMultiSelect
                    label="Driver Filters"
                    name="driverFilters"
                    value={step.driverFilters}
                    optionselected={(e) => handleMultiSelectChange(e, index, 'driverFilters')}
                    placeholder="Select driver filters"
                    required
                    invalid={!!errors.steps[index].driverFilters}
                    invalidmessage={errors.steps[index].driverFilters}
                  >
                    {driverFilterOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McMultiSelect>
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Driver Operator"
                    name="driverOperator"
                    value={step.driverOperator}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Select an operator"
                    required
                    invalid={!!errors.steps[index].driverOperator}
                    invalidmessage={errors.steps[index].driverOperator}
                  >
                    {operatorOptions.map((option) => (
                      <McOption key={option.value} value={option.value}>
                        {option.label}
                      </McOption>
                    ))}
                  </McSelect>
                </div>
              </div>
            ))}
          </div>
        );
      default:
        return <div className={styles.tabContent}>No Tab Selected</div>;
    }
  };

  return (
    <ErrorBoundary>
      <div className={styles.pageWrapper}>
        {isLoading && (
          <div className={styles.loader}>Loading...</div>
        )}
        <div className={styles.container}>
          <div className={styles.card}>
            <div className={styles.buttonContainer}>
              <McButton
                label="Back"
                appearance="neutral"
                click={handleCancel}
                className={styles.actionButton}
              />
              <McButton
                label="Save"
                appearance="primary"
                click={handleSave}
                className={styles.actionButton}
                loading={isLoading}
                disabled={isLoading}
              />
            </div>

            <div className={styles.tabs}>
              <button
                className={`${styles.tabButton} ${activeTab === 'ruleInfo' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('ruleInfo')}
              >
                Rule Info
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'step' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('step')}
              >
                Step
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'source' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('source')}
              >
                Source
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'preAggregate' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('preAggregate')}
              >
                Pre-Aggregate
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'join' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('join')}
              >
                Join
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'allocation' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('allocation')}
              >
                Allocation
              </button>
              <button
                className={`${styles.tabButton} ${activeTab === 'driver' ? styles.activeTab : ''}`}
                onClick={() => setActiveTab('driver')}
              >
                Driver
              </button>
            </div>

            {renderTabContent()}
          </div>
        </div>
      </div>
    </ErrorBoundary>
  );
};

export default CreateRules;
-- RECHCECKING
-- ICA_Bank_Decline_Threshold_Block
DROP TABLE team_kingkong.offus_ICA_Bank_Decline_Threshold_Block_breaches;

-- CREATE TABLE team_kingkong.offus_ICA_Bank_Decline_Threshold_Block_breaches AS
INSERT INTO team_kingkong.offus_ICA_Bank_Decline_Threshold_Block_breaches
with offus_txn as
(SELECT globalcardindex, transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp, paymethod
, case when edc_mid is not null then 'EDC' else 'QR' end as mid_type
, isindian, txn_status
FROM
    (SELECT DISTINCT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (select distinct transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , globalcardindex
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    , paymethod
    , isindian
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN DATE(DATE'2025-05-01' - INTERVAL '1' DAY) AND DATE'2025-05-31'
    and paymethod in ('CREDIT_CARD','DEBIT_CARD')
    AND actionrecommended <> 'BLOCK' AND responsestatus = 'SUCCESS') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT DISTINCT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b
ON a.paytmmerchantid = b.edc_mid
INNER JOIN
    (select distinct txn_id as pg_txn_id, txn_status
    from dwh.pg_olap
    where ingest_date BETWEEN DATE'2025-05-01' AND DATE(DATE'2025-05-31' + INTERVAL '1' DAY)
    and txn_started_at BETWEEN  DATE'2025-05-01' AND DATE(DATE'2025-05-31' + INTERVAL '1' DAY)) d
on a.transactionid = d.pg_txn_id)

SELECT * FROM
    (SELECT a.globalcardindex, A.transactionid, A.txn_amount, A.txn_date, A.paytmmerchantid, A.txn_timestamp
    , A.mid_type, A.paymethod
    , COUNT(IF((A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 3600000, B.transactionid, NULL)) AS txn1_hr
    , 3 txn1_hr_threshold

    , COUNT(IF((A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 21600000, B.transactionid, NULL)) AS txn6_hr
    , 3 txn6_hr_threshold

    , COUNT(IF((A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 122400000, B.transactionid, NULL)) AS txn24_hr
    , 5 AS txn24_hr_threshold

    , 'ICA_Bank_Decline_Threshold_Block' AS rule_name
    FROM
        (SELECT * FROM offus_txn
        WHERE txn_date BETWEEN DATE'2025-05-01' AND  DATE'2025-05-31'
        AND isindian = 'false')A
    INNER JOIN
        (SELECT * FROM offus_txn
        WHERE txn_status = 'CLOSED')B
    ON A.globalcardindex = b.globalcardindex AND A.paytmmerchantid = B.paytmmerchantid
    AND A.transactionid <> B.transactionid AND A.txn_timestamp > B.txn_timestamp
    GROUP BY 1,2,3,4,5,6,7,8)
WHERE (txn1_hr >= txn1_hr_threshold) OR 
(txn6_hr >= txn6_hr_threshold AND txn_amount > 10000) OR
(txn24_hr >= txn24_hr_threshold) 
;
-- ICA_PerCard_PerMID_TXN_Limit
-- DROP TABLE team_kingkong.offus_ICA_PerCard_PerMID_TXN_Limit_breaches;
 
CREATE TABLE team_kingkong.offus_ICA_PerCard_PerMID_TXN_Limit_breaches AS
-- INSERT INTO team_kingkong.offus_ICA_PerCard_PerMID_TXN_Limit_breaches
with offus_txn as
(SELECT globalcardindex, transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp, paymethod
, case when edc_mid is not null then 'EDC' else 'QR' end as mid_type
, mcc, isindian, isedcrequest
, CASE WHEN mcc IN (5411, 5812, 9399, 8211, 7999, 7011, 5813, 4511, 8071, 8062) THEN 1 ELSE 0 END AS non_risky_high_mcc
, CASE WHEN mcc IN (5962, 7273, 7995, 5122, 6051, 6012, 5993, 5968, 5966, 5912, 6211, 5816, 4816, 5967) THEN 1 ELSE 0 END AS risky_mcc
FROM
    (SELECT DISTINCT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (select distinct transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , globalcardindex
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    , paymethod
    , isindian
    , isedcrequest
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN DATE(DATE'2025-01-01' - INTERVAL '30' DAY) AND DATE'2025-01-31' -- BETWEEN date'2025-01-31' AND
    and paymethod in ('CREDIT_CARD','DEBIT_CARD')
    AND actionrecommended <> 'BLOCK' AND responsestatus = 'SUCCESS') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT DISTINCT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b
ON a.paytmmerchantid = b.edc_mid
INNER JOIN
    (select distinct txn_id as pg_txn_id, mcc
    from dwh.pg_olap
    where ingest_date BETWEEN DATE'2025-01-01' AND DATE(DATE'2025-01-31' + INTERVAL '1' DAY)
    and txn_started_at BETWEEN  DATE'2025-01-01' AND DATE(DATE'2025-01-31' + INTERVAL '1' DAY)
    and txn_status = 'SUCCESS') d
on a.transactionid = d.pg_txn_id)
 
SELECT * FROM
    (SELECT a.globalcardindex, A.transactionid, A.txn_amount, A.txn_date, A.paytmmerchantid, A.txn_timestamp
    , A.mid_type, A.paymethod, A.mcc, A.non_risky_high_mcc, A.risky_mcc
    , COUNT(IF((A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 60000, B.transactionid, NULL)) AS txn1_min
    , CASE WHEN A.risky_mcc > 0 THEN 1
    WHEN A.non_risky_high_mcc > 0 THEN 1
    WHEN A.risky_mcc = 0 AND A.non_risky_high_mcc = 0 THEN 1
    END AS txn1_min_threshold
    , COUNT(IF((A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 86400000, B.transactionid, NULL)) AS txn1_day
    , CASE WHEN A.risky_mcc > 0 THEN 3
    WHEN A.non_risky_high_mcc > 0 THEN 5
    WHEN A.risky_mcc = 0 AND A.non_risky_high_mcc = 0 THEN 3
    END AS txn1_day_threshold
    , COUNT(IF((A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 604800000, B.transactionid, NULL)) AS txn7_day
    , CASE WHEN A.risky_mcc > 0 THEN 5
    WHEN A.non_risky_high_mcc > 0 THEN 10
    WHEN A.risky_mcc = 0 AND A.non_risky_high_mcc = 0 THEN 5
    END AS txn7_day_threshold
    , COUNT(IF((A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 2592000000, B.transactionid, NULL)) AS txn30_day
    , CASE WHEN A.risky_mcc > 0 THEN 15
    WHEN A.non_risky_high_mcc > 0 THEN 25
    WHEN A.risky_mcc = 0 AND A.non_risky_high_mcc = 0 THEN 15 
    END AS txn30_day_threshold
 
    , 'ICA_PerCard_PerMID_TXN_Limit' AS rule_name
    FROM
        (SELECT * FROM offus_txn
        WHERE txn_date BETWEEN DATE'2025-01-01' AND  DATE'2025-01-31'
        AND isindian = 'false' AND isedcrequest = 'true')A
    INNER JOIN
        (SELECT * FROM offus_txn)B
    ON A.globalcardindex = b.globalcardindex AND A.paytmmerchantid = B.paytmmerchantid
    AND A.transactionid <> B.transactionid
    AND A.txn_timestamp > B.txn_timestamp
    GROUP BY 1,2,3,4,5,6,7,8,9,10,11)
WHERE (txn1_min >= txn1_min_threshold) OR 
(txn1_day >= txn1_day_threshold) OR
(txn7_day >= txn7_day_threshold) OR
(txn30_day >= txn30_day_threshold)
-- ICA_OddTime_PerCard_PerMID_EDC
CREATE TABLE team_kingkong.offus_ICA_OddTime_PerCard_PerMID_EDC_breaches AS
-- INSERT INTO team_kingkong.offus_ICA_OddTime_PerCard_PerMID_EDC_breaches
with offus_txn as
(SELECT transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp, globalcardindex
, case when edc_mid is not null then 'EDC' else 'QR' end as mid_type
, merchantcategory, merchantsubcategory, isindian FROM
    (SELECT DISTINCT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (select distinct transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    , globalcardindex
    , merchantcategory, merchantsubcategory, isindian
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN DATE(DATE'2025-02-01' - INTERVAL '1' DAY) AND DATE'2025-02-28'
    and paymethod in ('CREDIT_CARD','DEBIT_CARD')
    AND actionrecommended <> 'BLOCK') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT DISTINCT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b
ON a.paytmmerchantid = b.edc_mid)

SELECT * FROM
    (SELECT A.transactionid, A.txn_amount, A.txn_date, A.paytmmerchantid, A.globalcardindex, A.mid_type, 'ICA_OddTime_PerCard_PerMID_EDC' AS rule_name, A.txn_timestamp
    , 5000 as per_txn_limit
    , COUNT(B.transactionid) as txn1_day
    , 2 as txn1_day_threshold
    FROM
        (SELECT * FROM offus_txn
        WHERE txn_date BETWEEN date '2025-02-01' AND DATE'2025-02-28'
        AND isindian = 'false'
        AND HOUR(FROM_UNIXTIME(txn_timestamp / 1000)) BETWEEN 0 AND 4
        AND (merchantsubcategory NOT IN ('Restaurant', 'Foodcourt','Restaurants and Bars', 'Fast Food and QSR' , 'Hotel', 'Aviation','Tours and Travel Agency' , 'Pharmacy', 'Hospital','Taxi','Pharmacy', 'Hospital', 'Taxi')
        OR merchantcategory NOT IN ('Airport','Gas and Petrol')))A
    LEFT JOIN
        (SELECT * FROM offus_txn)B
    ON A.globalcardindex = B.globalcardindex AND A.paytmmerchantid = B.paytmmerchantid AND (A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 86400000 -- <= 1day
    AND A.transactionid <> B.transactionid
    GROUP BY 1,2,3,4,5,6,7,8)
WHERE (txn_amount > per_txn_limit) OR (txn1_day>= txn1_day_threshold)
-- LIMIT 100
;
-- Merchant_PerTxnLimit_Check
-- CREATE TABLE team_kingkong.offus_Merchant_PerTxnLimit_Check_breaches AS
INSERT INTO team_kingkong.offus_Merchant_PerTxnLimit_Check_breaches
SELECT transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp, paymethod
, case when edc_mid is not null then 'EDC' else 'QR' end as mid_type
, C.per_txn_limit
, C.limit_date
FROM
    (SELECT DISTINCT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (select distinct transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , globalcardindex
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    , paymethod
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN DATE(DATE'2025-05-01' - INTERVAL '1' DAY) AND DATE'2025-05-31'
    and paymethod in ('UPI')
    AND actionrecommended <> 'BLOCK' AND responsestatus = 'SUCCESS') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT DISTINCT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b
ON a.paytmmerchantid = b.edc_mid
INNER JOIN
    (SELECT content as mid, CAST(comment AS DOUBLE) as per_txn_limit, "timestamp" as limit_date
    FROM team_kingkong.merchant_limit_list)C
ON a.paytmmerchantid = C.mid AND a.txn_date > DATE(FROM_UNIXTIME(CAST(limit_date AS double) / 1000))
WHERE a.txn_amount > C.per_txn_limit;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class CombatActionUI : MonoBehaviour
{
    [SerializeField] private GameObject visualContainer;
    [SerializeField] private Button[] combatActionButtons;

    void OnEnable ()
    {
        TurnManager.Instance.OnBeginTurn += OnBeginTurn;
        TurnManager.Instance.OnEndTurn += OnEndTurn;
    }

    void OnDisable ()
    {
        TurnManager.Instance.OnBeginTurn -= OnBeginTurn;
        TurnManager.Instance.OnEndTurn -= OnEndTurn;
    }

    void OnBeginTurn (Character character)
    {
        if(!character.IsPlayer)
            return;

        visualContainer.SetActive(true);

        for(int i = 0; i < combatActionButtons.Length; i++)
        {
            if(i < character.CombatActions.Count)
            {
                combatActionButtons[i].gameObject.SetActive(true);
                CombatAction ca = character.CombatActions[i];

                combatActionButtons[i].GetComponentInChildren<TextMeshProUGUI>().text = ca.DisplayName;
                combatActionButtons[i].onClick.RemoveAllListeners();
                combatActionButtons[i].onClick.AddListener(() => OnClickCombatAction(ca));
            }
            else
            {
                combatActionButtons[i].gameObject.SetActive(false);
            }
        }
    }

    void OnEndTurn (Character character)
    {
        visualContainer.SetActive(false);
    }

    public void OnClickCombatAction (CombatAction combatAction)
    {
        TurnManager.Instance.CurrentCharacter.CastCombatAction(combatAction);
    }
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: Xero Boost Days! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Canberra! Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-21: Wednesday, 21st July",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:Lunch: *Lunch*: Provided in our suite from *12pm*."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19jYzU3YWJkZTE4ZTE0YzVlYTYxMGU4OThjZjRhYWQ0MTNhYmIzMDBjZjBkMzVlNDg0M2M5NDQ4NDk3NDAyYjkyQGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20|*Canberra Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: Xero Boost Days! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Canberra! Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-14: Wednesday, 14th July",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:Lunch: *Lunch*: Provided in our suite from *12pm*."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19jYzU3YWJkZTE4ZTE0YzVlYTYxMGU4OThjZjRhYWQ0MTNhYmIzMDBjZjBkMzVlNDg0M2M5NDQ4NDk3NDAyYjkyQGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20|*Canberra Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: Xero Boost Days! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Canberra! Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-7: Wednesday, 7th July",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:Lunch: *Lunch*: Provided in our suite from *12pm*."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19jYzU3YWJkZTE4ZTE0YzVlYTYxMGU4OThjZjRhYWQ0MTNhYmIzMDBjZjBkMzVlNDg0M2M5NDQ4NDk3NDAyYjkyQGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20|*Canberra Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: Xero Boost Days! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Canberra! Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-30: Wednesday, 30th June",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:Lunch: *Lunch*: Provided in our suite from *12pm*."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19jYzU3YWJkZTE4ZTE0YzVlYTYxMGU4OThjZjRhYWQ0MTNhYmIzMDBjZjBkMzVlNDg0M2M5NDQ4NDk3NDAyYjkyQGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20|*Canberra Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: Xero Boost Days! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Canberra! Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-23: Wednesday, 23rd June",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:Lunch: *Lunch*: Provided in our suite from *12pm*."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19jYzU3YWJkZTE4ZTE0YzVlYTYxMGU4OThjZjRhYWQ0MTNhYmIzMDBjZjBkMzVlNDg0M2M5NDQ4NDk3NDAyYjkyQGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20|*Canberra Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: Xero Boost Days! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Canberra! Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-16: Wednesday, 16th June",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:Lunch: *Lunch*: Provided in our suite from *12pm*."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19jYzU3YWJkZTE4ZTE0YzVlYTYxMGU4OThjZjRhYWQ0MTNhYmIzMDBjZjBkMzVlNDg0M2M5NDQ4NDk3NDAyYjkyQGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20|*Canberra Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: Xero Boost Days! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Canberra! Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-9: Wednesday, 9th June",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:Lunch: *Lunch*: Provided in our suite from *12pm*."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19jYzU3YWJkZTE4ZTE0YzVlYTYxMGU4OThjZjRhYWQ0MTNhYmIzMDBjZjBkMzVlNDg0M2M5NDQ4NDk3NDAyYjkyQGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20|*Canberra Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: Xero Boost Days! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Canberra! Please see below for what's on this week! "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-2: Wednesday, 2nd June",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:Lunch: *Lunch*: Provided in our suite from *12pm*."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19jYzU3YWJkZTE4ZTE0YzVlYTYxMGU4OThjZjRhYWQ0MTNhYmIzMDBjZjBkMzVlNDg0M2M5NDQ4NDk3NDAyYjkyQGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20|*Canberra Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash
filebrowser -r /path/to/your/files
sudo add-apt-repository ppa:christian-boxdoerfer/fsearch-stable
sudo apt update
        
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":aboriginal_flag: Xero Boost Days - What's On :aboriginal_flag:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Good Morning Warrane, and welcome to Reconciliation Week!\n\n *Reconciliation Week (27 May – 3 June)* is a time for all Australians to learn about our shared histories, cultures, and achievements, and to explore how we can contribute to reconciliation in Australia. \n\n For Aboriginal and Torres Strait Islander peoples, it’s a powerful reminder of ongoing resilience, the importance of truth-telling, and the work still needed to achieve genuine equity and justice."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-28: Wednesday, 28th May",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:coffee: *Café Partnership*: Enjoy free coffee and café-style beverages from our Cafe partner *Naked Duck*.\n:breakfast: *Reconciliation breakfast*: from *9am* in the kitchen.\n\n :microphone: We will also have a video of our very own *Tony Davison* talking on behalf of the #reconciliation-au ERG around Xero's Reconciliation plan and what this week means! \n\n :slack: In the thread we will also have a special background for you to use on Google Meet if you would like to participate."
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-29: Thursday, 29th May",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":coffee: *Café Partnership*: Enjoy free coffee and café-style beverages from our Cafe partner *Naked Duck*.\n:lunch: *Lunch* provided by Naked Duck from *9pm* in the kitchen!."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y185aW90ZWV0cXBiMGZwMnJ0YmtrOXM2cGFiZ0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t|*Sydney Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":national-reconciliation-week-2025-logo: Boost Days - What's On This Week :national-reconciliation-week-2025-logo:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n\n Good morning Melbourne, and Welcome to Reconciliation Week. *Reconciliation Week - (27th May- 3rd June)* is a time for Australians to learn about our shared histories, cultures, and achievements, and to explore how we can contribute to the Reconciliation in Australia.\n\n \n\n For Aboriginal and Torres Strait Islander peoples, it's a powerful reminder of ongoing resilience, the importance of truth-telling, and the work still needed to achieve genuine equity and justice. "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Xero Café :coffee:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n :new-thing: *This week we are offering some delicious sweet treats from Indigeous supplier Jarrah Catering:* \n\n :biscuit: Gourmet Biscuits :lamington: Lamingtons \n\n :coffee: *Weekly Café Special:* _Honey Cinammon Latte_"
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": " Wednesday, 28 May :calendar-date-28:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " \n\n :lunch: *Light Lunch*: Inspired by Australian native ingredients. Lunch is from *12pm* in the L3 Kitchen & Wominjeka Breakout Space. Menu is in the:thread:\n \n :national-reconciliation-week-2025-logo::microphone: *12.30pm- Wominjeka Breakout Space*: As part of National Reconciliation week, Matt Jolly and Ash will share a presentation from Xero's RAP consultant *Rhys Paddock*. Rhys will talk about the importance of Reconciliation and how we can contribute to a more inclusive and respectful culture."
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Thursday, 29 May :calendar-date-29:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":pancakes: *Breakfast*: from *8:30am-10:30am* in the Wominjeka Breakout Space.   \n\n   \n\n "
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Friday, 30 May :calendar-date-30:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "*Social Happy Hour:* Celebrating the Hackathon results from 4pm-5.30pm. On the menu, we have the most delicious Paella from - *Simply Spanish* and a Friday FIESTA playlist that is sure to give you that *FRIDAY FEELING* :dancer::shallow_pan_of_food::spain:  "
			}
		},
		{
			"type": "divider"
		}
	]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Swiper Carousel with Circular Titles + Tabs + Progress Bar</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.css"/>

<style>

body {
font-family: "Manrope", Sans-serif;
padding: 30px;
background: #00000000;
}

.custom-home-slider .title-bar-wrapper {
width: 100%;
margin: 0 auto 30px;
position: relative;
height: 50px;
user-select: none;
}

.custom-home-slider .slide-titles {
position: relative;
height: 100%;
}

.custom-home-slider .slide-title-tab {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-weight: 700;
font-size: 45px;
color: #999;
cursor: pointer;
opacity: 0.5;
pointer-events: auto;
transition: all 0.3s ease;
white-space: nowrap;
user-select: none;
line-height: 1.2;
}

.custom-home-slider .slide-title-tab.left {
left: 0;
transform: translateY(-50%) translateX(0);
opacity: 0.6;
color: #666;
}

.custom-home-slider .slide-title-tab.center {
left: 50%;
transform: translate(-50%, -50%);
opacity: 1;
pointer-events: none;
white-space: normal; /* allow line break */
text-align: center;

}

.custom-home-slider .slide-title-tab.right {
right: 0;
transform: translateY(-50%) translateX(0);
opacity: 0.6;
color: #666;
}

.custom-home-slider .slide-title-tab.hidden {
opacity: 0;
pointer-events: none;
}

/* Swiper container */
.custom-home-slider .swiper {
width: 100%;
height: 520px;
margin: 0 auto;
padding: 20px 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
}

.custom-home-slider .swiper-slide {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
overflow: hidden;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}

/* Tabs inside each slide */
.custom-home-slider .tabs {
display: flex;
justify-content: center;
gap: 12px;
margin-bottom: 12px;
user-select: none;
}

.custom-home-slider .tab {
display: flex;
justify-content: center;
gap: 12px;
margin-bottom: 12px;
color: #fff;
padding: 8px 15px;
margin: 5px;
cursor: pointer;
background: unset;
border-radius: 54px;
text-transform: uppercase;
font-size: 18px;
}

.custom-home-slider .design .tab {
border: 1px solid #E36F30;
}

.custom-home-slider .design .tab.active {
background: linear-gradient(90deg, #E36F30 0%, #FBC32A 100%);
}

.custom-home-slider .design .tab:hover {
background: linear-gradient(90deg, #E36F30 0%, #FBC32A 100%);
}

.custom-home-slider .inno .tab {
border: 1px solid #d82afb;
}

.custom-home-slider .inno .tab:hover {
background: linear-gradient(90deg, #30BDE5 0%, #d82afb 100%);
}

.custom-home-slider .inno .tab.active {
background: linear-gradient(90deg, #30BDE5 0%, #d82afb 100%);
}

.custom-home-slider .marketing .tab {
border: 1px solid #2AFBED;
}

.custom-home-slider .marketing .tab:hover {
background: linear-gradient(90deg, #30E381 0%, #2AFBED 100%);
}

.custom-home-slider .marketing .tab.active {
background: linear-gradient(90deg, #30E381 0%, #2AFBED 100%);
}

.custom-home-slider .tab-content {
border-radius: 30px;
padding: 14px;
background: #3A3A3A;
height: 420px;
box-sizing: border-box;
display: flex;
}

/* Arrows */
.custom-home-slider .swiper-button-prev, .custom-home-slider .swiper-button-next {
color: #fff;
top: 50%;
}

.custom-home-slider .swiper-slide-active {
opacity: 1;
pointer-events: auto;
}

.custom-home-slider .swiper-button-prev {
left: 35%;
}

.custom-home-slider .swiper-button-next {
right: 35%;
}

/* Progress bar container */
.custom-home-slider .progress-container {
width: 100%;
height: 5px;
background: #D9D9D9;
border-radius: 3px;
margin: 50px auto 0;
overflow: hidden;
}

/* Progress bar fill */
.custom-home-slider .progress-bar {
height: 100%;
width: 0%;
background: linear-gradient(to right, #00c6ff, #9c27b0);
transition: width 0.4s ease;
}

.custom-home-slider .two-column {
display: flex;  
gap: 20px;           
align-items: center;
}

.custom-home-slider .left-column img {
width: 90%;
height: auto;
object-fit: cover;
}

.custom-home-slider .right-column {
flex: 1 1 70%;  
text-align: left;
display: flex;
flex-direction: column;
}

.custom-home-slider .icon {
margin-top: 12px;
width: 32px;
height: 32px;
}

.custom-home-slider .left-column {
flex: 1 1 20%;
text-align: center;
}

.custom-home-slider h2.grad-title {
font-weight: 800;
font-size: 50px;
margin: 0 0 10px 0;
background: linear-gradient(90deg, #FFFFFF, #767585);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}

.custom-home-slider p {
margin-top:0;    
font-size: 16px;
line-height: 1.5;
color: #A9A9A9;
font-weight: 300;
width: 55%;
}

.custom-home-slider .slide-title-tab.inn-title.center {
background: linear-gradient(90deg, #00c6ff, #9c27b0);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}

.custom-home-slider .slide-title-tab.marketing-title.center {
background: linear-gradient(90deg, #30E381, #2AFBED);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}

.custom-home-slider .slide-title-tab.design-title.center {
background: linear-gradient(90deg, #E36F30, #FBC32A);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}

.custom-home-slider .right-column a img:hover {
transform: rotate(45deg) scale(0.8);
transition: transform 0.5s ease;
}

.custom-home-slider .right-column a img {
transition: .3s ease all;
}

.custom-home-slider .swiper-button-prev:after, .custom-home-slider .swiper-button-next:after{
display:none;
}

.custom-home-slider .swiper-button-next img {
width: 25px;
}

.custom-home-slider .swiper-button-next {
border: none;
z-index: 1;
cursor: pointer;
width: 50px;
height: 50px;
border-radius: 100px;
background-color: #ffffff1a;
}

.custom-home-slider .swiper-button-prev img {
width: 25px;
}

.custom-home-slider .swiper-button-prev {
border: none;
z-index: 1;
cursor: pointer;
width: 50px;
height: 50px;
border-radius: 100px;
background-color: #ffffff1a;
}

.custom-home-slider .swiper-button-next:hover,  .swiper-button-prev:hover{
background-color: #444;
}

.custom-home-slider .right-column a {
width: fit-content;
}

.custom-home-slider .slide-title-tab.center:before {
content: "DIGITAL";
display: block !important;
}

/* MEDAI QUERY */

@media screen and (max-width: 1600px) {

.custom-home-slider .left-column img {
width: 100%;
}

}

@media screen and (max-width: 1440px) {

.custom-home-slider p {
width: 65%;
}

.custom-home-slider .swiper-button-next {
right: 30%;
}

.custom-home-slider .swiper-button-prev {
left: 30%;
}

.custom-home-slider .right-column a img {
width: 50px;
}

.custom-home-slider .tab-content
{
height: 370px;
}

}

@media screen and (max-width: 1366px) {

.custom-home-slider h2.grad-title {
font-size: 40px;
}

}

@media screen and (max-width: 1280px) {

.custom-home-slider .slide-title-tab {
font-size: 40px;
}
}

@media screen and (max-width: 1024px) {

.custom-home-slider .slide-title-tab {
font-size: 35px;
}

.custom-home-slider .tab {
font-size: 14px;
}

.custom-home-slider p {
width: 95%;
}

.custom-home-slider h2.grad-title {
font-size: 35px;
}

.custom-home-slider .tabs {gap: 10px;}

.custom-home-slider .tab {
padding: 8px 5px;
}

.custom-home-slider .tab-content {
height: 340px;
margin-top: 10px;
}

.custom-home-slider .right-column a img {
    width: 40px;
}

}

@media screen and (max-width: 820px) {

.custom-home-slider .slide-title-tab {
font-size: 30px;
}

.custom-home-slider .swiper-button-next, .custom-home-slider .swiper-button-prev {
width: 40px;
height: 40px;
}

.custom-home-slider .progress-container {
margin: 30px auto 0;

}

.custom-home-slider .tab {
font-size: 12px;
padding: 8px 7px;

}

.custom-home-slider h2.grad-title {
font-size: 30px;
}
}

@media screen and (max-width: 768px) {

.custom-home-slider .slide-title-tab {
font-size: 25px;
}

.custom-home-slider .tab {
font-size: 12px;
padding: 8px 4px;
}

}

@media screen and (max-width: 767px) {

.custom-home-slider .slide-title-tab.left, .custom-home-slider .slide-title-tab.right {
display: none;
}

.custom-home-slider .swiper-button-next {
right: 0;
}

.custom-home-slider .swiper-button-prev {
left: 0;
}

html, body{
padding: 0;

}

.custom-home-slider .two-column {
display: flex;
flex-direction: column;
height: auto;
gap: 0px;
}

.custom-home-slider .tab-content {
height: auto;
}

.custom-home-slider .swiper-slide {
height: auto;
}

.custom-home-slider .swiper.swiper-initialized.swiper-horizontal.swiper-ios.swiper-backface-hidden {
height: auto;
}

.custom-home-slider .swiper-wrapper {
height: auto;
}

.custom-home-slider .tabs {
display: flex;
flex-direction: column;
gap:5px;
}

.custom-home-slider .tab {
font-size: 14px;
padding: 12px 24px;
}

}

</style>
</head>

<body>

<div class="custom-home-slider">

<div class="title-bar-wrapper">
<div class="swiper-button-prev">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/left-arrow.png">
</div>
<div class="swiper-button-next">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/right-arrow.png">
</div>
<div class="slide-titles">
<div class="slide-title-tab inn-title" data-slide="0">INNOVATION</div>
<div class="slide-title-tab design-title" data-slide="1">DESIGN</div>
<div class="slide-title-tab marketing-title" data-slide="2">MARKETING</div>
</div>
</div>

<!-- Progress bar container -->
<div class="progress-container">
<div class="progress-bar"></div>
</div>

<div class="swiper">
<div class="swiper-wrapper">
<!-- Slide 1 INNOVATION -->
<div class="swiper-slide">
<div class="tabs-wrapper">
<div class="tabs inno">
<div class="tab active" data-tab="1">Mobile App</div>
<div class="tab" data-tab="2">Game App</div>
<div class="tab" data-tab="3">Web App</div>
<div class="tab" data-tab="4">MVP Startup</div>
<div class="tab" data-tab="5">AR</div>
<div class="tab" data-tab="6">VR</div>
<div class="tab" data-tab="7">E-Commerce</div>
<div class="tab" data-tab="8">Startup</div>
</div>
</div>

<div class="tab-content" data-content="1">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/mobile-group-1.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">MOBILE APP DEVELOPMENT</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="2" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/img-2.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">GAME APP DEVELOPMENT</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="3" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/img-3.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">WEB APPLICATION</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="4" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/img-4.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">MVP STARTUP DEVELOPMENT</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="5" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/mobile-group-1.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">AR DEVELOPMENT</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="6" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/img-6.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">VR DEVELOPMENT</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="7" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/img-7.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">ECOMMERCE WEBSITE</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="8" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/img-8.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">STARTUP DEVELOPMENT</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

</div>

<!-- Slide 2 DESIGN -->
<div class="swiper-slide">
<div class="tabs design">
<div class="tab active" data-tab="1">WEB DESIGN</div>
<div class="tab" data-tab="2">LOGO DESIGN</div>
<div class="tab" data-tab="3">2D-3D Illustration</div>
<div class="tab" data-tab="4">MOTION GRAPHICS</div>

</div>

<div class="tab-content" data-content="1">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/mobile-group-1.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">WEB DESIGN</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="2" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/mobile-group-1.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">LOGO DESIGN</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="3" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/mobile-group-1.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">2D-3D ILLUSTRATION</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="4" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/mobile-group-1.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">MOTION GRAPHICS</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

</div>

<!-- Slide 3 MARKETING -->
<div class="swiper-slide">
<div class="tabs marketing">
<div class="tab active" data-tab="1">SOCIAL MEDIA</div>
<div class="tab" data-tab="2">SEO</div>
</div>
<div class="tab-content" data-content="1">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/mobile-group-1.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">SOCIAL MEDIA MARKETING</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>

<div class="tab-content" data-content="2" style="display:none;">
<div class="two-column">
<div class="left-column">
<img src="https://dev.icreativesol.com/wp-content/uploads/2025/04/mobile-group-1.webp" alt="Sample Image" />
</div>
<div class="right-column">
<h2 class="grad-title">SEARCH ENGINE OPTIMIZATION</h2>
<p>The upsurge of the mobile game app industry is generating high revenues by capturing a large-scale market of game players. Building a game app that provides financial aid requires high-profile game app developers who help you discover unwavering strategies for game design and development that not only exist for entertainment but also generate hefty revenues for investors.</p>
<a href="https://www.your-link.com" target="_blank">
<img class="anim-arrow-icon" src="https://dev.icreativesol.com/wp-content/uploads/2025/04/white-btn.svg" alt="Mobile App Development" style="max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

<script src="https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.js"></script>

<script>
// Initialize Swiper
const swiper = new Swiper(".custom-home-slider .swiper", {
speed: 600,
slidesPerView: 1,
spaceBetween: 30,
navigation: {
nextEl: ".custom-home-slider .swiper-button-next",
prevEl: ".custom-home-slider .swiper-button-prev",
},
on: {
slideChange: () => {
  updateTitleTabs(swiper.activeIndex);
  updateProgressBar(swiper.activeIndex);
  resetInnerTabs(swiper.activeIndex);
},
},
});

// Elements references
const titleTabs = document.querySelectorAll(".custom-home-slider .slide-title-tab");
const progressBar = document.querySelector(".custom-home-slider .progress-bar");
const swiperSlides = document.querySelectorAll(".custom-home-slider .swiper-slide");

// Update big slide title tabs (left, center, right, hidden)
function updateTitleTabs(activeIndex) {
const total = titleTabs.length;

titleTabs.forEach((tab, idx) => {
tab.classList.remove("left", "center", "right", "hidden");

if (idx === activeIndex) {
  tab.classList.add("center");
} else if (
  (activeIndex === 0 && idx === 1) || // Innovation center => Design left
  (activeIndex === 1 && idx === 0) || // Design center => Innovation left
  (activeIndex === 2 && idx === 1)    // Marketing center => Design left
) {
  tab.classList.add("left");
} else if (
  (activeIndex === 0 && idx === 2) || // Innovation center => Marketing right
  (activeIndex === 1 && idx === 2) || // Design center => Marketing right
  (activeIndex === 2 && idx === 0)    // Marketing center => Innovation right
) {
  tab.classList.add("right");
} else {
  tab.classList.add("hidden");
}
});
}

// Initially call it
updateTitleTabs(swiper.activeIndex);

// Clicking big slide title tabs moves swiper slide
titleTabs.forEach((tab, index) => {
tab.addEventListener("click", () => {
swiper.slideTo(index);
});
});

// Update progress bar width according to slide index
function updateProgressBar(activeIndex) {
const totalSlides = swiperSlides.length;
const progressPercent = ((activeIndex + 1) / totalSlides) * 100;
progressBar.style.width = progressPercent + "%";
}
// Initial progress bar
updateProgressBar(swiper.activeIndex);

// Handle inner tabs switching inside each slide
swiperSlides.forEach((slide) => {
const tabs = slide.querySelectorAll(".tab");
const contents = slide.querySelectorAll(".tab-content");

tabs.forEach((tab) => {
tab.addEventListener("click", () => {
  // Remove active from all tabs in this slide
  tabs.forEach((t) => t.classList.remove("active"));
  // Hide all content in this slide
  contents.forEach((content) => (content.style.display = "none"));

  // Activate clicked tab
  tab.classList.add("active");
  // Show related content
  const dataTab = tab.getAttribute("data-tab");
  const contentToShow = slide.querySelector(`.tab-content[data-content="${dataTab}"]`);
  if (contentToShow) contentToShow.style.display = "flex";
});
});
});

// When slide changes, reset inner tabs to first tab active + content visible
function resetInnerTabs(activeIndex) {
swiperSlides.forEach((slide, idx) => {
const tabs = slide.querySelectorAll(".tab");
const contents = slide.querySelectorAll(".tab-content");

if (idx === activeIndex) {
  // Show first tab and content in active slide
  tabs.forEach((tab, i) => {
    tab.classList.toggle("active", i === 0);
  });
  contents.forEach((content, i) => {
    content.style.display = i === 0 ? "flex" : "none";
  });
} else {
  // Hide all contents in inactive slides
  contents.forEach((content) => (content.style.display = "none"));
  tabs.forEach((tab) => tab.classList.remove("active"));
}
});
}
// Reset inner tabs on load
resetInnerTabs(swiper.activeIndex);


</script>
</body>
</html>
<?php
function solutionsliderloop() {
    $arg = array(
        'post_type' => 'solution',
        'posts_per_page' => 8,
        'order' => 'Asc',
    );
    $solutionPost = new WP_Query($arg);
    if ($solutionPost->have_posts()): ?>
<div class="solution-swiper-wrapper">
    <div class="swiper solutionswiper">
        <div class="swiper-wrapper">
            <?php while ($solutionPost->have_posts()): $solutionPost->the_post(); ?>
            <div class="swiper-slide <?php the_field('class'); ?>">
                <div class="solutionswiper-inner">
                    <div class="swiper-number">
                        <h3><?php the_title(); ?></h3>
                    </div>
                    <div class="swiper-content">
                        <?php the_content(); ?>
                    </div>
                </div>
            </div>
            <?php endwhile; ?>
        </div>
        <img src="<?php echo get_stylesheet_directory_uri(); ?>/img/solutioncircle.svg" alt="" class="circle-img">
        <img src="<?php echo get_stylesheet_directory_uri(); ?>/img/solutiondrone.svg" alt=""
            class="rotatedroneimg-img">
    </div>
    <div class="swiper-button-next">
        <img src="<?php echo get_stylesheet_directory_uri(); ?>/img/solutionright.svg" alt="">
    </div>
    <div class="swiper-button-prev">
        <img src="<?php echo get_stylesheet_directory_uri(); ?>/img/solutionleft.svg" alt="">
    </div>
</div>
<?php endif;
    wp_reset_postdata();
}
add_shortcode('solutionslider'








var mySwiper = new Swiper('.solutionswiper', {
  loop: true,
  slidesPerView: 1.7,
  navigation: {
    nextEl: '.swiper-button-next',
    prevEl: '.swiper-button-prev',
  },
  breakpoints: {
    10: {
      slidesPerView: 1,
    },
    620: {
      slidesPerView: 1.7,
    },
  },
  on: {
    slideChange: function () {
      let currentIndex = mySwiper.realIndex;
      const droneImage = document.querySelector(".rotatedroneimg-img");
      const circleImage = document.querySelector(".circle-img");
      const getResponsiveValues = () => {
        if (window.innerWidth <= 420) {
          return {
            drone: [
              { x: 135, y: -37 },   // Slide 1
              { x: 296, y: 104 },  // Slide 2
              { x: -66, y: 310 },  // Slide 3
              { x: -70, y: 90 }   // Slide 4
            ],
            circle: [
              { x: 0, y: -3, scale: 1 },    // Slide 1
              { x: 4, y: 0, scale: 1 }, // Slide 2
              { x: 4, y: -3, scale: 1 },// Slide 3
              { x: 6, y: -1, scale: 1 } // Slide 4
            ]
          };
        }
        else if (window.innerWidth <= 480) {
          return {
            drone: [
              { x: 111, y: -49 },   // Slide 1
              { x: 280, y: 104 },  // Slide 2
              { x: -66, y: 310 },  // Slide 3
              { x: -70, y: 90 }   // Slide 4
            ],
            circle: [
              { x: 0, y: -3, scale: 1 },    // Slide 1
              { x: 6, y: 0, scale: 1 }, // Slide 2
              { x: 4, y: -3, scale: 1 },// Slide 3
              { x: 6, y: -1, scale: 1 } // Slide 4
            ]
          };
        }
        else if (window.innerWidth <= 620) {
          return {
            drone: [
              { x: 111, y: -49 },   // Slide 1
              { x: 280, y: 104 },  // Slide 2
              { x: -66, y: 310 },  // Slide 3
              { x: -70, y: 90 }   // Slide 4
            ],
            circle: [
              { x: 0, y: -3, scale: 1 },    // Slide 1
              { x: 6, y: 0, scale: 1 }, // Slide 2
              { x: 4, y: -3, scale: 1 },// Slide 3
              { x: 6, y: -1, scale: 1 } // Slide 4
            ]
          };
        }
         else if (window.innerWidth <= 767) {
          return {
            drone: [
              { x: 114, y: 40 },   // Slide 1
              { x: 239, y: 175 },  // Slide 2
              { x: -75, y: 350 },  // Slide 3
              { x: -52, y: 158 }   // Slide 4
            ],
            circle: [
              { x: 0, y: -3, scale: 1 },    // Slide 1
              { x: 12, y: 0, scale: 1 }, // Slide 2
              { x: 14, y: -3, scale: 1 },// Slide 3
              { x: 16, y: -5, scale: 1 } // Slide 4
            ]
          };
        }
        else if (window.innerWidth <= 1024) {
          return {
            drone: [
              { x: 114, y: 40 },   // Slide 1
              { x: 239, y: 175 },  // Slide 2
              { x: -75, y: 350 },  // Slide 3
              { x: -52, y: 158 }   // Slide 4
            ],
            circle: [
              { x: 0, y: -3, scale: 1 },    // Slide 1
              { x: 12, y: 0, scale: 1 }, // Slide 2
              { x: 14, y: -3, scale: 1 },// Slide 3
              { x: 16, y: -5, scale: 1 } // Slide 4
            ]
          };
        }
         else {
          return {
            drone: [
              { x: 114, y: 40 },   // Slide 1
              { x: 303, y: 195 },  // Slide 2
              { x: -62, y: 400 },  // Slide 3
              { x: -52, y: 158 }   // Slide 4
            ],
            circle: [
              { x: 0, y: -3, scale: 1 },    // Slide 1
              { x: 12, y: 0, scale: 1 }, // Slide 2
              { x: 9, y: -3, scale: 1 },// Slide 3
              { x: 9, y: -3, scale: 1 } // Slide 4
            ]
          };
        }
      };
      const { drone, circle } = getResponsiveValues();
      if (droneImage) {
        const translationDrone = drone[currentIndex % drone.length];
        droneImage.style.transform = `translate(${translationDrone.x}%, ${translationDrone.y}%) scale(1.1)`;
        droneImage.style.transition = "transform 0.5s ease";
      }
      if (circleImage) {
        const translationCircle = circle[currentIndex % circle.length];
        circleImage.style.transform = `translate(${translationCircle.x}%, ${translationCircle.y}%) scale(${translationCircle.scale})`;
        circleImage.style.transition = "transform 0.5s ease";





















/*
 Theme Name:   Woodmart Child
 Description:  Woodmart Child Theme
 Author:       XTemos
 Author URI:   http://xtemos.com
 Template:     woodmart
 Version:      1.0.0
 Text Domain:  woodmart
*/

.wp-dark-mode-floating-switch.wp-dark-mode-ignore.wp-dark-mode-animation.wp-dark-mode-animation-bounce {
    display: none !important;
}

.main-page-wrapper {
    margin: 0 !important;
    padding: 0 !important;
    background-color: black !important;
}

.whb-header {
    background-color: black;
    margin-bottom: 0px !important;
}

div#mainbanner {
    height: 100%;
    min-height: 780px;
    display: flex;
    align-items: center;
    margin-top: -70px;
    z-index: 9999;
}

.video-background {
    position: absolute;
    top: -158px;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0.5;
}

.video-background video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    min-height: 720px;
}

.site-logo img {
    max-height: 100px !important;
    width: 100%;
    max-width: 290px !important;
    transform: none !important;
    perspective: none !important;
    height: 100% !important;
    padding: 0 !important;
}

a.wd-logo.wd-sticky-logo {
    margin-top: -15px !important;
    /* transition: none; */
}

.whb-general-header {
    height: 110px !important;
    display: flex;
    align-items: center;
    /* backdrop-filter: blur(30px); */
    background-color: rgba(255, 255, 255, 0.04) !important;
}

.wd-prefooter {
    padding-bottom: 0 !important;
}

ul#menu-mainmenu li a {
    text-transform: capitalize;
    color: #fff;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
    opacity: 0.6;
}

ul#menu-mainmenu li {
    margin-left: 10px;
}

ul#menu-mainmenu li a:hover {
    opacity: 2;
}

ul#menu-mainmenu li.current-menu-item a {
    opacity: 2;
}

.mainbtn {
    text-transform: capitalize;
    color: #fff !important;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 300;
    line-height: normal;
    border-radius: 500px;
    background: linear-gradient(92deg, #004999 -19.88%, #00efeb 107.62%);
    box-shadow: none !important;
}

.homecontentbox .info-box-inner {
    width: 70%;
    margin: 0 auto;
    font-weight: 300;
}

.homecontentbox {
    margin: 60px 0 0 !important;
}

div#togglebutton a {
    background: linear-gradient(92deg,
            #004999 -19.88%,
            #00efeb 107.62%) !important;
    color: #fff !important;
    font-size: 12px !important;
    border: none !important;
    -webkit-animation: bounce 2s infinite;
    animation: bounce 2s infinite;
}

div#togglebutton a:hover {
    -webkit-animation: none;
    animation: none;
}

div#togglebutton a .vc_btn3-icon {
    font-size: 23px !important;
    height: 11px !important;
    line-height: 14px !important;
    font-weight: 600 !important;
}

@-webkit-keyframes bounce {

    0%,
    20%,
    50%,
    80%,
    100% {
        transform: translateY(0);
    }

    40% {
        transform: translateY(-30px);
    }

    60% {
        transform: translateY(-15px);
    }
}

@keyframes bounce {

    0%,
    20%,
    50%,
    80%,
    100% {
        transform: translateY(0);
    }

    40% {
        transform: translateY(-30px);
    }

    60% {
        transform: translateY(-15px);
    }
}

div#togglebutton {
    position: relative;
    top: 80px;
    margin-bottom: 0 !important;
    z-index: 9999;
}

div#aboutsec {
    padding: 100px 0;
}

#leftimgsect {
    display: flex;
    align-items: center;
    position: relative !important;
}

#leftimgsect2 {
    display: flex;
    align-items: center;
}

#rightimgsect {
    display: flex;
    align-items: center;
}

.drone-sect {
    padding: 100px 0;
}

.whb-main-header {
    background: rgba(255, 255, 255, 0.04);
    backdrop-filter: blur(30px);
    background-color: rgba(255, 255, 255, 0.04) !important;
    box-shadow: none !important;
    z-index: 9999999 !important;
}

.card-content {
    height: 100%;
    min-height: 220px;
    padding: 30px 20px 0;
}

.card-description p {
    color: #fff;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 400;
    margin: 0 !important;
    line-height: 23px;
}

.card-description p {
    color: #fff;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 300;
    margin: 0 !important;
    line-height: 26px;
}

.service-card-sect .card img {
    width: 100%;
}

.service-card-sect .card {
    background-color: #202020;
    color: #fff;
    border-radius: 30px;
    margin-bottom: 40px;
    border: none !important;
    transition: 0.3s all;
    cursor: pointer;
}

footer.footer-container.color-scheme-dark {
    background: linear-gradient(92deg, #004999 -19.88%, #00efeb 107.62%);
    box-shadow: 0px 0px 16px 0px rgba(0, 0, 0, 0.1);
    padding: 100px 0 0;
}

.service-card-sect .card:hover {
    background: linear-gradient(92deg,
            #004999 -19.88%,
            #00efeb 107.62%) !important;
}

div#leftimgsect:before {
    content: "";
    position: absolute;
    left: 0px;
    bottom: 0;
    width: 50%;
    height: 510px;
    background-image: url(img/leftbefore.png);
    background-size: 95%;
    background-repeat: no-repeat;
    background-position: left;
}

div#leftimgsect2:before {
    content: "";
    position: absolute;
    left: 0;
    bottom: -70px;
    width: 50%;
    height: 520px;
    background-image: url(img/leftbefore.png);
    background-size: 100% 66%;
    background-repeat: no-repeat;
    background-position: left;
}

div#rightimgsect:before {
    content: "";
    position: absolute;
    right: 0;
    bottom: 0;
    width: 50%;
    height: 490px;
    background-image: url(img/rightbefore.png);
    background-size: 120%;
    background-repeat: no-repeat;
    background-position: left;
}

div#leftimgsect2 .info-box-wrapper {
    padding-left: 70px;
}

div#leftimgsect .info-box-wrapper {
    padding-left: 40px;
}

h2#wework {
    font-size: 50px;
    font-style: normal;
    font-weight: 700;
    line-height: normal;
    margin-bottom: 90px;
}

div#buttonimg {
    margin: 0 -6px;
}

div#mobilebtnsect {
    padding: 70px 0 140px;
}

div#mobilebtnsect h2#wework {
    margin-bottom: 50px;
}

.service-card-sect .card .card-content h2 {
    font-family: "Karla";
    font-size: 34px;
    font-style: normal;
    font-weight: 700;
    line-height: normal;
    color: #fff;
}

div#servicesect {
    padding: 50px 0 50px;
}

div#mobileimg1 img {
    width: 100%;
    max-width: 400px !important;
}

div#mobileimg2 img {
    width: 100%;
    max-width: 400px !important;
}

.simple-box .info-box-title {
    line-height: 45px !important;
    margin-bottom: 25px !important;
}

div#mobileimg3 img {
    width: 100%;
    max-width: 380px !important;
}

div#footerlogo img {
    width: 100%;
}

div#copyrighttext p {
    margin: 0 !important;
    color: #fff;
    font-size: 20px;
}

.newsletter-signup h3 {
    color: #fff;
    font-size: 24px;
    font-style: normal;
    font-weight: 600;
    line-height: normal;
    text-transform: uppercase;
    margin-bottom: 50px;
}

.newsletter-signup {
    text-align: center;
}

h2#footerhead {
    color: #fff;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 600;
    line-height: normal;
    margin-bottom: 40px;
}

.newsletter-input input.wpcf7-form-control.wpcf7-text.wpcf7-validates-as-required {
    border: none !important;
    color: #fff;
    padding: 0;
}

div#footerlogo {
    margin-bottom: 80px;
}

.newsletter-input input.wpcf7-form-control.wpcf7-text.wpcf7-validates-as-required::placeholder {
    color: #fff;
    font-family: "BAGOS";
    font-size: 18px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
    opacity: 0.7;
}

.newsletter-input input.wpcf7-form-control.wpcf7-text.wpcf7-validates-as-required::focus {
    color: #fff;
    font-family: "BAGOS";
    font-size: 18px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
    opacity: 0.7;
}

.newsletter-input input.news-send {
    border-radius: 10px;
    background: #fff;
    font-size: 16px;
    font-style: normal;
    font-weight: 500;
    line-height: normal;
    color: #004999;
    width: 20%;
     padding: 12px;
    height: 50px !important;
}

.newsletter-signup span.wpcf7-not-valid-tip {
    position: absolute;
    margin: 0 auto;
    left: 0;
    right: 0;
}

footer .screen-reader-response {
    display: none !important;
}
.newsletter-input p {
    width: 60%;
    margin: 0 auto;
    display: flex;
    text-align: center;
    justify-content: space-between;
    border-bottom: 1px solid #fff;
    padding-bottom: 18px;
}

.newsletter-input span.wpcf7-spinner {
    display: none !important;
}

span.wpcf7-form-control-wrap {
    width: 100%;
}

div#footerrow {
    padding: 80px 0 40px;
}

div#footermenu ul li a {
    color: #fff;
    font-family: "BAGOS";
    font-size: 18px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
}

div#footermenu ul li span.list-content {
    color: #fff;
    font-family: "BAGOS";
    font-size: 18px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
}

div#footermenu ul li {
    padding-bottom: 8px;
}

div#footermenu ul.wd-list-type-icon {
    margin-bottom: 10px !important;
}

div#footermenu ul li span.list-icon {
    font-size: 24px;
}

.galleryswiper {
    width: 1055px;
    padding-top: 50px;
    padding-bottom: 50px;
}

.galleryswiper .swiper-slide {
    width: 345px;
}

.galleryswiper .swiper-slide img.slide-img {
    width: 100%;
    height: 500px;
    object-fit: cover;
    border-radius: 20px;
    border: 4px solid #004999;
    position: relative;
}

.social-links span.wd-label {
    font-size: 22px;
    width: 100%;
}

.galleryswiper .swiper-slide {
    border-radius: 20px;
}

.galleryswiper .swiper-slide.swiper-slide-active:before {
    display: none !important;
}

.galleryswiper .swiper-slide:before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(0deg,
            rgba(255, 255, 255, 0.35) 0%,
            rgba(255, 255, 255, 0.35) 100%),
        lightgray;
    z-index: 999999;
    width: 100%;
    height: 100%;
    opacity: 0.4;
    border-radius: 20px;
    transition: 0.3s ease-in;
}

.gallery-swiper-wrapper .swiper-button-next:after {
    content: "" !important;
    font-size: 0 !important;
}

.gallery-swiper-wrapper .swiper-button-prev:after {
    content: "" !important;
    font-size: 0 !important;
}

.gallery-swiper-wrapper .swiper-button-next,
.gallery-swiper-wrapper .swiper-button-prev {
    width: 50px;
}

.gallery-swiper-wrapper .swiper-button-next {
    transform: translate(-40px, 52px);
}

.gallery-swiper-wrapper .swiper-button-prev {
    transform: translate(40px, 50px);
}

div#galleryslidersect {
    background-color: #202020;
    padding: 100px 0 60px;
}

.faqcontbox {
    flex-direction: column-reverse !important;
}

div#questionsect .faqcontbox svg {
    width: 530px !important;
    height: 350px  !important;
    object-fit: cover;
    margin-left: -120px;
}

div#questionsect {
    padding: 130px 0 0;
}

div#questionsect .info-svg-wrapper.info-icon {
    width: 100% !important;
    height: 100% !important;
}

.faq-wrapper .accordion-button:not(.collapsed) {
    background-color: #ffffff00 !important;
    border-radius: 13px !important;
    color: #fff;
    padding-bottom: 20px !important;
}

.faq-wrapper button.accordion-button.collapsed {
    border-radius: 30px !important;
}

.faq-wrapper .accordion-button {
    background-color: #202020 !important;
    box-shadow: none !important;
    border: none !important;
    padding: 32px 40px;
    color: #fff;
    font-family: "Karla";
    font-size: 22px;
    font-weight: 400;
    text-align: left;
    display: flex;
    justify-content: flex-start;
}

.faq-wrapper .accordion-button span {
    margin-left: 22px;
    text-transform: math-auto;
}

.faq-wrapper .accordion-item {
    background-color: #202020 !important;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 13px !important;
    margin-bottom: 15px;
}

.faq-wrapper .accordion-body {
    background-color: #202020 !important;
    padding: 8px 28px 30px;
    color: #fff;
    border-radius: 13px;
}

.accordion-body p {
    margin: 0;
    font-family: "Karla";
    font-size: 18px;
    font-weight: 400;
    border-top: 1px solid rgba(255, 255, 255, 0.2);
    padding-top: 18px;
}

.faqcontbox .info-box-inner {
    width: 75%;
}

.faq-wrapper .accordion-header {
    background-color: #ffffff00 !important;
    border-radius: 13px !important;
}

.faq-wrapper .accordion-button.collapsed::before {
    content: "\002B";
}

.accordion-button:not(.collapsed)::after {
    display: none !important;
}

.faq-wrapper .accordion-button::before {
    content: "\2212";
    font-size: 38px;
    position: absolute;
    left: 20px;
    color: #fff;
    background-color: transparent !important;
    border: none !important;
    width: 40px;
    height: 40px;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 7px;
    font-weight: 300;
}

.error404 .main-page-wrapper {
    background-image: url(img/404.png);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: bottom;
}

.page-id-164 .main-page-wrapper {
    background-image: url(img/404.png);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: bottom;
}

.error404 .main-page-wrapper .site-content.col-12 {
    padding: 116px 0 90px;
}

.error404 .page-header:before {
    font-weight: 500 !important;
}

.error404 .page-content p {
    text-align: center;
    font-family: "Karla";
    font-size: 34px !important;
    font-style: normal;
    font-weight: 400;
    color: #fff;
    line-height: 38px;
    margin: 70px 0 10px;
}

a.erorrbutton {
    text-transform: capitalize;
    color: #fff !important;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 300;
    line-height: normal;
    border-radius: 500px;
    background: linear-gradient(92deg, #004999 -19.88%, #00efeb 107.62%);
    box-shadow: none !important;
    padding: 14px 40px;
    position: relative;
    top: 50px;
}

div#thankyoubtn a {
    text-transform: capitalize;
    color: #fff !important;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 300;
    line-height: normal;
    border-radius: 500px;
    background: linear-gradient(92deg, #004999 -19.88%, #00efeb 107.62%);
    box-shadow: none !important;
    padding: 14px 40px;
    border: none;
}

.thank-box .info-box-inner {
    width: 40%;
    margin: 30px auto 10px;
}

div#thanksect {
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: flex-end;
    min-height: 560px;
    padding-bottom: 30px;
}

.page-title {
    height: 100%;
    min-height: 340px;
    padding: 0 !important;
    display: flex;
    align-items: flex-end;
    margin: 0px !important;
    background-color: transparent !important;
}

/* .page-title::after {
      content: '';
      position: absolute;
      width: 100%;
      height: 300px;
      bottom: 0; 
      box-shadow: 0px 20px 30px rgba(13, 15, 15, 0.56);
      background: linear-gradient(0deg, rgba(14, 14, 14, 0.73) 0%, rgba(14, 14, 14, 0.00) 100%);
      filter: blur(10px); 
      z-index: -1;
  } */

.page-title .breadcrumbs {
    display: none;
}

.contact-box .info-box-inner {
    width: 77%;
    margin: 36px 0 26px;
}

.social-links {
    padding-top: 24px !important;
}

.social-links a.wd-social-icon.social-instagram {
    background: linear-gradient(315deg,
            #fbe18a 0.96%,
            #fcbb45 21.96%,
            #f75274 38.96%,
            #d53692 52.96%,
            #8f39ce 74.96%,
            #5b4fe9 100.96%);
    border-radius: 15px !important;
    box-shadow: none !important;
    margin-right: 20px !important;
}

div#contactsect .social-links span.wd-icon {
    font-size: 30px !important;
    line-height: 0 !important;
}

div#contactsect .wd-social-icons {
    display: flex;
    flex-wrap: wrap;
}

.social-links a.wd-social-icon.social-youtube {
    background-color: #ff0000 !important;
    border-radius: 15px !important;
    box-shadow: none !important;
}

.homecontentbox {
    position: relative;
    z-index: 9999;
}

div#lottie-animation svg {
    width: 770px !important;
    height: 450px !important;
    position: relative;
    right: -120px;
}

.form-image {
    text-align: center;
}

.contact-man-form {
    border-radius: 20px;
    background: #202020;
    position: relative;
    padding: 30px 30px;
    top: -30px;
}

.contact-man-form label {
    color: #fff;
    font-family: Karla;
    font-size: 20px;
    font-style: normal;
    font-weight: 400;
}

.contact-man-form .form-row input {
    border-radius: 16px !important;
    background-color: #35363a !important;
    border: none !important;
    height: 60px !important;
}

.contact-man-form .form-row textarea {
    border-radius: 16px !important;
    background-color: #35363a !important;
    border: none !important;
    height: 150px !important;
    min-height: 100% !important;
}

div#contactcol .wpb_wrapper {
    position: relative;
    padding: 0 30px 0px;
    border-top: 0px !important;
    border-radius: 20px;
}

div#contactcol .wpb_wrapper:before {
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    opacity: 0.6;
    top: 0;
    background: linear-gradient(75deg, #004999 -17.88%, #00efeb 110.62%);
    left: 0px;
    border-radius: 20px;
    border: 14px solid #56b9f2;
}

div#contactsect .icons-size-large .wd-social-icon {
    width: 60px;
    height: 60px;
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 0;
}

.page-id-177 div#contactsect {
    padding: 14rem 0 5rem;
    overflow: hidden;
}
.page-id-177
div#contactsect:before{
    bottom: 150px;
}

div#contactsect:before {
    content: "";
    position: absolute;
    left: -40px;
    width: 780px;
    height: 690px;
    background-image: url(img/contact-before.svg);
    background-size: 90%;
    z-index: 0;
    background-repeat: no-repeat;
    background-position: top;
    bottom: 270px;
}

.page-id-177 .main-page-wrapper {
    background-image: url(img/contactusbg.png);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: top;
    margin: -100px 0 0 !important;
}

.page-id-177 .main-page-wrapper .page-title-default .entry-title.title {
    padding-bottom: 10px;
}

.form-row-sumbit input {
    text-transform: capitalize;
    color: #fff !important;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 300;
    line-height: normal;
    border-radius: 500px;
    background: linear-gradient(92deg, #004999 -19.88%, #00efeb 107.62%);
    box-shadow: none !important;
    padding: 12px 42px;
}

div#faqtitttle h1 {
    color: #fff;
    text-align: center;
    font-family: "Karla";
    font-size: 60px;
    font-style: normal;
    font-weight: 700;
    line-height: 58px;
}

div#faqtitttle h1 strong {
    background: linear-gradient(92deg, #004999 -19.88%, #00efeb 107.62%);
    background-clip: text;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    font-family: Karla;
}

div#faqcontent p {
    font-family: Karla;
    font-size: 22px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
    color: #fff;
    margin: 0 auto;
    width: 50%;
}

div#faqheader {
    padding: 100px 0 100px;
}

.page-id-230 .main-page-wrapper {
    background-image: url(img/faqbg.png);
    background-repeat: no-repeat;
    background-size: 100%;
    background-position: top;
    margin: -10px 0 0 !important;
}

.page-id-183 .main-page-wrapper {
    background-image: url(img/servicebg.png);
    background-repeat: no-repeat;
    background-size: 100%;
    background-position: top;
    margin: -10px 0 0 !important;
}

div#mainfaqsect {
    padding: 10rem 0 2rem;
}

.page-id-230 div#contactsect {
    padding: 17rem 0 0;
}

div#mainfaqsect .faq-wrapper {
    width: 80%;
    margin: 0 auto;
}

div#faqtitttle h1 strong {
    font-weight: 700 !important;
}

div#serviceheader div#faqcontent p {
    margin: 0;
    width: 75%;
}

div#serviceheader div#faqtitttle h1 strong {
    background: none;
    color: #56b9f2 !important;
    -webkit-text-fill-color: #56b9f2 !important;
}

div#serviceheader {
    padding: 200px 0;
}

.service-box {
    height: 350px;
    margin-bottom: 35px;
    border-radius: 16px;
    position: relative;
    display: flex;
    align-items: flex-end;
    padding: 0px 20px;
    object-fit: cover;
}

.service-box-content h2 {
    color: #fff;
    font-family: "Karla";
    font-style: normal;
    font-weight: 700;
    line-height: normal;
    font-size: 30px;
    transition: transform 0.3s ease-in-out;
    margin-bottom: 0 !important;
}

.service-box-description p {
    color: #fff;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
}

.service-box-description {
    width: 100%;
    transition: opacity 0.3s ease-in;
    margin-left: auto;
    opacity: 0;
}

.service-box:hover .service-box-description {
    opacity: 1;
}

.service-box:hover .service-box-content h2 {
    transform: translateY(-20px);
}

div#servicemainsect {
    padding-top: 80px;
}

.page-id-183 div#contactsect {
    padding: 200px 0 100px;
}

div#galleryheader {
    padding: 100px 0;
}

div#galleryheader div#faqcontent p {
    margin: 0;
    width: 85%;
}

.page-id-181 h2#wework {
    margin-bottom: 35px;
}

.galleryhome-swiper-wrapper .swiper-slide img {
    width: 100%;
    height: 100%;
    min-height: 540px;
    object-fit: cover;
    border-radius: 16px;
}

div#faqboxcol {
    padding: 50px 0 0 100px;
}

.galleryhome-swiper-wrapper {
    position: relative;
}

.galleryhome-swiper-wrapper:before {
    content: "";
    position: absolute;
    width: 550px;
    height: 530px;
    opacity: 0.5;
    background: var(--Gradient-Color,
            linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%),
            linear-gradient(92deg, #ff6801 1.46%, #993e01 84.37%));
    filter: blur(155.1724090576172px);
    z-index: 0;
}

div#heartimg img {
    width: 100%;
    object-fit: cover;
    max-width: 350px;
}

div#heartimg {
    margin: 0 !important;
}

div#thanksect .vc_column-inner {
    padding: 0 !important;
}

div#mobilebtnsect div#appbuttonrow img {
    position: relative !important;
    left: 0 !important;
}


div#appbuttonrow img {
    width: 100%;
    max-width: 230px;
    object-fit: cover;

}

div#aboutheader {
    height: 630px;
}

button#lg-next-1 {
    position: absolute;
}

div#lg-outer-1 {
    z-index: 999999999;
}

div#lg-backdrop-1 {
    z-index: 99999999;
}

.gallery-container {
    flex-wrap: wrap;
    display: flex;
    margin-top: 70px;
}

.gallery-inner {
    width: 15%;
    text-align: center;
    cursor: pointer;
    margin: 0 5px 15px;
}

a.gallery-inner img {
    width: 100%;
    max-width: 100%;
    object-fit: cover;
}

div#galleryimagessect {
    padding: 100px 0 100px;
}

div#aboutheader div#faqcontent p {
    margin: 0;
    width: 100%;
}

div#aboutcontentcol {
    padding-top: 90px;
}

div#aboutsecondsect {
    padding: 100px 0 100px;
    position: relative;
    background-image: linear-gradient(349deg,
            #22272b 7.94%,
            rgba(14, 14, 14, 0) 72.21%);
}

.aerial-about {
    color: #fff;
    font-family: Karla;
    font-size: 22px;
    font-style: normal;
    font-weight: 400;
    line-height: 32px;
}

div#img1 img {
    width: 100%;
    max-width: 540px;
    object-fit: cover;
    border-radius: 18px;
    position: relative;
}

div#img2 img {
    width: 100%;
    max-width: 540px;
    object-fit: cover;
    border-radius: 18px;
    z-index: 999;
    position: relative;
}

.team-content-box img.info-icon.image-1 {
    width: 100%;
    max-width: 140px;
    object-fit: cover;
}

.team-content-box .info-box-title {
    position: absolute;
    top: 0;
}

div#leftteamrow {
    padding: 0 50px 100px;
}

div#rightteamrow {
    padding: 0 50px 0;
}

.team-content-box .info-box-subtitle {
    padding-top: 60px;
}

div#teamsect {
    padding: 150px 0;
}

div#teamsect:before {
    content: "";
    position: absolute;
    left: 0;
    top: 20%;
    width: 450px;
    height: 550px;
    opacity: 0.5;
    background: var(--Second-Primary-Color,
            linear-gradient(92deg, #004999 -19.88%, #00efeb 107.62%));
    filter: blur(150px);
}

div#teamsect:after {
    content: "";
    position: absolute;
    right: 0;
    bottom: 9%;
    width: 470px;
    height: 500px;
    opacity: 0.5;
    background: var(--Second-Primary-Color,
            linear-gradient(92deg, #004999 -19.88%, #00efeb 107.62%));
    filter: blur(150px);
}



div#quipimg img {
    margin-bottom: -54px;
    width: 100%;
    max-width: 960px;
    object-fit: cover;
    position: relative;
    z-index: 99;
}



.equip-box {
    position: relative;
}

div#equipsrows .info-box-inner p {
    width: 60%;
    margin: 0 auto;
}

div#equipsrows {
    padding-bottom: 20px;
}

div#equipsect {
    padding-top: 100px;
}

div#equipsect:before {
    content: "";
    position: absolute;
    left: 0;
    border-radius: 1808px;
    background: #fff;
    bottom: -80%;
    margin: 0 auto;
    right: 0;
    width: 1040px;
    height: 1020px;
    z-index: 0;
}


.page-id-23 div#contactsect {
    padding: 150px 0;
}

.page-id-296 div#propertinformobileimg img {
    width: 100%;
    max-width: 400px;
    object-fit: cover;
}

div#employedsect div#appbuttonrow .wpb_wrapper {
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
}

div#employedsect div#buttonimg {
    margin: 0px 30px 0 0px;
    width: fit-content;
}

div#employedsect h2#wework {
    margin-bottom: 20px !important;
}

div#employedsect {
    height: 100%;
    min-height: 950px;
    background-position: center;
    background-size: 100%;
    background-repeat: no-repeat;
    padding-top: 60px;
    margin-top: 100px;
    margin-bottom: 100px;
}

div#employedariellogo {
    margin-bottom: 10px;
}

div#employedcol {
    padding-left: 80px;
}

div#appfeaturebg {
    background-size: 100% !important;
    background-position: bottom;
    background-repeat: no-repeat;
    padding-bottom: 30px;
    margin-bottom: 50px;
}

div#appfeaturebg:before {
    content: "";
    position: absolute;
    top: 10%;
    right: 10%;
    width: 500px;
    height: 360px;
    background-image: url(img/dronecamera.svg);
    background-position: center;
    background-size: 100%;
    background-repeat: no-repeat;
}

div#mainappimg img {
    width: 100%;
    max-width: 350px;
    object-fit: cover;
}

div#mainappimg {
    margin: 0 !important;
}

div#appfeaturethmubimg img {
    width: 100%;
    max-width: 480px;
    object-fit: cover;
}

div#appfeaturesect div#faqcontent p {
    margin: 0;
    width: 80%;
}

div#appfeaturesect {
    padding: 80px 0;
}

div#mobileappbtnwrapper .wpb_column.vc_column_container.vc_col-sm-6 {
    width: fit-content;
}

div#mobileappbtnwrapper img {
    width: 100%;
    max-width: 190px;
    object-fit: cover;
}

h4#solutionheading {
    color: #fff;
    font-family: "Karla";
    font-size: 40px;
    font-style: normal;
    font-weight: 500;
    width: 50%;
    line-height: 46px;
}

.swiper-number h3 {
    color: #56b9f2;
    font-family: "Karla";
    font-size: 220px;
    font-style: normal;
    font-weight: 500;
    line-height: 260px;
    letter-spacing: -7px;
}

.swiper-content {
    padding-left: 50px;
}

.swiper-content p {
    color: #fff;
    font-family: "Karla";
    font-size: 48px;
    font-style: normal;
    font-weight: 600;
    line-height: 56px;
    margin: 0;
}

.solutionswiper-inner {
    display: flex;
    align-items: center;
}

.solution-swiper-wrapper .swiper-button-next:after {
    content: "" !important;
    font-size: 0 !important;
}

.solution-swiper-wrapper .swiper-button-prev:after {
    content: "" !important;
    font-size: 0 !important;
}

div#solutionsect {
    border-radius: 40px;
    background: #202020;
    margin: 0 4px 100px;
    padding: 70px 0 80px 140px !important;
    position: relative;
}

.solution-swiper-wrapper .swiper-button-next,
.solution-swiper-wrapper .swiper-button-prev {
    background-color: #fff;
    border-radius: 100%;
    width: 50px;
    height: 50px;
    top: 100%;
    margin-right: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 0px;
}

div#solutionsect .vc_column-inner {
    padding: 0 !important;
}

.solution-swiper-wrapper {
    position: relative;
}

.solution-swiper-wrapper .swiper-button-prev {
    right: 70px !important;
    left: inherit;
}

.solutionswiper {
    padding-top: 30px;
    padding-left: 40px;
}

.rotatedroneimg-img {
    transition: transform 0.5s ease;
    transform-origin: center;
}

.rotatedroneimg-img:hover {
    cursor: pointer;
}

img.circle-img {
    position: absolute;
    top: 65px;
    width: 100%;
    left: -2px;
    max-width: 192px;
}

img.rotatedroneimg-img {
    position: absolute;
    top: 20px;
    width: 48px;
}

.wd-toolbar.wd-toolbar-label-show.woodmart-toolbar-label-show.woodmart-toolbar {
    height: 0 !important;
    display: none !important;
}

.mobile-nav {
    z-index: 999999999 !important;
    width: 450px !important;
    background-color: #000 !important;
    /* background: linear-gradient(92deg, #004999 -19.88%, #00efeb 107.62%); */
}

.mobile-nav .wd-action-btn.wd-style-text>a:before {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 0em;
    height: 0em;
    font-size: 22px;
    font-weight: 400;
    opacity: 2;
    filter: brightness(6.5);
}

.mobile-nav .wd-action-btn.wd-style-text>a {
    position: relative;
    font-weight: 600;
    font-size: 0px !important;
    line-height: 1;
    color: #f2e0d6;
}

.wd-header-mobile-nav .wd-tools-icon:before {
    font-size: 40px !important;
}

.mobile-nav ul li a {
    text-transform: capitalize;
    color: #fff !important;
    font-family: "Karla";
    font-size: 20px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
    opacity: 0.6;
}

.mobile-nav ul li.current-menu-item a {
    opacity: 2;
}

.mobile-nav ul li a:hover {
    opacity: 2;
}

.wd-nav-mobile li a {
    border-bottom: none !important;
}

.mobil
    
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Ice Cream Hero</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body,
      html {
        margin: 0;
        padding: 0;
        font-family: "Poppins", sans-serif;
        background: #00aaff;
        overflow: hidden;
      }

      .hero-section {
        width: 100vw;
        height: 100vh;
        position: relative;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 50px;
        overflow: hidden;
      }
img.floating.float6.active {
    left: 1px;
}
      .hero-text {
        max-width: 40%;
        color: white;
        opacity: 0;
        transform: translateX(-100px);
        transition: all 1s  ease-out;
        /* Slowed from 1s to 1.8s */
        z-index: 3;
      }

      .hero-text.show {
        opacity: 1;
        transform: translateX(0);
      }

      .hero-text h1 {
        font-size: 3rem;
        margin-bottom: 1rem;
      }

      .hero-text p {
        font-size: 1.1rem;
        margin-bottom: 1.5rem;
      }

      .btns img {
        width: 150px;
        margin-right: 10px;
        transition: all 0.8s ease;
        /* Slowed from 0.5s to 0.8s */
      }

      .btns img:hover {
        transform: scale(1.05);
      }

      .main-image {
        height: 100%;
        max-height: 90vh;
        opacity: 0;
        transform: translateY(80px);
        position: absolute;
        right: 50px;
        transition: all 2.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        /* Slowed from 1.5s to 2.2s */
        z-index: 2;
      }

      .main-image.loaded {
        opacity: 1;
        bottom: 0;
        left: 38%;
        transform: translateY(0) translateX(0);
      }

      .main-image.active {
        transform: rotate(5deg);
        left: 63%;
        bottom: 0;
        transition: all 1.5s ease;
        /* Slowed from 1s to 1.5s */
      }

      .floating {
        position: absolute;
        opacity: 0;
        transform: translateY(80px);
        transition: all 2.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
      }

      .floating.loaded {
        opacity: 1;
        transform: translateY(0);
      }

img.floating.float1.active {
    left: 61%;
    top: 4%;
}
      .floating.active {
        transition: all 1.8s ease;
      }
img.floating.float4.active {
    bottom: 4%;
    right: 35%;
        z-index: 999;
}
img.floating.float3.active {
    bottom: 32%;
    left: 55%;
}
      .float1 {
        top: 9%;
        left: 9%;
        transition-delay: 0.2s;
      }
img.floating.float2.active {
    top: 8%;
    right: 14%;
}

      .float2 {
        top: 13%;
        right: 28%;
        transition-delay: 0.3s;
        width: 80px;
      }

      .float3 {
        bottom: 32%;
        left: 30%;
        transition-delay: 0.4s;
      }

      .float4 {
        bottom: 20%;
        right: 32%;
        transition-delay: 0.5s;
        width: 110px;
      }

      .float5 {
        top: 50%;
        right: 1%;
        transition-delay: 0.6s;
        width: 102px;
      }

.float6 {
    bottom: 4%;
    left: -2%;
    transition-delay: 0.7s;
    width: 190px;
}
      .float7 {
        bottom: 0px;
        right: 4%;
        transition-delay: 0.8s;
        width: 200px;
      }
    </style>
  </head>

  <body>
    <div class="hero-section">
      <div class="hero-text">
        <h1>ICE CREAM ON THE BOAT</h1>
        <p>
          Make your picnic by the lake even more memorable with our on-demand
          ice cream app. Whether you're relaxing with fam, spending time with
          friends, or vibing solo – we deliver frozen treats straight to your
          picnic spot.
        </p>
        <div class="btns">
          <img src="appstore.png" alt="App Store" />
          <img src="playstore.png" alt="Google Play" />
        </div>
      </div>

      <div id="mainimagebox">
        <img
          src="/images/hands-phone.png"
          class="main-image"
          alt="Phone"
          onload="imageLoaded(this)"
        />
        <img
          src="/images/cone.png"
          class="floating float1"
          onload="imageLoaded(this)"
        />
        <img
          src="/images/blueberry.png"
          class="floating float2"
          onload="imageLoaded(this)"
        />
        <img
          src="/images/blur-blueberry.png"
          class="floating float3"
          onload="imageLoaded(this)"
        />
        <img
          src="/images/cup.png"
          class="floating float4"
          onload="imageLoaded(this)"
        />
        <img
          src="/images/chocbar.png"
          class="floating float5"
          onload="imageLoaded(this)"
        />
        <img
          src="/images/boat-left.png"
          class="floating float6"
          onload="imageLoaded(this)"
        />
        <img
          src="/images/blur-boat.png"
          class="floating float7"
          onload="imageLoaded(this)"
        />
      </div>
    </div>

    <script>
      function imageLoaded(img) {
        img.classList.add("loaded");

        if (
          img.classList.contains("main-image") ||
          img.classList.contains("floating")
        ) {
          setTimeout(() => {
            img.classList.add("active");
          }, 4000);
        }
      }

      window.addEventListener("DOMContentLoaded", () => {
        const heroText = document.querySelector(".hero-text");

        setTimeout(() => {
          heroText.classList.add("show");
        }, 5000);
      });
    </script>
  </body>
</html>
<head>
  <meta charset="utf-8" />
  <title>My test page</title>
</head>
// Total number of reviews for current product [count_reviews
add_shortcode('count_reviews', function() {
    $product_id = get_queried_object_id();

    if ( ! $product_id ) {
        return '';
    }

    $args = array(
        'post_type'      => 'simple-reviews',
        'post_status'    => 'publish',
        'posts_per_page' => -1,
        'meta_query'     => array(
            array(
                'key'     => 'review_post_id',
                'value'   => $product_id,
                'compare' => '=',
            ),
            array(
                'key'     => 'rating_review',
                'value'   => '',
                'compare' => '!=',
            ),
        ),
        'fields' => 'ids',
    );

    $query = new WP_Query($args);
    $total_reviews = count($query->posts);

    $label = plural_form($total_reviews, 'отзыв', 'отзыва', 'отзывов');

    return "{$total_reviews} {$label}";
});

// Функция склонения слов по числам
function plural_form($number, $form1, $form2, $form5) {
    $n = abs($number) % 100;
    $n1 = $n % 10;
    if ($n > 10 && $n < 20) return $form5;
    if ($n1 > 1 && $n1 < 5) return $form2;
    if ($n1 == 1) return $form1;
    return $form5;
}
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { McButton, McInput } from '@maersk-global/mds-react-wrapper';
import { McSelect } from '@maersk-global/mds-react-wrapper/components-core/mc-select';
import { McOption } from '@maersk-global/mds-react-wrapper/components-core/mc-option';
import styles from '../styles/CreateRule.module.css';
import data from '../data/PnLGroup.json';

const CreateRules = () => {
  const navigate = useNavigate();
  const [activeTab, setActiveTab] = useState('ruleInfo');
  const [ruleData, setRuleData] = useState({
    num: '',
    name: '',
    desc: '',
    custRefID: '',
    ruleGroup: '',
    isActive: 'Y',
    pnlGroup: '',
  });
  const [steps, setSteps] = useState([
    {
      stepNo: '',
      stepName: 'Single Step',
      StepDesc: '',
      stepType: 'S',
      preAggregatorColumns: '',
      sourceTable: '',
      sourceFilters: '',
      joinColumns: '',
      allocationColumns: '',
      driverTableID: '',
      driverWeightColumn: '',
      driverFilters: '',
    },
  ]);
  const [errors, setErrors] = useState({ rule: {}, steps: [{}] });

  // Extract PnL Group options from JSON, with fallback for empty data
  const pnlGroups = data.PnLGroups ? Object.keys(data.PnLGroups) : [];

  // Get Rule Group options based on selected PnL Group, with fallback
  const ruleGroups = ruleData.pnlGroup && data.PnLGroups[ruleData.pnlGroup]
    ? data.PnLGroups[ruleData.pnlGroup].RuleGroups || []
    : [];

  const addStep = () => {
    setSteps((prevSteps) => [
      ...prevSteps,
      {
        stepNo: '',
        stepName: 'Single Step',
        StepDesc: '',
        stepType: 'S',
        preAggregatorColumns: '',
        sourceTable: '',
        sourceFilters: '',
        joinColumns: '',
        allocationColumns: '',
        driverTableID: '',
        driverWeightColumn: '',
        driverFilters: '',
      },
    ]);
    setErrors((prevErrors) => ({
      ...prevErrors,
      steps: [...prevErrors.steps, {}],
    }));
  };

  const validateForm = () => {
    const newErrors = { rule: {}, steps: steps.map(() => ({})) };
    let isValid = true;

    // Validate rule data
    Object.keys(ruleData).forEach((key) => {
      if (key !== 'isActive' && !ruleData[key]) {
        newErrors.rule[key] = 'This field is required';
        isValid = false;
      }
    });

    // Validate steps
    steps.forEach((step, index) => {
      const stepErrors = {};
      Object.keys(step).forEach((key) => {
        if (key !== 'stepName' && key !== 'stepType' && !step[key]) {
          stepErrors[key] = 'This field is required';
          isValid = false;
        }
      });
      newErrors.steps[index] = stepErrors;
    });

    setErrors(newErrors);
    return isValid;
  };

  const handleInputChange = (e, stepIndex = null) => {
    const { name, value } = e.target;
    console.log(`Input changed: ${name} = ${value}${stepIndex !== null ? ` (Step ${stepIndex + 1})` : ''}`);
    
    if (stepIndex !== null) {
      setSteps((prevSteps) => {
        const newSteps = [...prevSteps];
        newSteps[stepIndex] = { ...newSteps[stepIndex], [name]: value };
        return newSteps;
      });
      setErrors((prevErrors) => ({
        ...prevErrors,
        steps: prevErrors.steps.map((stepErrors, i) =>
          i === stepIndex ? { ...stepErrors, [name]: '' } : stepErrors
        ),
      }));
    } else {
      setRuleData((prevData) => ({
        ...prevData,
        [name]: value,
        ...(name === 'pnlGroup' ? { ruleGroup: '' } : {}),
      }));
      setErrors((prevErrors) => ({
        ...prevErrors,
        rule: { ...prevErrors.rule, [name]: '' },
      }));
    }
  };

  const handleSelectChange = (e) => {
    const { name, value } = e.target;
    console.log(`Select changed: ${name} = ${value}`);
    setRuleData((prevData) => ({
      ...prevData,
      [name]: value,
      ...(name === 'pnlGroup' ? { ruleGroup: '' } : {}),
    }));
    setErrors((prevErrors) => ({
      ...prevErrors,
      rule: { ...prevErrors.rule, [name]: '' },
    }));
  };

  const handleSave = async () => {
    if (!validateForm()) {
      console.log('Validation failed:', errors);
      alert('Please fill out all required fields.');
      return;
    }

    // Parse comma-separated columns and filters
    const parseColumns = (input) => input.split(',').map((item) => item.trim()).filter((item) => item);
    const parseFilters = (input) => {
      const filters = input.split(';').map((item) => item.trim()).filter((item) => item);
      return filters.map((filter) => {
        const [name, filterType, values] = filter.split(':').map((item) => item.trim());
        return { name, filterType, values };
      });
    };

    const ruleJson = {
      rules: {
        rule: [
          {
            num: ruleData.num,
            name: ruleData.name,
            desc: ruleData.desc,
            custRefID: ruleData.custRefID,
            ruleGroup: ruleData.ruleGroup,
            isActive: ruleData.isActive,
            Step: steps.map((step, index) => ({
              stepNo: step.stepNo || `${ruleData.num}.${index + 1}`,
              stepName: step.stepName === 'Single Step' ? 'single' : 'multi',
              StepDesc: step.StepDesc,
              stepType: step.stepType,
              isActive: 'Y',
              SourceTable: {
                id: '1',
                Name: step.sourceTable,
              },
              sourceFilters: {
                columns: parseFilters(step.sourceFilters),
              },
              preAggregator: {
                columns: parseColumns(step.preAggregatorColumns),
              },
              join: {
                columns: parseColumns(step.joinColumns),
              },
              allocation: {
                columns: parseColumns(step.allocationColumns),
              },
              driver: {
                driverTableID: step.driverTableID,
                driverWeightColumn: step.driverWeightColumn,
                driverFilters: {
                  columns: parseFilters(step.driverFilters),
                },
              },
            })),
          },
        ],
      },
    };

    console.log('Saving Rule Data:', JSON.stringify(ruleJson, null, 2));

    try {
      const response = await fetch('/api/rules', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(ruleJson),
      });

      if (response.ok) {
        console.log('Rule created successfully');
        navigate('/');
      } else {
        console.error('Failed to create rule:', response.statusText);
        alert('Failed to create rule. Please try again.');
      }
    } catch (error) {
      console.error('Error during API call:', error);
      alert('An error occurred while saving the rule.');
    }
  };

  const handleCancel = () => {
    console.log('Cancelling rule creation');
    navigate('/');
  };

  const renderTabContent = () => {
    console.log('Rendering tab:', activeTab);
    switch (activeTab) {
      case 'ruleInfo':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Rule Information</h3>
            <div className={styles.formGrid}>
              <div className={styles.gridItem}>
                <McSelect
                  label="PnL Group"
                  name="pnlGroup"
                  value={ruleData.pnlGroup}
                  optionselected={handleSelectChange}
                  placeholder="Select a PnL Group"
                  required
                  invalid={!!errors.rule.pnlGroup}
                  invalidmessage={errors.rule.pnlGroup}
                >
                  {pnlGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
              <div className={styles.gridItem}>
                <McSelect
                  label="Rule Group"
                  name="ruleGroup"
                  value={ruleData.ruleGroup}
                  optionselected={handleSelectChange}
                  placeholder={ruleGroups.length ? "Select a Rule Group" : "Select a PnL Group first"}
                  required
                  disabled={!ruleData.pnlGroup || !ruleGroups.length}
                  invalid={!!errors.rule.ruleGroup}
                  invalidmessage={errors.rule.ruleGroup}
                >
                  {ruleGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Number"
                name="num"
                value={ruleData.num}
                input={handleInputChange}
                placeholder="Enter rule number"
                required
                invalid={!!errors.rule.num}
                invalidmessage={errors.rule.num}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Name"
                name="name"
                value={ruleData.name}
                input={handleInputChange}
                placeholder="Enter rule name"
                required
                invalid={!!errors.rule.name}
                invalidmessage={errors.rule.name}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Description"
                name="desc"
                value={ruleData.desc}
                input={handleInputChange}
                placeholder="Enter rule description"
                multiline
                rows={3}
                required
                invalid={!!errors.rule.desc}
                invalidmessage={errors.rule.desc}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Customer Reference ID"
                name="custRefID"
                value={ruleData.custRefID}
                input={handleInputChange}
                placeholder="Enter customer reference ID"
                required
                invalid={!!errors.rule.custRefID}
                invalidmessage={errors.rule.custRefID}
              />
            </div>
          </div>
        );
      case 'step':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Step Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Step Number"
                    name="stepNo"
                    value={step.stepNo}
                    input={(e) => handleInputChange(e, index)}
                    placeholder={`Enter step number (e.g., ${ruleData.num}.${index + 1})`}
                    required
                    invalid={!!errors.steps[index].stepNo}
                    invalidmessage={errors.steps[index].stepNo}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Name"
                    name="stepName"
                    value={step.stepName}
                    optionselected={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select Step Name"
                    invalid={!!errors.steps[index].stepName}
                    invalidmessage={errors.steps[index].stepName}
                  >
                    <McOption value="Single Step">Single Step</McOption>
                    <McOption value="Multi Step">Multi Step</McOption>
                  </McSelect>
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Step Description"
                    name="StepDesc"
                    value={step.StepDesc}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter step description"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].StepDesc}
                    invalidmessage={errors.steps[index].StepDesc}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Type"
                    name="stepType"
                    value={step.stepType}
                    optionselected={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select a PnL Group"
                    invalid={!!errors.steps[index].stepType}
                    invalidmessage={errors.steps[index].stepType}
                  >
                    <McOption value="S">S</McOption>
                    <McOption value="M">M</McOption>
                  </McSelect>
                </div>
              </div>
            ))}
            <div className={styles.buttonContainer}>
              <McButton
                label="Add Step"
                appearance="secondary"
                click={addStep}
                className={styles.actionButton}
              />
            </div>
          </div>
        );
      case 'preAggregate':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Pre-Aggregate Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Pre-Aggregator Columns"
                    name="preAggregatorColumns"
                    value={step.preAggregatorColumns}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter columns (comma-separated)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].preAggregatorColumns}
                    invalidmessage={errors.steps[index].preAggregatorColumns}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'source':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Source Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Source Table"
                    name="sourceTable"
                    value={step.sourceTable}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter source table name"
                    required
                    invalid={!!errors.steps[index].sourceTable}
                    invalidmessage={errors.steps[index].sourceTable}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Source Filters"
                    name="sourceFilters"
                    value={step.sourceFilters}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter filters (e.g., PNL_LINE:IN:PnL.DVC.214,PnL.DVC.215;MOVE_TYPE:EQ:EX)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].sourceFilters}
                    invalidmessage={errors.steps[index].sourceFilters}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'join':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Join Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Join Columns"
                    name="joinColumns"
                    value={step.joinColumns}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter columns (comma-separated)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].joinColumns}
                    invalidmessage={errors.steps[index].joinColumns}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'allocation':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Allocation Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Allocation Columns"
                    name="allocationColumns"
                    value={step.allocationColumns}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter columns (comma-separated)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].allocationColumns}
                    invalidmessage={errors.steps[index].allocationColumns}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'driver':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Driver Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Table ID"
                    name="driverTableID"
                    value={step.driverTableID}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver table ID"
                    required
                    invalid={!!errors.steps[index].driverTableID}
                    invalidmessage={errors.steps[index].driverTableID}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Weight Column"
                    name="driverWeightColumn"
                    value={step.driverWeightColumn}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver weight column"
                    required
                    invalid={!!errors.steps[index].driverWeightColumn}
                    invalidmessage={errors.steps[index].driverWeightColumn}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Filters"
                    name="driverFilters"
                    value={step.driverFilters}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter filters (e.g., ALLOC_MOVE:IN:GATE-IN,ON-RAIL;MCHE_SHP:NTEQ:0)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].driverFilters}
                    invalidmessage={errors.steps[index].driverFilters}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      default:
        return <div className={styles.tabContent}>No Tab Selected</div>;
    }
  };

  return (
    <div className={styles.pageWrapper}>
      <div className={styles.container}>
        <div className={styles.card}>
          <div className={styles.buttonContainer}>
            <McButton
              label="Back"
              appearance="neutral"
              click={handleCancel}
              className={styles.actionButton}
            />
            <McButton
              label="Save"
              appearance="primary"
              click={handleSave}
              className={styles.actionButton}
            />
          </div>

          <div className={styles.tabs}>
            <button
              className={`${styles.tabButton} ${activeTab === 'ruleInfo' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('ruleInfo')}
            >
              Rule Info
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'step' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('step')}
            >
              Step
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'preAggregate' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('preAggregate')}
            >
              Pre-Aggregate
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'source' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('source')}
            >
              Source
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'join' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('join')}
            >
              Join
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'allocation' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('allocation')}
            >
              Allocation
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'driver' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('driver')}
            >
              Driver
            </button>
          </div>

          {renderTabContent()}
        </div>
      </div>
    </div>
  );
};

export default CreateRules;



import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { McButton, McInput } from '@maersk-global/mds-react-wrapper';
import { McSelect } from '@maersk-global/mds-react-wrapper/components-core/mc-select';
import { McOption } from '@maersk-global/mds-react-wrapper/components-core/mc-option';
import styles from '../styles/CreateRule.module.css';
import data from '../data/PnLGroup.json';

const CreateRules = () => {
  const navigate = useNavigate();
  const [activeTab, setActiveTab] = useState('ruleInfo');
  const [ruleData, setRuleData] = useState({
    num: '',
    name: '',
    desc: '',
    custRefID: '',
    ruleGroup: '',
    isActive: 'Y',
    pnlGroup: '',
  });
  const [steps, setSteps] = useState([
    {
      stepNo: '',
      stepName: 'Single Step',
      StepDesc: '',
      stepType: 'S',
      preAggregatorColumns: '',
      sourceTable: '',
      sourceFilters: '',
      joinColumns: '',
      allocationColumns: '',
      driverTableID: '',
      driverWeightColumn: '',
      driverFilters: '',
    },
  ]);
  const [errors, setErrors] = useState({ rule: {}, steps: [{}] });

  // Extract PnL Group options from JSON, with fallback for empty data
  const pnlGroups = data.PnLGroups ? Object.keys(data.PnLGroups) : [];

  // Get Rule Group options based on selected PnL Group, with fallback
  const ruleGroups = ruleData.pnlGroup && data.PnLGroups[ruleData.pnlGroup]
    ? data.PnLGroups[ruleData.pnlGroup].RuleGroups || []
    : [];

  const addStep = () => {
    setSteps((prevSteps) => [
      ...prevSteps,
      {
        stepNo: '',
        stepName: '',
        StepDesc: '',
        stepType: '',
        preAggregatorColumns: '',
        sourceTable: '',
        sourceFilters: '',
        joinColumns: '',
        allocationColumns: '',
        driverTableID: '',
        driverWeightColumn: '',
        driverFilters: '',
      },
    ]);
    setErrors((prevErrors) => ({
      ...prevErrors,
      steps: [...prevErrors.steps, {}],
    }));
  };

  const validateForm = () => {
    const newErrors = { rule: {}, steps: steps.map(() => ({})) };
    let isValid = true;

    // Validate rule data (all fields are mandatory)
    Object.keys(ruleData).forEach((key) => {
      if (!ruleData[key]) {
        newErrors.rule[key] = 'This field is required';
        isValid = false;
      }
    });

    // Validate steps (all fields are mandatory)
    steps.forEach((step, index) => {
      const stepErrors = {};
      Object.keys(step).forEach((key) => {
        if (!step[key]) {
          stepErrors[key] = 'This field is required';
          isValid = false;
        }
      });
      newErrors.steps[index] = stepErrors;
    });

    setErrors(newErrors);
    return isValid;
  };

  const handleInputChange = (e, stepIndex = null) => {
    const { name, value } = e.target;
    console.log(`Input changed: ${name} = ${value}${stepIndex !== null ? ` (Step ${stepIndex + 1})` : ''}`);
    
    if (stepIndex !== null) {
      setSteps((prevSteps) => {
        const newSteps = [...prevSteps];
        newSteps[stepIndex] = { ...newSteps[stepIndex], [name]: value };
        return newSteps;
      });
      setErrors((prevErrors) => ({
        ...prevErrors,
        steps: prevErrors.steps.map((stepErrors, i) =>
          i === stepIndex ? { ...stepErrors, [name]: '' } : stepErrors
        ),
      }));
    } else {
      setRuleData((prevData) => ({
        ...prevData,
        [name]: value,
        ...(name === 'pnlGroup' ? { ruleGroup: '' } : {}),
      }));
      setErrors((prevErrors) => ({
        ...prevErrors,
        rule: { ...prevErrors.rule, [name]: '' },
      }));
    }
  };

  const handleSelectChange = (e) => {
    const { name, value } = e.target;
    console.log(`Select changed: ${name} = ${value}`);
    setRuleData((prevData) => ({
      ...prevData,
      [name]: value,
      ...(name === 'pnlGroup' ? { ruleGroup: '' } : {}),
    }));
    setErrors((prevErrors) => ({
      ...prevErrors,
      rule: { ...prevErrors.rule, [name]: '' },
    }));
  };

  const handleSave = async () => {
    if (!validateForm()) {
      console.log('Validation failed:', JSON.stringify(errors, null, 2));
      alert('Please fill out all required fields.');
      return;
    }

    // Parse comma-separated columns and filters
    const parseColumns = (input) => input.split(',').map((item) => item.trim()).filter((item) => item);
    const parseFilters = (input) => {
      const filters = input.split(';').map((item) => item.trim()).filter((item) => item);
      return filters.map((filter) => {
        const [name, filterType, values] = filter.split(':').map((item) => item.trim());
        return { name, filterType, values };
      });
    };

    const ruleJson = {
      rules: {
        rule: [
          {
            num: ruleData.num,
            name: ruleData.name,
            desc: ruleData.desc,
            custRefID: ruleData.custRefID,
            ruleGroup: ruleData.ruleGroup,
            isActive: ruleData.isActive,
            Step: steps.map((step, index) => ({
              stepNo: step.stepNo || `${ruleData.num}.${index + 1}`,
              stepName: step.stepName === 'Single Step' ? 'single' : 'multi',
              StepDesc: step.StepDesc,
              stepType: step.stepType,
              isActive: 'Y',
              SourceTable: {
                id: '1',
                Name: step.sourceTable,
              },
              sourceFilters: {
                columns: parseFilters(step.sourceFilters),
              },
              preAggregator: {
                columns: parseColumns(step.preAggregatorColumns),
              },
              join: {
                columns: parseColumns(step.joinColumns),
              },
              allocation: {
                columns: parseColumns(step.allocationColumns),
              },
              driver: {
                driverTableID: step.driverTableID,
                driverWeightColumn: step.driverWeightColumn,
                driverFilters: {
                  columns: parseFilters(step.driverFilters),
                },
              },
            })),
          },
        ],
      },
    };

    console.log('Saving Rule Data:', JSON.stringify(ruleJson, null, 2));

    try {
      const response = await fetch('/api/rules', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: JSON.stringify(ruleJson),
      });

      if (response.ok) {
        console.log('Rule created successfully');
        alert('Rule created successfully!');
        navigate('/');
      } else {
        const errorText = await response.text();
        console.error('Failed to create rule:', response.status, errorText);
        alert(`Failed to create rule: ${errorText || response.statusText}`);
      }
    } catch (error) {
      console.error('Error during API call:', error.message);
      alert('An error occurred while saving the rule. Please try again.');
    }
  };

  const handleCancel = () => {
    console.log('Cancelling rule creation');
    navigate('/');
  };

  const renderTabContent = () => {
    console.log('Rendering tab:', activeTab);
    switch (activeTab) {
      case 'ruleInfo':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Rule Information</h3>
            <div className={styles.formGrid}>
              <div className={styles.gridItem}>
                <McSelect
                  label="PnL Group"
                  name="pnlGroup"
                  value={ruleData.pnlGroup}
                  optionselected={handleSelectChange}
                  placeholder="Select a PnL Group"
                  required
                  invalid={!!errors.rule.pnlGroup}
                  invalidmessage={errors.rule.pnlGroup}
                >
                  {pnlGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
              <div className={styles.gridItem}>
                <McSelect
                  label="Rule Group"
                  name="ruleGroup"
                  value={ruleData.ruleGroup}
                  optionselected={handleSelectChange}
                  placeholder={ruleGroups.length ? "Select a Rule Group" : "Select a PnL Group first"}
                  required
                  disabled={!ruleData.pnlGroup || !ruleGroups.length}
                  invalid={!!errors.rule.ruleGroup}
                  invalidmessage={errors.rule.ruleGroup}
                >
                  {ruleGroups.map((group) => (
                    <McOption key={group} value={group}>
                      {group}
                    </McOption>
                  ))}
                </McSelect>
              </div>
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Number"
                name="num"
                value={ruleData.num}
                input={handleInputChange}
                placeholder="Enter rule number"
                required
                invalid={!!errors.rule.num}
                invalidmessage={errors.rule.num}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Rule Name"
                name="name"
                value={ruleData.name}
                input={handleInputChange}
                placeholder="Enter rule name"
                required
                invalid={!!errors.rule.name}
                invalidmessage={errors.rule.name}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Description"
                name="desc"
                value={ruleData.desc}
                input={handleInputChange}
                placeholder="Enter rule description"
                multiline
                rows={3}
                required
                invalid={!!errors.rule.desc}
                invalidmessage={errors.rule.desc}
              />
            </div>
            <div className={styles.inputGroup}>
              <McInput
                label="Customer Reference ID"
                name="custRefID"
                value={ruleData.custRefID}
                input={handleInputChange}
                placeholder="Enter customer reference ID"
                required
                invalid={!!errors.rule.custRefID}
                invalidmessage={errors.rule.custRefID}
              />
            </div>
            <div className={styles.inputGroup}>
              <McSelect
                label="Is Active"
                name="isActive"
                value={ruleData.isActive}
                optionselected={handleSelectChange}
                required
                invalid={!!errors.rule.isActive}
                invalidmessage={errors.rule.isActive}
              >
                <McOption value="Y">Yes</McOption>
                <McOption value="N">No</McOption>
              </McSelect>
            </div>
          </div>
        );
      case 'step':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Step Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    minutiae
                    label="Step Number"
                    name="stepNo"
                    value={step.stepNo}
                    input={(e) => handleInputChange(e, index)}
                    placeholder={`Enter step number (e.g., ${ruleData.num}.${index + 1})`}
                    required
                    invalid={!!errors.steps[index].stepNo}
                    invalidmessage={errors.steps[index].stepNo}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Name"
                    name="stepName"
                    value={step.stepName}
                    optionselected={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select Step Name"
                    invalid={!!errors.steps[index].stepName}
                    invalidmessage={errors.steps[index].stepName}
                  >
                    <McOption value="Single Step">Single Step</McOption>
                    <McOption value="Multi Step">Multi Step</McOption>
                  </McSelect>
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Step Description"
                    name="StepDesc"
                    value={step.StepDesc}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter step description"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].StepDesc}
                    invalidmessage={errors.steps[index].StepDesc}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McSelect
                    label="Step Type"
                    name="stepType"
                    value={step.stepType}
                    optionselected={(e) => handleInputChange(e, index)}
                    required
                    placeholder="Select Step Type"
                    invalid={!!errors.steps[index].stepType}
                    invalidmessage={errors.steps[index].stepType}
                  >
                    <McOption value="S">S</McOption>
                    <McOption value="M">M</McOption>
                  </McSelect>
                </div>
              </div>
            ))}
            <div className={styles.buttonContainer}>
              <McButton
                label="Add Step"
                appearance="secondary"
                click={addStep}
                className={styles.actionButton}
              />
            </div>
          </div>
        );
      case 'preAggregate':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Pre-Aggregate Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Pre-Aggregator Columns"
                    name="preAggregatorColumns"
                    value={step.preAggregatorColumns}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter columns (comma-separated)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].preAggregatorColumns}
                    invalidmessage={errors.steps[index].preAggregatorColumns}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'source':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Source Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Source Table"
                    name="sourceTable"
                    value={step.sourceTable}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter source table name"
                    required
                    invalid={!!errors.steps[index].sourceTable}
                    invalidmessage={errors.steps[index].sourceTable}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Source Filters"
                    name="sourceFilters"
                    value={step.sourceFilters}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter filters (e.g., PNL_LINE:IN:PnL.DVC.214,PnL.DVC.215;MOVE_TYPE:EQ:EX)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].sourceFilters}
                    invalidmessage={errors.steps[index].sourceFilters}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'join':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Join Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Join Columns"
                    name="joinColumns"
                    value={step.joinColumns}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter columns (comma-separated)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].joinColumns}
                    invalidmessage={errors.steps[index].joinColumns}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'allocation':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Allocation Columns</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Allocation Columns"
                    name="allocationColumns"
                    value={step.allocationColumns}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter columns (comma-separated)"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].allocationColumns}
                    invalidmessage={errors.steps[index].allocationColumns}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      case 'driver':
        return (
          <div className={styles.tabContent}>
            <h3 className={styles.sectionTitle}>Driver Information</h3>
            {steps.map((step, index) => (
              <div key={index} className={styles.stepCase}>
                <h4>Step {index + 1}</h4>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Table ID"
                    name="driverTableID"
                    value={step.driverTableID}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver table ID"
                    required
                    invalid={!!errors.steps[index].driverTableID}
                    invalidmessage={errors.steps[index].driverTableID}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Weight Column"
                    name="driverWeightColumn"
                    value={step.driverWeightColumn}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter driver weight column"
                    required
                    invalid={!!errors.steps[index].driverWeightColumn}
                    invalidmessage={errors.steps[index].driverWeightColumn}
                  />
                </div>
                <div className={styles.inputGroup}>
                  <McInput
                    label="Driver Filters"
                    name="driverFilters"
                    value={step.driverFilters}
                    input={(e) => handleInputChange(e, index)}
                    placeholder="Enter filters"
                    multiline
                    rows={3}
                    required
                    invalid={!!errors.steps[index].driverFilters}
                    invalidmessage={errors.steps[index].driverFilters}
                  />
                </div>
              </div>
            ))}
          </div>
        );
      default:
        return <div className={styles.tabContent}>No Tab Selected</div>;
    }
  };

  return (
    <div className={styles.pageWrapper}>
      <div className={styles.container}>
        <div className={styles.card}>
          <div className={styles.buttonContainer}>
            <McButton
              label="Back"
              appearance="neutral"
              click={handleCancel}
              className={styles.actionButton}
            />
            <McButton
              label="Save"
              appearance="primary"
              click={handleSave}
              className={styles.actionButton}
            />
          </div>

          <div className={styles.tabs}>
            <button
              className={`${styles.tabButton} ${activeTab === 'ruleInfo' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('ruleInfo')}
            >
              Rule Info
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'step' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('step')}
            >
              Step
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'preAggregate' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('preAggregate')}
            >
              Pre-Aggregate
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'source' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('source')}
            >
              Source
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'join' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('join')}
            >
              Join
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'allocation' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('allocation')}
            >
              Allocation
            </button>
            <button
              className={`${styles.tabButton} ${activeTab === 'driver' ? styles.activeTab : ''}`}
              onClick={() => setActiveTab('driver')}
            >
              Driver
            </button>
          </div>

          {renderTabContent()}
        </div>
      </div>
    </div>
  );
};

export default CreateRules;
Launch your own pro-level crypto exchange with Hivelance’s Coinbase Clone Script integrated with AI-powered trading bots. Built for expert traders, it features advanced order types, real-time analytics, top-tier security, and goes live in just 10 days. Fast, customizable, and ready for the bull market!

🔥 Key Features of Coinbase Clone Script

✅ Smart Trading Bots for 24/7 automated trading
✅ Advanced Order Types – Limit, Stop, Market, and more
✅ Real-Time Price Charts & Analytics
✅ High-Speed Matching Engine
✅ Bank-Grade Security Protocols
✅ KYC/AML Integration for Compliance
✅ Multi-Currency & Wallet Support
✅ Responsive Web & Mobile Interface
✅ 100% Customizable & Bug-Free Script
✅ And More

💡 Ideal for entrepreneurs & crypto startups

📞 Contact us now & dominate the crypto trading market!

Website - https://www.hivelance.com/coinbase-clone-script
Telegram - https://telegram.me/HiveLance
Call/WhatsApp - 918438595928
-- ICA_OddTime_PerCard_PerMID_EDC
CREATE TABLE team_kingkong.offus_ICA_OddTime_PerCard_PerMID_EDC_breaches AS
-- INSERT INTO team_kingkong.offus_ICA_OddTime_PerCard_PerMID_EDC_breaches
with offus_txn as
(SELECT transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp, globalcardindex
, case when edc_mid is not null then 'EDC' else 'QR' end as mid_type
, merchantcategory, merchantsubcategory, isindian FROM
    (SELECT DISTINCT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (select distinct transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    , globalcardindex
    , merchantcategory, merchantsubcategory, isindian
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN DATE(DATE'2025-02-01' - INTERVAL '1' DAY) AND DATE'2025-02-28'
    and paymethod in ('CREDIT_CARD','DEBIT_CARD')
    AND actionrecommended <> 'BLOCK') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT DISTINCT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b
ON a.paytmmerchantid = b.edc_mid)

SELECT * FROM
    (SELECT A.transactionid, A.txn_amount, A.txn_date, A.paytmmerchantid, A.globalcardindex, A.mid_type, 'ICA_OddTime_PerCard_PerMID_EDC' AS rule_name, A.txn_timestamp
    , 5000 as per_txn_limit
    , COUNT(B.transactionid) as txn1_day
    , 2 as txn1_day_threshold
    FROM
        (SELECT * FROM offus_txn
        WHERE txn_date BETWEEN date '2025-02-01' AND DATE'2025-02-28'
        AND isindian = 'false'
        AND HOUR(FROM_UNIXTIME(txn_timestamp / 1000)) BETWEEN 0 AND 4
        AND (merchantsubcategory NOT IN ('Restaurant', 'Foodcourt','Restaurants and Bars', 'Fast Food and QSR' , 'Hotel', 'Aviation','Tours and Travel Agency' , 'Pharmacy', 'Hospital','Taxi','Pharmacy', 'Hospital', 'Taxi')
        OR merchantcategory NOT IN ('Airport','Gas and Petrol')))A
    LEFT JOIN
        (SELECT * FROM offus_txn)B
    ON A.globalcardindex = B.globalcardindex AND A.paytmmerchantid = B.paytmmerchantid AND (A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 86400000 -- <= 1day
    AND A.transactionid <> B.transactionid
    GROUP BY 1,2,3,4,5,6,7,8)
WHERE (txn_amount > per_txn_limit) OR (txn1_day>= txn1_day_threshold)
-- LIMIT 100
;
-- RECHECK
-- Merchant_PerTxnLimit_Check

-- DROP TABLE team_kingkong.offus_Merchant_PerTxnLimit_Check_breaches;

-- CREATE TABLE team_kingkong.offus_Merchant_PerTxnLimit_Check_breaches AS
INSERT INTO team_kingkong.offus_Merchant_PerTxnLimit_Check_breaches
SELECT transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp, paymethod
, case when edc_mid is not null then 'EDC' else 'QR' end as mid_type
, C.per_txn_limit
, C.limit_date
FROM
    (SELECT DISTINCT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (select distinct transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , globalcardindex
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    , paymethod
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN DATE(DATE'2025-05-01' - INTERVAL '1' DAY) AND DATE'2025-05-31'
    and paymethod in ('UPI')
    AND actionrecommended <> 'BLOCK' AND responsestatus = 'SUCCESS') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT DISTINCT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b
ON a.paytmmerchantid = b.edc_mid
INNER JOIN
    (SELECT content as mid, CAST(comment AS DOUBLE) as per_txn_limit, "timestamp" as limit_date
    FROM team_kingkong.merchant_limit_list)C
ON a.paytmmerchantid = C.mid AND a.txn_date > DATE(FROM_UNIXTIME(CAST(limit_date AS double) / 1000))
WHERE a.txn_amount > C.per_txn_limit;
-- ICA_OddTime_PerCard_PerMID_EDC
DROP TABLE team_kingkong.offus_ICA_OddTime_PerCard_PerMID_EDC_breaches;

-- CREATE TABLE team_kingkong.offus_ICA_OddTime_PerCard_PerMID_EDC_breaches AS
INSERT INTO team_kingkong.offus_ICA_OddTime_PerCard_PerMID_EDC_breaches
with offus_txn as
(SELECT transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp, globalcardindex
, case when edc_mid is not null then 'EDC' else 'QR' end as mid_type
, merchantcategory, merchantsubcategory, isindian FROM
    (SELECT DISTINCT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (select distinct transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    , globalcardindex
    , merchantcategory, merchantsubcategory, isindian
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN DATE(DATE'2025-05-01' - INTERVAL '1' DAY) AND DATE'2025-05-31'
    and paymethod in ('CREDIT_CARD','DEBIT_CARD')
    AND actionrecommended <> 'BLOCK' AND responsestatus = 'SUCCESS') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT DISTINCT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b
ON a.paytmmerchantid = b.edc_mid)

SELECT * FROM
    (SELECT A.transactionid, A.txn_amount, A.txn_date, A.paytmmerchantid, A.globalcardindex, A.mid_type, 'ICA_OddTime_PerCard_PerMID_EDC' AS rule_name, A.txn_timestamp
    , 5000 as per_txn_limit
    , COUNT(B.transactionid) as txn1_day
    , 2 as txn1_day_threshold
    FROM
        (SELECT * FROM offus_txn
        WHERE txn_date BETWEEN date '2025-05-01' AND DATE'2025-05-31'
        AND isindian = 'false'
        AND HOUR(FROM_UNIXTIME(txn_timestamp / 1000)) BETWEEN 0 AND 4
        AND (merchantsubcategory NOT IN ('Restaurant', 'Foodcourt','Restaurants and Bars', 'Fast Food and QSR' , 'Hotel', 'Aviation','Tours and Travel Agency' , 'Pharmacy', 'Hospital','Taxi','Pharmacy', 'Hospital', 'Taxi')
        OR merchantcategory NOT IN ('Airport','Gas and Petrol')))A
    LEFT JOIN
        (SELECT * FROM offus_txn)B
    ON A.globalcardindex = B.globalcardindex AND A.paytmmerchantid = B.paytmmerchantid AND (A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 86400000 -- <= 1day
    AND A.transactionid <> B.transactionid
    GROUP BY 1,2,3,4,5,6,7,8)
WHERE (txn_amount > per_txn_limit) OR (txn1_day>= txn1_day_threshold)
;
-- CCUPI_vpa_mid_hourly_limit
DROP TABLE team_kingkong.offus_CCUPI_vpa_mid_hourly_limit_breaches;

-- CREATE TABLE team_kingkong.offus_CCUPI_vpa_mid_hourly_limit_breaches AS
INSERT INTO team_kingkong.offus_CCUPI_vpa_mid_hourly_limit_breaches
with offus_txn as
(SELECT DISTINCT transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp, small_vpa
, case when edc_mid is not null then 'EDC' else 'QR' end as mid_type FROM
    (SELECT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (select transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    , small_vpa
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN DATE(DATE'2025-05-01' - INTERVAL '1' DAY) AND DATE'2025-05-31'
    AND isupicc = 'true' AND actionrecommended <> 'BLOCK' AND responsestatus = 'SUCCESS') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b
ON a.paytmmerchantid = b.edc_mid)

SELECT * FROM
    (SELECT A.transactionid, A.txn_amount, A.txn_date, A.paytmmerchantid, A.small_vpa, A.mid_type, 'CCUPI_vpa_mid_hourly_limit' AS rule_name, A.txn_timestamp
    , COUNT(B.transactionid) as txn1_hr
    , 3 as txn1_hr_threshold
    , SUM(B.txn_amount) as amt1_hr
    , 2000 as amt1_hr_threshold
    FROM
        (SELECT * FROM offus_txn
        WHERE txn_date BETWEEN date '2025-05-01' AND DATE'2025-05-31')A
    INNER JOIN
        (SELECT * FROM offus_txn)B
    ON A.small_vpa = B.small_vpa AND A.paytmmerchantid = B.paytmmerchantid AND (A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 3600000 -- <= 1hour
    AND A.transactionid <> B.transactionid
    GROUP BY 1,2,3,4,5,6,7,8)
WHERE (txn1_hr >= txn1_hr_threshold) AND ((amt1_hr + txn_amount) > amt1_hr_threshold)
;
-- CCUPI_vpa_mid_daily_limit
-- DROP TABLE team_kingkong.offus_CCUPI_vpa_mid_daily_limit_breaches;

-- CREATE TABLE team_kingkong.offus_CCUPI_vpa_mid_daily_limit_breaches AS
INSERT INTO team_kingkong.offus_CCUPI_vpa_mid_daily_limit_breaches
with offus_txn as
(SELECT transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp, small_vpa
, case when edc_mid is not null then 'EDC' else 'QR' end as mid_type FROM
    (SELECT DISTINCT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (
    select distinct transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    , small_vpa
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN DATE(DATE'2025-05-01' - INTERVAL '1' DAY) AND DATE'2025-05-31'
    -- and paymethod in ('CREDIT_CARD','DEBIT_CARD','EMI','EMI_DC')
    AND isupicc = 'true' AND actionrecommended <> 'BLOCK') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT DISTINCT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b
ON a.paytmmerchantid = b.edc_mid)

SELECT * FROM
    (SELECT A.transactionid, A.txn_amount, A.txn_date, A.paytmmerchantid, A.small_vpa, A.mid_type, 'CCUPI_vpa_mid_daily_limit' AS rule_name, A.txn_timestamp
    , COUNT(B.transactionid) as txn1_day
    , 5 as txn1_day_threshold
    FROM
        (SELECT * FROM offus_txn
        WHERE txn_date BETWEEN date '2025-05-01' AND DATE'2025-05-31'
        AND txn_amount <= 2000)A
    INNER JOIN
        (SELECT * FROM offus_txn)B
    ON A.small_vpa = B.small_vpa AND A.paytmmerchantid = B.paytmmerchantid AND (A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 3600000 -- <= 1hour
    AND A.transactionid <> B.transactionid
    GROUP BY 1,2,3,4,5,6,7,8)
WHERE txn1_day >= txn1_day_threshold
;
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":aboriginal_flag: Xero Boost Days - What's On :aboriginal_flag:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Good Morning Meanjin, and welcome to Reconciliation Week!\n\n *Reconciliation Week (27 May – 3 June)* is a time for all Australians to learn about our shared histories, cultures, and achievements, and to explore how we can contribute to reconciliation in Australia. \n\n For Aboriginal and Torres Strait Islander peoples, it’s a powerful reminder of ongoing resilience, the importance of truth-telling, and the work still needed to achieve genuine equity and justice."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-3: Monday, 26th May",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n:coffee: *Café Partnership*: Enjoy free coffee and café-style beverages from our Cafe partner *Edwards*.\n:Lunch: *Lunch*: from *12pm* in the kitchen.\n:massage:*Wellbeing*: Pilates at *SP Brisbane City* is bookable every Monday!"
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-28: Wednesday, 28th May",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":coffee: *Café Partnership*: Enjoy free coffee and café-style beverages from our Cafe partner *Edwards*.\n:lunch: *Reconciliation Week Morning Tea* provided by <https://jarrahcatering.com.au/|Jarrah Catering> from *9:30am* in the kitchen! \n\n :microphone: We will also have a video of our very own *Tony Davison* talking on behalf of the #reconciliation-au ERG around Xero's Reconciliation plan and what this week means! \n\n :slack: In the thread we will also have a special background for you to use on Google Meet if you would like to participate."
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":calendar-date-30: Friday, 30th May",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":cheers-9743: *Social Happy Hour* from 3pm - 4pm in the kitchen! Wind down for the week over some delicious drinks & nibbles."
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "Stay tuned to this channel for more details, check out the <https://calendar.google.com/calendar/u/0?cid=Y19uY2M4cDN1NDRsdTdhczE0MDhvYjZhNnRjb0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t|*Brisbane Social Calendar*>, and get ready to Boost your workdays!\n\nLove,\nWX Team :party-wx:"
			}
		}
	]
}
<?php
// Exit if accessed directly
if ( !defined( 'ABSPATH' ) ) exit;

// BEGIN ENQUEUE PARENT ACTION
// AUTO GENERATED - Do not modify or remove comment markers above or below:

if ( !function_exists( 'chld_thm_cfg_locale_css' ) ):
    function chld_thm_cfg_locale_css( $uri ){
        if ( empty( $uri ) && is_rtl() && file_exists( get_template_directory() . '/rtl.css' ) )
            $uri = get_template_directory_uri() . '/rtl.css';
        return $uri;
    }
endif;
add_filter( 'locale_stylesheet_uri', 'chld_thm_cfg_locale_css' );
         
if ( !function_exists( 'child_theme_configurator_css' ) ):
    function child_theme_configurator_css() {
        wp_enqueue_style( 'chld_thm_cfg_child', trailingslashit( get_stylesheet_directory_uri() ) . 'style.css', array( 'hello-elementor','hello-elementor','hello-elementor-theme-style','hello-elementor-header-footer' ) );
    }
endif;
add_action( 'wp_enqueue_scripts', 'child_theme_configurator_css', 10 );

// END ENQUEUE PARENT ACTION



/*
Theme Name: Hello Elementor Child
Theme URI: https://elementor.com/hello-theme/?utm_source=wp-themes&utm_campaign=theme-uri&utm_medium=wp-dash
Template: hello-elementor
Author: Elementor Team
Author URI: https://elementor.com/?utm_source=wp-themes&utm_campaign=author-uri&utm_medium=wp-dash
Description: Hello Elementor is a lightweight and minimalist WordPress theme that was built specifically to work seamlessly with the Elementor site builder plugin. The theme is free, open-source, and designed for users who want a flexible, easy-to-use, and customizable website. The theme, which is optimized for performance, provides a solid foundation for users to build their own unique designs using the Elementor drag-and-drop site builder. Its simplicity and flexibility make it a great choice for both beginners and experienced Web Creators.
Tags: accessibility-ready,flexible-header,custom-colors,custom-menu,custom-logo,featured-images,rtl-language-support,threaded-comments,translation-ready
Version: 3.3.0.1744009168
Updated: 2025-04-07 06:59:28

*/

<?php
// Exit if accessed directly
if ( !defined( 'ABSPATH' ) ) exit;

// BEGIN ENQUEUE PARENT ACTION
// AUTO GENERATED - Do not modify or remove comment markers above or below:

if ( !function_exists( 'chld_thm_cfg_locale_css' ) ):
    function chld_thm_cfg_locale_css( $uri ){
        if ( empty( $uri ) && is_rtl() && file_exists( get_template_directory() . '/rtl.css' ) )
            $uri = get_template_directory_uri() . '/rtl.css';
        return $uri;
    }
endif;
add_filter( 'locale_stylesheet_uri', 'chld_thm_cfg_locale_css' );
         
if ( !function_exists( 'child_theme_configurator_css' ) ):
    function child_theme_configurator_css() {
        wp_enqueue_style( 'chld_thm_cfg_child', trailingslashit( get_stylesheet_directory_uri() ) . 'style.css', array( 'hello-elementor','hello-elementor','hello-elementor-theme-style','hello-elementor-header-footer' ) );
    }
endif;
add_action( 'wp_enqueue_scripts', 'child_theme_configurator_css', 10 );

// END ENQUEUE PARENT ACTION



/*
Theme Name: Hello Elementor Child
Theme URI: https://elementor.com/hello-theme/?utm_source=wp-themes&utm_campaign=theme-uri&utm_medium=wp-dash
Template: hello-elementor
Author: Elementor Team
Author URI: https://elementor.com/?utm_source=wp-themes&utm_campaign=author-uri&utm_medium=wp-dash
Description: Hello Elementor is a lightweight and minimalist WordPress theme that was built specifically to work seamlessly with the Elementor site builder plugin. The theme is free, open-source, and designed for users who want a flexible, easy-to-use, and customizable website. The theme, which is optimized for performance, provides a solid foundation for users to build their own unique designs using the Elementor drag-and-drop site builder. Its simplicity and flexibility make it a great choice for both beginners and experienced Web Creators.
Tags: accessibility-ready,flexible-header,custom-colors,custom-menu,custom-logo,featured-images,rtl-language-support,threaded-comments,translation-ready
Version: 3.3.0.1744009168
Updated: 2025-04-07 06:59:28

*/

-- MONTHLY BREACH COUNT
-- team_kingkong.offus_MID_CCDC_Daily_TXN_limit_Check_breaches
SELECT substr(cast(txn_date as varchar(30)), 1, 7) as year_month
, COUNT(transactionid) AS breach_cnt
, SUM(txn_amount) as breach_amt
, COUNT(DISTINCT globalcardindex) as card_cnt
FROM
(SELECT DISTINCT *
FROM team_kingkong.offus_MID_CCDC_Daily_TXN_limit_Check_breaches)
GROUP BY 1 ORDER BY 1;

-- team_kingkong.offus_edc_card_velocity_count_breaches
SELECT substr(cast(txn_date as varchar(30)), 1, 7) as year_month
, COUNT(transactionid) AS breach_cnt
, SUM(txn_amount) as breach_amt
, COUNT(DISTINCT globalcardindex) as card_cnt
FROM
(SELECT DISTINCT *
FROM team_kingkong.offus_edc_card_velocity_count_breaches)
GROUP BY 1 ORDER BY 1;

-- team_kingkong.offus_Merchant_PerTxnLimit_Check_breaches
SELECT substr(cast(txn_date as varchar(30)), 1, 7) as year_month
, COUNT(transactionid) AS breach_cnt
, SUM(txn_amount) as breach_amt
-- , COUNT(DISTINCT globalcardindex) as card_cnt
FROM
(SELECT DISTINCT *
FROM team_kingkong.offus_Merchant_PerTxnLimit_Check_breaches)
GROUP BY 1 ORDER BY 1;

-- team_kingkong.offus_edc_card_velocity_amount_breaches
SELECT substr(cast(txn_date as varchar(30)), 1, 7) as year_month
, COUNT(transactionid) AS breach_cnt
, SUM(txn_amount) as breach_amt
, COUNT(DISTINCT globalcardindex) as card_cnt
FROM
(SELECT DISTINCT *
FROM team_kingkong.offus_edc_card_velocity_amount_breaches)
GROUP BY 1 ORDER BY 1;

-- team_kingkong.offus_MID_UPI_Daily_TXN_limit_Check_breaches
SELECT substr(cast(txn_date as varchar(30)), 1, 7) as year_month
, COUNT(transactionid) AS breach_cnt
, SUM(txn_amount) as breach_amt
, COUNT(DISTINCT globalcardindex) as card_cnt
FROM
(SELECT DISTINCT *
FROM team_kingkong.offus_MID_UPI_Daily_TXN_limit_Check_breaches)
GROUP BY 1 ORDER BY 1;

-- team_kingkong.offus_oil_gas_dc_limit_EDC_breaches
SELECT substr(cast(txn_date as varchar(30)), 1, 7) as year_month
, COUNT(transactionid) AS breach_cnt
, SUM(txn_amount) as breach_amt
, COUNT(DISTINCT globalcardindex) as card_cnt
FROM
(SELECT DISTINCT *
FROM team_kingkong.offus_oil_gas_dc_limit_EDC_breaches)
GROUP BY 1 ORDER BY 1;

-- team_kingkong.offus_ICA_Unsafe_Country_Transactions_breaches
SELECT substr(cast(txn_date as varchar(30)), 1, 7) as year_month
, COUNT(transactionid) AS breach_cnt
, SUM(txn_amount) as breach_amt
, COUNT(DISTINCT globalcardindex) as card_cnt
FROM
(SELECT DISTINCT *
FROM team_kingkong.offus_ICA_Unsafe_Country_Transactions_breaches)
GROUP BY 1 ORDER BY 1;
-- RECHCECKING
-- OFFUS : edc_card_velocity_amount
-- edc_card_velocity_amount 

-- DROP TABLE IF EXISTS team_kingkong.offus_edc_card_velocity_amount_breaches2;
-- DROP TABLE IF EXISTS team_kingkong.offus_edc_card_velocity_amount_breaches;

-- CREATE TABLE team_kingkong.offus_edc_card_velocity_amount_breaches AS
INSERT INTO team_kingkong.offus_edc_card_velocity_amount_breaches
with offus_txn as
(SELECT DISTINCT globalcardindex, transactionid, txn_amount, txn_date, paytmmerchantid, txn_timestamp FROM
    (SELECT DISTINCT pg_mid from cdo.total_offline_merchant_base_snapshot_v3) f
INNER join
    (select transactionid
    , cast(eventamount as double)/100 as txn_amount
    , paytmmerchantid
    , globalcardindex
    , DATE(dl_last_updated) AS txn_date
    , CAST(velocitytimestamp AS DOUBLE) AS txn_timestamp
    from cdp_risk_transform.maquette_flattened_offus_snapshot_v3
    where dl_last_updated BETWEEN date'2025-05-01' AND DATE'2025-05-31'
    and paymethod in ('CREDIT_CARD','DEBIT_CARD','EMI','EMI_DC')
    AND actionrecommended <> 'BLOCK' AND responsestatus = 'SUCCESS'
    AND paytmmerchantid IS NOT NULL AND paytmmerchantid <> '' AND paytmmerchantid <> ' '
    AND globalcardindex IS NOT NULL AND globalcardindex <> '' AND globalcardindex <> ' ') a
on a.paytmmerchantid = f.pg_mid
LEFT JOIN
    (SELECT mid AS edc_mid FROM paytmpgdb.entity_edc_info_snapshot_v3
    WHERE terminal_status = 'ACTIVE' AND dl_last_updated >= DATE '2010-01-01') b 
ON a.paytmmerchantid = b.edc_mid)

SELECT * FROM 
    (SELECT A.globalcardindex, A.transactionid, A.txn_amount, A.txn_date, A.paytmmerchantid, 'edc_card_velocity_amount' AS rule_name, A.txn_timestamp
    , SUM(IF((A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 21600000, B.txn_amount, NULL)) AS amt6_hr
    , 1000000 AS amt6_hr_threshold
    , SUM(B.txn_amount) as amt24_hr
    , 2000000 AS amt4_hr_threshold
    FROM
        (SELECT * FROM offus_txn
        WHERE txn_date  BETWEEN date'2025-05-01' AND DATE'2025-05-31')A
    INNER JOIN
        (SELECT * FROM offus_txn)B
    ON A.globalcardindex = b.globalcardindex AND A.paytmmerchantid = B.paytmmerchantid AND A.transactionid <> B.transactionid
    AND (A.txn_timestamp - B.txn_timestamp) BETWEEN 0 AND 86400000 -- <= 1d
    GROUP BY 1,2,3,4,5,6,7)
WHERE ((amt6_hr + txn_amount) > 1000000) OR ((amt24_hr + txn_amount) > 2000000);
star

Tue May 27 2025 05:51:09 GMT+0000 (Coordinated Universal Time) https://kanhasoft.com/custom-amazon-seller-tools.html

@kanhasoft #amazonsellers tools #customamazon sellers tools #sellerstools

star

Tue May 27 2025 05:48:53 GMT+0000 (Coordinated Universal Time) https://kanhasoft.com/mobile-app-development.html

@kanhasoft #mobileapplication development #mobileapplication #appdevelopment #ai-poweredapplications

star

Tue May 27 2025 05:45:04 GMT+0000 (Coordinated Universal Time) https://kanhasoft.com/web-app-development.html

@kanhasoft

star

Tue May 27 2025 00:40:59 GMT+0000 (Coordinated Universal Time)

@Y@sir

star

Mon May 26 2025 20:36:58 GMT+0000 (Coordinated Universal Time) https://www.linuxuntu.com/install-safari-linux/

@v1ral_ITS

star

Mon May 26 2025 20:32:51 GMT+0000 (Coordinated Universal Time) https://www.linuxuntu.com/install-safari-linux/

@v1ral_ITS

star

Mon May 26 2025 18:39:09 GMT+0000 (Coordinated Universal Time)

@krisha_joshi

star

Mon May 26 2025 17:43:19 GMT+0000 (Coordinated Universal Time) https://www.reddit.com/r/AskTechnology/comments/11la29u/whatsapp_desktop_taking_up_42_gb_of_disc_space/?tl=de

@2late #whatsapp

star

Mon May 26 2025 15:39:06 GMT+0000 (Coordinated Universal Time)

@krisha_joshi

star

Mon May 26 2025 10:31:13 GMT+0000 (Coordinated Universal Time) https://www.coinsclone.com/otc-trading-platform-development/

@CharleenStewar #otctradingplatform #otc trading platform development

star

Mon May 26 2025 10:23:30 GMT+0000 (Coordinated Universal Time) https://maticz.com/ico-development

@Abiraminounq

star

Mon May 26 2025 10:11:49 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Mon May 26 2025 10:11:17 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Mon May 26 2025 10:10:45 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Mon May 26 2025 10:10:06 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Mon May 26 2025 08:49:21 GMT+0000 (Coordinated Universal Time)

@iliavial #c#

star

Mon May 26 2025 04:01:20 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Mon May 26 2025 04:00:42 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Mon May 26 2025 04:00:12 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Mon May 26 2025 03:59:35 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Mon May 26 2025 03:58:59 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Mon May 26 2025 03:56:55 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Mon May 26 2025 03:55:30 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Mon May 26 2025 03:54:36 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Mon May 26 2025 01:46:04 GMT+0000 (Coordinated Universal Time) https://filebrowser.org/installation

@v1ral_ITS

star

Mon May 26 2025 01:41:17 GMT+0000 (Coordinated Universal Time) https://launchpad.net/~christian-boxdoerfer/+archive/ubuntu/fsearch-stable

@v1ral_ITS

star

Mon May 26 2025 00:39:02 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Sun May 25 2025 08:07:11 GMT+0000 (Coordinated Universal Time) https://github.com/theapache64/SoundCloud-Downloader

@Evil_Smile

star

Sun May 25 2025 01:04:14 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Sat May 24 2025 23:25:24 GMT+0000 (Coordinated Universal Time)

@BilalRaza12

star

Sat May 24 2025 23:24:23 GMT+0000 (Coordinated Universal Time)

@BilalRaza12

star

Sat May 24 2025 23:17:52 GMT+0000 (Coordinated Universal Time)

@BilalRaza12

star

Sat May 24 2025 11:27:44 GMT+0000 (Coordinated Universal Time) https://www.firekirin.xyz:8888/Store.aspx

@cholillo18

star

Sat May 24 2025 10:35:25 GMT+0000 (Coordinated Universal Time) https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata

@cholillo18

star

Sat May 24 2025 06:44:32 GMT+0000 (Coordinated Universal Time) https://www.beleaftechnologies.com/crypto-algo-trading-bot-development

@raydensmith #crypto #algo #cryptoalgotradingbot

star

Fri May 23 2025 11:48:34 GMT+0000 (Coordinated Universal Time)

@mastaklance

star

Fri May 23 2025 11:14:19 GMT+0000 (Coordinated Universal Time)

@krisha_joshi

star

Fri May 23 2025 11:13:40 GMT+0000 (Coordinated Universal Time)

@krisha_joshi

star

Fri May 23 2025 11:03:10 GMT+0000 (Coordinated Universal Time) https://www.hivelance.com/coinbase-clone-script

@peterkester96 #coinbase #coinbaseclonescript

star

Fri May 23 2025 09:17:00 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Fri May 23 2025 09:16:30 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Fri May 23 2025 09:15:59 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Fri May 23 2025 06:44:07 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Fri May 23 2025 05:54:58 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Fri May 23 2025 02:21:08 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Thu May 22 2025 14:33:37 GMT+0000 (Coordinated Universal Time) https://www.techigator.ae/services/logo-design-dubai

@Ethanturner

star

Thu May 22 2025 14:00:32 GMT+0000 (Coordinated Universal Time)

@BilalRaza12

star

Thu May 22 2025 14:00:16 GMT+0000 (Coordinated Universal Time)

@BilalRaza12

star

Thu May 22 2025 09:58:48 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Thu May 22 2025 09:58:47 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

Save snippets that work with our extensions

Available in the Chrome Web Store Get Firefox Add-on Get VS Code extension