Repository: dream-aim-deliver/dad-e-class-monorepo
Pull request #55: [TSK-631] [TSK-681] [TSK-682] migration(ui-kit): Buy Coaching Sessions
Author: login: jatinrise
Labels: name: ui-kit
Created: 2025-03-05T10:29:29Z | Merged: 2025-03-19T11:17:33Z


## Description

<!-- Provide a clear and concise description of the changes made in this pull request for UI-Kit components. -->

- Component name: `Buy Coaching Sessions`
- Figma link or screenshot: [Figma Design](https://www.figma.com/)

<img width="1003" alt="Screenshot 2025-03-05 at 15 51 32" src="https://github.com/user-attachments/assets/173e17e7-83b3-4a44-bb6b-33dd22abfd44" />



---

## Checklist

- [x] I have run the lint, build, and test commands locally, as explained in the README, before creating this PR to ensure the code is clean and functional.
- [x] My changes align with the **core models** that have been discussed and agreed upon.
- [x] The component design has been compared to the **Figma design** to ensure consistency.
- [x] The **component is fully responsive** and behaves as expected across different screen sizes.
- [x] The component **supports keyboard web accessibility** (if applicable).
- [x] I have written **unit tests** or **integration tests** to cover the component's functionality above the coverage threshold.
- [x] I have updated the **documentation** for the component, including usage examples and guidelines.
- [x] I have **commented** my code, particularly in complex or non-obvious areas.
- [x] The **component is properly exported** for use in other parts of the application.

---

## Related Issues

<!-- Link any related issue or task. For example: -->
Fixes #45



Linked issue #45: [TSK-631] [TSK-681] [TSK-682] migration(ui-kit):  Buy Coaching Sessions
![Image](https://github.com/user-attachments/assets/e3dd587d-ea5a-40bf-a814-d26eaf657f86)

Diff:
diff --git a/packages/translations/src/lib/dictionaries/base.ts b/packages/translations/src/lib/dictionaries/base.ts
index bcf161931..57836425a 100644
--- a/packages/translations/src/lib/dictionaries/base.ts
+++ b/packages/translations/src/lib/dictionaries/base.ts
@@ -1,3 +1,4 @@
+
 import { z } from 'zod';
 
 export const DictionarySchema = z.object({
@@ -119,6 +120,13 @@ export const DictionarySchema = z.object({
       loadingText: z.string(),
       noAvailableSessionText: z.string(),
     }),
+    buyCoachingSession: z.object({
+      title:z.string(),
+      description:z.string(),
+      buttonText:z.string(),
+      total:z.string(),
+      minutes:z.string(),
+    })
   }),
 
 });
diff --git a/packages/translations/src/lib/dictionaries/de.ts b/packages/translations/src/lib/dictionaries/de.ts
index b9a3c7f74..47eba4e20 100644
--- a/packages/translations/src/lib/dictionaries/de.ts
+++ b/packages/translations/src/lib/dictionaries/de.ts
@@ -118,6 +118,13 @@ export const DE: TDictionary = {
       teaches: "Lehrt",
       viewProfile: "Profil ansehen",
       bookSession: "Sitzung buchen",
+    },
+    buyCoachingSession: {
+      title: "Coaching-Sitzung kaufen",
+      description: "Plane deine Sitzungen jederzeit mit deinem Lieblingscoach.",
+      buttonText: "Coaching-Sitzungen kaufen",
+      total: "Gesamt",
+      minutes: "Minuten"
     }
   },
 };
diff --git a/packages/translations/src/lib/dictionaries/en.ts b/packages/translations/src/lib/dictionaries/en.ts
index 494a689c2..41aa06102 100644
--- a/packages/translations/src/lib/dictionaries/en.ts
+++ b/packages/translations/src/lib/dictionaries/en.ts
@@ -118,6 +118,13 @@ export const EN: TDictionary = {
       teaches: "Teaches",
       viewProfile: "View Profile",
       bookSession: "Book a Session",
+    },
+    buyCoachingSession:{
+      title: 'Buy Coaching Session',
+      description: 'Schedule your sessions anytime, each with your favourite coach.',
+      buttonText: 'Buy coaching sessions',
+      total: 'Total',
+      minutes: 'minutes'
     }
   },
 };
diff --git a/packages/ui-kit/lib/assets/css/tailwind.css b/packages/ui-kit/lib/assets/css/tailwind.css
index 0d89bb635..c074b0338 100644
--- a/packages/ui-kit/lib/assets/css/tailwind.css
+++ b/packages/ui-kit/lib/assets/css/tailwind.css
@@ -132,6 +132,9 @@
   --color-slider-knob-stroke: var(--color-slider-knob-stroke);
   --color-slider-progress-fill: var(--color-slider-progress-fill);
   --color-slider-progress-stroke: var(--color-slider-progress-stroke);
+
+  --color-tooltip-background: var(--tooltip-background);
+  --color-tooltip-border: var(--tooltip-border);
 }
 
 @layer base {
diff --git a/packages/ui-kit/lib/components/buy-coaching-session.tsx b/packages/ui-kit/lib/components/buy-coaching-session.tsx
new file mode 100644
index 000000000..80dcd11db
--- /dev/null
+++ b/packages/ui-kit/lib/components/buy-coaching-session.tsx
@@ -0,0 +1,135 @@
+import React, { useEffect, useMemo, useState } from 'react'
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations'
+import { Button } from './button'
+import { InputField } from './input-field'
+import { IconPlus } from './icons/icon-plus'
+import { IconMinus } from './icons/icon-minus'
+import Tooltip from './tooltip'
+
+type Course = {
+    id: string;
+    title: string;
+    price: number;
+    duration: number;
+    totalSessions: number;
+    content: string
+}
+
+export interface BuyCoachingSessionProps extends isLocalAware {
+    courses: Course[];
+    onClick: () => void;
+    currencyType: string;
+}
+
+/**
+ * A reusable component for purchasing coaching sessions, allowing users to select courses and adjust session quantities.
+ *
+ * @param courses An array of available courses, each containing:
+ *   - `id`: A unique identifier for the course.
+ *   - `title`: The name of the course.
+ *   - `price`: The price per session of the course.
+ *   - `duration`: The duration of a single session in minutes.
+ *   - `totalSessions`: The number of sessions the user wants to purchase.
+ * @param onClick A callback function triggered when the purchase button is clicked.
+ * @param locale A string representing the current locale for translations.
+ * @param currencyType The type of currency to display for course prices.
+ *
+ * @example
+ * <BuyCoachingSession
+ *   courses={[
+ *     { id: "1", title: "Python Basics", price: 50, duration: 60, totalSessions: 1 },
+ *     { id: "2", title: "React Advanced", price: 75, duration: 90, totalSessions: 2 }
+ *   ]}
+ *   onClick={() => console.log("Purchase initiated")}
+ *   locale="en"
+ *   currencyType="USD"
+ * />
+ */
+function BuyCoachingSession({ courses, onClick, locale,currencyType }: BuyCoachingSessionProps) {
+    const dictionary = getDictionary(locale);
+    const [courseList, setCourseList] = useState<Course[]>(courses);
+    const totalCost = useMemo(() => {
+        return courseList.reduce((acc, course) => acc + course.price * course.totalSessions, 0);
+    }, [courseList]);
+
+    function handleIncrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id ? { ...course, totalSessions: course.totalSessions + 1 } : course
+            )
+        );
+    }
+    function handleDecrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id && course.totalSessions > 0
+                    ? { ...course, totalSessions: course.totalSessions - 1 }
+                    : course
+            )
+        );
+    }
+    function handleInputChange(id: string, value: string): void {
+        const newValue = Number(value);
+        if (!isNaN(newValue) && newValue >= 0) {
+            setCourseList(prevCourses =>
+                prevCourses.map(course =>
+                    course.id === id ? { ...course, totalSessions: newValue } : course
+                )
+            );
+        }
+    }
+
+    return (
+        <div className='min-auto border border-card-stroke rounded-medium bg-card-fill px-4 py-6 flex flex-col gap-6'>
+            {/* Header */}
+            <div className='flex flex-col gap-2'>
+                <h5 className='text-text-primary text-lg md:text-[1.5rem]'>
+                    {dictionary.components.buyCoachingSession.title}
+                </h5>
+                <p className='text-text-secondary text-md md:text-xl leading-6 md:leading-[1.875rem]'>
+                    {dictionary.components.buyCoachingSession.description}
+                </p>
+            </div>
+
+            {/* Body */}
+            <div className="flex flex-col">
+                {courseList.map(course => (
+                    <div key={course.id} className="flex justify-between items-center py-3 border-b border-divider">
+                        <div className="flex flex-col gap-2">
+                            <h6 className="flex items-center gap-2 text-text-primary text-md md:text-lg">
+                            <Tooltip contentClassName='max-w-[200px]' text={course.title} description={course.content}/>
+                            </h6>
+                            <div className='flex gap-2 items-center text-text-secondary'>
+                                <p className="text-xs md:text-sm font-important">{course.price} {currencyType}</p>
+                                <p className="text-sm md:text-md">{course.duration} {dictionary.components.buyCoachingSession.minutes}</p>
+                            </div>
+                        </div>
+                        <div className="flex items-center gap-2">
+                            <button onClick={() => handleDecrement(course.id)} aria-label="decrease" className="p-2">
+                                <IconMinus classNames='text-button-text-text' size="6" />
+                            </button>
+                            <InputField
+                                type='number'
+                                className='w-[3rem] h-[3rem] text-lg text-center'
+                                value={course.totalSessions.toString()}
+                                setValue={(value) => handleInputChange(course.id, value)}
+                            />
+                            <button onClick={() => handleIncrement(course.id)} aria-label="increase">
+                                <IconPlus classNames='text-button-text-text' size="6" />
+                            </button>
+                        </div>
+                    </div>
+                ))}
+            </div>
+
+            <h6 className='text-right text-text-primary font-normal'>
+                {dictionary.components.buyCoachingSession.total}: {totalCost} {currencyType}
+            </h6>
+
+            {/* Footer */}
+            <Button onClick={onClick} variant='primary' text={dictionary.components.buyCoachingSession.buttonText} />
+        </div>
+    );
+}
+
+export default BuyCoachingSession
\ No newline at end of file
diff --git a/packages/ui-kit/lib/components/icons/icon-infocircle.tsx b/packages/ui-kit/lib/components/icons/icon-infocircle.tsx
new file mode 100644
index 000000000..73a8a7c21
--- /dev/null
+++ b/packages/ui-kit/lib/components/icons/icon-infocircle.tsx
@@ -0,0 +1,19 @@
+import { type IconProps, generateClassesForIcon } from './icon';
+
+/**
+ * IconPlus
+ * @usage <IconInfoCircle  />
+ */
+export const IconInfoCircle = (props: IconProps) => {
+  return (
+    <svg xmlns="http://www.w3.org/2000/svg" 
+    viewBox="0 0 16 16"
+    className={generateClassesForIcon(props)} >
+    <g opacity="1">
+      <path d="M8.00065 1.8335C4.32465 1.8335 1.33398 4.82416 1.33398 8.50016C1.33398 12.1762 4.32465 15.1668 8.00065 15.1668C11.6767 15.1668 14.6673 12.1762 14.6673 8.50016C14.6673 4.82416 11.6767 1.8335 8.00065 1.8335ZM8.00065 13.8335C5.05998 13.8335 2.66732 11.4408 2.66732 8.50016C2.66732 5.5595 5.05998 3.16683 8.00065 3.16683C10.9413 3.16683 13.334 5.5595 13.334 8.50016C13.334 11.4408 10.9413 13.8335 8.00065 13.8335Z"/>
+      <path d="M7.33398 7.8335H8.66732V11.8335H7.33398V7.8335ZM7.33398 5.16683H8.66732V6.50016H7.33398V5.16683Z"/>
+    </g>
+  </svg>
+  
+  );
+};
diff --git a/packages/ui-kit/lib/components/icons/icon-minus.tsx b/packages/ui-kit/lib/components/icons/icon-minus.tsx
new file mode 100644
index 000000000..d03024b5b
--- /dev/null
+++ b/packages/ui-kit/lib/components/icons/icon-minus.tsx
@@ -0,0 +1,15 @@
+import { type IconProps, generateClassesForIcon } from './icon';
+
+/**
+ * IconPlus
+ * @usage <IconMinus />
+ */
+export const IconMinus = (props: IconProps) => {
+  return (
+    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
+    className={generateClassesForIcon(props)}
+    >
+  <path d="M5 11H19V13H5V11Z" />
+</svg>
+  );
+};
diff --git a/packages/ui-kit/lib/components/input-field.tsx b/packages/ui-kit/lib/components/input-field.tsx
index 5034803ec..c53d1f948 100644
--- a/packages/ui-kit/lib/components/input-field.tsx
+++ b/packages/ui-kit/lib/components/input-field.tsx
@@ -1,4 +1,5 @@
 import React, { FC, ReactNode, useState } from 'react';
