{"$schema":"https://ui.shadcn.com/schema/registry-item.json","author":"Eduardo Calvo <educlopez93@gmail.com>","css":{},"dependencies":["motion","lucide-react","@radix-ui/react-popover"],"description":"A UserAccountAvatar component for SmoothUI.","devDependencies":[],"files":[{"content":"\"use client\";\n\nimport {\n  Content as PopoverContent,\n  Portal as PopoverPortal,\n  Root as PopoverRoot,\n  Trigger as PopoverTrigger,\n} from \"@radix-ui/react-popover\";\nimport { Eye, Package, User } from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useState } from \"react\";\n\nexport interface UserData {\n  avatar: string;\n  email: string;\n  name: string;\n}\n\nexport interface Order {\n  date: string;\n  id: string;\n  progress: number;\n  status: \"processing\" | \"shipped\" | \"delivered\";\n}\n\nexport interface UserAccountAvatarProps {\n  className?: string;\n  onOrderView?: (orderId: string) => void;\n  onProfileSave?: (user: UserData) => void;\n  orders?: Order[];\n  user: UserData;\n}\n\nconst mockOrders: Order[] = [\n  { date: \"2023-03-15\", id: \"ORD001\", progress: 100, status: \"delivered\" },\n  { date: \"2023-03-20\", id: \"ORD002\", progress: 66, status: \"shipped\" },\n];\n\nexport default function UserAccountAvatar({\n  user,\n  orders = mockOrders,\n  onProfileSave,\n  onOrderView,\n  className = \"\",\n}: UserAccountAvatarProps) {\n  const [activeSection, setActiveSection] = useState<string | null>(null);\n  const [userData, setUserData] = useState<UserData>(user);\n  const shouldReduceMotion = useReducedMotion();\n\n  const handleSectionClick = (section: string) => {\n    setActiveSection(activeSection === section ? null : section);\n  };\n\n  const handleProfileSave = (e: React.FormEvent<HTMLFormElement>) => {\n    e.preventDefault();\n    const formData = new FormData(e.currentTarget);\n    const updatedUser = {\n      ...userData,\n      email: formData.get(\"email\") as string,\n      name: formData.get(\"name\") as string,\n    };\n    setUserData(updatedUser);\n    if (onProfileSave) {\n      onProfileSave(updatedUser);\n    }\n    setActiveSection(null);\n  };\n\n  const renderEditProfile = () => (\n    <form className=\"flex flex-col gap-3 p-4\" onSubmit={handleProfileSave}>\n      <div className=\"flex flex-col gap-1.5\">\n        <label\n          className=\"font-medium text-muted-foreground text-xs\"\n          htmlFor=\"name\"\n        >\n          Name\n        </label>\n        <input\n          className=\"rounded-md border border-border bg-background px-3 py-2 text-foreground text-sm outline-none transition-colors placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary\"\n          defaultValue={userData.name}\n          id=\"name\"\n          name=\"name\"\n          placeholder=\"Enter your name\"\n          type=\"text\"\n        />\n      </div>\n      <div className=\"flex flex-col gap-1.5\">\n        <label\n          className=\"font-medium text-muted-foreground text-xs\"\n          htmlFor=\"email\"\n        >\n          Email\n        </label>\n        <input\n          className=\"rounded-md border border-border bg-background px-3 py-2 text-foreground text-sm outline-none transition-colors placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary\"\n          defaultValue={userData.email}\n          id=\"email\"\n          name=\"email\"\n          placeholder=\"Enter your email\"\n          type=\"email\"\n        />\n      </div>\n\n      <button\n        className=\"mt-2 cursor-pointer rounded-md bg-brand px-4 py-2.5 font-semibold text-sm text-white shadow-sm transition-all hover:bg-brand/90 hover:shadow-md active:scale-[0.98] active:bg-brand\"\n        type=\"submit\"\n      >\n        Save Changes\n      </button>\n    </form>\n  );\n\n  const getStatusColor = (status: Order[\"status\"]) => {\n    if (status === \"processing\") {\n      return \"bg-blue-500\";\n    }\n    if (status === \"shipped\") {\n      return \"bg-amber-500\";\n    }\n    return \"bg-emerald-500\";\n  };\n\n  const renderLastOrders = () => (\n    <div className=\"flex flex-col gap-3 p-4\">\n      {orders.map((order) => (\n        <div\n          className=\"flex flex-col gap-3 rounded-lg border border-border bg-muted/30 p-3 transition-colors hover:bg-muted/50\"\n          key={order.id}\n        >\n          <div className=\"flex items-center justify-between\">\n            <div className=\"font-semibold text-sm\">{order.id}</div>\n            <div className=\"text-muted-foreground text-xs\">{order.date}</div>\n          </div>\n          <div className=\"flex items-center gap-3\">\n            <div className=\"flex-1 space-y-2\">\n              <div className=\"flex items-center justify-between text-xs\">\n                <span className=\"font-medium text-foreground capitalize\">\n                  {order.status}\n                </span>\n                <span className=\"text-muted-foreground\">{order.progress}%</span>\n              </div>\n              <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n                <motion.div\n                  animate={\n                    shouldReduceMotion ? {} : { width: `${order.progress}%` }\n                  }\n                  className={`h-full rounded-full ${getStatusColor(order.status)}`}\n                  initial={shouldReduceMotion ? {} : { width: 0 }}\n                  transition={\n                    shouldReduceMotion\n                      ? { duration: 0 }\n                      : {\n                          damping: 30,\n                          duration: 0.4,\n                          stiffness: 300,\n                          type: \"spring\" as const,\n                        }\n                  }\n                />\n              </div>\n            </div>\n            <button\n              aria-label=\"View Order\"\n              className=\"flex shrink-0 cursor-pointer items-center justify-center rounded-md border border-border bg-background p-2 transition-colors hover:border-primary hover:bg-muted\"\n              onClick={() => {\n                onOrderView?.(order.id);\n              }}\n              type=\"button\"\n            >\n              <Eye className=\"text-muted-foreground\" size={16} />\n            </button>\n          </div>\n        </div>\n      ))}\n    </div>\n  );\n\n  return (\n    <PopoverRoot>\n      <PopoverTrigger asChild>\n        <button\n          className={`flex cursor-pointer items-center gap-2 rounded-full border bg-background ${className}`}\n          type=\"button\"\n        >\n          <img\n            alt=\"User Avatar\"\n            className=\"rounded-full\"\n            draggable={false}\n            height={48}\n            src={userData.avatar}\n            width={48}\n          />\n        </button>\n      </PopoverTrigger>\n      <PopoverPortal>\n        <PopoverContent\n          className=\"z-50 w-64 overflow-hidden rounded-xl border bg-background shadow-xl\"\n          onOpenAutoFocus={(e) => e.preventDefault()}\n          sideOffset={8}\n          style={{ pointerEvents: \"auto\" }}\n        >\n          <motion.div\n            animate={shouldReduceMotion ? {} : { height: \"auto\" }}\n            initial={shouldReduceMotion ? {} : { height: \"auto\" }}\n            style={{ pointerEvents: \"auto\" }}\n            transition={\n              shouldReduceMotion\n                ? { duration: 0 }\n                : { bounce: 0, duration: 0.25, type: \"spring\" as const }\n            }\n          >\n            <div\n              className=\"flex flex-col divide-y divide-border\"\n              style={{ pointerEvents: \"auto\" }}\n            >\n              <button\n                className={`flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2.5 font-medium text-sm transition-colors ${\n                  activeSection === \"profile\"\n                    ? \"bg-primary text-primary-foreground\"\n                    : \"text-foreground hover:bg-muted\"\n                }`}\n                onClick={() => {\n                  handleSectionClick(\"profile\");\n                }}\n                onKeyDown={(e) => {\n                  if (e.key === \"Enter\" || e.key === \" \") {\n                    e.preventDefault();\n                    handleSectionClick(\"profile\");\n                  }\n                }}\n                type=\"button\"\n              >\n                <User className=\"shrink-0\" size={16} />\n                Edit Profile\n              </button>\n              <AnimatePresence initial={false}>\n                {activeSection === \"profile\" && (\n                  <motion.div\n                    animate={\n                      shouldReduceMotion\n                        ? { height: \"auto\", opacity: 1 }\n                        : {\n                            filter: \"blur(0px)\",\n                            height: \"auto\",\n                            opacity: 1,\n                          }\n                    }\n                    exit={\n                      shouldReduceMotion\n                        ? { height: 0, opacity: 0, transition: { duration: 0 } }\n                        : { filter: \"blur(10px)\", height: 0, opacity: 0 }\n                    }\n                    initial={\n                      shouldReduceMotion\n                        ? { height: 0, opacity: 0 }\n                        : { filter: \"blur(10px)\", height: 0, opacity: 0 }\n                    }\n                    transition={\n                      shouldReduceMotion\n                        ? { duration: 0 }\n                        : { bounce: 0, duration: 0.25, type: \"spring\" as const }\n                    }\n                  >\n                    {renderEditProfile()}\n                  </motion.div>\n                )}\n              </AnimatePresence>\n              <button\n                className={`flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2.5 font-medium text-sm transition-colors ${\n                  activeSection === \"orders\"\n                    ? \"bg-primary text-primary-foreground\"\n                    : \"text-foreground hover:bg-muted\"\n                }`}\n                onClick={() => {\n                  handleSectionClick(\"orders\");\n                }}\n                onKeyDown={(e) => {\n                  if (e.key === \"Enter\" || e.key === \" \") {\n                    e.preventDefault();\n                    handleSectionClick(\"orders\");\n                  }\n                }}\n                type=\"button\"\n              >\n                <Package className=\"shrink-0\" size={16} />\n                Last Orders\n              </button>\n              <AnimatePresence initial={false}>\n                {activeSection === \"orders\" && (\n                  <motion.div\n                    animate={\n                      shouldReduceMotion\n                        ? { height: \"auto\", opacity: 1 }\n                        : {\n                            filter: \"blur(0px)\",\n                            height: \"auto\",\n                            opacity: 1,\n                          }\n                    }\n                    exit={\n                      shouldReduceMotion\n                        ? { height: 0, opacity: 0, transition: { duration: 0 } }\n                        : { filter: \"blur(10px)\", height: 0, opacity: 0 }\n                    }\n                    initial={\n                      shouldReduceMotion\n                        ? { height: 0, opacity: 0 }\n                        : { filter: \"blur(10px)\", height: 0, opacity: 0 }\n                    }\n                    transition={\n                      shouldReduceMotion\n                        ? { duration: 0 }\n                        : { bounce: 0, duration: 0.25, type: \"spring\" as const }\n                    }\n                  >\n                    {renderLastOrders()}\n                  </motion.div>\n                )}\n              </AnimatePresence>\n            </div>\n          </motion.div>\n        </PopoverContent>\n      </PopoverPortal>\n    </PopoverRoot>\n  );\n}\n","path":"index.tsx","target":"components/smoothui/user-account-avatar/index.tsx","type":"registry:ui"}],"name":"user-account-avatar","registryDependencies":["https://smoothui.dev/r/tokens.json"],"title":"User Account Avatar","type":"registry:ui"}