Change tooltip text of hrizontal bar names in apexcharts

I’m working with charts and specifically with Apexcharts. Is there a way to change the tooltip text that appears when cursor is over the bar name?
The default situation show the same name of the bar in its tooltip, but I’d like to show other informations.
enter image description here

As you can see in the picture above I’d like to change the “Play maker” tooltip in the white box with a different text.

Thanks

Keep the show class as it is when i click elsewhere on the screen

I’m using Bootstrap 4, and my HTML structure is set up correctly. I’ve also added JavaScript to prevent the .show class from being removed, but it’s not working as expected. I want the .show class to persist even after clicking anywhere on the screen. However, currently, whenever I click outside, the .show class is removed automatically. How can I prevent this behavior? i am talking about the dropdown legal-doc-dropdown show in this show class.

<div class="dropdown legal-doc-dropdown show">
    <button type="button" class="drop--down--btn dropdown-toggle" data-toggle="dropdown">
        Policy
    </button>
    <div class="dropdown-menu show">
        <a class="dropdown-item active" href="#">Privacy Policy</a>
        <a class="dropdown-item" href="#">Subscription Cancellation</a>
        <a class="dropdown-item" href="#">Cookies Policy</a>
        <a class="dropdown-item" href="#">Supported Currencies</a>
        <a class="dropdown-item" href="#">Acceptable Use Policy</a>
    </div>
</div>

<div class="dropdown legal-doc-dropdown">
    <button type="button" class="drop--down--btn dropdown-toggle" data-toggle="dropdown">
        Connected Account Agreement
    </button>
    <div class="dropdown-menu">
        <a class="dropdown-item active" href="#">Dropdown 1</a>
        <a class="dropdown-item" href="#">Dropdown 2</a>
        <a class="dropdown-item" href="#">Dropdown 3</a>
        <a class="dropdown-item" href="#">Dropdown 4</a>
        <a class="dropdown-item" href="#">Dropdown 5</a>
    </div>
</div>

<div class="dropdown legal-doc-dropdown">
    <button type="button" class="drop--down--btn dropdown-toggle" data-toggle="dropdown">
        Payments Company Terms
    </button>
    <div class="dropdown-menu">
        <a class="dropdown-item active" href="#">Dropdown 1</a>
        <a class="dropdown-item" href="#">Dropdown 2</a>
        <a class="dropdown-item" href="#">Dropdown 3</a>
        <a class="dropdown-item" href="#">Dropdown 4</a>
        <a class="dropdown-item" href="#">Dropdown 5</a>
    </div>
</div>

JS - 

jQuery(document).ready(function () {
    // Open the first dropdown on page load
    jQuery(".legal-doc-dropdown:first .dropdown-menu").addClass("show");
    jQuery(".legal-doc-dropdown:first").addClass("show");

    // Click event for dropdowns
    jQuery(".dropdown-toggle").click(function (event) {
        event.stopPropagation(); // Prevent event bubbling

        var $thisDropdown = jQuery(this).next(".dropdown-menu");
        var $thisParent = jQuery(this).closest(".legal-doc-dropdown");

        // Check if the clicked dropdown is already open
        var isOpen = $thisParent.hasClass("show");

        // Close all dropdowns
        jQuery(".dropdown-menu").removeClass("show");
        jQuery(".legal-doc-dropdown").removeClass("show");

        // If it was not open, then open it
        if (!isOpen) {
            $thisDropdown.addClass("show");
            $thisParent.addClass("show");
        }
    });

    // Prevent dropdown from closing when clicking inside it
    jQuery(".dropdown-menu").click(function (event) {
        event.stopPropagation();
    });
});

How to refer to static field in mapped types in TypeScript?

