{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "team-settings",
  "title": "Team Settings",
  "description": "Team settings and configuration with avatar upload.",
  "registryDependencies": [
    "avatar",
    "badge",
    "button",
    "card",
    "field",
    "input-group",
    "separator",
    "textarea"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/team/team-settings.tsx",
      "content": "\"use client\";\n\nimport { Loader2, Save, Upload } from \"lucide-react\";\nimport { useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  Avatar,\n  AvatarFallback,\n  AvatarImage,\n} from \"@/registry/new-york/ui/avatar\";\nimport { Badge } from \"@/registry/new-york/ui/badge\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/registry/new-york/ui/card\";\nimport {\n  Field,\n  FieldContent,\n  FieldDescription,\n  FieldError,\n  FieldLabel,\n} from \"@/registry/new-york/ui/field\";\nimport {\n  InputGroup,\n  InputGroupInput,\n} from \"@/registry/new-york/ui/input-group\";\nimport { Separator } from \"@/registry/new-york/ui/separator\";\nimport { Textarea } from \"@/registry/new-york/ui/textarea\";\n\nexport interface TeamSettings {\n  name: string;\n  description?: string;\n  avatar?: string;\n  slug?: string;\n  color?: string;\n  defaultModel?: string;\n  defaultAccessScope?: string[];\n  metadata?: Record<string, string>;\n}\n\nexport interface TeamSettingsProps {\n  settings?: TeamSettings;\n  plan?: \"free\" | \"pro\" | \"enterprise\";\n  onSave?: (settings: TeamSettings) => Promise<void>;\n  onAvatarUpload?: (file: File) => Promise<string>;\n  onAvatarRemove?: () => Promise<void>;\n  className?: string;\n  availableModels?: string[];\n}\n\nfunction getInitials(name: string): string {\n  return name\n    .split(\" \")\n    .map((n) => n[0])\n    .join(\"\")\n    .toUpperCase()\n    .slice(0, 2);\n}\n\nexport default function TeamSettings({\n  settings,\n  plan = \"free\",\n  onSave,\n  onAvatarUpload,\n  onAvatarRemove,\n  className,\n  availableModels = [\"gpt-4\", \"gpt-3.5-turbo\", \"claude-3-opus\"],\n}: TeamSettingsProps) {\n  const [localSettings, setLocalSettings] = useState<TeamSettings>(\n    settings || {\n      name: \"\",\n      description: \"\",\n      slug: \"\",\n      color: \"#3b82f6\",\n    }\n  );\n  const [isSaving, setIsSaving] = useState(false);\n  const [isUploadingAvatar, setIsUploadingAvatar] = useState(false);\n  const [errors, setErrors] = useState<Record<string, string>>({});\n  const fileInputRef = useRef<HTMLInputElement | null>(null);\n\n  const hasChanges = JSON.stringify(localSettings) !== JSON.stringify(settings);\n\n  const handleSave = async () => {\n    setErrors({});\n\n    if (!localSettings.name.trim()) {\n      setErrors({ name: \"Team name is required\" });\n      return;\n    }\n\n    setIsSaving(true);\n    try {\n      await onSave?.(localSettings);\n    } catch (error) {\n      setErrors({\n        _general:\n          error instanceof Error ? error.message : \"Failed to save settings\",\n      });\n    } finally {\n      setIsSaving(false);\n    }\n  };\n\n  const handleAvatarChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n    const file = e.target.files?.[0];\n    if (!(file && onAvatarUpload)) return;\n\n    setIsUploadingAvatar(true);\n    try {\n      const url = await onAvatarUpload(file);\n      setLocalSettings((prev) => ({ ...prev, avatar: url }));\n    } catch (error) {\n      setErrors({\n        _general:\n          error instanceof Error ? error.message : \"Failed to upload avatar\",\n      });\n    } finally {\n      setIsUploadingAvatar(false);\n      if (fileInputRef.current) {\n        fileInputRef.current.value = \"\";\n      }\n    }\n  };\n\n  const handleAvatarRemove = async () => {\n    if (!onAvatarRemove) return;\n    setIsUploadingAvatar(true);\n    try {\n      await onAvatarRemove();\n      setLocalSettings((prev) => ({ ...prev, avatar: undefined }));\n    } catch (error) {\n      setErrors({\n        _general:\n          error instanceof Error ? error.message : \"Failed to remove avatar\",\n      });\n    } finally {\n      setIsUploadingAvatar(false);\n    }\n  };\n\n  return (\n    <Card className={cn(\"w-full shadow-xs\", className)}>\n      <CardHeader>\n        <div className=\"flex flex-col gap-1\">\n          <CardTitle>Team Settings</CardTitle>\n          <CardDescription>\n            Manage your team&apos;s name, appearance, and defaults\n          </CardDescription>\n        </div>\n      </CardHeader>\n      <CardContent>\n        <div className=\"flex flex-col gap-6\">\n          {errors._general && (\n            <div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-3\">\n              <p className=\"text-destructive text-sm\">{errors._general}</p>\n            </div>\n          )}\n\n          {/* Avatar */}\n          <div className=\"flex flex-col gap-4\">\n            <Field>\n              <FieldLabel>Team Avatar</FieldLabel>\n              <FieldContent>\n                <div className=\"flex items-center gap-4\">\n                  <Avatar className=\"size-20\">\n                    <AvatarImage\n                      alt={localSettings.name}\n                      src={localSettings.avatar}\n                    />\n                    <AvatarFallback className=\"text-lg\">\n                      {getInitials(localSettings.name || \"Team\")}\n                    </AvatarFallback>\n                  </Avatar>\n                  <div className=\"flex flex-col gap-2\">\n                    <div className=\"flex gap-2\">\n                      <input\n                        accept=\"image/*\"\n                        className=\"hidden\"\n                        onChange={handleAvatarChange}\n                        ref={fileInputRef}\n                        type=\"file\"\n                      />\n                      <Button\n                        disabled={isUploadingAvatar || !onAvatarUpload}\n                        onClick={() => fileInputRef.current?.click()}\n                        size=\"sm\"\n                        type=\"button\"\n                        variant=\"outline\"\n                      >\n                        {isUploadingAvatar ? (\n                          <>\n                            <Loader2 className=\"size-4 animate-spin\" />\n                            Uploading…\n                          </>\n                        ) : (\n                          <>\n                            <Upload className=\"size-4\" />\n                            Upload\n                          </>\n                        )}\n                      </Button>\n                      {localSettings.avatar && onAvatarRemove && (\n                        <Button\n                          disabled={isUploadingAvatar}\n                          onClick={handleAvatarRemove}\n                          size=\"sm\"\n                          type=\"button\"\n                          variant=\"outline\"\n                        >\n                          Remove\n                        </Button>\n                      )}\n                    </div>\n                    <FieldDescription>\n                      Recommended size: 256x256px. Max 2MB.\n                    </FieldDescription>\n                  </div>\n                </div>\n              </FieldContent>\n            </Field>\n          </div>\n\n          <Separator />\n\n          {/* Basic Info */}\n          <div className=\"flex flex-col gap-4\">\n            <Field>\n              <FieldLabel htmlFor=\"name\">\n                Team Name <span className=\"text-destructive\">*</span>\n              </FieldLabel>\n              <FieldContent>\n                <InputGroup>\n                  <InputGroupInput\n                    id=\"name\"\n                    onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n                      setLocalSettings((prev) => ({\n                        ...prev,\n                        name: e.target.value,\n                      }))\n                    }\n                    placeholder=\"Acme Inc.\"\n                    type=\"text\"\n                    value={localSettings.name}\n                  />\n                </InputGroup>\n              </FieldContent>\n              {errors.name && <FieldError>{errors.name}</FieldError>}\n            </Field>\n\n            <Field>\n              <FieldLabel htmlFor=\"description\">Description</FieldLabel>\n              <FieldContent>\n                <Textarea\n                  id=\"description\"\n                  onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) =>\n                    setLocalSettings((prev) => ({\n                      ...prev,\n                      description: e.target.value,\n                    }))\n                  }\n                  placeholder=\"What does your team do?\"\n                  rows={3}\n                  value={localSettings.description || \"\"}\n                />\n              </FieldContent>\n            </Field>\n\n            <Field>\n              <FieldLabel htmlFor=\"slug\">Team Slug</FieldLabel>\n              <FieldContent>\n                <InputGroup>\n                  <InputGroupInput\n                    id=\"slug\"\n                    onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n                      setLocalSettings((prev) => ({\n                        ...prev,\n                        slug: e.target.value.toLowerCase().replace(/\\s+/g, \"-\"),\n                      }))\n                    }\n                    placeholder=\"acme-inc\"\n                    type=\"text\"\n                    value={localSettings.slug || \"\"}\n                  />\n                </InputGroup>\n              </FieldContent>\n              <FieldDescription>\n                Used in URLs. Only lowercase letters, numbers, and hyphens.\n              </FieldDescription>\n            </Field>\n\n            <Field>\n              <FieldLabel htmlFor=\"color\">Team Color</FieldLabel>\n              <FieldContent>\n                <div className=\"flex items-center gap-3\">\n                  <input\n                    className=\"size-10 rounded border\"\n                    id=\"color\"\n                    onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n                      setLocalSettings((prev) => ({\n                        ...prev,\n                        color: e.target.value,\n                      }))\n                    }\n                    type=\"color\"\n                    value={localSettings.color || \"#3b82f6\"}\n                  />\n                  <InputGroup className=\"flex-1\">\n                    <InputGroupInput\n                      onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n                        setLocalSettings((prev) => ({\n                          ...prev,\n                          color: e.target.value,\n                        }))\n                      }\n                      placeholder=\"#3b82f6\"\n                      type=\"text\"\n                      value={localSettings.color || \"\"}\n                    />\n                  </InputGroup>\n                </div>\n              </FieldContent>\n              <FieldDescription>\n                Used for team branding and UI accents\n              </FieldDescription>\n            </Field>\n          </div>\n\n          <Separator />\n\n          {/* Defaults */}\n          <div className=\"flex flex-col gap-4\">\n            <h3 className=\"font-medium text-sm\">Default Settings</h3>\n            {availableModels.length > 0 && (\n              <Field>\n                <FieldLabel htmlFor=\"default-model\">\n                  Default AI Model\n                </FieldLabel>\n                <FieldContent>\n                  <select\n                    className=\"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\"\n                    id=\"default-model\"\n                    onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>\n                      setLocalSettings((prev) => ({\n                        ...prev,\n                        defaultModel: e.target.value,\n                      }))\n                    }\n                    value={localSettings.defaultModel || \"\"}\n                  >\n                    <option value=\"\">None (use user preference)</option>\n                    {availableModels.map((model) => (\n                      <option key={model} value={model}>\n                        {model}\n                      </option>\n                    ))}\n                  </select>\n                </FieldContent>\n                <FieldDescription>\n                  Default AI model for new team members\n                </FieldDescription>\n              </Field>\n            )}\n          </div>\n\n          <Separator />\n\n          {/* Plan Info */}\n          <div className=\"flex items-center justify-between rounded-lg border bg-muted/30 p-4\">\n            <div className=\"flex flex-col gap-1\">\n              <span className=\"font-medium text-sm\">Current Plan</span>\n              <span className=\"text-muted-foreground text-sm\">\n                {plan === \"free\" && \"Free plan\"}\n                {plan === \"pro\" && \"Pro plan\"}\n                {plan === \"enterprise\" && \"Enterprise plan\"}\n              </span>\n            </div>\n            <Badge variant={plan === \"enterprise\" ? \"default\" : \"secondary\"}>\n              {plan}\n            </Badge>\n          </div>\n\n          {/* Save Button */}\n          {onSave && (\n            <div className=\"flex justify-end\">\n              <Button\n                aria-busy={isSaving}\n                data-loading={isSaving}\n                disabled={!hasChanges}\n                onClick={handleSave}\n                type=\"button\"\n              >\n                {isSaving ? (\n                  <>\n                    <Loader2 className=\"size-4 animate-spin\" />\n                    Saving…\n                  </>\n                ) : (\n                  <>\n                    <Save className=\"size-4\" />\n                    Save Changes\n                  </>\n                )}\n              </Button>\n            </div>\n          )}\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "team"
  ],
  "type": "registry:ui"
}