Browse Source

chore/prune deps (#50)

* chore(package.json): remove unused dependencies and add daisyui for UI components
chore(ui): delete unused components including customer-carousel, faq-chat-accordion, tiptap-editor, and workout-session-calendar to clean up the codebase and improve maintainability

* refactor: rm some unused files

* chore: update .gitignore to include _next/ directory for Next.js builds
refactor(next.config.ts): remove experimental turbo rules and unused webpack config for cleaner configuration
feat(package.json): add daisyui dependency for enhanced UI components and add @next/bundle-analyzer for better bundle analysis

* chore(pnpm-lock.yaml): update dependencies to include daisyui, @next/bundle-analyzer, and various other packages for improved functionality and compatibility

* chore: pnpm lock
Mat B. 1 month ago
parent
commit
a196269c6d

+ 1 - 0
.gitignore

@@ -15,6 +15,7 @@
 
 # next.js
 /.next/
+/_next/
 /out/
 
 # production

+ 0 - 17
next.config.ts

@@ -1,7 +1,6 @@
 import type { NextConfig } from "next";
 
 const nextConfig: NextConfig = {
-  /* config options here */
   reactStrictMode: true,
   images: {
     unoptimized: true,
@@ -13,22 +12,6 @@ const nextConfig: NextConfig = {
       },
     ],
   },
-  experimental: {
-    turbo: {
-      rules: {
-        "*.svg": {
-          loaders: ["@svgr/webpack"],
-          as: "*.js",
-        },
-      },
-    },
-  },
-  // webpack: (config, { dev }) => {
-  //   if (dev) {
-  //     config.devtool = "cheap-module-source-map";
-  //   }
-  //   return config;
-  // },
 };
 
 export default nextConfig;

+ 2 - 32
package.json