I can get a type representing class members like this:

    class ClassWithFields {
        public instanceMember: {a: string, b: number};
        public secondInstanceMember: {q: "q"};
        public static staticMember: {c: string, d: Date};
    }
    type getClassFieldType<TClass, TKey extends keyof TClass> = TClass[TKey];
    type getClassFieldFields<TClass, TKey extends keyof TClass> = keyof TClass[TKey];
    
    /**
     * Resolves to:
     *      const testClassFieldType: {
     *           q: "q";
     *      }
     * IDE suggests following things for second template arg: instanceMember, secondInstanceMember
     */
    const testClassFieldType: getClassFieldType<ClassWithFields, "secondInstanceMember">;

    /**
     * Resolves to:
     *     const testGetClassFieldFields: "a" | "b"
     */
    const testGetClassFieldFields: getClassFieldFields<ClassWithFields, "instanceMember">;

I want the same, specifically the list of fields, in a static field. That is I want a type that given ClassWithFields and "staticMember", should resolve to key list containing "c"|"d".

Show useQuery is still polling when either initial fetch or refetch?

I am using v4 of useQuery to poll and re-poll an endpoint until the correct data is fetched, or until a max re-poll count has been met.

However, I’m not sure how I can most effectively that the loading/re-polling is finished at the point where either:

  1. Received all of the data I need in order to progress (may be less re-polls than the max)
  2. Reached my max re-poll limit

Currently I am using a useState to capture whether it’s still polling:

const [isPolling, setIsPolling] = useState(true);

and changing the value of this within the refetchInterval based on whether I am at the maximum re-poll limit, or if I have the data I need.

refetchInterval: (result, query) => {
  const pollingEnded = result.data.stillWaitingForResults || query.state.dataUpdateCount < MAX_RE_POLL_COUNT;

  setIsPolling(!pollingEnded);
  return hasPollingEnded ? false : REFETCH_INTERVAL;
},

This is then used to return an object to show if the useQuery function is still polling etc.

return {
  isLoading: isPolling,
  ...
}

However, I’m not entirely sure that this is the “correct” way to do this.

I know that there are several props as part of the request result that can indicate the status of the useQuery call, such as:

  • fetchStatus – “fetching” whenever queryFn is executing, including initial loading and background fetches
  • isFetching – Derived from the fetchStatus
  • isLoading – Derived from the status
  • isRefetching – True whenever background refetching (does not include initial loading) – Equivalent to isFetching && !isLoading

Is there a better way in which I can achieve this with the props returned by the useQuery, as I’m not 100% clear which would show all the loading statuses of the query such as initial and refetch poll?

Add KID and ALG in jose JWKS for generated key pair

I’ve generated a private/public key pair with Jose.generateKeyPair() and I exported the public key with Jose.exportJWK()

But the exported key does not have a KID or ALG property. Is there a way to generate these properties, or do I have to add them manually? Or, conversely, do I need to go about this differently?

import { NextResponse } from "next/server"
import { generateKeyPair, exportJWK } from "jose"

export async function GET(request: Request) {
    const { publicKey, privateKey } = await generateKeyPair("RS256")
    const exportedPublicKey = await exportJWK(publicKey)

    return NextResponse.json({
        keys: [exportedPublicKey]
    })
}

jest setupFilesAfterEnv spyOn not triggering

I am trying to create a spyOn in my jest setupFilesAfterEnv to detect network/axios calls and check URL. If the URL is a real URL ie does not contain ‘fake’, then throw an error. If it passes, continue as normal.

user.service.ts

const USER_DETAILS_API = 'https://home.details/rest';
const USER_INVOICE_API = 'https://home.invoice/rest';

export async function getUserDetails(userId) {
    return axios.get(USER_DETAILS_API = '/' + userId, {
        responseType: 'json'
    })
}

user.service.spec.ts

import axios from 'axios';

import * as UserService from '../user.service';


