← back to Letsbegin

components/ui/SidebarDemo.tsx

222 lines

"use client";
import React, { useState } from "react";
import { Sidebar, SidebarBody, SidebarLink } from "@/components/ui/sidebar";
import {
  FileText,
  RefreshCw,
  Play,
  FolderOpen,
  Settings,
  LogOut,
  Rocket,
  Zap
} from "lucide-react";
import Link from "next/link";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";

interface SidebarDemoProps {
  currentStep: number;
  onStepChange: (step: number) => void;
  onLogout: () => void;
  username?: string;
  children: React.ReactNode;
}

export function SidebarDemo({
  currentStep,
  onStepChange,
  onLogout,
  username = "Admin",
  children
}: SidebarDemoProps) {
  const links = [
    {
      label: "Quick Mode",
      href: "#",
      icon: (
        <Zap className={cn(
          "h-5 w-5 flex-shrink-0",
          currentStep === 0 ? "text-[var(--primary)]" : "text-neutral-700 dark:text-neutral-200"
        )} />
      ),
      step: 0,
    },
    {
      label: "PRD Generator",
      href: "#",
      icon: (
        <FileText className={cn(
          "h-5 w-5 flex-shrink-0",
          currentStep === 1 ? "text-[var(--primary)]" : "text-neutral-700 dark:text-neutral-200"
        )} />
      ),
      step: 1,
    },
    {
      label: "Ralph Converter",
      href: "#",
      icon: (
        <RefreshCw className={cn(
          "h-5 w-5 flex-shrink-0",
          currentStep === 2 ? "text-[var(--primary)]" : "text-neutral-700 dark:text-neutral-200"
        )} />
      ),
      step: 2,
    },
    {
      label: "Ralphy Boy Runner",
      href: "#",
      icon: (
        <Play className={cn(
          "h-5 w-5 flex-shrink-0",
          currentStep === 3 ? "text-[var(--primary)]" : "text-neutral-700 dark:text-neutral-200"
        )} />
      ),
      step: 3,
    },
  ];

  const bottomLinks = [
    {
      label: "Projects",
      href: "#",
      icon: (
        <FolderOpen className="text-neutral-700 dark:text-neutral-200 h-5 w-5 flex-shrink-0" />
      ),
    },
    {
      label: "Settings",
      href: "#",
      icon: (
        <Settings className="text-neutral-700 dark:text-neutral-200 h-5 w-5 flex-shrink-0" />
      ),
    },
  ];

  const [open, setOpen] = useState(false);

  return (
    <div
      className={cn(
        "flex flex-col md:flex-row bg-[var(--background)] w-full flex-1 border border-[var(--border)] overflow-hidden",
        "h-screen"
      )}
    >
      <Sidebar open={open} setOpen={setOpen}>
        <SidebarBody className="justify-between gap-10 bg-[var(--surface)] border-r border-[var(--border)]">
          <div className="flex flex-col flex-1 overflow-y-auto overflow-x-hidden">
            {open ? <Logo /> : <LogoIcon />}

            {/* Step Navigation */}
            <div className="mt-8 flex flex-col gap-2">
              <div className="text-xs text-[var(--foreground-tertiary)] px-2 mb-2">
                {open && "WORKFLOW"}
              </div>
              {links.map((link, idx) => (
                <button
                  key={idx}
                  onClick={() => onStepChange(link.step)}
                  className={cn(
                    "flex items-center justify-start gap-2 group/sidebar py-2 px-2 rounded-lg transition-colors",
                    currentStep === link.step
                      ? "bg-[var(--primary)]/20 text-[var(--primary)]"
                      : "hover:bg-[var(--surface-alt)]"
                  )}
                >
                  {link.icon}
                  <motion.span
                    animate={{
                      display: open ? "inline-block" : "none",
                      opacity: open ? 1 : 0,
                    }}
                    className={cn(
                      "text-sm group-hover/sidebar:translate-x-1 transition duration-150 whitespace-pre inline-block !p-0 !m-0",
                      currentStep === link.step
                        ? "text-[var(--primary)] font-medium"
                        : "text-[var(--foreground)]"
                    )}
                  >
                    {link.label}
                  </motion.span>
                </button>
              ))}
            </div>

            {/* Bottom Links */}
            <div className="mt-auto flex flex-col gap-2">
              <div className="text-xs text-[var(--foreground-tertiary)] px-2 mb-2">
                {open && "TOOLS"}
              </div>
              {bottomLinks.map((link, idx) => (
                <SidebarLink key={idx} link={link} />
              ))}
            </div>
          </div>

          {/* User Section */}
          <div className="border-t border-[var(--border)] pt-4">
            <button
              onClick={onLogout}
              className="flex items-center justify-start gap-2 group/sidebar py-2 px-2 rounded-lg hover:bg-red-500/10 transition-colors w-full"
            >
              <LogOut className="text-red-400 h-5 w-5 flex-shrink-0" />
              <motion.span
                animate={{
                  display: open ? "inline-block" : "none",
                  opacity: open ? 1 : 0,
                }}
                className="text-red-400 text-sm whitespace-pre inline-block !p-0 !m-0"
              >
                Logout ({username})
              </motion.span>
            </button>
          </div>
        </SidebarBody>
      </Sidebar>

      {/* Main Content */}
      <div className="flex flex-1 overflow-hidden">
        <div className="flex-1 overflow-auto">
          {children}
        </div>
      </div>
    </div>
  );
}

export const Logo = () => {
  return (
    <Link
      href="#"
      className="font-normal flex space-x-2 items-center text-sm py-1 relative z-20"
    >
      <div className="h-6 w-6 bg-gradient-to-br from-[var(--primary)] to-[var(--secondary)] rounded-lg flex items-center justify-center flex-shrink-0">
        <Rocket className="h-4 w-4 text-white" />
      </div>
      <motion.span
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        className="font-bold text-[var(--foreground)] whitespace-pre"
      >
        GoodQuestionRalph
      </motion.span>
    </Link>
  );
};

export const LogoIcon = () => {
  return (
    <Link
      href="#"
      className="font-normal flex space-x-2 items-center text-sm py-1 relative z-20"
    >
      <div className="h-6 w-6 bg-gradient-to-br from-[var(--primary)] to-[var(--secondary)] rounded-lg flex items-center justify-center flex-shrink-0">
        <Rocket className="h-4 w-4 text-white" />
      </div>
    </Link>
  );
};

export default SidebarDemo;