@@ -21,7 +21,6 @@
   },
   "dependencies": {
     "@auth/prisma-adapter": "^2.8.0",
-    "@aws-sdk/client-s3": "^3.787.0",
     "@dnd-kit/core": "^6.3.1",
     "@dnd-kit/modifiers": "^9.0.0",
     "@dnd-kit/sortable": "^10.0.0",
@@ -58,37 +57,18 @@
     "@tailwindcss/typography": "^0.5.16",
     "@tanstack/react-query": "^5.74.3",
     "@tanstack/react-query-devtools": "^5.74.4",
-    "@tiptap/extension-bold": "^2.11.7",
-    "@tiptap/extension-bullet-list": "^2.11.7",
-    "@tiptap/extension-italic": "^2.11.7",
-    "@tiptap/extension-list-item": "^2.11.7",
-    "@tiptap/extension-ordered-list": "^2.11.7",
-    "@tiptap/extension-placeholder": "^2.11.7",
-    "@tiptap/extension-strike": "^2.11.7",
-    "@tiptap/react": "^2.11.7",
-    "@tiptap/starter-kit": "^2.11.7",
     "@vercel/functions": "^2.0.3",
     "better-auth": "^1.2.7",
     "canvas-confetti": "^1.9.3",
     "class-variance-authority": "^0.7.1",
     "clsx": "^2.1.1",
     "csv-parser": "^3.2.0",
+    "daisyui": "^5.0.43",
     "dayjs": "^1.11.13",
-    "embla-carousel-auto-scroll": "^8.6.0",
-    "embla-carousel-react": "^8.6.0",
     "eslint-config-prettier": "^10.1.1",
     "framer-motion": "^12.7.2",
     "geist": "^1.3.1",
-    "i": "^0.3.7",
-    "is-ua-webview": "^1.1.2",
-    "isomorphic-dompurify": "^2.24.0",
-    "lodash.debounce": "^4.0.8",
-    "lodash.findkey": "^4.6.0",
-    "lodash.set": "^4.3.2",
-    "lottie-react": "^2.4.1",
     "lucide-react": "^0.487.0",
-    "mime": "^4.0.7",
-    "nanoid": "^5.1.5",
     "next": "15.2.3",
     "next-international": "^1.3.1",
     "next-mdx-remote": "^5.0.0",
@@ -101,19 +81,11 @@
     "pg": "^8.14.1",
     "prisma": "^6.5.0",
     "react": "^19.0.0",
-    "react-calendly": "^4.3.1",
-    "react-colorful": "^5.6.1",
     "react-dom": "^19.0.0",
-    "react-facebook-pixel": "^1.0.4",
-    "react-github-contribution-calendar": "^2.2.0",
     "react-hook-form": "^7.55.0",
     "react-icons": "^5.5.0",
-    "react-qrcode-logo": "^3.0.0",
     "resend": "^4.2.0",
-    "sharp": "^0.34.1",
     "sonner": "^2.0.3",
-    "stripe": "^18.0.0",
-    "tw-animate-css": "^1.2.5",
     "usehooks-ts": "^3.1.1",
     "vaul": "^1.1.2",
     "zod": "^3.24.2",
@@ -123,10 +95,9 @@
     "@eslint/compat": "^1.2.7",
     "@eslint/eslintrc": "^3.3.1",
     "@eslint/js": "^9.28.0",
+    "@next/bundle-analyzer": "^15.3.4",
     "@next/eslint-plugin-next": "^15.2.4",
     "@types/canvas-confetti": "^1.9.0",
-    "@types/lodash.debounce": "^4.0.9",
-    "@types/lodash.set": "^4.3.9",
     "@types/node": "^20",
     "@types/nprogress": "^0.2.3",
     "@types/react": "^19",
@@ -134,7 +105,6 @@
     "@typescript-eslint/eslint-plugin": "^8.29.0",
     "@typescript-eslint/parser": "^8.29.0",
     "autoprefixer": "^10.4.21",
-    "daisyui": "^5.0.43",
     "env-cmd": "^10.1.0",
     "eslint": "^9.23.0",
     "eslint-config-next": "15.2.3",

File diff suppressed because it is too large
+ 123 - 878
pnpm-lock.yaml


+ 0 - 216
src/components/ui/carousel.tsx

@@ -1,216 +0,0 @@
-"use client";
-
-import * as React from "react";
-import { ArrowLeft, ArrowRight } from "lucide-react";
-import useEmblaCarousel, { UseEmblaCarouselType } from "embla-carousel-react";
-
-import { cn } from "@/shared/lib/utils";
-import { Button } from "@/components/ui/button";
-
-type CarouselApi = UseEmblaCarouselType[1];
-type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
-type CarouselOptions = UseCarouselParameters[0];
-type CarouselPlugin = UseCarouselParameters[1];
-
-type CarouselProps = {
-  opts?: CarouselOptions;
-  plugins?: CarouselPlugin;
-  orientation?: "horizontal" | "vertical";
-  setApi?: (api: CarouselApi) => void;
-};
-
-type CarouselContextProps = {
-  carouselRef: ReturnType<typeof useEmblaCarousel>[0];
-  api: ReturnType<typeof useEmblaCarousel>[1];
-  scrollPrev: () => void;
-  scrollNext: () => void;
-  canScrollPrev: boolean;
-  canScrollNext: boolean;
-} & CarouselProps;
-
-const CarouselContext = React.createContext<CarouselContextProps | null>(null);
-
-function useCarousel() {
-  const context = React.useContext(CarouselContext);
-
-  if (!context) {
-    throw new Error("useCarousel must be used within a <Carousel />");
-  }
-
-  return context;
-}
-
-const Carousel = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement> & CarouselProps>(
-  ({ orientation = "horizontal", opts, setApi, plugins, className, children, ...props }, ref) => {
-    const [carouselRef, api] = useEmblaCarousel(
-      {
-        ...opts,
-        axis: orientation === "horizontal" ? "x" : "y",
-      },
-      plugins,
-    );
-    const [canScrollPrev, setCanScrollPrev] = React.useState(false);
-    const [canScrollNext, setCanScrollNext] = React.useState(false);
-
-    const onSelect = React.useCallback((api: CarouselApi) => {
-      if (!api) {
-        return;
-      }
-
-      setCanScrollPrev(api.canScrollPrev());
-      setCanScrollNext(api.canScrollNext());
-    }, []);
-
-    const scrollPrev = React.useCallback(() => {
-      api?.scrollPrev();
-    }, [api]);
-
-    const scrollNext = React.useCallback(() => {
-      api?.scrollNext();
-    }, [api]);
-
-    const handleKeyDown = React.useCallback(
-      (event: React.KeyboardEvent<HTMLDivElement>) => {
-        if (event.key === "ArrowLeft") {
-          event.preventDefault();
-          scrollPrev();
-        } else if (event.key === "ArrowRight") {
-          event.preventDefault();
-          scrollNext();
-        }
-      },
-      [scrollPrev, scrollNext],
-    );
-
-    React.useEffect(() => {
-      if (!api || !setApi) {
-        return;
-      }
-
-      setApi(api);
-    }, [api, setApi]);
-
-    React.useEffect(() => {
-      if (!api) {
-        return;
-      }
-
-      onSelect(api);
-      api.on("reInit", onSelect);
-      api.on("select", onSelect);
-
-      return () => {
-        api?.off("select", onSelect);
-      };
-    }, [api, onSelect]);
-
-    return (
-      <CarouselContext.Provider
-        value={{
-          carouselRef,
-          api: api,
-          opts,
-          orientation: orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
-          scrollPrev,
-          scrollNext,
-          canScrollPrev,
-          canScrollNext,
-        }}
-      >
-        <div
-          aria-roledescription="carousel"
-          className={cn("relative", className)}
-          onKeyDownCapture={handleKeyDown}
-          ref={ref}
-          role="region"
-          {...props}
-        >
-          {children}
-        </div>
-      </CarouselContext.Provider>
-    );
-  },
-);
-Carousel.displayName = "Carousel";
-
-const CarouselContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => {
-  const { carouselRef, orientation } = useCarousel();
-
-  return (
-    <div className="overflow-hidden" ref={carouselRef}>
-      <div className={cn("flex", orientation === "horizontal" ? "" : "flex-col", className)} ref={ref} {...props} />
-    </div>
-  );
-});
-CarouselContent.displayName = "CarouselContent";
-
-const CarouselItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => {
-  const { orientation } = useCarousel();
-
-  return (
-    <div
-      aria-roledescription="slide"
-      className={cn("shrink-0 grow-0", orientation === "horizontal" ? "" : "pt-4", className)}
-      ref={ref}
-      role="group"
-      {...props}
-    />
-  );
-});
-CarouselItem.displayName = "CarouselItem";
-
-const CarouselPrevious = React.forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(
-  ({ className, variant = "outline", ...props }, ref) => {
-    const { orientation, scrollPrev, canScrollPrev } = useCarousel();
-
-    return (
-      <Button
-        className={cn(
-          "absolute size-[30px]",
-          orientation === "horizontal"
-            ? "-bottom-8 left-[39%] sm:-left-12 sm:top-1/2 sm:-translate-y-1/2"
-            : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
-          className,
-        )}
-        disabled={!canScrollPrev}
-        onClick={scrollPrev}
-        ref={ref}
-        variant={variant}
-        {...props}
-      >
-        <ArrowLeft className="h-4 w-4" />
-        <span className="sr-only">Previous slide</span>
-      </Button>
-    );
-  },
-);
-CarouselPrevious.displayName = "CarouselPrevious";
-
-const CarouselNext = React.forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(
-  ({ className, variant = "outline", ...props }, ref) => {
-    const { orientation, scrollNext, canScrollNext } = useCarousel();
-
-    return (
-      <Button
-        className={cn(
-          "absolute size-[30px]",
-          orientation === "horizontal"
-            ? "-bottom-8 right-[39%] sm:-right-12 sm:top-1/2 sm:-translate-y-1/2"
-            : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
-          className,
-        )}
-        disabled={!canScrollNext}
-        onClick={scrollNext}
-        ref={ref}
-        variant={variant}
-        {...props}
-      >
-        <ArrowRight className="h-4 w-4" />
-        <span className="sr-only">Next slide</span>
-      </Button>
-    );
-  },
-);
-CarouselNext.displayName = "CarouselNext";
-
-export { type CarouselApi, Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext };

+ 0 - 55
src/components/ui/color-picker.tsx

@@ -1,55 +0,0 @@
-import { HexColorPicker } from "react-colorful";
-import { useEffect, useState } from "react";
-import * as Popover from "@radix-ui/react-popover";
-
-import { Input } from "@/components/ui/input";
-
-interface HexColorPopoverPickerProps {
-  value: string;
-  onChange: (color: string) => void;
-  disabled?: boolean;
-}
-
-export function HexColorPopoverPicker({ value, onChange, disabled }: HexColorPopoverPickerProps) {
-  const [open, setOpen] = useState(false);
-  const [color, setColor] = useState(value ?? "#000000");
-
-  // Sync color if value changes from outside
-  useEffect(() => {
-    setColor(value ?? "#000000");
-  }, [value]);
-
-  return (
-    <Popover.Root onOpenChange={setOpen} open={open}>
-      <Popover.Trigger asChild>
-        <button
-          aria-label="Choisir une couleur"
-          className="h-8 w-12 rounded border border-gray-300"
-          disabled={disabled}
-          style={{ background: color }}
-          type="button"
-        />
-      </Popover.Trigger>
-      <Popover.Portal>
-        <Popover.Content className="z-50 rounded border bg-white p-4 shadow-lg" sideOffset={8}>
-          <HexColorPicker
-            color={color}
-            onChange={(c) => {
-              setColor(c);
-              onChange?.(c);
-            }}
-          />
-          <Input
-            className="font-mono mt-2 w-24 rounded border px-2 py-1"
-            onChange={(e) => {
-              setColor(e.target.value);
-              onChange?.(e.target.value);
-            }}
-            type="text"
-            value={color}
-          />
-        </Popover.Content>
-      </Popover.Portal>
-    </Popover.Root>
-  );
-}

+ 0 - 115
src/components/ui/customer-carousel.tsx

@@ -1,115 +0,0 @@
-// This template requires the Embla Auto Scroll plugin to be installed:
-// npm install embla-carousel-auto-scroll
-
-"use client";
-
-import AutoScroll from "embla-carousel-auto-scroll";
-
-import { Carousel, CarouselContent, CarouselItem } from "@/components/ui/carousel";
-
-interface Logo {
-  id: string;
-  description: string;
-  image: string;
-  className?: string;
-}
-
-interface Logos3Props {
-  heading?: string;
-  logos?: Logo[];
-  className?: string;
-}
-
-export const CustomerCarousel = ({
-  heading = "Trusted by these companies",
-  logos = [
-    {
-      id: "logo-1",
-      description: "Logo 1",
-      image: "/images/brands/9.png",
-      className: "h-24 w-auto",
-    },
-    {
-      id: "logo-2",
-      description: "Logo 2",
-      image: "/images/brands/4.png",
-      className: "h-20 w-auto",
-    },
-    {
-      id: "logo-3",
-      description: "Logo 3",
-      image: "/images/brands/14.png",
-      className: "h-32 w-auto",
-    },
-    {
-      id: "logo-4",
-      description: "Logo 4",
-      image: "/images/brands/19.png",
-      className: "h-24 w-auto",
-    },
-    {
-      id: "logo-5",
-      description: "Logo 5",
-      image: "/images/brands/24.png",
-      className: "h-24 w-auto",
-    },
-    {
-      id: "logo-6",
-      description: "Logo 6",
-      image: "/images/brands/29.png",
-      className: "h-24 w-auto",
-    },
-    {
-      id: "logo-7",
-      description: "Logo 7",
-      image: "/images/brands/33.png",
-      className: "h-24 w-auto",
-    },
-    {
-      id: "logo-8",
-      description: "Logo 8",
-      image: "/images/brands/37.png",
-      className: "h-24 w-auto",
-    },
-    {
-      id: "logo-9",
-      description: "Logo 9",
-      image: "/images/brands/41.png",
-      className: "h-24 w-auto",
-    },
-    {
-      id: "logo-10",
-      description: "Logo 10",
-      image: "/images/brands/46.png",
-      className: "h-32 w-auto",
-    },
-  ],
-}: Logos3Props) => {
-  return (
-    <section className="py-32 pt-0 md:pt-16">
-      <div className="container flex flex-col items-center text-center">
-        <h1 className="my-6 text-pretty text-3xl font-bold lg:text-4xl">{heading}</h1>
-      </div>
-      <div className="pt-10 md:pt-16 lg:pt-20">
-        <div className="relative mx-auto flex items-center justify-center overflow-hidden lg:max-w-5xl">
-          <Carousel opts={{ loop: true }} plugins={[AutoScroll({ playOnInit: true })]}>
-            <CarouselContent className="ml-0">
-              {logos.map((logo) => (
-                <CarouselItem className="basis-1/11 flex justify-center pl-0 sm:basis-1/4 md:basis-1/5 lg:basis-1/6" key={logo.id}>
-                  <div className="mx-6 flex shrink-0 items-center justify-center md:mx-10">
-                    <div>
-                      {/* eslint-disable-next-line @next/next/no-img-element */}
-                      <img alt={logo.description} className={logo.className} src={logo.image} />
-                    </div>
-                  </div>
-                </CarouselItem>
-              ))}
-            </CarouselContent>
-          </Carousel>
-          <div className="from-background absolute inset-y-0 left-0 w-12 bg-gradient-to-r to-transparent"></div>
-          <div className="from-background absolute inset-y-0 right-0 w-12 bg-gradient-to-l to-transparent"></div>
-        </div>
-      </div>
-    </section>
-  );
-};