+import { cn } from '../utils/style-utils';
 
 export interface InputFieldProps {
   hasLeftContent?: boolean;
@@ -15,8 +16,9 @@ export interface InputFieldProps {
     | 'error';
   value?: string;
   setValue: (value: string) => void;
-  type?: 'text' | 'password';
+  type?: 'text' | 'password'|'number';
   id?: string;
+  className?: string;
 }
 
 /**
@@ -63,6 +65,7 @@ export const InputField: FC<InputFieldProps> = ({
   setValue,
   type = 'text',
   id,
+  className
 }) => {
   const [borderColor, setBorderColor] = useState(false);
 
@@ -77,11 +80,12 @@ export const InputField: FC<InputFieldProps> = ({
 
   return (
     <div
-      className={`flex gap-2 items-start justify-center flex-col px-3 py-[10px] bg-input-fill border rounded-medium w-full 
-      ${stateClasses[state]} 
-      `}
+      className={cn(`flex gap-2 items-start justify-center flex-col px-3 py-[10px] bg-input-fill border rounded-medium 
+      ${stateClasses[state]}
+      ${className} 
+      `)}
     >
-      <div className="flex items-center md:gap-2 w-full">
+      <div className="flex items-center md:gap-2 w-full h-full">
         {hasLeftContent && leftContent}
         <input
           data-testid={id}
@@ -92,7 +96,7 @@ export const InputField: FC<InputFieldProps> = ({
           onChange={(e) => setValue(e.target.value)}
           onFocus={() => setBorderColor(true)}
           onBlur={() => setBorderColor(false)}
-          className="bg-transparent outline-none flex-1 placeholder-text-secondary min-w-0"
+          className="bg-transparent outline-none [-moz-appearance:textfield] flex-1 placeholder-text-secondary h-full w-full appearance-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none text-center"
         />
         {hasRightContent && rightContent}
       </div>
diff --git a/packages/ui-kit/lib/components/tooltip.tsx b/packages/ui-kit/lib/components/tooltip.tsx
new file mode 100644
index 000000000..8911743f9
--- /dev/null
+++ b/packages/ui-kit/lib/components/tooltip.tsx
@@ -0,0 +1,123 @@
+import React, { useState, useRef, useEffect } from 'react';
+import { IconInfoCircle } from './icons/icon-infocircle';
+import { cn } from '../utils/style-utils';
+
+interface TooltipProps {
+  text: string;
+  title?: string;
+  description: string;
+  contentClassName?: string;
+  tipPosition?: 'up' | 'down' | 'auto';
+}
+/**
+ * A reusable Tooltip component that provides additional information when a user hovers over or focuses on the trigger element.
+ * The tooltip dynamically adjusts its position to prevent it from being cut off by the viewport.
+ *
+ * @param text The text displayed alongside the info icon.
+ * @param title Optional title displayed inside the tooltip.
+ * @param description The main content of the tooltip.
+ * @param contentClassName Optional className for styling the tooltip content.
+ * @param tipPosition Position of the tooltip: 'up', 'down', or 'auto' (default: 'auto').
+ *
+ * @state showTooltip Controls the visibility of the tooltip.
+ * @state position Determines whether the tooltip appears above (`up`) or below (`down`) the trigger element.
+ *
+ * @ref tooltipRef Reference to the tooltip element.
+ * @ref triggerRef Reference to the element that triggers the tooltip.
+ *
+ * @behavior
+ * - Displays the tooltip when the user hovers or focuses on the trigger element.
+ * - Uses `Element.getBoundingClientRect()` to check if the tooltip is cut off.
+ * - When tipPosition is 'auto':
+ *   - Moves **downward** if it overflows the top.
+ *   - Moves **upward** if it overflows the bottom.
+ *   - Defaults to 'up' if there's enough space.
+ * - Includes an arrow (`<div>`) to visually connect the tooltip with the trigger element.
+ *
+ * @example
+ * <Tooltip 
+ *   text="More Info" 
+ *   title="Important Notice" 
+ *   description="This tooltip provides extra details about this feature."
+ *   tipPosition="auto"
+ * />
+ */
+
+function Tooltip({ text, title, description, contentClassName, tipPosition = 'auto' }: TooltipProps) {
+  const [showTooltip, setShowTooltip] = useState(false);
+  const [position, setPosition] = useState<'up' | 'down'>(tipPosition === 'auto' ? 'up' : tipPosition);
+
+  const tooltipRef = useRef<HTMLDivElement>(null);
+  const triggerRef = useRef<HTMLDivElement>(null);
+
+  useEffect(() => {
+    if (showTooltip && tooltipRef.current && triggerRef.current) {
+      // Only adjust position automatically if tipPosition is 'auto'
+      if (tipPosition === 'auto') {
+        const tooltipRect = tooltipRef.current.getBoundingClientRect();
+        const triggerRect = triggerRef.current.getBoundingClientRect();
+        const viewportHeight = window.innerHeight;
+
+        if (tooltipRect.top < 0) {
+          setPosition('down');
+        } else if (tooltipRect.bottom > viewportHeight) {
+          setPosition('up');
+        } else {
+          // Default to up if there's enough space
+          setPosition('up');
+        }
+      } else {
+        // Use the explicitly provided position
+        setPosition(tipPosition as 'up' | 'down');
+      }
+    }
+  }, [showTooltip, tipPosition]);
+
+  return (
+    <div className="relative inline-block">
+      {/* Tooltip trigger element */}
+      <div
+        ref={triggerRef}
+        className="inline-flex gap-1 items-center text-sm leading-[150%] cursor-pointer"
+        onMouseEnter={() => setShowTooltip(true)}
+        onMouseLeave={() => setShowTooltip(false)}
+        onFocus={() => setShowTooltip(true)}
+        onBlur={() => setShowTooltip(false)}
+        tabIndex={0}
+        aria-describedby="tooltip-content"
+        role="button"
+      >
+        <span className="line-clamp-2 text-text-secondary">{text}</span>
+        <IconInfoCircle size="4" classNames={cn('flex-shrink-0 text-text-secondary hover:text-text-primary')} />
+      </div>
+
+      {showTooltip && (
+        <div
+          id="tooltip-content"
+          ref={tooltipRef}
+          className={cn(
+            'absolute z-10 p-4 text-xs text-text-secondary leading-[150%] bg-tooltip-background border-tooltip-border rounded-lg shadow-lg opacity-100 transition-opacity duration-200',
+            position === 'up' ? 'bottom-6 -right-1' : 'top-6 -right-1',
+            contentClassName
+          )}
+          role="tooltip"
+        >
+          {title && <span className="font-important text-sm text-text-primary">{title}</span>}
+          <p className="text-sm font-normal text-text-secondary">{description}</p>
+
+          {/* Tooltip arrow */}
+          <div
+            className={cn(
+              'w-0 h-0 border-l-8 border-r-8 border-transparent absolute',
+              position === 'up'
+                ? '-bottom-1.5 right-1 border-t-8 border-t-tooltip-background'
+                : '-top-1.5 right-1 border-b-8 border-b-tooltip-background'
+            )}
+          ></div>
+        </div>
+      )}
+    </div>
+  );
+}
+
+export default Tooltip;
\ No newline at end of file
diff --git a/packages/ui-kit/lib/index.ts b/packages/ui-kit/lib/index.ts
index ff321976f..82c400f46 100644
--- a/packages/ui-kit/lib/index.ts
+++ b/packages/ui-kit/lib/index.ts
@@ -3,8 +3,6 @@ import { DragAndDrop } from './components/drag&drop/drag&drop';
 import { Badge } from './components/badge';
 
 
-export { ProfileTabs, DragAndDrop, Badge };
-export * as contexts from './contexts';
 export { Button, type ButtonProps } from './components/button';
 export { DummySkills, type DummySkillsProps } from './components/dummy-skills';
 export { IconButton, type IconButtonProps } from './components/icon-button';
@@ -23,4 +21,7 @@ export { ThemeProvider, useTheme } from './contexts';
 export { VisitorCourseCard } from './components/coursecard/visitor-coursecard/visitor-coursecard';
 export { CoachCourseCard } from './components/coursecard/coach-coursecard/coach-coursecard';
 export { StudentCourseCard } from './components/coursecard/student-coursecard/student-course-card';
-export { CourseCreatorCard } from './components/coursecard/course-creator-coursecard/course-creator-card';
\ No newline at end of file
+export { CourseCreatorCard } from './components/coursecard/course-creator-coursecard/course-creator-card';
+export { ProfileTabs, DragAndDrop, Badge };
+export { default  as  BuyCoachingSession, type BuyCoachingSessionProps } from './components/buy-coaching-session'
+export * as contexts from './contexts';
diff --git a/packages/ui-kit/stories/buy-coaching-session.stories.tsx b/packages/ui-kit/stories/buy-coaching-session.stories.tsx
new file mode 100644
index 000000000..3f3c62605
--- /dev/null
+++ b/packages/ui-kit/stories/buy-coaching-session.stories.tsx
@@ -0,0 +1,90 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import BuyCoachingSession from '../lib/components/buy-coaching-session';
+
+const meta: Meta<typeof BuyCoachingSession> = {
+    title: 'Components/BuyCoachingSession',
+    component: BuyCoachingSession,
+    tags: ['autodocs'],
+    argTypes: {
+        locale: {
+            control: 'select',
+            options: ['en', 'de'],
+            defaultValue: 'en'
+        }
+
+    }
+};
+
+export default meta;
+
+type Story = StoryObj<typeof BuyCoachingSession>;
+
+const mockCourses = [
+    {
+        id: '1',
+        title: 'Web Development Fundamentals',
+        price: 50,
+        duration: 60,
+        totalSessions: 0,
+        content:"This is a simple tooltip with some information"
+    },
+    {
+        id: '2',
+        title: 'Advanced React Techniques',
+        price: 75,
+        duration: 90,
+        totalSessions: 0,
+     
+        content:"This is a simple tooltip with some information"
+    },
+    {
+        id: '3',
+        title: 'UI/UX Design Masterclass',
+        price: 60,
+        duration: 45,
+        totalSessions: 0,
+        content:"This is a simple tooltip with some information"
+    }
+];
+
+export const Default: Story = {
+    args: {
+        onClick:()=>alert('Clicked'),
+        courses: mockCourses,
+        currencyType: 'CHF',
+        locale: 'en'
+    }
+};
+
+export const WithPreselectedSessions: Story = {
+    args: {
+        onClick:()=>alert('Clicked'),
+        courses: mockCourses.map(course => ({
+            ...course,
+            totalSessions: course.id === '2' ? 3 : 1
+        })),
+        currencyType: 'CHF',
+        locale: 'en'
+    }
+};
+
+export const GermanLocale: Story = {
+    args: {
+        onClick:()=>alert('Angeklickt'),
+        courses: mockCourses,
+        currencyType: 'CHF',
+        locale: 'de'
+    }
+};
+
+export const GermanLocaleWithSessions: Story = {
+    args: {
+        onClick:()=>alert('Angeklickt'),
+        courses: mockCourses.map(course => ({
+            ...course,
+            totalSessions: course.id === '1' ? 2 : course.id === '3' ? 1 : 0
+        })),
+        currencyType: 'CHF',
+        locale: 'de'
+    }
+};
\ No newline at end of file
diff --git a/packages/ui-kit/stories/icons/icon-info-circle.stories.tsx b/packages/ui-kit/stories/icons/icon-info-circle.stories.tsx
new file mode 100644
index 000000000..9a53d6d36
--- /dev/null
+++ b/packages/ui-kit/stories/icons/icon-info-circle.stories.tsx
@@ -0,0 +1,47 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { IconInfoCircle } from '../../lib/components/icons/icon-infocircle';
+
+// Meta configuration for Storybook
+const meta: Meta<typeof IconInfoCircle > = {
+  title: 'Icons/IconInfoCircle',
+  component: IconInfoCircle ,
+  tags: ['autodocs'],
+  argTypes: {
+    size: {
+      control: { type: 'number' },
+      description: 'The size of the icon (height and width in Tailwind CSS).',
+      defaultValue: 6,
+    },
+    fill: {
+      control: { type: 'text' },
+      description: 'The fill color of the icon.',
+      defaultValue: 'currentColor',
+    },
+    classNames: {
+      control: { type: 'text' },
+      description:
+        'Additional TailwindCSS classes to apply to the icon for styling.',
+    },
+  },
+  parameters: {
+    layout: 'centered',
+  },
+};
+
+export default meta;
+type Story = StoryObj<typeof IconInfoCircle>;
+
+// Default story
+export const Default: Story = {
+  args: {
+    classNames: 'text-button-secondary-text',
+  },
+};
+
+// Customizable story
+export const CustomIcon: Story = {
+  args: {
+    size: '10',
+    classNames: 'rotate-45 text-button-secondary-text',
+  },
+};
diff --git a/packages/ui-kit/stories/icons/icon-minus.stories.tsx b/packages/ui-kit/stories/icons/icon-minus.stories.tsx
new file mode 100644
index 000000000..646252e1c
--- /dev/null
+++ b/packages/ui-kit/stories/icons/icon-minus.stories.tsx
@@ -0,0 +1,47 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { IconMinus } from '../../lib/components/icons/icon-minus';
+
+// Meta configuration for Storybook
+const meta: Meta<typeof IconMinus > = {
+  title: 'Icons/IconMinus',
+  component: IconMinus ,
+  tags: ['autodocs'],
+  argTypes: {
+    size: {
+      control: { type: 'number' },
+      description: 'The size of the icon (height and width in Tailwind CSS).',
+      defaultValue: 6,
+    },
+    fill: {
+      control: { type: 'text' },
+      description: 'The fill color of the icon.',
+      defaultValue: 'currentColor',
+    },
+    classNames: {
+      control: { type: 'text' },
+      description:
+        'Additional TailwindCSS classes to apply to the icon for styling.',
+    },
+  },
+  parameters: {
+    layout: 'centered',
+  },
+};
+
+export default meta;
+type Story = StoryObj<typeof IconMinus>;
+
+// Default story
+export const Default: Story = {
+  args: {
+    classNames: 'text-button-secondary-text',
+  },
+};
+
+// Customizable story
+export const CustomIcon: Story = {
+  args: {
+    size: '10',
+    classNames: 'rotate-45 text-button-secondary-text',
+  },
+};
diff --git a/packages/ui-kit/stories/tooltip.stories.tsx b/packages/ui-kit/stories/tooltip.stories.tsx
new file mode 100644
index 000000000..a983a2d70
--- /dev/null
+++ b/packages/ui-kit/stories/tooltip.stories.tsx
@@ -0,0 +1,120 @@
+// Tooltip.stories.tsx
+import type { Meta, StoryObj } from '@storybook/react';
+import Tooltip from '../lib/components/tooltip';
+
+const meta: Meta<typeof Tooltip> = {
+  title: 'Components/Tooltip',
+  component: Tooltip,
+  parameters: {
+    layout: 'centered',
+  },
+  tags: ['autodocs'],
+  argTypes: {
+    text: {
+      control: 'text',
+      description: 'The trigger text that users interact with',
+    },
+    title: {
+      control: 'text',
+      description: 'Optional title displayed at the top of the tooltip',
+    },
+    description: {
+      control: 'text',
+      description: 'The content displayed in the tooltip',
+    },
+    contentClassName: {
+      control: 'text',
+      description: 'Custom class names for styling the tooltip content',
+    },
+    tipPosition: {
+      control: { type: 'select' },
+      options: ['up', 'down', 'auto'],
+      description: 'Position of the tooltip relative to the trigger',
+      defaultValue: 'auto',
+    },
+  },
+};
+
+export default meta;
+type Story = StoryObj<typeof Tooltip>;
+
+export const Basic: Story = {
+  args: {
+    text: 'Hover me',
+    description: 'This is a simple tooltip with some information',
+  },
+};
+
+export const WithTitle: Story = {
+  args: {
+    text: 'Hover for details',
+    title: 'Important Information',
+    description: 'This tooltip includes a title and description',
+  },
+};
+
+export const LongContent: Story = {
+  args: {
+    text: 'Help',
+    title: 'Need Assistance?',
+
+    description:
+      'This tooltip contains a longer description that demonstrates how the component handles multiple lines of text. The max-width property ensures the tooltip has a reasonable width on screen.',
+
+    tipPosition: 'auto',
+    contentClassName: 'w-[200px]\n',
+  },
+};
+
+export const CustomStyles: Story = {
+  args: {
+    text: 'Custom Styled',
+    title: 'Custom Tooltip',
+    description:
+      'This tooltip uses custom classes for styling both the trigger and content.',
+    contentClassName: 'bg-slate-800 text-white border-blue-500',
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: 'Tooltip with custom styling applied through className props',
+      },
+    },
+  },
+};
+
+export const PositionTop: Story = {
+  args: {
+    text: 'Always on Top',
+    description: 'This tooltip is forced to appear above the trigger element',
+    tipPosition: 'up',
+  },
+};
+
+export const PositionBottom: Story = {
+  args: {
+    text: 'Always on Bottom',
+    description: 'This tooltip is forced to appear below the trigger element',
+    tipPosition: 'auto',
+    contentClassName: 'max-w-[500px]',
+    title: 'hellop',
+  },
+};
+
+// You might want to manually test focus behavior
+export const Keyboard: Story = {
+  args: {
+    text: 'Tab to me',
+    title: 'Keyboard Accessible',
+    description:
+      'This tooltip should appear when you tab to it, demonstrating keyboard accessibility',
+  },
+  parameters: {
+    docs: {
+      description: {
+        story:
+          'This story helps test keyboard focus behavior. Try tabbing to the element.',
+      },
+    },
+  },
+};
diff --git a/packages/ui-kit/tests/buy-coaching-session.test.tsx b/packages/ui-kit/tests/buy-coaching-session.test.tsx
new file mode 100644
index 000000000..9c288ab56
--- /dev/null
+++ b/packages/ui-kit/tests/buy-coaching-session.test.tsx
@@ -0,0 +1,75 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+import BuyCoachingSession from '../lib/components/buy-coaching-session';
+import { vi } from 'vitest';
+
+vi.mock('@maany_shr/e-class-translations', () => ({
+  getDictionary: () => ({
+    components: {
+      buyCoachingSession: {
+        title: 'Buy Coaching Sessions',
+        description: 'Choose your coaching sessions.',
+        minutes: 'min',
+        total: 'Total',
+        buttonText: 'Buy Now'
+      }
+    }
+  }),
+  isLocalAware: vi.fn()
+}));
+
+const mockCourses = [
+  { id: '1', title: 'React Basics', price: 50, duration: 30, totalSessions: 1 },
+  { id: '2', title: 'Advanced Next.js', price: 100, duration: 60, totalSessions: 2 },
+];
+
+describe('BuyCoachingSession Component', () => {
+  test('renders component with courses', () => {
+    render(<BuyCoachingSession onClick={vi.fn()} currencyType='CHF' courses={mockCourses} locale="en" />);
+
+    expect(screen.getByText('Buy Coaching Sessions')).toBeInTheDocument();
+    expect(screen.getByText('Choose your coaching sessions.')).toBeInTheDocument();
+    expect(screen.getByText('React Basics')).toBeInTheDocument();
+    expect(screen.getByText('Advanced Next.js')).toBeInTheDocument();
+    expect(screen.getByText('50 CHF')).toBeInTheDocument();
+    expect(screen.getByText('100 CHF')).toBeInTheDocument();
+    expect(screen.getByText('Total: 250 CHF')).toBeInTheDocument();
+  });
+
+  test('increments and decrements session count', () => {
+    render(<BuyCoachingSession onClick={vi.fn()} currencyType='CHF' courses={mockCourses} locale="en" />);
+
+    const increaseButton = screen.getAllByLabelText('increase')[0];
+    const decreaseButton = screen.getAllByLabelText('decrease')[0];
+    const inputField = screen.getAllByRole('spinbutton')[0];
+
+    // Check initial value
+    expect(inputField).toHaveValue(1);
+
+    // Increment
+    fireEvent.click(increaseButton);
+    expect(inputField).toHaveValue(2);
+
+    // Decrement
+    fireEvent.click(decreaseButton);
+    expect(inputField).toHaveValue(1);
+  });
+
+  test('updates session count from input', () => {
+    render(<BuyCoachingSession onClick={vi.fn()} currencyType='CHF' courses={mockCourses} locale="en" />);
+
+    const inputField = screen.getAllByRole('spinbutton')[0];
+
+    fireEvent.change(inputField, { target: { value: '5' } });
+    expect(inputField).toHaveValue(5);
+  });
+
+  test('updates total cost correctly', () => {
+    render(<BuyCoachingSession onClick={vi.fn()} currencyType='CHF' courses={mockCourses} locale="en" />);
+
+    const increaseButton = screen.getAllByLabelText('increase')[0];
+
+    fireEvent.click(increaseButton);
+    
+    expect(screen.getByText('Total: 300 CHF')).toBeInTheDocument();
+  });
+});
\ No newline at end of file


