Skip to content

Instantly share code, notes, and snippets.

@portal7
Created October 16, 2024 21:26
Show Gist options
  • Save portal7/0ec99c976d22b056fbaa95841328959a to your computer and use it in GitHub Desktop.
Save portal7/0ec99c976d22b056fbaa95841328959a to your computer and use it in GitHub Desktop.
Editor Menu Avanzado
'use client'
import { useState } from 'react'
import { Check, X, ChevronDown, Loader2 } from 'lucide-react'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
const icons = [
'home', 'settings', 'user', 'mail', 'calendar', 'file', 'folder',
'image', 'video', 'music', 'search', 'heart', 'star', 'flag',
'bell', 'bookmark', 'eye', 'camera', 'message', 'phone'
]
const roles = [
'Admin', 'Editor', 'Viewer', 'Manager', 'Developer'
]
export default function EnhancedNavMenuEditor({ onSave, onCancel }) {
const [title, setTitle] = useState('')
const [uri, setUri] = useState('')
const [description, setDescription] = useState('')
const [icon, setIcon] = useState('')
const [status, setStatus] = useState(true)
const [selectedRoles, setSelectedRoles] = useState([])
const [isLoading, setIsLoading] = useState(false)
const handleSave = async (e) => {
e.preventDefault()
setIsLoading(true)
// Simulating an API call
await new Promise(resolve => setTimeout(resolve, 1000))
onSave({ title, uri, description, icon, status, roles: selectedRoles })
setIsLoading(false)
}
const toggleRole = (role) => {
setSelectedRoles(prev =>
prev.includes(role)
? prev.filter(r => r !== role)
: [...prev, role]
)
}
return (
<div className="w-full max-w-4xl mx-auto bg-card p-6 rounded-lg shadow-lg">
<h2 className="text-2xl font-bold mb-6">Craft Your Navigation Item</h2>
<div className="flex flex-col md:flex-row gap-6">
<div className="flex-1">
<form onSubmit={handleSave} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="title">Title</Label>
<Input
id="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Enter menu item title"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="uri">URI</Label>
<Input
id="uri"
value={uri}
onChange={(e) => setUri(e.target.value)}
placeholder="Enter menu item URI"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Enter menu item description"
rows={3}
/>
</div>
<div className="space-y-2">
<Label>Icon</Label>
<div className="grid grid-cols-5 gap-2">
{icons.map((iconName) => (
<Button
key={iconName}
variant={icon === iconName ? 'default' : 'outline'}
className="h-10 w-10 p-0"
onClick={() => setIcon(iconName)}
>
<span className="sr-only">{iconName}</span>
<i className={`icon-${iconName}`}></i>
</Button>
))}
</div>
</div>
<div className="flex items-center space-x-2">
<Label htmlFor="status">Status</Label>
<button
type="button"
role="switch"
aria-checked={status}
data-state={status ? 'checked' : 'unchecked'}
value="on"
id="status"
className={`peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 ${
status ? 'bg-primary' : 'bg-input'
}`}
onClick={() => setStatus(!status)}
>
<span
data-state={status ? 'checked' : 'unchecked'}
className={`pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform ${
status ? 'translate-x-5' : 'translate-x-0'
}`}
/>
</button>
<span className="text-sm text-muted-foreground">
{status ? 'Enabled' : 'Disabled'}
</span>
</div>
<div className="space-y-2">
<Label>Allowed Roles</Label>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="w-full justify-between">
{selectedRoles.length > 0
? `${selectedRoles.length} role(s) selected`
: "Select roles..."}
<ChevronDown className="ml-2 h-4 w-4 opacity-50" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
{roles.map((role) => (
<DropdownMenuCheckboxItem
key={role}
checked={selectedRoles.includes(role)}
onCheckedChange={() => toggleRole(role)}
>
{role}
</DropdownMenuCheckboxItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="flex justify-end space-x-4 pt-4">
<Button type="button" variant="outline" onClick={onCancel}>
Cancel
</Button>
<Button type="submit" disabled={isLoading}>
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Save Changes
</Button>
</div>
</form>
</div>
<div className="flex-1 bg-accent p-4 rounded-lg">
<h3 className="text-lg font-semibold mb-4">Live Preview</h3>
<div className="bg-card p-4 rounded-md shadow-sm">
<div className="flex items-center space-x-2">
{icon && <i className={`icon-${icon} text-primary`}></i>}
<span className="font-medium">{title || 'Menu Item Title'}</span>
</div>
<div className="text-sm text-muted-foreground mt-1">
{description || 'Item description goes here'}
</div>
<div className="mt-2 text-xs">
<span className={`inline-block px-2 py-1 rounded-full ${
status ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}>
{status ? 'Enabled' : 'Disabled'}
</span>
</div>
{selectedRoles.length > 0 && (
<div className="mt-2 flex flex-wrap gap-1">
{selectedRoles.map(role => (
<span key={role} className="text-xs bg-primary/10 text-primary px-2 py-1 rounded-full">
{role}
</span>
))}
</div>
)}
</div>
</div>
</div>
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment