Repository: dream-aim-deliver/dad-e-class-monorepo
Pull request #109: [TSK-717] [TSK-718] [TSK-719] ui-kit: NavBar
Author: login: ObitoDevv
Labels: name: ui-kit
Created: 2025-03-23T21:33:38Z | Merged: 2025-03-26T22:28:27Z

## Description

<!-- Provide a clear and concise description of the changes made in this pull request for UI-Kit components. -->

- Component name: `Navbar` 
- Figma link or screenshot: [Figma Design](https://www.figma.com/design/8KEwRuOoD5IgxTtFAtLlyS/Just_Do_Ad-1.2?node-id=6240-57821&t=IrCukPhd3jcco8aU-4)

expanded:

![Image](https://github.com/user-attachments/assets/5618f0af-5b7b-4091-97af-eb9a980f13ac)

expanded logged in:

![Image](https://github.com/user-attachments/assets/a0026520-fa4f-4e0e-b2cd-9cee2a149e56)

collapsed:

![Image](https://github.com/user-attachments/assets/a183cc84-c721-410c-906b-cbede4ff200a)

collapsed logged in:

![Image](https://github.com/user-attachments/assets/a62c4690-4cc5-4d49-988c-e5215ac3bb77)

Collapses depending on width of the screen
---

## 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 #74 



Linked issue #74: [TSK-717] [TSK-718] [719] ui-kit: NavBar
Figma link:
https://www.figma.com/design/8KEwRuOoD5IgxTtFAtLlyS/Just_Do_Ad-1.2?node-id=6240-57821&t=IrCukPhd3jcco8aU-4

In use:

expanded:

![Image](https://github.com/user-attachments/assets/5618f0af-5b7b-4091-97af-eb9a980f13ac)

expanded logged in:

![Image](https://github.com/user-attachments/assets/a0026520-fa4f-4e0e-b2cd-9cee2a149e56)

collapsed:

![Image](https://github.com/user-attachments/assets/a183cc84-c721-410c-906b-cbede4ff200a)

collapsed logged in:

![Image](https://github.com/user-attachments/assets/a62c4690-4cc5-4d49-988c-e5215ac3bb77)

Collapses depending on width of the screen
Linked issue comments: user: login: ObitoDevv
created_at: 2025-03-22T13:32:22Z
body: ![Image](https://github.com/user-attachments/assets/f56a916b-ec90-4555-a906-92ad4708ce18)

![Image](https://github.com/user-attachments/assets/0e130a0f-cf12-43ec-ae00-d17e15657877)

@alebg Here sir, I have a question -> which text i have to use like here in images ? As you can see text are different in mobile version and desktop version. Or its correct and I have made exactly like this ??

author_association: COLLABORATOR
user: login: ObitoDevv
created_at: 2025-03-23T11:33:56Z
body: @maany Sir, while creating storybook for navbar from takeing  new pull from main. I'm getting these below erros. Can you please assist me to resolve these errors ? 

![Image](https://github.com/user-attachments/assets/c0f63e96-d56a-471a-ae62-52dbc551cd2f)

![Image](https://github.com/user-attachments/assets/d052092b-5657-47d9-b2e0-b7479d25e22b)
author_association: COLLABORATOR
user: login: alebg
created_at: 2025-03-23T11:45:55Z
body: > [@maany](https://github.com/maany) Sir, while creating storybook for navbar from takeing new pull from main. I'm getting these below erros. Can you please assist me to resolve these errors ?

@ObitoDevv did you put the environment file we gave to @anilrise11 in the correct place ?
author_association: CONTRIBUTOR
user: login: ObitoDevv
created_at: 2025-03-23T11:53:28Z
body: @alebg Yes sir, I have added `.env.locale` file in the apps/platfrom . Still this error is coming.
author_association: COLLABORATOR
user: login: alebg
created_at: 2025-03-23T11:58:04Z
body: > [@alebg](https://github.com/alebg) Yes sir, I have added `.env.locale` file in the apps/platfrom . Still this error is coming.

Did you re-run `pnpm install` ? If so, could you try instead `pnpm cache delete && rm -f pnpm-lock.yaml && pnpm install --force` ?
author_association: CONTRIBUTOR
user: login: ObitoDevv
created_at: 2025-03-23T12:21:31Z
body: > > [@alebg](https://github.com/alebg) Yes sir, I have added `.env.locale` file in the apps/platfrom . Still this error is coming.
> 
> Did you re-run `pnpm install` ? If so, could you try instead `pnpm cache delete && rm -f pnpm-lock.yaml && pnpm install --force` ?

sir , I tried this but still the same issue is coming. 
author_association: COLLABORATOR
user: login: ObitoDevv
created_at: 2025-03-23T12:28:26Z
body: @alebg @maany  Sir, 

I’m facing an issue when using `Image` and `Link` from `next/image` and `next/link` in the Navbar component. The imports work fine in the Next.js app, but when I try to use them in Storybook with Vite, I get a `Failed to fetch dynamically imported module error`. It seems Storybook struggles to resolve these Next.js-specific imports, possibly due to compatibility or configuration issues with Vite. I’ve tried replacing them with standard `<img>` and `<a> ` tags, which resolves the error, but I’d like to keep the Next.js features if possible. Could you suggest a fix to make these imports work in Storybook? or is it good to use `<img>` and `<a> ` tags for you?
author_association: COLLABORATOR
user: login: alebg
created_at: 2025-03-23T13:26:30Z
body: > [@alebg](https://github.com/alebg) [@maany](https://github.com/maany) Sir,
> 
> I’m facing an issue when using `Image` and `Link` from `next/image` and `next/link` in the Navbar component. The imports work fine in the Next.js app, but when I try to use them in Storybook with Vite, I get a `Failed to fetch dynamically imported module error`. It seems Storybook struggles to resolve these Next.js-specific imports, possibly due to compatibility or configuration issues with Vite. I’ve tried replacing them with standard `<img>` and `<a> ` tags, which resolves the error, but I’d like to keep the Next.js features if possible. Could you suggest a fix to make these imports work in Storybook? or is it good to use `<img>` and `<a> ` tags for you?

@ObitoDevv thanks for the details. So, in the storybook you can just instantiate any image, and for "navigation" just pass a callback with an alert. No need to use next.js in storybook at all (nor in the ui-kit in general).

I'm hoping it is clear that this one, as well as the footer, are both layout components as explained on Friday right? so both should receive children as props and just style them. Then, when we code the actual page in the app (not here in the ui-kit), we will instantiate the actual children with next.js features
author_association: CONTRIBUTOR

Diff:
diff --git a/apps/platform/src/components/home.tsx b/apps/platform/src/components/home.tsx
index d8d561bc6..093a72c01 100644
--- a/apps/platform/src/components/home.tsx
+++ b/apps/platform/src/components/home.tsx
@@ -60,6 +60,7 @@ export default function Home(props: HomeProps) {
         locale={props.locale as TLocale}
         skills={['React', 'TypeScript', 'TailwindCSS']}
       />
+      
     </div>
   );
 }
diff --git a/packages/translations/src/lib/dictionaries/base.ts b/packages/translations/src/lib/dictionaries/base.ts
index 650e087e0..b7db979c3 100644
--- a/packages/translations/src/lib/dictionaries/base.ts
+++ b/packages/translations/src/lib/dictionaries/base.ts
@@ -174,6 +174,10 @@ export const DictionarySchema = z.object({
     coachingOnDemandBanner: z.object({
       noImageText: z.string(),
     }),
+    navbar: z.object({
+      workspace: z.string(),
+      login: z.string(),
+    }),
   }),
 
 });
diff --git a/packages/translations/src/lib/dictionaries/de.ts b/packages/translations/src/lib/dictionaries/de.ts
index 1f2ae52b9..2b480c24d 100644
--- a/packages/translations/src/lib/dictionaries/de.ts
+++ b/packages/translations/src/lib/dictionaries/de.ts
@@ -161,5 +161,9 @@ export const DE: TDictionary = {
     coachingOnDemandBanner: {
       noImageText: 'Kein Bild verfügbar',
     },
+    navbar: {
+      workspace: 'Arbeitsbereich',
+      login: 'Anmelden',
+    },
   },
 };
diff --git a/packages/translations/src/lib/dictionaries/en.ts b/packages/translations/src/lib/dictionaries/en.ts
index 1da93f28c..34283b653 100644
--- a/packages/translations/src/lib/dictionaries/en.ts
+++ b/packages/translations/src/lib/dictionaries/en.ts
@@ -160,5 +160,9 @@ export const EN: TDictionary = {
     coachingOnDemandBanner: {
       noImageText: 'No image available',
     },
+    navbar: {
+      workspace: 'Workspace',
+      login: 'Login',
+    },
   },
 };
diff --git a/packages/ui-kit/lib/components/icons/icon-chat.tsx b/packages/ui-kit/lib/components/icons/icon-chat.tsx
new file mode 100644
index 000000000..8ee8250f2
--- /dev/null
+++ b/packages/ui-kit/lib/components/icons/icon-chat.tsx
@@ -0,0 +1,18 @@
+import { type IconProps, generateClassesForIcon } from './icon';
+
+/**
+ * IconCertification
+ * @usage <IconCertification />
+ */
+export const IconChat = (props: IconProps) => {
+  return (
+    <svg
+      xmlns="http://www.w3.org/2000/svg"
+      viewBox="0 0 24 24"
+      className={generateClassesForIcon(props)}
+    >
+      <path d="M5 18V21.766L11.277 18H16C17.103 18 18 17.103 18 16V8C18 6.897 17.103 6 16 6H4C2.897 6 2 6.897 2 8V16C2 17.103 2.897 18 4 18H5ZM4 8H16V16H10.723L7 18.234V16H4V8Z"/>
+      <path d="M20 2H8C6.897 2 6 2.897 6 4H18C19.103 4 20 4.897 20 6V14C21.103 14 22 13.103 22 12V4C22 2.897 21.103 2 20 2Z" />
+    </svg>
+  );
+};
diff --git a/packages/ui-kit/lib/components/icons/icon-hamburger-menu.tsx b/packages/ui-kit/lib/components/icons/icon-hamburger-menu.tsx
new file mode 100644
index 000000000..5aeac1dd4
--- /dev/null
+++ b/packages/ui-kit/lib/components/icons/icon-hamburger-menu.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import { type IconProps, generateClassesForIcon } from './icon';
+
+/**
+ * IconGroup
+ * @usage <IconGroup />
+ */
+export const IconHamburgerMenu = (props: IconProps) => {
+    return (
+
+        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" className={generateClassesForIcon(props)}>
+            <path d="M4 6H20V8H4V6ZM4 11H20V13H4V11ZM4 16H20V18H4V16Z" />
+        </svg>
+    );
+};
diff --git a/packages/ui-kit/lib/components/navbar.tsx b/packages/ui-kit/lib/components/navbar.tsx
new file mode 100644
index 000000000..3f1e4e761
--- /dev/null
+++ b/packages/ui-kit/lib/components/navbar.tsx
@@ -0,0 +1,257 @@
+import React, { useState, ReactNode } from 'react';
+import { getDictionary, isLocalAware, locales, TLocale } from '@maany_shr/e-class-translations';
+import { Button } from './button';
+import { Dropdown } from './dropdown';
+import { IconClose } from './icons/icon-close';
+import { IconHamburgerMenu } from './icons/icon-hamburger-menu';
+import { IconChat } from './icons/icon-chat';
+import { UserAvatar } from './avatar/user-avatar';
+
+interface NavbarProps extends isLocalAware {
+  isLoggedIn: boolean;
+  notificationCount?: number;
+  onChangeLanguage?: (locale: string) => void;
+  children: React.ReactNode;
+  userProfile?: React.ReactNode;
+  userProfileImageSrc?: string;
+  userName?: string;
+  logoSrc?: string;
+  availableLocales: TLocale[];
+}
+
+/**
+ * A responsive Navbar component for the e-class platform.
+ *
+ * @param isLoggedIn Indicates whether the user is logged in.
+ * @param notificationCount The number of unread notifications.
+ * @param onChangeLanguage Callback function triggered when the language is changed. Receives the new locale as an argument.
+ * @param children The children elements to be rendered in the Navbar.
+ * @param userProfile The user profile component to be rendered in the Navbar.
+ * @param userProfileImageSrc The URL of the user's profile image.
+ * @param userName The user's full name.
+ * @param logoSrc The URL of the platform's logo.
+ * @param availableLocales An array of available locales for the language dropdown.
+ *
+ * @example
+ * <Navbar
+ *   isLoggedIn={true}
+ *   notificationCount={3}
+ *   onChangeLanguage={(locale) => console.log("Language changed to:", locale)}
+ *   userProfile={<UserAvatar imageUrl="https://example.com/avatar.jpg" size="small" fullName="John Doe" />}
+ *   userProfileImageSrc="https://example.com/avatar.jpg"
+ *   userName="John Doe"
+ *   logoSrc="https://example.com/logo.png"
+ *   availableLocales={['en', 'de']}
+ * >
+ *   <a href="/courses">Courses</a>
+ *   <a href="/profile">Profile</a>
+ * </Navbar>
+ */
+export const Navbar: React.FC<NavbarProps> = ({
+  isLoggedIn,
+  locale: initialLocale,
+  notificationCount = 0,
+  onChangeLanguage,
+  children,
+  userProfile,
+  userProfileImageSrc,
+  userName,
+  logoSrc,
+  availableLocales,
+}) => {
+  const [isMenuOpen, setIsMenuOpen] = useState(false);
+  const [currentLocale, setCurrentLocale] = useState<TLocale>(initialLocale);
+  const dictionary = getDictionary(currentLocale);
+
+  const toggleMenu = () => {
+    setIsMenuOpen(!isMenuOpen);
+  };
+
+  const handleLocaleChange = (newLocale: TLocale) => {
+    if (availableLocales.includes(newLocale)) {
+      setCurrentLocale(newLocale);
+      if (onChangeLanguage) {
+        onChangeLanguage(newLocale);
+      }
+    }
+  };
+
+  const languageOptions = availableLocales.map(locale => ({
+    label: locale.toUpperCase(),
+    value: locale
+  }));
+
+  const defaultUserProfile = (
+    <div className="flex items-center space-x-2"> {/* Smaller gap between avatar and workspace */}
+      <UserAvatar
+        imageUrl={userProfileImageSrc}
+        size="small"
+        fullName={userName}
+        className="p-0 ml-3"
+      />
+      <a href="/workspace">
+        <span className="hover:text-button-primary-fill cursor-pointer">
+          {dictionary.components.navbar.workspace}
+        </span>
+      </a>
+    </div>
+  );
+
+  const formatNotificationCount = (count: number) => {
+    if (count > 99) return "99+";
+    return count.toString();
+  };
+
+  return (
+    <nav className="bg-transparent text-white py-4 px-4 flex items-center justify-between">
+      {/* Logo */}
+      <div className="flex items-center">
+        <a href="/">
+          <img
+            src={logoSrc}
+            width={40}
+            height={40}
+            alt="Logo"
+          />
+        </a>
+      </div>
+
+      {/* Desktop Menu (Large Screens) */}
+      <div className="hidden lg:flex items-center space-x-6 ml-auto">
+        {children}
+      </div>
+
+      {/* Right Section (Profile+Workspace, Chat, Language Dropdown) */}
+      <div className="hidden lg:flex items-center space-x-6 ml-2.5">
+        {isLoggedIn ? (
+          <>
+            {userProfile || defaultUserProfile}
+            <div className="relative flex items-center">
+              <IconChat size="6" classNames="cursor-pointer" />
+              {notificationCount > 0 && (
+                <span className="absolute p-2 -top-4 left-4 leading-[150%] font-bold bg-button-primary-fill text-black text-xs rounded-full h-6 w-6 flex items-center justify-center overflow-hidden">
+                  {formatNotificationCount(notificationCount)}
+                </span>
+              )}
+            </div>
+          </>
+        ) : (
+          <a href="/login">
+            <Button text={dictionary.components.navbar.login} variant="primary" size="medium" className="ml-3" />
+          </a>
+        )}
+        <div className="relative">
+          <Dropdown
+            type="simple"
+            options={languageOptions}
+            onSelectionChange={(selected) => {
+              if (typeof selected === 'string' && locales.includes(selected as TLocale)) {
+                handleLocaleChange(selected as TLocale);
+              }
+            }}
+            text={{ simpleText: "" }}
+            defaultValue={currentLocale}
+          />
+        </div>
+      </div>
+
+      {/* Mobile Menu Button (Small Screens) */}
+      <div className="lg:hidden">
+        <div className="relative flex items-center space-x-4">
+          {isLoggedIn && (
+            <div className="flex items-center space-x-4">
+              <div className="flex items-center space-x-2"> {/* Smaller gap for mobile */}
+                {userProfile ? (
+                  <div className="scale-75 origin-left">
+                    {userProfile}
+                  </div>
+                ) : (
+                  <UserAvatar
+                    imageUrl={userProfileImageSrc}
+                    size="small"
+                    fullName={userName}
+                    className="ml-1"
+                  />
+                )}
+                <a href="/workspace">
+                  <span className="hover:text-button-primary-fill cursor-pointer text-sm">
+                    {dictionary.components.navbar.workspace}
+                  </span>
+                </a>
+              </div>
+              <div className="relative flex items-center">
+                <IconChat size="6" classNames="cursor-pointer" />
+                {notificationCount > 0 && (
+                  <span className="absolute p-2 -top-4 left-4 leading-[150%] font-bold bg-button-primary-fill text-black text-xs rounded-full h-6 w-6 flex items-center justify-center overflow-hidden">
+                    {formatNotificationCount(notificationCount)}
+                  </span>
+                )}
+              </div>
+            </div>
+          )}
+
+          <Button
+            onClick={toggleMenu}
+            iconRight={<IconHamburgerMenu size="8" />}
+            hasIconRight
+            variant="text"
+            size="medium"
+            className="focus:outline-none p-0"
+          />
+        </div>
+      </div>
+
+      {/* Mobile Menu */}
+      {isMenuOpen && (
+        <div className="absolute top-0 left-0 w-full h-full bg-button-primary-text text-white flex flex-col items-center justify-center lg:hidden">
+          <div className="absolute top-4 left-0 right-0 flex justify-between items-center px-6 w-full">
+            <a href="/">
+              <img
+                src={logoSrc}
+                alt="Logo"
+                width={38}
+                height={38}
+                className="cursor-pointer"
+              />
+            </a>
+
+            <Button
+              onClick={toggleMenu}
+              iconRight={<IconClose classNames="w-8 h-8" />}
+              hasIconRight
+              variant="text"
+              size="big"
+              className="focus:outline-none p-0"
+            />
+          </div>
+
+          <div className="flex flex-col items-center space-y-6 py-4">
+            {children}
+            <Dropdown
+              type="simple"
+              options={languageOptions}
+              onSelectionChange={(selected) => {
+                if (typeof selected === 'string' && locales.includes(selected as TLocale)) {
+                  handleLocaleChange(selected as TLocale);
+                }
+              }}
+              text={{ simpleText: "" }}
+              defaultValue={currentLocale}
+            />
+            {!isLoggedIn && (
+              <a href="/login">
+                <Button
+                  text={dictionary.components.navbar.login}
+                  variant="primary"
+                  size="medium"
+                />
+              </a>
+            )}
+          </div>
+        </div>
+      )}
+    </nav>
+  );
+};
+
+export default Navbar;
\ No newline at end of file
diff --git a/packages/ui-kit/lib/index.ts b/packages/ui-kit/lib/index.ts
index 82c400f46..8fb727c00 100644
--- a/packages/ui-kit/lib/index.ts
+++ b/packages/ui-kit/lib/index.ts
@@ -25,3 +25,4 @@ export { CourseCreatorCard } from './components/coursecard/course-creator-course
 export { ProfileTabs, DragAndDrop, Badge };
 export { default  as  BuyCoachingSession, type BuyCoachingSessionProps } from './components/buy-coaching-session'
 export * as contexts from './contexts';
+export {Navbar} from './components/navbar';
\ No newline at end of file
diff --git a/packages/ui-kit/stories/icons/icon-chat.stories.tsx b/packages/ui-kit/stories/icons/icon-chat.stories.tsx
new file mode 100644
index 000000000..a3eb7e559
--- /dev/null
+++ b/packages/ui-kit/stories/icons/icon-chat.stories.tsx
@@ -0,0 +1,47 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { IconChat } from '../../lib/components/icons/icon-chat';
+
+// Meta configuration for Storybook
+const meta: Meta<typeof IconChat> = {
+  title: 'Icons/IconChat',
+  component: IconChat,
+  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 IconChat>;
+
+// 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/navbar.stories.tsx b/packages/ui-kit/stories/navbar.stories.tsx
new file mode 100644
index 000000000..2c47eae77
--- /dev/null
+++ b/packages/ui-kit/stories/navbar.stories.tsx
@@ -0,0 +1,264 @@
+import { Meta, StoryObj } from '@storybook/react';
+import { Navbar } from '../lib/components/navbar';
+import { NextIntlClientProvider } from 'next-intl';
+import { locales, TLocale } from '@maany_shr/e-class-translations';
+import { useState, useEffect } from 'react';
+
+// Mock dictionary structure (aligned with TDictionary)
+const mockMessages = {
+  components: {
+    navbar: {
+      offers: 'Offers',
+      coaching: 'Coaching',
+      howItWorks: 'How It Works',
+      about: 'About',
+      workspace: 'Workspace',
+      login: 'Login',
+    },
+  },
+};
+
+const mockMessagesDe = {
+  components: {
+    navbar: {
+      offers: 'Angebote',
+      coaching: 'Coaching',
+      howItWorks: 'Wie es funktioniert',
+      about: 'Über uns',
+      workspace: 'Arbeitsbereich',
+      login: 'Anmelden',
+    },
+  },
+};
+
+// Navigation links using the dictionary
+const NavLinks = ({ locale }: { locale: TLocale }) => {
+  const messages = locale === 'en' ? mockMessages : mockMessagesDe;
+  const t = messages.components.navbar;
+
+  return (
+    <>
+      <a href="/offers">
+        <span className="hover:text-button-primary-fill cursor-pointer">
+          {t.offers}
+        </span>
+      </a>
+      <a href="/coaching">
+        <span className="hover:text-button-primary-fill cursor-pointer">
+          {t.coaching}
+        </span>
+      </a>
+      <a href="/how-it-works">
+        <span className="hover:text-button-primary-fill cursor-pointer">
+          {t.howItWorks}
+        </span>
+      </a>
+      <a href="/about">
+        <span className="hover:text-button-primary-fill cursor-pointer">
+          {t.about}
+        </span>
+      </a>
+    </>
+  );
+};
+
+// Wrapper component to manage state and sync with args.locale
+const NavbarWrapper = (args: any) => {
+  const [locale, setLocale] = useState<TLocale>(args.locale);
+
+  // Sync internal state with args.locale when it changes via Storybook controls
+  useEffect(() => {
+    setLocale(args.locale);
+  }, [args.locale]);
+
+  const handleLocaleChange = (newLocale: string) => {
+    setLocale(newLocale as TLocale);
+  };
+
+  return (
+    <NextIntlClientProvider
+      locale={locale}
+      messages={locale === 'en' ? mockMessages : mockMessagesDe}
+    >
+      <Navbar
+        {...args}
+        locale={locale}
+        onChangeLanguage={handleLocaleChange}
+      >
+        <NavLinks locale={locale} />
+      </Navbar>
+    </NextIntlClientProvider>
+  );
+};
+
+const meta: Meta<typeof Navbar> = {
+  title: 'Components/Navbar',
+  component: Navbar,
+  tags: ['autodocs'],
+  parameters: {
+    layout: 'fullscreen',
+  },
+  decorators: [
+    (Story) => (
+      <div className="min-h-screen">
+        <Story />
+      </div>
+    ),
+  ],
+  argTypes: {
+    isLoggedIn: {
+      control: 'boolean',
+      description: 'Whether the user is logged in.',
+    },
+    locale: {
+      control: 'select',
+      options: locales,
+      description: 'The locale for language selection.',
+    },
+    notificationCount: {
+      control: 'number',
+      description: 'Number of notifications to display.',
+    },
+    userProfileImageSrc: {
+      control: 'text',
+      description: 'URL for the user profile image.',
+    },
+    userName: {
+      control: 'text',
+      description: 'The name of the user.',
+    },
+    logoSrc: {
+      control: 'text',
+      description: 'URL for the logo image.',
+    },
+    availableLocales: {
+      control: 'multi-select',
+      options: locales,
+      description: 'Array of available locales for the language dropdown.',
+    },
+  },
+};
+
+export default meta;
+
+// Template uses the wrapper component
+const Template: StoryObj<typeof Navbar> = {
+  render: (args) => <NavbarWrapper {...args} />,
+};
+
+export const LoggedOut: StoryObj<typeof Navbar> = {
+  ...Template,
+  args: {
+    isLoggedIn: false,
+    locale: 'en',
+    notificationCount: 0,
+    userProfileImageSrc:
+      'https://res.cloudinary.com/dgk9gxgk4/image/upload/v1733464948/2151206389_1_c38sda.jpg',
+    userName: 'John Doe',
+    logoSrc: 'https://res.cloudinary.com/dowkwaxnn/image/upload/v1742810063/a_atmfwj.png',
+    availableLocales: locales,
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: 'Navbar when the user is not logged in.',
+      },
+    },
+  },
+};
+
+export const LoggedInWithNotifications: StoryObj<typeof Navbar> = {
+  ...Template,
+  args: {
+    isLoggedIn: true,
+    locale: 'en',
+    notificationCount: 200,
+    userProfileImageSrc:
+      'https://res.cloudinary.com/dgk9gxgk4/image/upload/v1733464948/2151206389_1_c38sda.jpg',
+    userName: 'John Doe',
+    logoSrc: 'https://res.cloudinary.com/dowkwaxnn/image/upload/v1742810063/a_atmfwj.png',
+    availableLocales: locales,
+  },
+};
+
+export const LoggedOutGerman: StoryObj<typeof Navbar> = {
+  ...Template,
+  args: {
+    isLoggedIn: false,
+    locale: 'de',
+    notificationCount: 0,
+    userProfileImageSrc:
+      'https://res.cloudinary.com/dgk9gxgk4/image/upload/v1733464948/2151206389_1_c38sda.jpg',
+    userName: 'John Doe',
+    logoSrc: 'https://res.cloudinary.com/dowkwaxnn/image/upload/v1742810063/a_atmfwj.png',
+    availableLocales: locales,
+  },
+};
+
+export const MobileViewLoggedIn: StoryObj<typeof Navbar> = {
+  ...Template,
+  args: {
+    isLoggedIn: true,
+    locale: 'en',
+    notificationCount: 2,
+    userProfileImageSrc:
+      'https://res.cloudinary.com/dgk9gxgk4/image/upload/v1733464948/2151206389_1_c38sda.jpg',
+    userName: 'John Doe',
+    logoSrc: 'https://res.cloudinary.com/dowkwaxnn/image/upload/v1742810063/a_atmfwj.png',
+    availableLocales: locales,
+  },
+  parameters: {
+    viewport: {
+      defaultViewport: 'mobile1',
+    },
+  },
+};
+
+export const LoggedInNoNotifications: StoryObj<typeof Navbar> = {
+  ...Template,
+  args: {
+    isLoggedIn: true,
+    locale: 'en',
+    notificationCount: 0,
+    userProfileImageSrc:
+      'https://res.cloudinary.com/dgk9gxgk4/image/upload/v1733464948/2151206389_1_c38sda.jpg',
+    userName: 'John Doe',
+    logoSrc: 'https://res.cloudinary.com/dowkwaxnn/image/upload/v1742810063/a_atmfwj.png',
+    availableLocales: locales,
+  },
+};
+
+export const CustomUserProfile: StoryObj<typeof Navbar> = {
+  ...Template,
+  args: {
+    isLoggedIn: true,
+    locale: 'en',
+    notificationCount: 1,
+    userProfileImageSrc:
+      'https://res.cloudinary.com/dgk9gxgk4/image/upload/v1733464948/2151206389_1_c38sda.jpg',
+    userName: 'John Doe',
+    logoSrc: 'https://res.cloudinary.com/dowkwaxnn/image/upload/v1742810063/a_atmfwj.png',
+    availableLocales: locales,
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: 'Navbar with a custom user profile image.',
+      },
+    },
+  },
+};
+
+export const LocalizedNavLinks: StoryObj<typeof Navbar> = {
+  ...Template,
+  args: {
+    isLoggedIn: true,
+    locale: 'de',
+    notificationCount: 0,
+    userProfileImageSrc:
+      'https://res.cloudinary.com/dgk9gxgk4/image/upload/v1733464948/2151206389_1_c38sda.jpg',
+    userName: 'John Doe',
+    logoSrc: 'https://res.cloudinary.com/dowkwaxnn/image/upload/v1742810063/a_atmfwj.png',
+    availableLocales: locales,
+  },
+};
\ No newline at end of file
diff --git a/packages/ui-kit/tests/navbar.test.tsx b/packages/ui-kit/tests/navbar.test.tsx
new file mode 100644
index 000000000..79fa2fa2f
--- /dev/null
+++ b/packages/ui-kit/tests/navbar.test.tsx
@@ -0,0 +1,218 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+import { Navbar } from '../lib/components/navbar';
+import { vi } from 'vitest';
+
+vi.mock('@maany_shr/e-class-translations', () => ({
+  getDictionary: (locale) => ({
+    components: {
+      navbar: {
+        workspace: locale === 'en' ? 'Workspace' : 'Arbeitsbereich',
+        login: locale === 'en' ? 'Login' : 'Anmelden',
+      },
+    },
+  }),
+  isLocalAware: vi.fn(),
+  locales: ['en', 'de'], // Added locales to match component usage
+}));
+
+vi.mock('../lib/components/button', () => ({
+  Button: ({ text, onClick, iconRight, hasIconRight, variant, size, className }) => (
+    <button
+      onClick={onClick}
+      className={className}
+      data-variant={variant}
+      data-size={size}
+      data-has-icon-right={hasIconRight ? 'true' : 'false'}
+      data-testid="mock-button"
+    >
+      {text}
+      {iconRight && <span data-testid="icon-right">{iconRight}</span>}
+    </button>
+  ),
+}));
+
+vi.mock('../lib/components/dropdown', () => ({
+  Dropdown: ({ options, onSelectionChange, defaultValue, type, text }) => (
+    <select
+      onChange={(e) => onSelectionChange(e.target.value)}
+      defaultValue={defaultValue}
+      data-type={type}
+      data-text={text?.simpleText}
+      data-testid="language-dropdown"
+    >
+      {options.map((option) => (
+        <option key={option.value} value={option.value}>
+          {option.label}
+        </option>
+      ))}
+    </select>
+  ),
+}));
+
+vi.mock('../lib/components/icons/icon-close', () => ({
+  IconClose: ({ classNames }) => (
+    <div data-testid="icon-close" className={classNames}>
+      Close Icon
+    </div>
+  ),
+}));
+
+vi.mock('../lib/components/icons/icon-hamburger-menu', () => ({
+  IconHamburgerMenu: ({ size }) => (
+    <div data-testid="icon-hamburger" data-size={size}>
+      Hamburger Icon
+    </div>
+  ),
+}));
+
+vi.mock('../lib/components/icons/icon-chat', () => ({
+  IconChat: ({ size, classNames }) => (
+    <div data-testid="icon-chat" data-size={size} className={classNames}>
+      Chat Icon
+    </div>
+  ),
+}));
+
+vi.mock('../lib/components/avatar/user-avatar', () => ({
+  UserAvatar: ({ imageUrl, size, fullName, className }) => (
+    <div
+      data-testid="user-avatar"
+      data-image-url={imageUrl}
+      data-size={size}
+      data-full-name={fullName}
+      className={className}
+    >
+      Avatar
+    </div>
+  ),
+}));
+
+describe('Navbar Component', () => {
+  test('renders correctly when not logged in with English locale', () => {
+    const onChangeLanguageMock = vi.fn();
+    render(
+      <Navbar
+        isLoggedIn={false}
+        locale="en"
+        onChangeLanguage={onChangeLanguageMock}
+        availableLocales={['en', 'de']}
+      >
+        <a href="/home">Home</a>
+        <a href="/about">About</a>
+      </Navbar>
+    );
+
+    expect(screen.getByAltText('Logo')).toBeInTheDocument();
+    expect(screen.getByText('Home')).toBeInTheDocument();
+    expect(screen.getByText('About')).toBeInTheDocument();
+    expect(screen.getByText('Login')).toBeInTheDocument();
+    expect(screen.getByTestId('language-dropdown')).toHaveValue('en');
+  });
+
+  it('renders user profile and workspace when logged in', () => {
+    render(
+      <Navbar isLoggedIn={true} locale="en" availableLocales={['en', 'de']}>
+        <a href="/home">Home</a>
+      </Navbar>
+    );
+    const userAvatars = screen.getAllByTestId('user-avatar');
+    expect(userAvatars.length).toBe(2); // Desktop and mobile
+    expect(userAvatars[0]).toHaveClass('p-0', 'ml-3'); // Desktop version
+    const workspaceLinks = screen.getAllByText('Workspace');
+    expect(workspaceLinks.length).toBe(2); // Desktop and mobile
+    expect(workspaceLinks[0]).toBeInTheDocument();
+  });
+
+  it('displays notification count and icon when provided', () => {
+    render(
+      <Navbar
+        isLoggedIn={true}
+        locale="en"
+        notificationCount={2}
+        availableLocales={['en', 'de']}
+      >
+        <a href="/home">Home</a>
+      </Navbar>
+    );
+    const notifications = screen.getAllByText('2');
+    expect(notifications.length).toBe(2); // Desktop and mobile
+    expect(notifications[0]).toHaveClass('h-6'); // Desktop version
+    expect(screen.getAllByTestId('icon-chat').length).toBe(2); // Desktop and mobile
+  });
+
+  test('calls onChangeLanguage when language dropdown changes', () => {
+    const onChangeLanguageMock = vi.fn();
+    render(
+      <Navbar
+        isLoggedIn={false}
+        locale="en"
+        onChangeLanguage={onChangeLanguageMock}
+        availableLocales={['en', 'de']}
+      >
+        <a href="/home">Home</a>
+      </Navbar>
+    );
+
+    const dropdown = screen.getByTestId('language-dropdown');
+    fireEvent.change(dropdown, { target: { value: 'de' } });
+    expect(onChangeLanguageMock).toHaveBeenCalledWith('de');
+    expect(dropdown).toHaveValue('de');
+  });
+
+  test('renders custom user profile when provided', () => {
+    const customProfile = <div data-testid="custom-profile">Custom Profile</div>;
+    render(
+      <Navbar
+        isLoggedIn={true}
+        locale="en"
+        onChangeLanguage={vi.fn()}
+        userProfile={customProfile}
+        availableLocales={['en', 'de']}
+      >
+        <a href="/home">Home</a>
+      </Navbar>
+    );
+
+    const customProfiles = screen.getAllByTestId('custom-profile');
+    expect(customProfiles.length).toBe(2); // Desktop and mobile
+    expect(customProfiles[1].parentElement).toHaveClass('scale-75'); // Mobile version
+    expect(screen.queryByTestId('user-avatar')).not.toBeInTheDocument();
+  });
+
+  test('renders correctly in German when locale is "de"', () => {
+    render(
+      <Navbar
+        isLoggedIn={true}
+        locale="de"
+        onChangeLanguage={vi.fn()}
+        availableLocales={['en', 'de']}
+      >
+        <a href="/home">Home</a>
+      </Navbar>
+    );
+
+    const workspaces = screen.getAllByText('Arbeitsbereich');
+    expect(workspaces.length).toBe(2); // Desktop and mobile
+    expect(workspaces[1]).toHaveClass('text-sm'); // Mobile version
+    expect(screen.getByTestId('language-dropdown')).toHaveValue('de');
+  });
+
+  test('shows hamburger menu and includes desktop menu structure', () => {
+    render(
+      <Navbar
+        isLoggedIn={true}
+        locale="en"
+        onChangeLanguage={vi.fn()}
+        availableLocales={['en', 'de']}
+      >
+        <a href="/home">Home</a>
+      </Navbar>
+    );
+
+    const desktopMenu = screen.getByText('Home').closest('div');
+    expect(desktopMenu).toHaveClass('hidden', 'lg:flex');
+    expect(
+      screen.getByTestId('mock-button').querySelector('[data-testid="icon-right"]')
+    ).toBeInTheDocument();
+  });
+});
\ No newline at end of file


Reviews:
user: login: alebg
body: @ObitoDevv thanks a lot! so as per our discussion, please:

- [x] Carla's comments on the style
- [x] Change the notification number to "99+" if there are more than 99 notifications, and prevent the overflow of "99+"
- [x] Show the notification icon (without yellow circle and number) if there are 0 notificatons
- [x] Logo is hardcoded, but we need to re-use this with different logos (Bewerbeagentur, Just Do Ad, Jobrand, CMS)
- [x] Wait for me to come back about where the locale options should come from
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T08:53:22Z
user: login: carlapbg
body: In general looks good. Just two things:

- The font weight should be bold (important) for the Notification count
- The gap between every item in the nav bar should be the same. Now I can see that the "about" is so close to the "login"  button and the dropdown (when the user needs to login). Also the avatar and "workspace" are so close to "about" and the notification icon
<img width="534" alt="Screenshot 2025-03-24 at 19 25 20" src="https://github.com/user-attachments/assets/ef3aacd8-46b0-4c30-b84e-1afbcc636f12" />
<img width="640" alt="Screenshot 2025-03-24 at 19 26 07" src="https://github.com/user-attachments/assets/bce66f82-1e35-44bc-afa4-c0eda0e73e24" />


**This is how it should look**
![Screenshot 2025-03-24 at 19 29 30](https://github.com/user-attachments/assets/c601f696-a1bd-4674-befa-20c9e64588b8)
![Screenshot 2025-03-24 at 19 30 13](https://github.com/user-attachments/assets/23fab0c1-d85b-4884-82d7-85bd99ec803c)

state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-24T18:31:12Z
user: login: alebg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T11:59:06Z
user: login: alebg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T11:59:54Z
user: login: alebg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T12:01:08Z
user: login: alebg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T12:02:23Z
user: login: alebg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T12:04:53Z
user: login: alebg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T12:05:40Z
user: login: alebg
body: @ObitoDevv Very good, thanks for addressing all we discussed! Now please just address my latest comments about the locale handling. We'll just pass available locales as an array of strings, and then the component needs to figure out the rest from there
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T12:13:00Z
user: login: carlapbg
body: Thanks for the changes! Just keep in mind that the avatar and “Workspace” should be a single element in the navbar. Which means that the gap between them should be much smaller than the gap between the other elements:

![Screenshot 2025-03-25 at 16 04 13](https://github.com/user-attachments/assets/12c2ab56-524e-4439-84af-d8dbf845c02f)

state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-25T15:14:54Z
user: login: alebg
body: @ObitoDevv Code-wise it looks good to me, thanks a lot.

- [x] One detail about the story, could you please make it s.t. your examples are in fact localized? changing the locale with the dropdown doesn't change the Links' languages, only the workspace and login. I don't think you'd need to change anything in navbar.tsx, only in the story.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T10:58:16Z
user: login: alebg
body: @ObitoDevv we just fixed the storybook font issue and rebased your branch. All good, thanks a lot!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T22:27:31Z
user: login: carlapbg
body: Looks good! Thank you
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-26T22:28:21Z

Review comments:
diff_hunk: @@ -145,6 +145,16 @@ export const DictionarySchema = z.object({
       total:z.string(),
       minutes:z.string(),
     }),
+    navbar: z.object({
+      offers: z.string(),
+      coaching: z.string(),
+      howItWorks: z.string(),
+      about: z.string(),
+      workspace: z.string(),
+      login: z.string(),
+      english: z.string(),
path: packages/translations/src/lib/dictionaries/base.ts
user: login: alebg
body: @ObitoDevv Please don't put the language names in the dictionaries
created_at: 2025-03-25T11:59:06Z
author_association: CONTRIBUTOR
path: packages/ui-kit/lib/assets/images/logo.png
user: login: alebg
body: @ObitoDevv please remove this if it's not being used anymore
created_at: 2025-03-25T11:59:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,230 @@
+import React, { useState, ReactNode } from 'react';
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations';
+import { Button } from './button';
+import { Dropdown } from './dropdown';
+import { IconClose } from './icons/icon-close';
+import { IconHamburgerMenu } from './icons/icon-hamburger-menu';
+import { IconChat } from './icons/icon-chat';
+import { UserAvatar } from './avatar/user-avatar';
+
+interface NavbarProps extends isLocalAware {
+  isLoggedIn: boolean;
+  notificationCount?: number;
+  onChangeLanguage?: (locale: string) => void;
+  children: React.ReactNode;
+  userProfile?: React.ReactNode;
+  userProfileImageSrc?: string;
+  userName?: string;
+  logoSrc?: string;
path: packages/ui-kit/lib/components/navbar.tsx
user: login: alebg
body: @ObitoDevv Please add another prop `availableLocales`, which will be just an array of strings
created_at: 2025-03-25T12:01:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,230 @@
+import React, { useState, ReactNode } from 'react';
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations';
+import { Button } from './button';
+import { Dropdown } from './dropdown';
+import { IconClose } from './icons/icon-close';
+import { IconHamburgerMenu } from './icons/icon-hamburger-menu';
+import { IconChat } from './icons/icon-chat';
+import { UserAvatar } from './avatar/user-avatar';
+
+interface NavbarProps extends isLocalAware {
+  isLoggedIn: boolean;
+  notificationCount?: number;
+  onChangeLanguage?: (locale: string) => void;
+  children: React.ReactNode;
+  userProfile?: React.ReactNode;
+  userProfileImageSrc?: string;
+  userName?: string;
+  logoSrc?: string;
+}
+
+export const Navbar: React.FC<NavbarProps> = ({
+  isLoggedIn,
+  locale: initialLocale,
+  notificationCount = 0,
+  onChangeLanguage,
+  children,
+  userProfile,
+  userProfileImageSrc,
+  userName,
+  logoSrc,
+}) => {
+  const [isMenuOpen, setIsMenuOpen] = useState(false);
+  const [currentLocale, setCurrentLocale] = useState(initialLocale);
+  const dictionary = getDictionary(currentLocale);
+
+  const toggleMenu = () => {
+    setIsMenuOpen(!isMenuOpen);
+  };
+
+  const handleLocaleChange = (newLocale: string) => {
+    if (newLocale === "en" || newLocale === "de") {
path: packages/ui-kit/lib/components/navbar.tsx
user: login: alebg
body: @ObitoDevv So instead of hardcoding "en" and "de" here, you will just check if `newLocale` matches any of `availableLocales`
created_at: 2025-03-25T12:02:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,230 @@
+import React, { useState, ReactNode } from 'react';
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations';
+import { Button } from './button';
+import { Dropdown } from './dropdown';
+import { IconClose } from './icons/icon-close';
+import { IconHamburgerMenu } from './icons/icon-hamburger-menu';
+import { IconChat } from './icons/icon-chat';
+import { UserAvatar } from './avatar/user-avatar';
+
+interface NavbarProps extends isLocalAware {
+  isLoggedIn: boolean;
+  notificationCount?: number;
+  onChangeLanguage?: (locale: string) => void;
+  children: React.ReactNode;
+  userProfile?: React.ReactNode;
+  userProfileImageSrc?: string;
+  userName?: string;
+  logoSrc?: string;
+}
+
+export const Navbar: React.FC<NavbarProps> = ({
+  isLoggedIn,
+  locale: initialLocale,
+  notificationCount = 0,
+  onChangeLanguage,
+  children,
+  userProfile,
+  userProfileImageSrc,
+  userName,
+  logoSrc,
+}) => {
+  const [isMenuOpen, setIsMenuOpen] = useState(false);
+  const [currentLocale, setCurrentLocale] = useState(initialLocale);
+  const dictionary = getDictionary(currentLocale);
+
+  const toggleMenu = () => {
+    setIsMenuOpen(!isMenuOpen);
+  };
+
+  const handleLocaleChange = (newLocale: string) => {
+    if (newLocale === "en" || newLocale === "de") {
+      setCurrentLocale(newLocale);
+    }
+    if (onChangeLanguage) {
+      onChangeLanguage(newLocale);
+    }
+  };
+
+  const defaultUserProfile = (
+    <div className="relative">
+      <UserAvatar
+        imageUrl={userProfileImageSrc}
+        size="small"
+        fullName={userName}
+        className='ml-3 p-0'
+      />
+    </div>
+  );
+
+  const formatNotificationCount = (count: number) => {
+    if (count > 99) return "99+";
+    return count.toString();
+  };
+
+  return (
+    <nav className="bg-transparent text-white py-4 px-4 flex items-center justify-between">
+      {/* Logo */}
+      <div className="flex items-center">
+        <a href="/">
+          <img
+            src={logoSrc}
+            width={40}
+            height={40}
+            alt="Logo"
+          />
+        </a>
+      </div>
+
+      {/* Desktop Menu (Large Screens) */}
+      <div className="hidden lg:flex items-center space-x-6 ml-auto">
+        {children}
+      </div>
+
+      {/* Right Section (Profile, Workspace, Language Dropdown) */}
+      <div className="hidden lg:flex items-center space-x-6 ml-2.5">
+        {isLoggedIn ? (
+          <>
+            {userProfile || defaultUserProfile}
+            <div className="relative">
+              <a href="/workspace">
+                <span className="hover:text-button-primary-fill cursor-pointer">
+                  {dictionary.components.navbar.workspace}
+                </span>
+              </a>
+            </div>
+            <div className="relative flex items-center">
+              <IconChat size='6' classNames='cursor-pointer' />
+              {notificationCount > 0 &&
+                <span className="absolute p-2 -top-4 left-4 leading-[150%] font-bold bg-button-primary-fill text-black text-xs rounded-full h-6 w-6 flex items-center justify-center overflow-hidden ">
+                  {formatNotificationCount(notificationCount)}
+                </span>
+              }
+            </div>
+          </>
+        ) : (
+          <a href="/login">
+            <Button text={dictionary.components.navbar.login} variant="primary" size="medium" className="ml-3" />
+          </a>
+        )}
+        <div className="relative">
+          <Dropdown
+            type="simple"
+            options={[
+              { label: "ENG", value: "en" },
path: packages/ui-kit/lib/components/navbar.tsx
user: login: alebg
body: @ObitoDevv Here please don't hard-code languages, but you can generate these pairs by looping over `availableLocales`. So it would be something like (pseudo-code):
`{ label: availableLocale.toUpperCase(), value: availableLocale }` for every availableLocale of availableLocales
created_at: 2025-03-25T12:04:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,230 @@
+import React, { useState, ReactNode } from 'react';
+import { getDictionary, isLocalAware } from '@maany_shr/e-class-translations';
+import { Button } from './button';
+import { Dropdown } from './dropdown';
+import { IconClose } from './icons/icon-close';
+import { IconHamburgerMenu } from './icons/icon-hamburger-menu';
+import { IconChat } from './icons/icon-chat';
+import { UserAvatar } from './avatar/user-avatar';
+
+interface NavbarProps extends isLocalAware {
+  isLoggedIn: boolean;
+  notificationCount?: number;
+  onChangeLanguage?: (locale: string) => void;
+  children: React.ReactNode;
+  userProfile?: React.ReactNode;
+  userProfileImageSrc?: string;
+  userName?: string;
+  logoSrc?: string;
+}
+
+export const Navbar: React.FC<NavbarProps> = ({
+  isLoggedIn,
+  locale: initialLocale,
+  notificationCount = 0,
+  onChangeLanguage,
+  children,
+  userProfile,
+  userProfileImageSrc,
+  userName,
+  logoSrc,
+}) => {
+  const [isMenuOpen, setIsMenuOpen] = useState(false);
+  const [currentLocale, setCurrentLocale] = useState(initialLocale);
+  const dictionary = getDictionary(currentLocale);
+
+  const toggleMenu = () => {
+    setIsMenuOpen(!isMenuOpen);
+  };
+
+  const handleLocaleChange = (newLocale: string) => {
+    if (newLocale === "en" || newLocale === "de") {
+      setCurrentLocale(newLocale);
+    }
+    if (onChangeLanguage) {
+      onChangeLanguage(newLocale);
+    }
+  };
+
+  const defaultUserProfile = (
+    <div className="relative">
+      <UserAvatar
+        imageUrl={userProfileImageSrc}
+        size="small"
+        fullName={userName}
+        className='ml-3 p-0'
+      />
+    </div>
+  );
+
+  const formatNotificationCount = (count: number) => {
+    if (count > 99) return "99+";
+    return count.toString();
+  };
+
+  return (
+    <nav className="bg-transparent text-white py-4 px-4 flex items-center justify-between">
+      {/* Logo */}
+      <div className="flex items-center">
+        <a href="/">
+          <img
+            src={logoSrc}
+            width={40}
+            height={40}
+            alt="Logo"
+          />
+        </a>
+      </div>
+
+      {/* Desktop Menu (Large Screens) */}
+      <div className="hidden lg:flex items-center space-x-6 ml-auto">
+        {children}
+      </div>
+
+      {/* Right Section (Profile, Workspace, Language Dropdown) */}
+      <div className="hidden lg:flex items-center space-x-6 ml-2.5">
+        {isLoggedIn ? (
+          <>
+            {userProfile || defaultUserProfile}
+            <div className="relative">
+              <a href="/workspace">
+                <span className="hover:text-button-primary-fill cursor-pointer">
+                  {dictionary.components.navbar.workspace}
+                </span>
+              </a>
+            </div>
+            <div className="relative flex items-center">
+              <IconChat size='6' classNames='cursor-pointer' />
+              {notificationCount > 0 &&
+                <span className="absolute p-2 -top-4 left-4 leading-[150%] font-bold bg-button-primary-fill text-black text-xs rounded-full h-6 w-6 flex items-center justify-center overflow-hidden ">
+                  {formatNotificationCount(notificationCount)}
+                </span>
+              }
+            </div>
+          </>
+        ) : (
+          <a href="/login">
+            <Button text={dictionary.components.navbar.login} variant="primary" size="medium" className="ml-3" />
+          </a>
+        )}
+        <div className="relative">
+          <Dropdown
+            type="simple"
+            options={[
+              { label: "ENG", value: "en" },
path: packages/ui-kit/lib/components/navbar.tsx
user: login: alebg
body: With this you won't need to hard-code language names or codes in the translations package
created_at: 2025-03-25T12:05:40Z
author_association: CONTRIBUTOR

PR comments:
user: login: notion-workspace[bot]
created_at: 2025-03-23T21:33:43Z
body: <a href="https://www.notion.so/migration-ui-kit-NavBar-1b55a7432d01807bb0a2f145443a9f32?pvs=8&n=github_linkback">migration(ui-kit): NavBar</a>
author_association: NONE
user: login: notion-workspace[bot]
created_at: 2025-03-23T21:33:44Z
body: <a href="https://www.notion.so/migration-ui-kit-NavBar-UX-design-1b55a7432d0180ee8aa0e1899a90fc72?pvs=8&n=github_linkback">migration(ui-kit): NavBar - UX design</a>
author_association: NONE
user: login: ObitoDevv
created_at: 2025-03-25T13:05:14Z
body: @alebg @maany  
"Hi sir, thanks for your insightful suggestions on the Navbar component! Your idea to add `availableLocales` as a `string[]`, check `newLocale` against it, and generate dropdown options dynamically was spot on for making it more flexible. However, it introduced a TypeScript issue: originally, `currentLocale` was typed as `"en" | "de"`, but with availableLocales as `string[]`, `newLocale` became a generic string, causing a type mismatch in `setCurrentLocale (Argument of type 'string' is not assignable to 'SetStateAction<"en" | "de">')`. This also risked runtime errors if an `unsupported locale` slipped into ` availableLocales`, since getDictionary expects specific values.

To resolve this while keeping your vision, I integrated `TLocale ("en" | "de")` from our `i18n config`, updated `availableLocales to TLocale[]`, and ensured `handleLocaleChange` only accepts `valid TLocale values`. This maintains type safety at compile time and aligns with our dictionary setup, what we do here? This keeps it scalable for future locales while avoiding type issues. Thanks again for the guidance—let me know if you have any thoughts!"
author_association: COLLABORATOR
user: login: alebg
created_at: 2025-03-26T00:14:09Z
body: > To resolve this while keeping your vision, I integrated `TLocale ("en" | "de")` from our `i18n config`, updated `availableLocales to TLocale[]`, and ensured `handleLocaleChange` only accepts `valid TLocale values`. This maintains type safety at compile time and aligns with our dictionary setup, what we do here? This keeps it scalable for future locales while avoiding type issues. Thanks again for the guidance—let me know if you have any thoughts!"

@ObitoDevv thanks a lot. On further discussion, you're on the right track, yes. So for now let's do it as you suggest. Please push your code once implemented so I can review it.
author_association: CONTRIBUTOR
user: login: ObitoDevv
created_at: 2025-03-26T08:34:08Z
body: @alebg @carlapbg  
I have done the changes as per the above discussion. Can you please review it? Thank you for reviewing it.
author_association: COLLABORATOR
user: login: ObitoDevv
created_at: 2025-03-26T12:14:42Z
body: > @ObitoDevv Code-wise it looks good to me, thanks a lot.
> 
> * One detail about the story, could you please make it s.t. your examples are in fact localized? changing the locale with the dropdown doesn't change the Links' languages, only the workspace and login. I don't think you'd need to change anything in navbar.tsx, only in the story.

Hi @alebg sir,

Thank you for the feedback! You’re absolutely right about the localization issue in the story—initially, changing the locale via the dropdown or Storybook controls wasn’t updating the navigation links’ languages, only the `workspace` and `login` text.

I’ve updated the Storybook file to ensure that the examples are fully localized. The `NavLinks` component now uses the dictionary from `NextIntlClientProvider` (via `mockMessages` or `mockMessagesDe`) based on the current `locale`, so all navigation links (`Offers`, `Coaching`, `How It Works`, `About`) update correctly when the locale changes. I also added state management in a `NavbarWrapper` component with a `useEffect` hook to sync the `locale` with Storybook controls, while keeping the dropdown functional. This way, both the controls and the dropdown can change the language seamlessly.

No changes were needed in `navbar.tsx`, as you suspected—just the story file was adjusted. You can check the latest version in the PR; it should now reflect the localized behavior you were looking for. Please let me know if there’s anything else you’d like me to tweak!
author_association: COLLABORATOR
user: login: carlapbg
created_at: 2025-03-26T14:35:01Z
body: Hi, thanks for the changes! Just remember that the default font is the Nunito, already in the tailwind.css. Everything else looks good
author_association: COLLABORATOR
user: login: notion-workspace[bot]
created_at: 2025-03-26T15:02:34Z
body: <a href="https://www.notion.so/migration-ui-kit-NavBar-code-1b55a7432d0180b18a61e7c5d31070ff?pvs=8&n=github_linkback">migration(ui-kit): NavBar - code</a>
author_association: NONE
user: login: alebg
created_at: 2025-03-26T15:05:00Z
body: @ObitoDevv @carlapbg what's the issue with the fonts? Carla's been commenting this in more than one PR.

If they should be picked up by the theme, @ObitoDevv could you then change the theme config s.t. it has the correct font?
author_association: CONTRIBUTOR