Reviews:
user: login: alebg
body: Hello @jatinrise , ~I can't get the storybook working when in your branch, could you please take a look? This is what I see:~

![immagine](https://github.com/user-attachments/assets/4f985b52-af9c-4792-9158-3e810ce9069f)

state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T08:11:35Z
user: login: carlapbg
body: Hi guys! Here In the Safari browser the numbers appear slightly cut off.

<img width="156" alt="Screenshot 2025-03-06 at 15 57 09" src="https://github.com/user-attachments/assets/526ec912-59be-43c3-ba6c-3563ddf0bd02" />

state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T15:11:46Z
user: login: alebg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T22:29:04Z
user: login: alebg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T22:30:14Z
user: login: alebg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T22:32:49Z
user: login: alebg
body: Hi @jatinrise thanks! Now i can get the storybook working. So a couple of comments:

![image](https://github.com/user-attachments/assets/40b681ef-c943-4a2b-9b19-7af1201d9b4b)

~1. In Firefox, we get a default selector for numbers that appears on top of the numbers, please hide it~
~2. Check my inline comments~
3. Please address @carlapbg 's review, and let's wait for her answer to the question i made to her
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T22:33:48Z
user: login: carlapbg
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T22:48:47Z
user: login: carlapbg
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T23:18:10Z
user: login: jatinrise
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-07T11:23:03Z
user: login: carlapbg
body: Thanks! I just left a small change, please check my inline comment 
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T14:42:10Z
user: login: alebg
body: Hi @jatinrise , apart from Carla's latest comment, please also take into account that, as per the figma, the "i" icons next to the courses should display some information on hover. Right now they don't show anything. I mean this:

![immagine](https://github.com/user-attachments/assets/f336bd1e-9014-44e3-92a5-3d6ac494fb02)

Should behave as explained in the figma:
![immagine](https://github.com/user-attachments/assets/2926b01b-009c-495c-9d78-8738f1120065)

What the tooltip says should come from the outside as well, just like the course title and so on
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T19:49:26Z
user: login: alebg
body: @ObitoDevv thanks! So now there are two issues. Under certain conditions, the tooltip goes off screen. Two examples:

- Very long tool tip text:
![immagine](https://github.com/user-attachments/assets/6c69f635-e0f9-4779-9b79-2452efa0f489)
@carlapbg what would be the best way to handle text that is too long? truncate the tooltip text for example? Or maybe just restrict the amount of characters we can show on the tooltip?

- The tooltip component is placed close to the edge of the screen: 
![immagine](https://github.com/user-attachments/assets/d7f82888-bf78-45f9-b2c6-6e67d1e22130)



state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T18:51:24Z
user: login: carlapbg
body: Thank you! I have a couple of things, and also please check my inline comment.

- The icon should change to white when we do hover.
- When hover, the tooltip should have the option to add a title and description.

<img width="474" alt="Screenshot 2025-03-12 at 23 37 41" src="https://github.com/user-attachments/assets/c08c984b-0799-4e09-a344-49cfc1c80b63" />

state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T22:39:57Z
user: login: alebg
body: @jatinrise @carlapbg thanks for the discussion! Right, so limiting is the proper solution, but we'll do that in the backend, not here in the ui-kit. This means @jatinrise that you don't need to do anything else regarding the tooltip. Please address that one in-line comment that @carlapbg left and we're ready to accept this
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T23:37:26Z
user: login: carlapbg
body: Looks good to me now. Thank you, great job! 
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-19T10:32:23Z
user: login: alebg
body: great job @jatinrise, thanks a lot!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T11:15:50Z

Review comments:
path: packages/ui-kit/stories/buy-coaching-ssession.stories.tsx
user: login: alebg
body: @jatinrise there's a typo in the name of this file
created_at: 2025-03-06T22:29:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,146 @@
+import React, { useEffect, useState } from 'react'
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations'
+import { Button } from './button'
+import { InputField } from './input-field'
+import { IconPlus } from './icons/icon-plus'
+import { IconMinus } from './icons/icon-minus'
+import { IconInfoCircle } from './icons/icon-infocircle'
+
+/**
+ * Represents a course available for coaching sessions.
+ */
+type Course = {
+    id: string;
+    title: string;
+    price: number;
+    duration: number;
+    totalSessions: number;
+
+}
+
+/**
+ * Props for the BuyCoachingSession component.
+ */
+export interface BuyCoachingSessionProps extends isLocalAware {
+    /** List of courses available for purchase. */
+    courses: Course[];
+    onClick: () => void;
+}
+
+/**
+ * BuyCoachingSession Component
+ *
+ * This component allows users to purchase coaching sessions for different courses.
+ *
+ * @param {BuyCoachingSessionProps} props - The component properties.
+ * @returns {JSX.Element} The rendered component.
+ */
+function BuyCoachingSession({ courses,onClick, locale }: BuyCoachingSessionProps) {
+    const dictionary = getDictionary(locale);
+    const [totalCost, setTotalCost] = useState(0);
+    const [courseList, setCourseList] = useState<Course[]>(courses);
+
+    useEffect(() => {
+        const total = courseList.reduce((acc, course) => acc + course.price * course.totalSessions, 0);
+        setTotalCost(total);
+    }, [courseList]);
+
+    /**
+     * Increases the total sessions count for a specific course.
+     * 
+     * @param {string} id - The ID of the course to increment.
+     */
+    function handleIncrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id ? { ...course, totalSessions: course.totalSessions + 1 } : course
+            )
+        );
+    }
+
+    /**
+     * Decreases the total sessions count for a specific course, ensuring it doesn't go below 0.
+     * 
+     * @param {string} id - The ID of the course to decrement.
+     */
+    function handleDecrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id && course.totalSessions > 0
+                    ? { ...course, totalSessions: course.totalSessions - 1 }
+                    : course
+            )
+        );
+    }
+
+    /**
+     * Updates the total session count for a specific course based on user input.
+     * 
+     * @param {string} id - The ID of the course to update.
+     * @param {string} value - The new session count as a string.
+     */
+    function handleInputChange(id: string, value: string): void {
+        const newValue = Number(value);
+        if (!isNaN(newValue) && newValue >= 0) {
+            setCourseList(prevCourses =>
+                prevCourses.map(course =>
+                    course.id === id ? { ...course, totalSessions: newValue } : course
+                )
+            );
+        }
+    }
+
+    return (
+        <div className='min-auto border border-card-stroke rounded-medium bg-card-fill px-4 py-6 flex flex-col gap-6'>
+            {/* Header */}
+            <div className='flex flex-col gap-2'>
+                <h5 className='text-text-primary text-lg md:text-[1.5rem]'>
+                    {dictionary.components.buyCoachingSession.title}
+                </h5>
+                <p className='text-text-secondary text-md md:text-xl leading-6 md:leading-[1.875rem]'>
+                    {dictionary.components.buyCoachingSession.description}
+                </p>
+            </div>
+
+            {/* Body */}
+            <div className="flex flex-col">
+                {courseList.map(course => (
+                    <div key={course.id} className="flex justify-between items-center py-3 border-b border-divider">
+                        <div className="flex flex-col gap-2">
+                            <h6 className="flex items-center gap-2 text-text-primary text-md md:text-lg">
+                                {course.title} <IconInfoCircle size="4" />
+                            </h6>
+                            <div className='flex gap-2 items-center text-text-secondary'>
+                                <p className="text-xs md:text-sm">${course.price}</p>
path: packages/ui-kit/lib/components/buy-coaching-session.tsx
user: login: alebg
body: @jatinrise There's a hard-coded "$" here, but the price may not always be in USD
created_at: 2025-03-06T22:30:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,146 @@
+import React, { useEffect, useState } from 'react'
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations'
+import { Button } from './button'
+import { InputField } from './input-field'
+import { IconPlus } from './icons/icon-plus'
+import { IconMinus } from './icons/icon-minus'
+import { IconInfoCircle } from './icons/icon-infocircle'
+
+/**
+ * Represents a course available for coaching sessions.
+ */
+type Course = {
+    id: string;
+    title: string;
+    price: number;
+    duration: number;
+    totalSessions: number;
+
+}
+
+/**
+ * Props for the BuyCoachingSession component.
+ */
+export interface BuyCoachingSessionProps extends isLocalAware {
+    /** List of courses available for purchase. */
+    courses: Course[];
+    onClick: () => void;
+}
+
+/**
+ * BuyCoachingSession Component
+ *
+ * This component allows users to purchase coaching sessions for different courses.
+ *
+ * @param {BuyCoachingSessionProps} props - The component properties.
+ * @returns {JSX.Element} The rendered component.
+ */
+function BuyCoachingSession({ courses,onClick, locale }: BuyCoachingSessionProps) {
+    const dictionary = getDictionary(locale);
+    const [totalCost, setTotalCost] = useState(0);
+    const [courseList, setCourseList] = useState<Course[]>(courses);
+
+    useEffect(() => {
+        const total = courseList.reduce((acc, course) => acc + course.price * course.totalSessions, 0);
+        setTotalCost(total);
+    }, [courseList]);
+
+    /**
+     * Increases the total sessions count for a specific course.
+     * 
+     * @param {string} id - The ID of the course to increment.
+     */
+    function handleIncrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id ? { ...course, totalSessions: course.totalSessions + 1 } : course
+            )
+        );
+    }
+
+    /**
+     * Decreases the total sessions count for a specific course, ensuring it doesn't go below 0.
+     * 
+     * @param {string} id - The ID of the course to decrement.
+     */
+    function handleDecrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id && course.totalSessions > 0
+                    ? { ...course, totalSessions: course.totalSessions - 1 }
+                    : course
+            )
+        );
+    }
+
+    /**
+     * Updates the total session count for a specific course based on user input.
+     * 
+     * @param {string} id - The ID of the course to update.
+     * @param {string} value - The new session count as a string.
+     */
+    function handleInputChange(id: string, value: string): void {
+        const newValue = Number(value);
+        if (!isNaN(newValue) && newValue >= 0) {
+            setCourseList(prevCourses =>
+                prevCourses.map(course =>
+                    course.id === id ? { ...course, totalSessions: newValue } : course
+                )
+            );
+        }
+    }
+
+    return (
+        <div className='min-auto border border-card-stroke rounded-medium bg-card-fill px-4 py-6 flex flex-col gap-6'>
+            {/* Header */}
+            <div className='flex flex-col gap-2'>
+                <h5 className='text-text-primary text-lg md:text-[1.5rem]'>
+                    {dictionary.components.buyCoachingSession.title}
+                </h5>
+                <p className='text-text-secondary text-md md:text-xl leading-6 md:leading-[1.875rem]'>
+                    {dictionary.components.buyCoachingSession.description}
+                </p>
+            </div>
+
+            {/* Body */}
+            <div className="flex flex-col">
+                {courseList.map(course => (
+                    <div key={course.id} className="flex justify-between items-center py-3 border-b border-divider">
+                        <div className="flex flex-col gap-2">
+                            <h6 className="flex items-center gap-2 text-text-primary text-md md:text-lg">
+                                {course.title} <IconInfoCircle size="4" />
+                            </h6>
+                            <div className='flex gap-2 items-center text-text-secondary'>
+                                <p className="text-xs md:text-sm">${course.price}</p>
+                                <p className="text-sm md:text-md">{course.duration} {dictionary.components.buyCoachingSession.minutes}</p>
+                            </div>
+                        </div>
+                        <div className="flex items-center gap-2">
+                            <div onClick={() => handleDecrement(course.id)} aria-label="decrease">
+                                <IconMinus classNames='text-button-text-text' size="6" />
+                            </div>
+                            <InputField
+                                type='number'
+                                className='w-[3rem] h-[2.5rem] text-lg text-center'
+                                value={course.totalSessions.toString()}
+                                setValue={(value) => handleInputChange(course.id, value)}
+                            />
+                            <div onClick={() => handleIncrement(course.id)} aria-label="increase">
+                                <IconPlus classNames='text-button-text-text' size="6" />
+                            </div>
+                        </div>
+                    </div>
+                ))}
+            </div>
+
+            <h6 className='text-right text-text-primary font-normal'>
+                {dictionary.components.buyCoachingSession.total}: {totalCost}
path: packages/ui-kit/lib/components/buy-coaching-session.tsx
user: login: alebg
body: @carlapbg maybe here it would be good to put the currency symbol or code (e.g., USD), next to the total amount? what do you think
created_at: 2025-03-06T22:32:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,146 @@
+import React, { useEffect, useState } from 'react'
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations'
+import { Button } from './button'
+import { InputField } from './input-field'
+import { IconPlus } from './icons/icon-plus'
+import { IconMinus } from './icons/icon-minus'
+import { IconInfoCircle } from './icons/icon-infocircle'
+
+/**
+ * Represents a course available for coaching sessions.
+ */
+type Course = {
+    id: string;
+    title: string;
+    price: number;
+    duration: number;
+    totalSessions: number;
+
+}
+
+/**
+ * Props for the BuyCoachingSession component.
+ */
+export interface BuyCoachingSessionProps extends isLocalAware {
+    /** List of courses available for purchase. */
+    courses: Course[];
+    onClick: () => void;
+}
+
+/**
+ * BuyCoachingSession Component
+ *
+ * This component allows users to purchase coaching sessions for different courses.
+ *
+ * @param {BuyCoachingSessionProps} props - The component properties.
+ * @returns {JSX.Element} The rendered component.
+ */
+function BuyCoachingSession({ courses,onClick, locale }: BuyCoachingSessionProps) {
+    const dictionary = getDictionary(locale);
+    const [totalCost, setTotalCost] = useState(0);
+    const [courseList, setCourseList] = useState<Course[]>(courses);
+
+    useEffect(() => {
+        const total = courseList.reduce((acc, course) => acc + course.price * course.totalSessions, 0);
+        setTotalCost(total);
+    }, [courseList]);
+
+    /**
+     * Increases the total sessions count for a specific course.
+     * 
+     * @param {string} id - The ID of the course to increment.
+     */
+    function handleIncrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id ? { ...course, totalSessions: course.totalSessions + 1 } : course
+            )
+        );
+    }
+
+    /**
+     * Decreases the total sessions count for a specific course, ensuring it doesn't go below 0.
+     * 
+     * @param {string} id - The ID of the course to decrement.
+     */
+    function handleDecrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id && course.totalSessions > 0
+                    ? { ...course, totalSessions: course.totalSessions - 1 }
+                    : course
+            )
+        );
+    }
+
+    /**
+     * Updates the total session count for a specific course based on user input.
+     * 
+     * @param {string} id - The ID of the course to update.
+     * @param {string} value - The new session count as a string.
+     */
+    function handleInputChange(id: string, value: string): void {
+        const newValue = Number(value);
+        if (!isNaN(newValue) && newValue >= 0) {
+            setCourseList(prevCourses =>
+                prevCourses.map(course =>
+                    course.id === id ? { ...course, totalSessions: newValue } : course
+                )
+            );
+        }
+    }
+
+    return (
+        <div className='min-auto border border-card-stroke rounded-medium bg-card-fill px-4 py-6 flex flex-col gap-6'>
+            {/* Header */}
+            <div className='flex flex-col gap-2'>
+                <h5 className='text-text-primary text-lg md:text-[1.5rem]'>
+                    {dictionary.components.buyCoachingSession.title}
+                </h5>
+                <p className='text-text-secondary text-md md:text-xl leading-6 md:leading-[1.875rem]'>
+                    {dictionary.components.buyCoachingSession.description}
+                </p>
+            </div>
+
+            {/* Body */}
+            <div className="flex flex-col">
+                {courseList.map(course => (
+                    <div key={course.id} className="flex justify-between items-center py-3 border-b border-divider">
+                        <div className="flex flex-col gap-2">
+                            <h6 className="flex items-center gap-2 text-text-primary text-md md:text-lg">
+                                {course.title} <IconInfoCircle size="4" />
+                            </h6>
+                            <div className='flex gap-2 items-center text-text-secondary'>
+                                <p className="text-xs md:text-sm">${course.price}</p>
+                                <p className="text-sm md:text-md">{course.duration} {dictionary.components.buyCoachingSession.minutes}</p>
+                            </div>
+                        </div>
+                        <div className="flex items-center gap-2">
+                            <div onClick={() => handleDecrement(course.id)} aria-label="decrease">
+                                <IconMinus classNames='text-button-text-text' size="6" />
+                            </div>
+                            <InputField
+                                type='number'
+                                className='w-[3rem] h-[2.5rem] text-lg text-center'
+                                value={course.totalSessions.toString()}
+                                setValue={(value) => handleInputChange(course.id, value)}
+                            />
+                            <div onClick={() => handleIncrement(course.id)} aria-label="increase">
+                                <IconPlus classNames='text-button-text-text' size="6" />
+                            </div>
+                        </div>
+                    </div>
+                ))}
+            </div>
+
+            <h6 className='text-right text-text-primary font-normal'>
+                {dictionary.components.buyCoachingSession.total}: {totalCost}
path: packages/ui-kit/lib/components/buy-coaching-session.tsx
user: login: carlapbg
body: Yes, the symbol or code should always appear. I think it looks better the currency code (e.g., USD, CHF)
created_at: 2025-03-06T22:48:47Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,146 @@
+import React, { useEffect, useState } from 'react'
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations'
+import { Button } from './button'
+import { InputField } from './input-field'
+import { IconPlus } from './icons/icon-plus'
+import { IconMinus } from './icons/icon-minus'
+import { IconInfoCircle } from './icons/icon-infocircle'
+
+/**
+ * Represents a course available for coaching sessions.
+ */
+type Course = {
+    id: string;
+    title: string;
+    price: number;
+    duration: number;
+    totalSessions: number;
+
+}
+
+/**
+ * Props for the BuyCoachingSession component.
+ */
+export interface BuyCoachingSessionProps extends isLocalAware {
+    /** List of courses available for purchase. */
+    courses: Course[];
+    onClick: () => void;
+}
+
+/**
+ * BuyCoachingSession Component
+ *
+ * This component allows users to purchase coaching sessions for different courses.
+ *
+ * @param {BuyCoachingSessionProps} props - The component properties.
+ * @returns {JSX.Element} The rendered component.
+ */
+function BuyCoachingSession({ courses,onClick, locale }: BuyCoachingSessionProps) {
+    const dictionary = getDictionary(locale);
+    const [totalCost, setTotalCost] = useState(0);
+    const [courseList, setCourseList] = useState<Course[]>(courses);
+
+    useEffect(() => {
+        const total = courseList.reduce((acc, course) => acc + course.price * course.totalSessions, 0);
+        setTotalCost(total);
+    }, [courseList]);
+
+    /**
+     * Increases the total sessions count for a specific course.
+     * 
+     * @param {string} id - The ID of the course to increment.
+     */
+    function handleIncrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id ? { ...course, totalSessions: course.totalSessions + 1 } : course
+            )
+        );
+    }
+
+    /**
+     * Decreases the total sessions count for a specific course, ensuring it doesn't go below 0.
+     * 
+     * @param {string} id - The ID of the course to decrement.
+     */
+    function handleDecrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id && course.totalSessions > 0
+                    ? { ...course, totalSessions: course.totalSessions - 1 }
+                    : course
+            )
+        );
+    }
+
+    /**
+     * Updates the total session count for a specific course based on user input.
+     * 
+     * @param {string} id - The ID of the course to update.
+     * @param {string} value - The new session count as a string.
+     */
+    function handleInputChange(id: string, value: string): void {
+        const newValue = Number(value);
+        if (!isNaN(newValue) && newValue >= 0) {
+            setCourseList(prevCourses =>
+                prevCourses.map(course =>
+                    course.id === id ? { ...course, totalSessions: newValue } : course
+                )
+            );
+        }
+    }
+
+    return (
+        <div className='min-auto border border-card-stroke rounded-medium bg-card-fill px-4 py-6 flex flex-col gap-6'>
+            {/* Header */}
+            <div className='flex flex-col gap-2'>
+                <h5 className='text-text-primary text-lg md:text-[1.5rem]'>
+                    {dictionary.components.buyCoachingSession.title}
+                </h5>
+                <p className='text-text-secondary text-md md:text-xl leading-6 md:leading-[1.875rem]'>
+                    {dictionary.components.buyCoachingSession.description}
+                </p>
+            </div>
+
+            {/* Body */}
+            <div className="flex flex-col">
+                {courseList.map(course => (
+                    <div key={course.id} className="flex justify-between items-center py-3 border-b border-divider">
+                        <div className="flex flex-col gap-2">
+                            <h6 className="flex items-center gap-2 text-text-primary text-md md:text-lg">
+                                {course.title} <IconInfoCircle size="4" />
+                            </h6>
+                            <div className='flex gap-2 items-center text-text-secondary'>
+                                <p className="text-xs md:text-sm">${course.price}</p>
+                                <p className="text-sm md:text-md">{course.duration} {dictionary.components.buyCoachingSession.minutes}</p>
+                            </div>
+                        </div>
+                        <div className="flex items-center gap-2">
+                            <div onClick={() => handleDecrement(course.id)} aria-label="decrease">
+                                <IconMinus classNames='text-button-text-text' size="6" />
+                            </div>
+                            <InputField
+                                type='number'
+                                className='w-[3rem] h-[2.5rem] text-lg text-center'
+                                value={course.totalSessions.toString()}
+                                setValue={(value) => handleInputChange(course.id, value)}
+                            />
+                            <div onClick={() => handleIncrement(course.id)} aria-label="increase">
+                                <IconPlus classNames='text-button-text-text' size="6" />
+                            </div>
+                        </div>
+                    </div>
+                ))}
+            </div>
+
+            <h6 className='text-right text-text-primary font-normal'>
+                {dictionary.components.buyCoachingSession.total}: {totalCost}
path: packages/ui-kit/lib/components/buy-coaching-session.tsx
user: login: carlapbg
body: @jatinrise Please adapt this to use prop for the currency code to be used here
created_at: 2025-03-06T23:18:10Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,146 @@
+import React, { useEffect, useState } from 'react'
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations'
+import { Button } from './button'
+import { InputField } from './input-field'
+import { IconPlus } from './icons/icon-plus'
+import { IconMinus } from './icons/icon-minus'
+import { IconInfoCircle } from './icons/icon-infocircle'
+
+/**
+ * Represents a course available for coaching sessions.
+ */
+type Course = {
+    id: string;
+    title: string;
+    price: number;
+    duration: number;
+    totalSessions: number;
+
+}
+
+/**
+ * Props for the BuyCoachingSession component.
+ */
+export interface BuyCoachingSessionProps extends isLocalAware {
+    /** List of courses available for purchase. */
+    courses: Course[];
+    onClick: () => void;
+}
+
+/**
+ * BuyCoachingSession Component
+ *
+ * This component allows users to purchase coaching sessions for different courses.
+ *
+ * @param {BuyCoachingSessionProps} props - The component properties.
+ * @returns {JSX.Element} The rendered component.
+ */
+function BuyCoachingSession({ courses,onClick, locale }: BuyCoachingSessionProps) {
+    const dictionary = getDictionary(locale);
+    const [totalCost, setTotalCost] = useState(0);
+    const [courseList, setCourseList] = useState<Course[]>(courses);
+
+    useEffect(() => {
+        const total = courseList.reduce((acc, course) => acc + course.price * course.totalSessions, 0);
+        setTotalCost(total);
+    }, [courseList]);
+
+    /**
+     * Increases the total sessions count for a specific course.
+     * 
+     * @param {string} id - The ID of the course to increment.
+     */
+    function handleIncrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id ? { ...course, totalSessions: course.totalSessions + 1 } : course
+            )
+        );
+    }
+
+    /**
+     * Decreases the total sessions count for a specific course, ensuring it doesn't go below 0.
+     * 
+     * @param {string} id - The ID of the course to decrement.
+     */
+    function handleDecrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id && course.totalSessions > 0
+                    ? { ...course, totalSessions: course.totalSessions - 1 }
+                    : course
+            )
+        );
+    }
+
+    /**
+     * Updates the total session count for a specific course based on user input.
+     * 
+     * @param {string} id - The ID of the course to update.
+     * @param {string} value - The new session count as a string.
+     */
+    function handleInputChange(id: string, value: string): void {
+        const newValue = Number(value);
+        if (!isNaN(newValue) && newValue >= 0) {
+            setCourseList(prevCourses =>
+                prevCourses.map(course =>
+                    course.id === id ? { ...course, totalSessions: newValue } : course
+                )
+            );
+        }
+    }
+
+    return (
+        <div className='min-auto border border-card-stroke rounded-medium bg-card-fill px-4 py-6 flex flex-col gap-6'>
+            {/* Header */}
+            <div className='flex flex-col gap-2'>
+                <h5 className='text-text-primary text-lg md:text-[1.5rem]'>
+                    {dictionary.components.buyCoachingSession.title}
+                </h5>
+                <p className='text-text-secondary text-md md:text-xl leading-6 md:leading-[1.875rem]'>
+                    {dictionary.components.buyCoachingSession.description}
+                </p>
+            </div>
+
+            {/* Body */}
+            <div className="flex flex-col">
+                {courseList.map(course => (
+                    <div key={course.id} className="flex justify-between items-center py-3 border-b border-divider">
+                        <div className="flex flex-col gap-2">
+                            <h6 className="flex items-center gap-2 text-text-primary text-md md:text-lg">
+                                {course.title} <IconInfoCircle size="4" />
+                            </h6>
+                            <div className='flex gap-2 items-center text-text-secondary'>
+                                <p className="text-xs md:text-sm">${course.price}</p>
+                                <p className="text-sm md:text-md">{course.duration} {dictionary.components.buyCoachingSession.minutes}</p>
+                            </div>
+                        </div>
+                        <div className="flex items-center gap-2">
+                            <div onClick={() => handleDecrement(course.id)} aria-label="decrease">
+                                <IconMinus classNames='text-button-text-text' size="6" />
+                            </div>
+                            <InputField
+                                type='number'
+                                className='w-[3rem] h-[2.5rem] text-lg text-center'
+                                value={course.totalSessions.toString()}
+                                setValue={(value) => handleInputChange(course.id, value)}
+                            />
+                            <div onClick={() => handleIncrement(course.id)} aria-label="increase">
+                                <IconPlus classNames='text-button-text-text' size="6" />
+                            </div>
+                        </div>
+                    </div>
+                ))}
+            </div>
+
+            <h6 className='text-right text-text-primary font-normal'>
+                {dictionary.components.buyCoachingSession.total}: {totalCost}
path: packages/ui-kit/lib/components/buy-coaching-session.tsx
user: login: jatinrise
body: @carlapbg thanks for the code review.

created_at: 2025-03-07T11:23:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,134 @@
+import React, { useEffect, useMemo, useState } from 'react'
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations'
+import { Button } from './button'
+import { InputField } from './input-field'
+import { IconPlus } from './icons/icon-plus'
+import { IconMinus } from './icons/icon-minus'
+import { IconInfoCircle } from './icons/icon-infocircle'
+
+type Course = {
+    id: string;
+    title: string;
+    price: number;
+    duration: number;
+    totalSessions: number;
+}
+
+export interface BuyCoachingSessionProps extends isLocalAware {
+    courses: Course[];
+    onClick: () => void;
+    currencyType: string;
+}
+
+/**
+ * A reusable component for purchasing coaching sessions, allowing users to select courses and adjust session quantities.
+ *
+ * @param courses An array of available courses, each containing:
+ *   - `id`: A unique identifier for the course.
+ *   - `title`: The name of the course.
+ *   - `price`: The price per session of the course.
+ *   - `duration`: The duration of a single session in minutes.
+ *   - `totalSessions`: The number of sessions the user wants to purchase.
+ * @param onClick A callback function triggered when the purchase button is clicked.
+ * @param locale A string representing the current locale for translations.
+ * @param currencyType The type of currency to display for course prices.
+ *
+ * @example
+ * <BuyCoachingSession
+ *   courses={[
+ *     { id: "1", title: "Python Basics", price: 50, duration: 60, totalSessions: 1 },
+ *     { id: "2", title: "React Advanced", price: 75, duration: 90, totalSessions: 2 }
+ *   ]}
+ *   onClick={() => console.log("Purchase initiated")}
+ *   locale="en"
+ *   currencyType="USD"
+ * />
+ */
+function BuyCoachingSession({ courses, onClick, locale,currencyType }: BuyCoachingSessionProps) {
+    const dictionary = getDictionary(locale);
+    const [courseList, setCourseList] = useState<Course[]>(courses);
+    const totalCost = useMemo(() => {
+        return courseList.reduce((acc, course) => acc + course.price * course.totalSessions, 0);
+    }, [courseList]);
+
+    function handleIncrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id ? { ...course, totalSessions: course.totalSessions + 1 } : course
+            )
+        );
+    }
+    function handleDecrement(id: string): void {
+        setCourseList(prevCourses =>
+            prevCourses.map(course =>
+                course.id === id && course.totalSessions > 0
+                    ? { ...course, totalSessions: course.totalSessions - 1 }
+                    : course
+            )
+        );
+    }
+    function handleInputChange(id: string, value: string): void {
+        const newValue = Number(value);
+        if (!isNaN(newValue) && newValue >= 0) {
+            setCourseList(prevCourses =>
+                prevCourses.map(course =>
+                    course.id === id ? { ...course, totalSessions: newValue } : course
+                )
+            );
+        }
+    }
+
+    return (
+        <div className='min-auto border border-card-stroke rounded-medium bg-card-fill px-4 py-6 flex flex-col gap-6'>
+            {/* Header */}
+            <div className='flex flex-col gap-2'>
+                <h5 className='text-text-primary text-lg md:text-[1.5rem]'>
+                    {dictionary.components.buyCoachingSession.title}
+                </h5>
+                <p className='text-text-secondary text-md md:text-xl leading-6 md:leading-[1.875rem]'>
+                    {dictionary.components.buyCoachingSession.description}
+                </p>
+            </div>
+
+            {/* Body */}
+            <div className="flex flex-col">
+                {courseList.map(course => (
+                    <div key={course.id} className="flex justify-between items-center py-3 border-b border-divider">
+                        <div className="flex flex-col gap-2">
+                            <h6 className="flex items-center gap-2 text-text-primary text-md md:text-lg">
+                                {course.title} <IconInfoCircle size="4" />
+                            </h6>
+                            <div className='flex gap-2 items-center text-text-secondary'>
+                                <p className="text-xs md:text-sm">{course.price} {currencyType}</p>
path: packages/ui-kit/lib/components/buy-coaching-session.tsx
user: login: carlapbg
body: For the course price, the font weight should be "important"
created_at: 2025-03-10T14:41:09Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,78 @@
+import React, { useState, useRef } from 'react';
+import { IconInfoCircle } from './icons/icon-infocircle';
+import { cn } from '../utils/style-utils';
+interface TooltipProps {
+    text: string;
+    content: string;
+  }
+/**
+ * Tooltip Props Interface
+ * @interface TooltipProps
+ * @property {string} text - The text displayed alongside the info icon
+ * @property {string} content - The content to display inside the tooltip
+ */
+
+
+/**
+ * Tooltip Component
+ * 
+ * A lightweight, accessible tooltip component that provides additional
+ * information when users hover over or focus on the element.
+ * 
+ * @component
+ * @example
+ * 
+ * <Tooltip 
+ *   text="Help" 
+ *   content="This is additional information provided by the tooltip." 
+ * />
+ * 
+ * @example
+ * // With longer content
+ * <Tooltip 
+ *   text="Important information" 
+ *   content="This tooltip contains a longer explanation that might span multiple lines." 
+ * />
+ */
+function Tooltip({ text, content}: TooltipProps) {
+  // State to control tooltip visibility
+  const [showTooltip, setShowTooltip] = useState(false);
+  
+  // Ref for the tooltip element for potential DOM manipulation
+  const tooltipRef = useRef<HTMLDivElement>(null);
+      
+  return (
+    <div className="relative inline-block">
+      {/* Tooltip trigger element */}
+      <span
+        className="inline-flex gap-1 items-center text-text-secondary text-sm leading-[150%] "
+        onMouseEnter={() => setShowTooltip(true)}
+        onMouseLeave={() => setShowTooltip(false)}
+        onFocus={() => setShowTooltip(true)}
+        onBlur={() => setShowTooltip(false)}
+        tabIndex={0}
+        aria-describedby="tooltip-content"
+        role="button"
+      >
+        {text}
+        <IconInfoCircle fill='text-text-secondary' classNames={cn('w-3 h-3')} />
path: packages/ui-kit/lib/components/tooltip.tsx
user: login: carlapbg
body: Can you do the Icon bigger? Let's do w-4 h-4
created_at: 2025-03-12T22:35:16Z
author_association: COLLABORATOR

PR comments:
user: login: notion-workspace[bot]
created_at: 2025-03-05T10:29:34Z
body: <a href="https://www.notion.so/Component-Migration-Buy-Coaching-Sessions-1ab5a7432d0180ca9c6fc3c0e40c17a1?pvs=8&n=github_linkback">Component<Migration>: Buy Coaching Sessions</a>
author_association: NONE
user: login: notion-workspace[bot]
created_at: 2025-03-11T22:54:50Z
body: <a href="https://www.notion.so/migration-ui-kit-Buy-Coaching-Sessions-UX-design-1b35a7432d0180a695c2f5e546df2a50?pvs=8&n=github_linkback">migration(ui-kit): Buy Coaching Sessions - UX design</a>
author_association: NONE
user: login: notion-workspace[bot]
created_at: 2025-03-11T22:54:51Z
body: <a href="https://www.notion.so/migration-ui-kit-Buy-Coaching-Sessions-code-1b35a7432d0180a58492f4c4831e252d?pvs=8&n=github_linkback">migration(ui-kit): Buy Coaching Sessions - code</a>
author_association: NONE
user: login: carlapbg
created_at: 2025-03-12T22:45:57Z
body: > @ObitoDevv thanks! So now there are two issues. Under certain conditions, the tooltip goes off screen. Two examples:
> 
> * Very long tool tip text:
>   ![immagine](https://private-user-images.githubusercontent.com/62880920/422036422-6c69f635-e0f9-4779-9b79-2452efa0f489.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDE4MTk0OTksIm5iZiI6MTc0MTgxOTE5OSwicGF0aCI6Ii82Mjg4MDkyMC80MjIwMzY0MjItNmM2OWY2MzUtZTBmOS00Nzc5LTliNzktMjQ1MmVmYTBmNDg5LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMTIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzEyVDIyMzk1OVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTExM2U3OWUzOWYxN2JkY2EyZjdjOGVhYzBhMDM4ZmJiMGM3YTQ0Zjg1ZTc4MWQwZDJhODY1MjUyODJmZmE5NmEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.ThRCfLZ267HHbrDMCPe8tJz4lOmU5qXXyEO_XEniT-M)
>   @carlapbg what would be the best way to handle text that is too long? truncate the tooltip text for example? Or maybe just restrict the amount of characters we can show on the tooltip?
> * The tooltip component is placed close to the edge of the screen:
>   ![immagine](https://private-user-images.githubusercontent.com/62880920/422037345-d7f82888-bf78-45f9-b2c6-6e67d1e22130.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDE4MTk0OTksIm5iZiI6MTc0MTgxOTE5OSwicGF0aCI6Ii82Mjg4MDkyMC80MjIwMzczNDUtZDdmODI4ODgtYmY3OC00NWY5LWIyYzYtNmU2N2QxZTIyMTMwLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMTIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzEyVDIyMzk1OVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTQ5NTcyZGI0Yzg3MzAwNTYzNjk5ZTU1ODdmNzIxNmE4ODAyOTMzZWViMDg4ZGQ0ZGMwYWRmNmJmMTk3ZTNlNWEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.Eowd7JvdXrAL4oU2Jd4q0dNotJ1quBCTP9KdPBkVV-A)

@ObitoDevv @alebg I think we can solve both examples by changing the tooltip's direction. For example, in Figma, we have an option where the tooltip box goes to the right instead of the left. This way, it can expand horizontally.

<img width="630" alt="Screenshot 2025-03-12 at 23 42 18" src="https://github.com/user-attachments/assets/8ab9deb7-0ba7-41ee-9520-9aecddde7b39" />

author_association: COLLABORATOR
user: login: jatinrise
created_at: 2025-03-13T10:25:59Z
body: Thank you for the feedback, @carlapbg @alebg ! My query regarding the tooltip component is: if the content is too large and requires scrolling to view, the content disappears because the hover effect does not persist.
author_association: COLLABORATOR
user: login: carlapbg
created_at: 2025-03-13T12:56:11Z
body: @jatinrise Maybe set a limit on the number of characters. Let's say 150, including the optional title
author_association: COLLABORATOR
user: login: jatinrise
created_at: 2025-03-19T06:16:19Z
body: Thank you  for the feedback,@alebg @carlapbg ! i have done the inline comment changes.
author_association: COLLABORATOR
user: login: nx-cloud[bot]
created_at: 2025-05-04T13:02:45Z
body: 
View your [CI Pipeline Execution ↗](https://cloud.nx.app/cipes/681769c7d16e6512fa658246?utm_source=pull-request&utm_medium=comment) for commit b8c9e4ef21f4fa35a74477170893abb9f9825ec7.

| Command | Status | Duration | Result |
|---------|--------|----------:|--------|
| `nx run cms-fastapi-sdk:generate-client` | ✅ Succeeded | 2s | [View ↗](https://cloud.nx.app/runs/vEQFpoaJOh?utm_source=pull-request&utm_medium=comment) |
| `nx run cms-fastapi:install:no-root` | ✅ Succeeded | 1s | [View ↗](https://cloud.nx.app/runs/yLwinBzFTQ?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=install` | ✅ Succeeded | 14s | [View ↗](https://cloud.nx.app/runs/vbypmpw5yz?utm_source=pull-request&utm_medium=comment) |

---

☁️ [Nx Cloud](https://cloud.nx.app?utm_source=pull-request&utm_medium=comment) last updated this comment at `2025-05-04 13:24:49` UTC

<!-- NX_CLOUD_APP_COMMENT_END -->
author_association: NONE