describe('UserService test', () => {
    beforeEach(() => {
        jest.restoreAllMocks();
    });

    it('should filter and get the correct value', () => {
        const getSpy = jest.spyOn(axios, 'get').mockReturnValue(Promise.resolve({
        data: {//blah}
    });

    await UserService.getUserDetails('123');
    expect(getSpy).toBeCalledTimes(1);
  });  
});

setupFilesAfterEnv.js

import axios from 'axios';

global.beforeEach(() => {
        console.log('before each');
        jest.spyOn(axios, 'get').mockImplementation((url, ...args) => {
            console.log('spying from setup file..', url);

            if(url && url.includes('.fake')) {
                return Promsie.resolve({data: {}})
            }
            else {
                return throwError('Live API called: ', url)
            }
        });
});

When i run my test, i see the following logs:

jest --verbose --config ./jest.config.cjs
console.log
  before each

How can I reduce excess space on the left in react-native-picker/picker?

Question

How can I reduce excess space on the left in react-native-picker/picker?

Background

I want to use scrolling pickers in multiple places on a screen. These pickers will need to be efficient in their spacing because there will be some next each other. So I want the pressable scrolling areas to be just the size of the element selected. In most cases these are just numbers of single or double digits.

I have made an example here where I have played with the different stylings, but in all cases there is space on the left that I cannot reduce any further. The parameter I have been adjusting is the width (in styles.picker) and if I make that width any smaller it will cut off the selected element.

Any suggestions?

Screenshot of the issue

enter image description here

Below is code to replicate this issue:

import { StyleSheet, Text, View } from "react-native";
import { Picker } from "@react-native-picker/picker";
import { useState } from "react";

export default function NativePicker02({ navigation }) {
  const [selectedNumber, setSelectedNumber] = useState(0);

  return (
      <View style={styles.container}>
        <Text>NativePicker02</Text>
        <View style={styles.vwPickerSelection}>
          <Text>selected: {selectedNumber}</Text>
        </View>
        <View style={styles.vwPicker}>
          <Picker
            selectedValue={selectedNumber}
            onValueChange={(itemValue, itemIndex) =>
              setSelectedNumber(itemValue)
            }
            style={styles.picker}
            itemStyle={{
              backgroundColor: "black",
              height: 50,
              justifyContent: "center",
              alignItems: "center",
              color: "white",
              width: 50,
            }}
          >
            <Picker.Item label="1" value="1" />
            <Picker.Item label="2" value="2" />
            <Picker.Item label="3" value="3" />
            <Picker.Item label="4" value="4" />
          </Picker>
        </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "gray",
  },
  vwPickerSelection: {
    backgroundColor: "white",
  },
  vwPicker: {
    width: 55,
    alignItems: "center", 
    justifyContent: "center",
    overflow: "hidden", 
  },
  picker: {
    height: 50,
    width: 50,
  },
});

Repository triggers first and then decorator

I have nest.js project, where i have decorator which is getting records of images by the id what is provided.

import { AfterLoad } from 'typeorm';
import { AppDataSource } from 'src/config/ormconfig';
import { Image } from '../modules/image/entities/image.entity';

export function WithImages() {
  return function (target: any) {
    target.prototype.loadImages = function () {
      console.log('AfterLoad triggered for:', this.id);
      this.fetchImages();
    };

    target.prototype.fetchImages = async function () {
      console.log('Fetching images for:', this.id);
      try {
        // Use the already initialized data source
        if (!AppDataSource.isInitialized) {
          await AppDataSource.initialize();
        }
        const imageRepository = AppDataSource.getRepository(Image);

        this.images = await imageRepository.find({
          where: { entityId: this.id },
        });
        console.log('result from decorator: ', this);

        // console.log('Images loaded:', this.images);
      } catch (error) {
        console.error('Error fetching images:', error);
      }
    };

    AfterLoad()(target.prototype, 'loadImages');
  };
}

and then in my user entity am using it like this:

@WithImages()
export class User extends BaseEntity {

  // /** Virtual images field */
  images: Image[];

}

I added console logs on decorator and on the repository to. How I see first is triggered the repository data and then the decorator data. I think because of that I don’t get images field in my response. So how can I fix it?

react devextreme form validation in tab item childcomponent not validating

can someone please help me figure out why my validation doesnt work for the dynamic child component inside the second tabitem ? In my main component i have tabs, first tabitem is form which is in the main component and for this it works perfectly but when i am on the second tab with dynamic form (it returns different forms based on a selectbox choice in the first tab) it doesnt seem to be validating the form.

for some reason dokladValidationRef.current is always null when i am on the second tab trying to validate it

const requestValidationRef = createRef<ValidationGroupRef>();
const invoiceValidationRef = createRef<ValidationGroupRef>();

  const handleValidate = (): boolean => {
    let isValid = true;

    if (ziadostValidationRef.current) {
      const requestResult = ziadostValidationRef.current?.instance().validate();
      isValid = isValid && requestResult.isValid;
    }

    if (dokladValidationRef.current) {
      const invoiceResult = dokladValidationRef.current?.instance().validate();
      isValid = isValid && invoiceResult.isValid;
    }

    return isValid;
  };



 <TabItems>
            <TabItem id="Request" active={activeTab === "Request"}>
              <ValidationGroup ref={requestValidationRef}>
               <TextBox className="form-control" value={detail?.Title} onValueChanged={(e) => handleRequestChange("Title", e.value)}>
                      <Validator>
                        <RequiredRule message="Title is required" />
                      </Validator>
                    </TextBox>
//..
//..
//other fields with validators

              </ValidationGroup>
            </TabItem>
            <TabItem id="Invoice" active={activeTab === "Invoice"}>
                <ValidationGroup ref={invoiceValidationRef}>
              {renderForm(requestLayout?.Form)}
            </ValidationGroup>
            </TabItem>
          </TabItems>

handleValidate is function which gets triggered on button click to save data (and if its not valid it shouldnt create the record)

renderForm function just returns tsx component with form textbox etc with validators

thank you for any help

Supabase search with “or” in nextjs project typescript

In this code, search.column is an array of column names, and search.value is the search term.

The goal is to search for records where any of the specified columns contain the search term (case-insensitive).

"use server"
let query = supabase.from(tableName).select("*", count);
    
if (search && search.columns.length > 0 && search.value.trim()) {
    const searchFilters = search.columns
      .map((column) => `${column}.ilike.%${search.value}%`)
      .join(',');
    query = query.or(searchFilters);
}

I expected to have data in the array based on the search no matter which column matched instead I got no data despite the search.value I sent is from a column named "title"

Checkbox style accordion causing entire page to jump when closing panels

Im working on a checkbox style accordion that works perfectly but judders the page when closing an open panel. I tried to add JavaScript to dynamically calculate and set the height of the content when the accordion is expanded but that didn’t work. Would appreciate any insight into this issue. Should I just abandon the checkbox style of accordion? Any suggestions for an accordion that doesn’t make the rest of the page content jump on closing panel? Thanks so much.

HTML structure:

 <section class="accordion">
  <div class="accordion__wrapper">
    <div class="tab item">
      <input type="checkbox" name="accordion-1" id="cb1">
      <label for="cb1" class="tab__label">
        <p>title here</p>
      </label>
      <div class="tab_content_wrapper">
        <div class="tab_content">
          <p>lipsum content here</p>
        </div>
      </div>
    </div>
    <div class="tab item">
      <input type="checkbox" name="accordion-1" id="cb2">
      <label for="cb2" class="tab__label">
        <p>title here</p>
      </label>
      <div class="tab_content_wrapper">
        <div class="tab_content">
        <p>lipsum content here</p>
        </div>
      </div>
    </div>
  </div>
</section>

relevant CSS:

.tab input {
position: absolute;
opacity: 0;
z-index: -1;
}
.tab_content_wrapper {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.35s;
}
.tab_content {
overflow: hidden;
}
.tab input:checked ~ .tab_content_wrapper {
grid-template-rows: 1fr;
}
.accordion {
overflow: hidden;
--theme: var(--secondary);
overflow: visible;
}

JS I tried:

document.addEventListener("DOMContentLoaded", function() {
const tabs = document.querySelectorAll('.tab input');

tabs.forEach(tab => {
tab.addEventListener('change', function() {
  const content = this.nextElementSibling.nextElementSibling;
  if (this.checked) {
    content.style.maxHeight = content.scrollHeight + 'px';
  } else {
    content.style.maxHeight = 0;
  }
 });
 });
 });

How to import pictures folder dynamically in React?

On my React app, I have a History page which displays a lot of pictures for each year. Since I can’t use a backend for this project, all my pictures are in my frontend. Having hundreds of them, importing them manually is not a possibility.
I first tried this in my YearHistory component :

  const { year } = useParams();

  const pictures = useMemo(() => {
    const images = require.context(`../../assets/history/${year}/images`, true);
    const imagesList = images.keys().map((image) => images(image));
    const imagesArray = imagesList.map((image, index) => {
      return <img key={index} src={image} alt={`image-${index}`} />;
    });
    return imagesArray;
  }, [year]);

But you can’t have a dynamic url with require.context() and you can’t use this function inside a React component anyway.

So I came up with the idea of having a utility file indexing the urls like this (I just copied one year here for clarity) :

const images2024 = require.context(`../../assets/history/2024/images`, true);
export const indexedImports = {
  2024: images2024,
};

Which I then imported in the YearHistory component like this :

  const { year } = useParams();

  const pictures = useMemo(() => {
    const images = indexedImports[year];
    const imagesList = images.keys().map((image) => images(image));
    const imagesArray = imagesList.map((image, index) => {
      return <img key={index} src={image} alt={`image-${index}`} />;
    });
    return imagesArray;
  }, [year]);

But using this method makes my app crash without much information.

Any idea on how I could dynamically import my pictures folders ?

Edit : it looks like there is just too much pictures. The first folder I tried to test it out had 700+ pictures, which was too much. I tried with a folder which has 25 of them and it works fine with my indexing file solution. Is there any way to load hundreds of pictures on the frontend or is putting them on a backend the only way ?

Pause Timer AND show answer upon hitting Submit button in PHP Javascript form

Designing a quiz for school kids with questions having 4 multiple choice answers (radio buttons) to be selected from.

  • The quiz has multiple questions on a self-submitting form – only 1 question will be presented at a time on the same form
  • Need to show a timer (which shows remaining time of whole quiz)

As soon as the user clicks submit button on a question after selecting a radio button option,

  1. he should see the correct answer & its explanation (like in a text box or in a simple p) AND
  2. the timer should stop to allow him time to go through the answer.

Here is my code so far:

As soon as the user selects his option choice radio and clicks SUBMIT button:

  1. The timer should pause at that value (allowing time to user to go thru the answer)
  2. The answer explanation details should appear
  3. The earlier clicked SUBMIT button should change to NEXT, clicking which should self-submit to the form and the timer should resume from paused value.

I’m stuck at 1 and 3, though 2 has been achieved.
Any leads please?

I’m stuck at how to PAUSE the timer once the user select his option & clicks on submit button

// Following gives 300 seconds (5 minutes) for quiz, and shows the time remaining:
var sec = 300;
var time = setInterval(myTimer, 1000);

function myTimer() {

  if (sec >= 60) {
    let minutes = Math.floor(sec / 60);
    tempsec = Math.round(sec % 60);
    document.getElementById('timer').innerHTML = minutes + " Minutes and " + tempsec + " Seconds left!";
  } else {
    document.getElementById('timer').innerHTML = sec + " Seconds left!";
  }
  sec--;
  if (sec == -1) {
    clearInterval(time);
    alert("Your Test Time is Over!");
  }
}
.ans-details {
  display: none;
}

[type="radio"]:checked~label~.ans-details {
  display: block;
}
<div id="timer">5 Minutes and 0 Seconds left!</div>
<form action="" method="post">
  <p>What is the formula of Pythagoras' Theorem?</p>
  <p>A) x+y</p>
  <p>B) x²+y²</p>
  <p>C) x³+y³</p>
  <p>D) x⁴+y⁴</p>

  <input type="radio" id="A" name="q1" value="A">
  <label for="A">A</label>
  <p class="ans-details">
    This text is shown when the user selects <b>A</b>.
  </p>

  <input type="radio" id="B" name="q1" value="B">
  <label for="B">B</label>
  <p class="ans-details">
    This text is shown when the user selects <b>B</b>.
  </p>

  <input type="radio" id="C" name="q1" value="C">
  <label for="C">C</label>
  <p class="ans-details">
    This text is shown when the user selects <b>C</b>.
  </p>

  <input type="radio" id="D" name="q1" value="D">
  <label for="D">D</label>
  <p class="ans-details">
    This text is shown when the user selects <b>D</b>.
  </p>

  <input type="submit">