+ 0 - 93
src/components/ui/faq-chat-accordion.tsx

@@ -1,93 +0,0 @@
-"use client";
-
-import * as React from "react";
-import { Minus, Plus } from "lucide-react";
-import DOMPurify from "isomorphic-dompurify";
-import { motion } from "framer-motion";
-import * as Accordion from "@radix-ui/react-accordion";
-
-import { useI18n } from "locales/client";
-import { cn } from "@/shared/lib/utils";
-
-export interface FAQItem {
-  id: number;
-  question: string;
-  answer: string;
-  icon?: string;
-  iconPosition?: "left" | "right";
-}
-
-interface FaqAccordionProps {
-  data: FAQItem[];
-  className?: string;
-  questionClassName?: string;
-  answerClassName?: string;
-}
-
-export function FaqAccordion({ data, className, questionClassName, answerClassName }: FaqAccordionProps) {
-  const [openItem, setOpenItem] = React.useState<string | null>(null);
-  const t = useI18n();
-
-  return (
-    <div className={cn("", className)}>
-      <div className="text-muted-foreground mb-4 text-sm">{t("commons.received_just_now")}</div>
-
-      <Accordion.Root collapsible onValueChange={(value) => setOpenItem(value)} type="single" value={openItem || ""}>
-        {data.map((item) => {
-          const cleanAnswerHTML = DOMPurify.sanitize(item.answer);
-
-          return (
-            <Accordion.Item className="mb-2" key={item.id} value={item.id.toString()}>
-              <Accordion.Header>
-                <Accordion.Trigger className="flex w-full items-center justify-start gap-x-4">
-                  <div
-                    className={cn(
-                      "relative flex items-center space-x-2 rounded-xl p-2 transition-colors",
-                      openItem === item.id.toString() ? "bg-primary/20 text-primary" : "bg-muted hover:bg-primary/10",
-                      questionClassName,
-                    )}
-                  >
-                    {item.icon && (
-                      <span
-                        className={cn("absolute bottom-6", item.iconPosition === "right" ? "right-0" : "left-0")}
-                        style={{
-                          transform: item.iconPosition === "right" ? "rotate(7deg)" : "rotate(-4deg)",
-                        }}
-                      >
-                        {item.icon}
-                      </span>
-                    )}
-                    <span className="font-medium">{item.question}</span>
-                  </div>
-
-                  <span className={cn("text-muted-foreground", openItem === item.id.toString() && "text-primary")}>
-                    {openItem === item.id.toString() ? <Minus className="h-5 w-5" /> : <Plus className="h-5 w-5" />}
-                  </span>
-                </Accordion.Trigger>
-              </Accordion.Header>
-              <Accordion.Content asChild forceMount>
-                <motion.div
-                  animate={openItem === item.id.toString() ? "open" : "collapsed"}
-                  className="overflow-hidden"
-                  initial="collapsed"
-                  transition={{ duration: 0.4 }}
-                  variants={{
-                    open: { opacity: 1, height: "auto" },
-                    collapsed: { opacity: 0, height: 0 },
-                  }}
-                >
-                  <div className="ml-7 mt-1 flex justify-end md:ml-16">
-                    <div
-                      className={cn("text-primary-foreground relative max-w-md rounded-2xl bg-primary px-4 py-2", answerClassName)}
-                      dangerouslySetInnerHTML={{ __html: cleanAnswerHTML }}
-                    />
-                  </div>
-                </motion.div>
-              </Accordion.Content>
-            </Accordion.Item>
-          );
-        })}
-      </Accordion.Root>
-    </div>
-  );
-}

