CoolFace
Apppublic

Leon4gr45/builder

sourceHugging Facemitupdated 2d agoView on Hugging Face
0likes
scripts-tab.tsx335 linesDownload Raw Back to publish-settings
1'use client';2 3import React, { useState } from 'react';4import { PublishSettings, ScriptConfig } from '@/lib/vfs/types';5import { Button } from '@/components/ui/button';6import { Input } from '@/components/ui/input';7import { Label } from '@/components/ui/label';8import { Textarea } from '@/components/ui/textarea';9import { Switch } from '@/components/ui/switch';10import { Badge } from '@/components/ui/badge';11import {12  Dialog,13  DialogContent,14  DialogDescription,15  DialogHeader,16  DialogTitle,17  DialogFooter,18} from '@/components/ui/dialog';19import {20  Select,21  SelectContent,22  SelectItem,23  SelectTrigger,24  SelectValue,25} from '@/components/ui/select';26import { Plus, Edit, Trash2, Code } from 'lucide-react';27 28interface ScriptsTabProps {29  settings: PublishSettings;30  onChange: (settings: PublishSettings) => void;31}32 33export function ScriptsTab({ settings, onChange }: ScriptsTabProps) {34  const [editingScript, setEditingScript] = useState<ScriptConfig | null>(null);35  const [isDialogOpen, setIsDialogOpen] = useState(false);36  const [scriptPosition, setScriptPosition] = useState<'head' | 'body'>('head');37 38  const allScripts = [39    ...settings.headScripts.map(s => ({ ...s, position: 'head' as const })),40    ...settings.bodyScripts.map(s => ({ ...s, position: 'body' as const })),41  ];42 43  const handleAddScript = () => {44    const newScript: ScriptConfig = {45      id: `script-${Date.now()}`,46      name: '',47      content: '',48      type: 'inline',49      enabled: true,50    };51    setEditingScript(newScript);52    setScriptPosition('head');53    setIsDialogOpen(true);54  };55 56  const handleEditScript = (script: ScriptConfig, position: 'head' | 'body') => {57    setEditingScript(script);58    setScriptPosition(position);59    setIsDialogOpen(true);60  };61 62  const handleDeleteScript = (scriptId: string) => {63    if (!confirm('Are you sure you want to delete this script?')) return;64 65    onChange({66      ...settings,67      headScripts: settings.headScripts.filter(s => s.id !== scriptId),68      bodyScripts: settings.bodyScripts.filter(s => s.id !== scriptId),69    });70  };71 72  const handleToggleScript = (scriptId: string, position: 'head' | 'body') => {73    const scripts = position === 'head' ? settings.headScripts : settings.bodyScripts;74    const updatedScripts = scripts.map(s =>75      s.id === scriptId ? { ...s, enabled: !s.enabled } : s76    );77 78    onChange({79      ...settings,80      [position === 'head' ? 'headScripts' : 'bodyScripts']: updatedScripts,81    });82  };83 84  const handleSaveScript = () => {85    if (!editingScript || !editingScript.name.trim()) {86      alert('Please provide a name for the script');87      return;88    }89 90    const targetArray = scriptPosition === 'head' ? settings.headScripts : settings.bodyScripts;91    const otherArray = scriptPosition === 'head' ? settings.bodyScripts : settings.headScripts;92 93    // Check if editing existing or creating new94    const existingIndex = targetArray.findIndex(s => s.id === editingScript.id);95    let updatedScripts;96 97    if (existingIndex >= 0) {98      // Update existing99      updatedScripts = [...targetArray];100      updatedScripts[existingIndex] = editingScript;101    } else {102      // Check if it exists in the other position103      const existsInOther = otherArray.some(s => s.id === editingScript.id);104      if (existsInOther) {105        // Move from other position to current106        const filtered = otherArray.filter(s => s.id !== editingScript.id);107        onChange({108          ...settings,109          [scriptPosition === 'head' ? 'headScripts' : 'bodyScripts']: [...targetArray, editingScript],110          [scriptPosition === 'head' ? 'bodyScripts' : 'headScripts']: filtered,111        });112        setIsDialogOpen(false);113        setEditingScript(null);114        return;115      } else {116        // Add new117        updatedScripts = [...targetArray, editingScript];118      }119    }120 121    onChange({122      ...settings,123      [scriptPosition === 'head' ? 'headScripts' : 'bodyScripts']: updatedScripts,124    });125 126    setIsDialogOpen(false);127    setEditingScript(null);128  };129 130  return (131    <div className="space-y-6">132      <div className="flex items-center justify-between">133        <div>134          <h3 className="text-lg font-semibold">Script Management</h3>135          <p className="text-sm text-muted-foreground">136            Add custom scripts to your published deployment137          </p>138        </div>139        <Button onClick={handleAddScript} size="sm">140          <Plus className="h-4 w-4 mr-2" />141          Add Script142        </Button>143      </div>144 145      {allScripts.length === 0 ? (146        <div className="text-center p-8 border-2 border-dashed rounded-lg">147          <Code className="h-12 w-12 mx-auto text-muted-foreground mb-3" />148          <h3 className="text-lg font-semibold mb-2">No Scripts Added</h3>149          <p className="text-sm text-muted-foreground mb-4">150            Add tracking scripts, analytics, or custom code to your deployment151          </p>152          <Button onClick={handleAddScript} variant="outline">153            <Plus className="h-4 w-4 mr-2" />154            Add Your First Script155          </Button>156        </div>157      ) : (158        <div className="space-y-4">159          {allScripts.map((script) => (160            <div161              key={script.id}162              className="flex items-start gap-4 p-4 border rounded-lg hover:bg-accent/50 transition-colors"163            >164              <div className="flex-1 min-w-0">165                <div className="flex items-center gap-2 mb-2">166                  <h4 className="font-semibold truncate">{script.name}</h4>167                  <Badge variant={script.position === 'head' ? 'default' : 'secondary'}>168                    {script.position === 'head' ? '<head>' : 'before </body>'}169                  </Badge>170                  <Badge variant="outline">171                    {script.type}172                  </Badge>173                  {script.async && <Badge variant="outline">async</Badge>}174                  {script.defer && <Badge variant="outline">defer</Badge>}175                </div>176                <p className="text-sm text-muted-foreground truncate">177                  {script.type === 'inline'178                    ? `${script.content.length} characters`179                    : script.content}180                </p>181              </div>182              <div className="flex items-center gap-2">183                <Switch184                  checked={script.enabled}185                  onCheckedChange={() => handleToggleScript(script.id, script.position)}186                />187                <Button188                  variant="ghost"189                  size="sm"190                  onClick={() => handleEditScript(script, script.position)}191                >192                  <Edit className="h-4 w-4" />193                </Button>194                <Button195                  variant="ghost"196                  size="sm"197                  onClick={() => handleDeleteScript(script.id)}198                >199                  <Trash2 className="h-4 w-4" />200                </Button>201              </div>202            </div>203          ))}204        </div>205      )}206 207      {/* Script Editor Dialog */}208      <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>209        <DialogContent className="max-w-2xl">210          <DialogHeader>211            <DialogTitle>212              {editingScript?.name ? 'Edit Script' : 'Add Script'}213            </DialogTitle>214            <DialogDescription>215              Configure a custom script to inject into your published deployment216            </DialogDescription>217          </DialogHeader>218 219          {editingScript && (220            <div className="space-y-4">221              <div className="space-y-2">222                <Label htmlFor="script-name">Script Name</Label>223                <Input224                  id="script-name"225                  placeholder="e.g., Google Analytics"226                  value={editingScript.name}227                  onChange={(e) =>228                    setEditingScript({ ...editingScript, name: e.target.value })229                  }230                />231              </div>232 233              <div className="grid grid-cols-2 gap-4">234                <div className="space-y-2">235                  <Label htmlFor="script-position">Position</Label>236                  <Select237                    value={scriptPosition}238                    onValueChange={(value: 'head' | 'body') => setScriptPosition(value)}239                  >240                    <SelectTrigger id="script-position">241                      <SelectValue />242                    </SelectTrigger>243                    <SelectContent>244                      <SelectItem value="head">In &lt;head&gt;</SelectItem>245                      <SelectItem value="body">Before &lt;/body&gt;</SelectItem>246                    </SelectContent>247                  </Select>248                </div>249 250                <div className="space-y-2">251                  <Label htmlFor="script-type">Type</Label>252                  <Select253                    value={editingScript.type}254                    onValueChange={(value: 'inline' | 'external') =>255                      setEditingScript({ ...editingScript, type: value })256                    }257                  >258                    <SelectTrigger id="script-type">259                      <SelectValue />260                    </SelectTrigger>261                    <SelectContent>262                      <SelectItem value="inline">Inline Script</SelectItem>263                      <SelectItem value="external">External URL</SelectItem>264                    </SelectContent>265                  </Select>266                </div>267              </div>268 269              <div className="space-y-2">270                <Label htmlFor="script-content">271                  {editingScript.type === 'inline' ? 'Script Code' : 'Script URL'}272                </Label>273                {editingScript.type === 'inline' ? (274                  <Textarea275                    id="script-content"276                    placeholder="<script>...</script>"277                    rows={8}278                    value={editingScript.content}279                    onChange={(e) =>280                      setEditingScript({ ...editingScript, content: e.target.value })281                    }282                    className="font-mono text-sm"283                  />284                ) : (285                  <Input286                    id="script-content"287                    type="url"288                    placeholder="https://example.com/script.js"289                    value={editingScript.content}290                    onChange={(e) =>291                      setEditingScript({ ...editingScript, content: e.target.value })292                    }293                  />294                )}295              </div>296 297              {editingScript.type === 'external' && (298                <div className="flex gap-4">299                  <div className="flex items-center space-x-2">300                    <Switch301                      id="script-async"302                      checked={editingScript.async || false}303                      onCheckedChange={(checked) =>304                        setEditingScript({ ...editingScript, async: checked })305                      }306                    />307                    <Label htmlFor="script-async">Async</Label>308                  </div>309                  <div className="flex items-center space-x-2">310                    <Switch311                      id="script-defer"312                      checked={editingScript.defer || false}313                      onCheckedChange={(checked) =>314                        setEditingScript({ ...editingScript, defer: checked })315                      }316                    />317                    <Label htmlFor="script-defer">Defer</Label>318                  </div>319                </div>320              )}321            </div>322          )}323 324          <DialogFooter>325            <Button variant="outline" onClick={() => setIsDialogOpen(false)}>326              Cancel327            </Button>328            <Button onClick={handleSaveScript}>Save Script</Button>329          </DialogFooter>330        </DialogContent>331      </Dialog>332    </div>333  );334}335