</form>

Testimonial Carousel Fix

I have a testimonials part on my website like this, when I have a total of three testimonials:

Total 3 testimonials

Now, if I add more testimonials, however, it becomes crowded like this:

Total 6 testimonials: Example

Let one section be three testimonials, as shown in the first image. I want a total of three sections, with three testimonials per each section like I showed in the first image. It should look exactly like that. So, a total of 9 testimonials (3 sections, 3 testimonials per section).

Here’s my code (with a total of 6 testimonials):

HTML:

<!-- Testimonials -->

<div class="testimonials-container">
    <h2 class="testimonials-title">What Our Users Say</h2>
    <h1 class="testimonials-heading">Trusted by Users & Consultants</h1>

    <div class="testimonial-wrapper">
        <button class="testimonial-nav left-nav" onclick="prevTestimonial()">&#10094;</button>

        <div class="testimonial-slider">
            <div class="testimonial active">
                <div class="star-rating">★★★★★</div>
                <div class="testimonial-text">
                    "This consultation service made it easy to find the right expert. The process was seamless, and I got valuable insights!"
                </div>
                <div class="testimonial-author">James R.</div>
                <div class="testimonial-role">Consultation User</div>
            </div>

            <div class="testimonial">
                <div class="star-rating">★★★★★</div>
                <div class="testimonial-text">
                    "The platform is intuitive and efficient. I booked a consult within minutes and got exactly the advice I needed!"
                </div>
                <div class="testimonial-author">Samantha L.</div>
                <div class="testimonial-role">User - Business Strategy Consultation</div>
            </div>

            <div class="testimonial">
                <div class="star-rating">★★★★★</div>
                <div class="testimonial-text">
                    "As a consultant, I appreciate the seamless booking system. Clients can connect with me easily, and the interface is smooth!"
                </div>
                <div class="testimonial-author">Dr. Michael K.</div>
                <div class="testimonial-role">Registered Consultant</div>
            </div>

            <div class="testimonial">
                <div class="star-rating">★★★★★</div>
                <div class="testimonial-text">
                    "As a consultant, I appreciate the seamless booking system. Clients can connect with me easily, and the interface is smooth!"
                </div>
                <div class="testimonial-author">Dr. Michael K.</div>
                <div class="testimonial-role">Registered Consultant</div>
            </div>

            <div class="testimonial">
                <div class="star-rating">★★★★★</div>
                <div class="testimonial-text">
                    "As a consultant, I appreciate the seamless booking system. Clients can connect with me easily, and the interface is smooth!"
                </div>
                <div class="testimonial-author">Dr. Michael K.</div>
                <div class="testimonial-role">Registered Consultant</div>
            </div>

            <div class="testimonial">
                <div class="star-rating">★★★★★</div>
                <div class="testimonial-text">
                    "As a consultant, I appreciate the seamless booking system. Clients can connect with me easily, and the interface is smooth!"
                </div>
                <div class="testimonial-author">Dr. Michael K.</div>
                <div class="testimonial-role">Registered Consultant</div>
            </div>
        </div>

        <button class="testimonial-nav right-nav" onclick="nextTestimonial()">&#10095;</button>
    </div>