+ 0 - 210
src/components/ui/tiptap-editor.tsx

@@ -1,210 +0,0 @@
-// src/shared/ui/tiptap-editor.tsx
-"use client";
-
-import React from "react";
-import clsx from "clsx";
-import { StarterKit } from "@tiptap/starter-kit";
-import { useEditor, EditorContent, Editor } from "@tiptap/react";
-import { Strike } from "@tiptap/extension-strike";
-import { Placeholder } from "@tiptap/extension-placeholder";
-import { OrderedList } from "@tiptap/extension-ordered-list";
-import { ListItem } from "@tiptap/extension-list-item";
-import { Italic } from "@tiptap/extension-italic";
-import { BulletList } from "@tiptap/extension-bullet-list";
-import { Bold } from "@tiptap/extension-bold";
-
-interface TiptapEditorProps {
-  value: string;
-  onChange: (value: string) => void;
-  onBlur?: () => void;
-  placeholder?: string;
-  className?: string;
-}
-
-export const TiptapEditor: React.FC<TiptapEditorProps> = ({ value, onChange, onBlur, placeholder = "Write something…", className }) => {
-  const editor = useEditor({
-    extensions: [
-      StarterKit.configure({
-        bulletList: false,
-        orderedList: false,
-        listItem: false,
-      }),
-      Bold,
-      Italic,
-      Strike,
-      BulletList,
-      OrderedList,
-      ListItem,
-      Placeholder.configure({
-        placeholder,
-        emptyEditorClass: "tiptap-placeholder",
-      }),
-    ],
-    content: value,
-    onUpdate: ({ editor }) => {
-      onChange(editor.getHTML());
-    },
-    onBlur: () => {
-      if (onBlur) {
-        onBlur();
-      }
-    },
-  });
-
-  if (!editor) return null;
-
-  return (
-    <div className={className}>
-      <TiptapMenuBar editor={editor} />
-      <div className="min-h-[120px] rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 shadow-sm transition-all focus-within:border-primary dark:border-zinc-700/50 dark:bg-zinc-800">
-        <EditorContent editor={editor} />
-      </div>
-      <style global jsx>{`
-        p {
-          margin: 0;
-        }
-        .tiptap-placeholder::before {
-          content: attr(data-placeholder);
-          color: #a0aec0;
-          opacity: 0.7;
-          font-style: italic;
-          pointer-events: none;
-          position: absolute;
-        }
-        .ProseMirror:focus {
-          outline: none;
-        }
-        .ProseMirror {
-          min-height: 80px;
-          color: var(--foreground);
-        }
-        .ProseMirror p.is-editor-empty:first-child::before {
-          color: var(--muted-foreground);
-          content: attr(data-placeholder);
-          float: left;
-          height: 0;
-          pointer-events: none;
-        }
-        .ProseMirror ul,
-        .ProseMirror ol {
-          padding-left: 1.5rem;
-          margin-top: 0.5rem;
-          margin-bottom: 0.5rem;
-        }
-        .ProseMirror ul {
-          list-style-type: disc;
-        }
-        .ProseMirror ol {
-          list-style-type: decimal;
-        }
-        .ProseMirror li {
-          margin-bottom: 0.25rem;
-        }
-        .ProseMirror strong {
-          font-weight: bold;
-        }
-        .ProseMirror em {
-          font-style: italic;
-        }
-        .ProseMirror s,
-        .ProseMirror strike {
-          text-decoration: line-through;
-        }
-      `}</style>
-    </div>
-  );
-};
-
-interface MenuBarProps {
-  editor: Editor;
-}
-
-const buttonBase = "rounded-md px-2 py-1 text-sm transition-all border border-transparent";
-
-const TiptapMenuBar: React.FC<MenuBarProps> = ({ editor }) => (
-  <div className="mb-2 flex flex-wrap gap-1">
-    <ToolbarButton active={editor.isActive("bold")} ariaLabel="Bold" onClick={() => editor.chain().focus().toggleBold().run()}>
-      <b>B</b>
-    </ToolbarButton>
-    <ToolbarButton active={editor.isActive("italic")} ariaLabel="Italic" onClick={() => editor.chain().focus().toggleItalic().run()}>
-      <i>I</i>
-    </ToolbarButton>
-    <ToolbarButton active={editor.isActive("strike")} ariaLabel="Strike" onClick={() => editor.chain().focus().toggleStrike().run()}>
-      <s>S</s>
-    </ToolbarButton>
-    <ToolbarButton
-      active={editor.isActive("bulletList")}
-      ariaLabel="Bullet List"
-      onClick={() => editor.chain().focus().toggleBulletList().run()}
-    >
-      <svg
-        fill="none"
-        height="16"
-        stroke="currentColor"
-        strokeLinecap="round"
-        strokeLinejoin="round"
-        strokeWidth="2"
-        viewBox="0 0 24 24"
-        width="16"
-        xmlns="http://www.w3.org/2000/svg"
-      >
-        <line x1="8" x2="21" y1="6" y2="6"></line>
-        <line x1="8" x2="21" y1="12" y2="12"></line>
-        <line x1="8" x2="21" y1="18" y2="18"></line>
-        <line x1="3" x2="3.01" y1="6" y2="6"></line>
-        <line x1="3" x2="3.01" y1="12" y2="12"></line>
-        <line x1="3" x2="3.01" y1="18" y2="18"></line>
-      </svg>
-    </ToolbarButton>
-    <ToolbarButton
-      active={editor.isActive("orderedList")}
-      ariaLabel="Ordered List"
-      onClick={() => editor.chain().focus().toggleOrderedList().run()}
-    >
-      <svg
-        fill="none"
-        height="16"
-        stroke="currentColor"
-        strokeLinecap="round"
-        strokeLinejoin="round"
-        strokeWidth="2"
-        viewBox="0 0 24 24"
-        width="16"
-        xmlns="http://www.w3.org/2000/svg"
-      >
-        <line x1="10" x2="21" y1="6" y2="6"></line>
-        <line x1="10" x2="21" y1="12" y2="12"></line>
-        <line x1="10" x2="21" y1="18" y2="18"></line>
-        <path d="M4 6h1v4"></path>
-        <path d="M4 10h2"></path>
-        <path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"></path>
-      </svg>
-    </ToolbarButton>
-  </div>
-);
-
-const ToolbarButton: React.FC<{
-  onClick: () => void;
-  active?: boolean;
-  ariaLabel?: string;
-  children: React.ReactNode;
-}> = ({ onClick, active, ariaLabel, children }) => (
-  <button
-    aria-label={ariaLabel}
-    aria-pressed={active}
-    className={clsx(
-      buttonBase,
-      "hover:bg-primary/10 hover:text-primary focus:bg-primary/10 focus:text-primary dark:hover:bg-primary/20 dark:focus:bg-primary/20",
-      active ? "bg-primary/20 text-primary dark:bg-primary/30" : "text-gray-700 dark:text-gray-300",
-      "transform transition-transform duration-150 ease-out",
-      "hover:scale-[1.05]",
-      "active:scale-[0.95]",
-      "flex items-center justify-center gap-1",
-    )}
-    onClick={onClick}
-    tabIndex={0}
-    type="button"
-  >
-    {children}
-  </button>
-);

+ 0 - 49
src/features/workout-session/ui/workout-session-calendar.tsx

@@ -1,49 +0,0 @@
-import Calendar from "react-github-contribution-calendar";
-import React from "react";
-
-import { workoutSessionLocal } from "@/shared/lib/workout-session/workout-session.local";
-
-// À placer dans le layout _app.tsx ou équivalent pour charger le CSS
-
-export function WorkoutSessionCalendar() {
-  // Récupère toutes les séances
-  const sessions = typeof window !== "undefined" ? workoutSessionLocal.getAll() : [];
-
-  // Génère un objet { 'YYYY-MM-DD': count } pour chaque jour avec au moins un workout
-  const values: Record<string, number> = {};
-  sessions.forEach((session) => {
-    // On ne compte qu'une fois par jour, même si plusieurs séances
-    const date = session.startedAt.slice(0, 10);
-    values[date] = (values[date] || 0) + 1;
-  });
-
-  // Trouve la date la plus récente pour le paramètre 'until'
-  const until =
-    sessions.length > 0
-      ? sessions.reduce((max, s) => (s.startedAt > max ? s.startedAt : max), sessions[0].startedAt).slice(0, 10)
-      : new Date().toISOString().slice(0, 10);
-
-  // Customisation
-  const panelColors = ["#E5E7EB", "#A7F3D0", "#34D399", "#059669", "#065F46"];
-  const weekNames = ["L", "M", "M", "J", "V", "S", "D"]; // TODO: i18n
-  const monthNames = ["Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Août", "Sep", "Oct", "Nov", "Déc"]; // TODO: i18n
-  const panelAttributes = { rx: 1, ry: 1, height: 10, width: 10 };
-  const weekLabelAttributes = { style: { fontSize: 10, fill: "#888" } };
-  const monthLabelAttributes = { style: { fontSize: 10, fill: "#333" } };
-
-  return (
-    <div className="my-8">
-      <h3 className="text-lg font-bold mb-2">Historique d&apos;entraînement</h3>
-      <Calendar
-        monthLabelAttributes={monthLabelAttributes}
-        monthNames={monthNames}
-        panelAttributes={panelAttributes}
-        panelColors={panelColors}
-        until={until}
-        values={values}
-        weekLabelAttributes={weekLabelAttributes}
-        weekNames={weekNames}
-      />
-    </div>
-  );
-}

Some files were not shown because too many files changed in this diff