</div>

CSS:

        /* Testimonials Section */
        .testimonials-container {
            background-color: #E7C79A; /* Soft beige background matching the website */
            padding: 60px 50px;
            text-align: center;
            border-radius: 15px;
            margin-top: 50px;
            max-width: 90%;
            margin-left: auto;
            margin-right: auto;
            position: relative;
            overflow: hidden;
        }

        /* Section Title */
        .testimonials-title {
            color: rgba(50, 40, 30, 0.8);
            letter-spacing: 1px;
            margin-bottom: 5px;
        }

        .testimonials-heading {
            font-weight: bold;
            color: #6D4C41; /* Brown color matching the website */
            margin-bottom: 30px; /* Moved testimonials 30px down */
        }

        /* Testimonials Wrapper */
        .testimonial-wrapper {
            display: flex;
            justify-content: center; /* Centers the testimonial */
            align-items: center;
            position: relative;
            overflow: hidden;
            width: 100%;
        }

        /* Hide testimonials except for the active one */
        .testimonial-slider {
            display: flex;
            transition: transform 0.5s ease-in-out; /* Smooth sliding effect */
            width: 210%; /* Ensures all testimonials are positioned in a row */
        }

        .testimonial {
            flex: 0 0 100%; /* Each testimonial takes full width */
            margin: 0 auto;
            background: white;
            padding: 30px;
            border-radius: 15px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            text-align: left;
            box-sizing: border-box;
            flex-basis: 20%;
            max-width: 800px;
        }

        /* Ensure only the active testimonial is shown */
        .testimonial.active {
            display: block;
        }

        /* Star Rating */
        .star-rating {
            color: #F4C150; /* Gold stars */
            font-size: 18px;
        }

        /* Testimonial Text */
        .testimonial-text {
            font-size: 16px;
            color: rgba(50, 40, 30, 0.9);
            line-height: 1.5;
        }

        /* Testimonial Author */
        .testimonial-author {
            font-size: 18px;
            font-weight: bold;
            color: #6D4C41; /* Brown matching the website */
        }

        .testimonial-role {
            font-size: 14px;
            color: rgba(50, 40, 30, 0.7);
        }

        /* Navigation Buttons */
        .testimonial-nav {
            position: absolute;
            top: 50%; /* Centers buttons relative to testimonial */
            transform: translateY(-50%);
            background: white;
            border: none;
            width: 40px;
            height: 40px;
            border-radius: 50%;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            font-size: 18px;
            color: #6D4C41;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .testimonial-nav:hover {
            background: #6D4C41;
            color: white;
        }

        .left-nav {
            left: 10px;
        }

        .right-nav {
            right: 10px;
        }

JavaScript (working fine, pasting just for reference):

    let currentTestimonialIndex = 0;
    const testimonialsPerSlide = 3;
    const totalTestimonials = 9; // Total testimonials
    const totalSections = totalTestimonials / testimonialsPerSlide;
    const slider = document.querySelector(".testimonial-slider");

    function showTestimonial(index) {
        let offset = -(index * 100); // Moves 100% per section
        slider.style.transform = `translateX(${offset}%)`;
    }

    function prevTestimonial() {
        currentTestimonialIndex = (currentTestimonialIndex === 0) ? totalSections - 1 : currentTestimonialIndex - 1;
        showTestimonial(currentTestimonialIndex);
    }

    function nextTestimonial() {
        currentTestimonialIndex = (currentTestimonialIndex === totalSections - 1) ? 0 : currentTestimonialIndex + 1;
        showTestimonial(currentTestimonialIndex);
    }