feat: replace unstyled native html selects with rich custom styled select components
This commit is contained in:
parent
6412bb20d1
commit
f2f9666fcf
4 changed files with 270 additions and 105 deletions
|
|
@ -5,6 +5,7 @@ import type { OrganizationMember } from '../../services/organizationService'
|
||||||
import { projectService } from '../../services/projectService'
|
import { projectService } from '../../services/projectService'
|
||||||
import { categoryService } from '../../services/categoryService'
|
import { categoryService } from '../../services/categoryService'
|
||||||
import { organizationService } from '../../services/organizationService'
|
import { organizationService } from '../../services/organizationService'
|
||||||
|
import { CustomSelect } from '../ui/Select'
|
||||||
|
|
||||||
interface CreateTaskModalProps {
|
interface CreateTaskModalProps {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
|
|
@ -272,18 +273,20 @@ export const CreateTaskModal: React.FC<CreateTaskModalProps> = ({
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<select
|
<CustomSelect
|
||||||
value={projectId ?? ''}
|
value={projectId ?? 0}
|
||||||
onChange={(e) => setProjectId(e.target.value ? Number(e.target.value) : undefined)}
|
onChange={(val) => setProjectId(Number(val) === 0 ? undefined : Number(val))}
|
||||||
className="w-full px-3 py-2 rounded-xl border border-border bg-surface text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand"
|
placeholder="Sem projeto"
|
||||||
>
|
leftIcon={<Folder className="w-3.5 h-3.5 text-brand" />}
|
||||||
<option value="">Sem projeto</option>
|
options={[
|
||||||
{projects.map((p) => (
|
{ value: 0, label: 'Sem projeto' },
|
||||||
<option key={p.id} value={p.id}>
|
...projects.map((p) => ({
|
||||||
{p.name}
|
value: p.id,
|
||||||
</option>
|
label: p.name,
|
||||||
))}
|
color: p.color || '#0f766e',
|
||||||
</select>
|
})),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -297,7 +300,7 @@ export const CreateTaskModal: React.FC<CreateTaskModalProps> = ({
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowAddCategory(!showAddCategory)}
|
onClick={() => setShowAddCategory(!showAddCategory)}
|
||||||
className="text-[11px] text-accent hover:underline font-medium"
|
className="text-[11px] text-accent hover:underline font-medium cursor-pointer"
|
||||||
>
|
>
|
||||||
{showAddCategory ? 'Cancelar' : '+ Nova'}
|
{showAddCategory ? 'Cancelar' : '+ Nova'}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -321,18 +324,20 @@ export const CreateTaskModal: React.FC<CreateTaskModalProps> = ({
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<select
|
<CustomSelect
|
||||||
value={categoryId ?? ''}
|
value={categoryId ?? 0}
|
||||||
onChange={(e) => setCategoryId(e.target.value ? Number(e.target.value) : undefined)}
|
onChange={(val) => setCategoryId(Number(val) === 0 ? undefined : Number(val))}
|
||||||
className="w-full px-3 py-2 rounded-xl border border-border bg-surface text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand"
|
placeholder="Sem categoria"
|
||||||
>
|
leftIcon={<Tag className="w-3.5 h-3.5 text-accent" />}
|
||||||
<option value="">Sem categoria</option>
|
options={[
|
||||||
{categories.map((c) => (
|
{ value: 0, label: 'Sem categoria' },
|
||||||
<option key={c.id} value={c.id}>
|
...categories.map((c) => ({
|
||||||
{c.name}
|
value: c.id,
|
||||||
</option>
|
label: c.name,
|
||||||
))}
|
color: c.color || '#d97706',
|
||||||
</select>
|
})),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -345,20 +350,20 @@ export const CreateTaskModal: React.FC<CreateTaskModalProps> = ({
|
||||||
<User className="w-3.5 h-3.5 text-muted" />
|
<User className="w-3.5 h-3.5 text-muted" />
|
||||||
Responsável
|
Responsável
|
||||||
</label>
|
</label>
|
||||||
<select
|
<CustomSelect
|
||||||
value={assigneeUserId ?? ''}
|
value={assigneeUserId ?? 0}
|
||||||
onChange={(e) =>
|
onChange={(val) => setAssigneeUserId(Number(val) === 0 ? undefined : Number(val))}
|
||||||
setAssigneeUserId(e.target.value ? Number(e.target.value) : undefined)
|
placeholder="Atribuir a mim mesmo / Sem responsável"
|
||||||
}
|
leftIcon={<User className="w-3.5 h-3.5 text-muted" />}
|
||||||
className="w-full px-3 py-2 rounded-xl border border-border bg-surface text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand"
|
options={[
|
||||||
>
|
{ value: 0, label: 'Sem responsável (atribuir a mim)' },
|
||||||
<option value="">Atribuir a mim mesmo / Sem responsável</option>
|
...members.map((m) => ({
|
||||||
{members.map((m) => (
|
value: m.userId,
|
||||||
<option key={m.userId} value={m.userId}>
|
label: m.userName,
|
||||||
{m.userName} ({m.userEmail})
|
description: m.userEmail,
|
||||||
</option>
|
})),
|
||||||
))}
|
]}
|
||||||
</select>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status */}
|
{/* Status */}
|
||||||
|
|
@ -366,16 +371,16 @@ export const CreateTaskModal: React.FC<CreateTaskModalProps> = ({
|
||||||
<label className="block text-xs font-semibold text-foreground mb-1.5">
|
<label className="block text-xs font-semibold text-foreground mb-1.5">
|
||||||
Status Inicial
|
Status Inicial
|
||||||
</label>
|
</label>
|
||||||
<select
|
<CustomSelect
|
||||||
value={status}
|
value={status}
|
||||||
onChange={(e) => setStatus(e.target.value as TaskStatus)}
|
onChange={(val) => setStatus(val as TaskStatus)}
|
||||||
className="w-full px-3 py-2 rounded-xl border border-border bg-surface text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand"
|
options={[
|
||||||
>
|
{ value: 'NOT_STARTED', label: 'A Fazer', color: '#71717a' },
|
||||||
<option value="NOT_STARTED">A Fazer (NOT_STARTED)</option>
|
{ value: 'IN_PROGRESS', label: 'Em Andamento', color: '#14b8a6' },
|
||||||
<option value="IN_PROGRESS">Em Andamento (IN_PROGRESS)</option>
|
{ value: 'COMPLETED', label: 'Concluído', color: '#10b981' },
|
||||||
<option value="COMPLETED">Concluído (COMPLETED)</option>
|
{ value: 'CANCELED', label: 'Cancelado', color: '#f43f5e' },
|
||||||
<option value="CANCELED">Cancelado (CANCELED)</option>
|
]}
|
||||||
</select>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
||||||
import { Play, Square, Clock, Folder, Tag, MoreHorizontal, CheckCircle2 } from 'lucide-react'
|
import { Play, Square, Clock, Folder, Tag, MoreHorizontal, CheckCircle2 } from 'lucide-react'
|
||||||
import type { Task, TaskStatus } from '../../types/task'
|
import type { Task, TaskStatus } from '../../types/task'
|
||||||
import { isTaskRunning, calculateTaskTotalSeconds, formatSecondsToTimer } from '../../lib/timeUtils'
|
import { isTaskRunning, calculateTaskTotalSeconds, formatSecondsToTimer } from '../../lib/timeUtils'
|
||||||
|
import { CustomSelect } from '../ui/Select'
|
||||||
|
|
||||||
interface TaskListViewProps {
|
interface TaskListViewProps {
|
||||||
tasks: Task[]
|
tasks: Task[]
|
||||||
|
|
@ -12,24 +13,6 @@ interface TaskListViewProps {
|
||||||
activeTaskSeconds?: number
|
activeTaskSeconds?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusBadge: Record<TaskStatus, { label: string; badgeClass: string }> = {
|
|
||||||
NOT_STARTED: {
|
|
||||||
label: 'A Fazer',
|
|
||||||
badgeClass: 'bg-zinc-800 text-zinc-300 border-zinc-700/60',
|
|
||||||
},
|
|
||||||
IN_PROGRESS: {
|
|
||||||
label: 'Em Andamento',
|
|
||||||
badgeClass: 'bg-teal-500/15 text-teal-300 border-teal-500/30',
|
|
||||||
},
|
|
||||||
COMPLETED: {
|
|
||||||
label: 'Concluído',
|
|
||||||
badgeClass: 'bg-emerald-500/15 text-emerald-300 border-emerald-500/30',
|
|
||||||
},
|
|
||||||
CANCELED: {
|
|
||||||
label: 'Cancelado',
|
|
||||||
badgeClass: 'bg-rose-500/15 text-rose-300 border-rose-500/30',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TaskListView: React.FC<TaskListViewProps> = ({
|
export const TaskListView: React.FC<TaskListViewProps> = ({
|
||||||
tasks,
|
tasks,
|
||||||
|
|
@ -145,17 +128,19 @@ export const TaskListView: React.FC<TaskListViewProps> = ({
|
||||||
|
|
||||||
{/* Status Dropdown */}
|
{/* Status Dropdown */}
|
||||||
<td className="py-3.5 px-4 whitespace-nowrap">
|
<td className="py-3.5 px-4 whitespace-nowrap">
|
||||||
<select
|
<div className="w-36">
|
||||||
value={task.status}
|
<CustomSelect
|
||||||
onChange={(e) => onStatusChange(task, e.target.value as TaskStatus)}
|
size="sm"
|
||||||
aria-label="Status da tarefa"
|
value={task.status}
|
||||||
className={`text-[11px] font-medium px-2 py-1 rounded-lg border cursor-pointer focus:outline-none focus:ring-1 focus:ring-brand ${statusBadge[task.status].badgeClass}`}
|
onChange={(newStatus) => onStatusChange(task, newStatus as TaskStatus)}
|
||||||
>
|
options={[
|
||||||
<option value="NOT_STARTED" className="bg-surface-raised text-foreground">A Fazer</option>
|
{ value: 'NOT_STARTED', label: 'A Fazer', color: '#71717a' },
|
||||||
<option value="IN_PROGRESS" className="bg-surface-raised text-foreground">Em Andamento</option>
|
{ value: 'IN_PROGRESS', label: 'Em Andamento', color: '#14b8a6' },
|
||||||
<option value="COMPLETED" className="bg-surface-raised text-foreground">Concluído</option>
|
{ value: 'COMPLETED', label: 'Concluído', color: '#10b981' },
|
||||||
<option value="CANCELED" className="bg-surface-raised text-foreground">Cancelado</option>
|
{ value: 'CANCELED', label: 'Cancelado', color: '#f43f5e' },
|
||||||
</select>
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Time Spent */}
|
{/* Time Spent */}
|
||||||
|
|
|
||||||
176
new-frontend/src/components/ui/Select.tsx
Normal file
176
new-frontend/src/components/ui/Select.tsx
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
import { useState, useRef, useEffect, type ReactNode } from 'react'
|
||||||
|
import { ChevronDown, Check } from 'lucide-react'
|
||||||
|
|
||||||
|
export interface SelectOption<T = string | number> {
|
||||||
|
value: T
|
||||||
|
label: string
|
||||||
|
icon?: ReactNode
|
||||||
|
color?: string
|
||||||
|
badgeClass?: string
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomSelectProps<T = string | number> {
|
||||||
|
value: T | undefined
|
||||||
|
onChange: (value: T) => void
|
||||||
|
options: SelectOption<T>[]
|
||||||
|
placeholder?: string
|
||||||
|
className?: string
|
||||||
|
triggerClassName?: string
|
||||||
|
disabled?: boolean
|
||||||
|
leftIcon?: ReactNode
|
||||||
|
size?: 'sm' | 'md'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CustomSelect<T extends string | number>({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
options,
|
||||||
|
placeholder = 'Selecione uma opção',
|
||||||
|
className = '',
|
||||||
|
triggerClassName = '',
|
||||||
|
disabled = false,
|
||||||
|
leftIcon,
|
||||||
|
size = 'md',
|
||||||
|
}: CustomSelectProps<T>) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
// Find currently selected option
|
||||||
|
const selectedOption = options.find((opt) => opt.value === value)
|
||||||
|
|
||||||
|
// Close on outside click
|
||||||
|
useEffect(() => {
|
||||||
|
const handleOutsideClick = (event: MouseEvent) => {
|
||||||
|
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
||||||
|
setIsOpen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
document.addEventListener('mousedown', handleOutsideClick)
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleOutsideClick)
|
||||||
|
}
|
||||||
|
}, [isOpen])
|
||||||
|
|
||||||
|
// Close on Escape key
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape' && isOpen) {
|
||||||
|
setIsOpen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener('keydown', handleKeyDown)
|
||||||
|
return () => window.removeEventListener('keydown', handleKeyDown)
|
||||||
|
}, [isOpen])
|
||||||
|
|
||||||
|
const handleSelect = (optValue: T) => {
|
||||||
|
onChange(optValue)
|
||||||
|
setIsOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSmall = size === 'sm'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} className={`relative inline-block w-full ${className}`}>
|
||||||
|
{/* Trigger Button */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
|
className={`w-full flex items-center justify-between gap-2.5 rounded-xl border border-border-soft bg-surface-raised/90 backdrop-blur-md text-foreground transition-all duration-200 outline-none select-none disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer ${
|
||||||
|
isOpen
|
||||||
|
? 'border-brand ring-2 ring-brand/20 shadow-xs'
|
||||||
|
: 'hover:border-brand/40 hover:bg-surface-raised'
|
||||||
|
} ${isSmall ? 'px-2.5 py-1 text-xs' : 'px-3.5 py-2.5 text-xs'} ${triggerClassName}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2 truncate">
|
||||||
|
{/* Custom Left Icon if supplied */}
|
||||||
|
{leftIcon && <span className="text-muted shrink-0">{leftIcon}</span>}
|
||||||
|
|
||||||
|
{/* Option's custom icon or colored dot */}
|
||||||
|
{selectedOption?.color && (
|
||||||
|
<span
|
||||||
|
className="w-2.5 h-2.5 rounded-full shrink-0"
|
||||||
|
style={{ backgroundColor: selectedOption.color }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{selectedOption?.icon && (
|
||||||
|
<span className="shrink-0">{selectedOption.icon}</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Label or Placeholder */}
|
||||||
|
{selectedOption ? (
|
||||||
|
<span className="truncate font-medium text-foreground">
|
||||||
|
{selectedOption.label}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="truncate text-muted">{placeholder}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Chevron Indicator */}
|
||||||
|
<ChevronDown
|
||||||
|
className={`w-3.5 h-3.5 text-muted transition-transform duration-200 shrink-0 ${
|
||||||
|
isOpen ? 'rotate-180 text-brand' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Dropdown Popover */}
|
||||||
|
{isOpen && (
|
||||||
|
<div className="absolute left-0 right-0 mt-1.5 z-50 min-w-[200px] max-h-64 overflow-y-auto rounded-2xl border border-border-soft bg-surface/95 backdrop-blur-xl p-1.5 shadow-panel animate-in fade-in zoom-in-95 duration-150">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
{options.map((option) => {
|
||||||
|
const isSelected = option.value === value
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={String(option.value)}
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleSelect(option.value)}
|
||||||
|
className={`w-full flex items-center justify-between gap-2.5 px-3 py-2 rounded-xl text-left text-xs transition-colors cursor-pointer ${
|
||||||
|
isSelected
|
||||||
|
? 'bg-brand/12 text-brand font-semibold'
|
||||||
|
: 'text-foreground hover:bg-surface-raised hover:text-foreground'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2.5 truncate">
|
||||||
|
{/* Colored dot */}
|
||||||
|
{option.color && (
|
||||||
|
<span
|
||||||
|
className="w-2.5 h-2.5 rounded-full shrink-0 shadow-xs"
|
||||||
|
style={{ backgroundColor: option.color }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Icon */}
|
||||||
|
{option.icon && (
|
||||||
|
<span className="shrink-0">{option.icon}</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Label and optional description */}
|
||||||
|
<div className="truncate">
|
||||||
|
<p className="truncate leading-tight">{option.label}</p>
|
||||||
|
{option.description && (
|
||||||
|
<p className="text-[10px] text-muted truncate mt-0.5 font-normal">
|
||||||
|
{option.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Checkmark indicator */}
|
||||||
|
{isSelected && (
|
||||||
|
<Check className="w-3.5 h-3.5 text-brand shrink-0" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ import type { Task, TaskStatus, Project } from '../types/task'
|
||||||
import { TaskKanbanView } from '../components/tasks/TaskKanbanView'
|
import { TaskKanbanView } from '../components/tasks/TaskKanbanView'
|
||||||
import { TaskListView } from '../components/tasks/TaskListView'
|
import { TaskListView } from '../components/tasks/TaskListView'
|
||||||
import { CreateTaskModal } from '../components/tasks/CreateTaskModal'
|
import { CreateTaskModal } from '../components/tasks/CreateTaskModal'
|
||||||
|
import { CustomSelect } from '../components/ui/Select'
|
||||||
|
|
||||||
export const TasksPage: React.FC = () => {
|
export const TasksPage: React.FC = () => {
|
||||||
const { activeOrg } = useAuth()
|
const { activeOrg } = useAuth()
|
||||||
|
|
@ -367,39 +368,37 @@ export const TasksPage: React.FC = () => {
|
||||||
|
|
||||||
{/* Project Filter */}
|
{/* Project Filter */}
|
||||||
{projects.length > 0 && (
|
{projects.length > 0 && (
|
||||||
<div className="relative">
|
<div className="w-48">
|
||||||
<select
|
<CustomSelect
|
||||||
value={selectedProjectId}
|
value={selectedProjectId}
|
||||||
onChange={(e) =>
|
onChange={(val) => setSelectedProjectId(val)}
|
||||||
setSelectedProjectId(
|
leftIcon={<Folder className="w-3.5 h-3.5 text-brand" />}
|
||||||
e.target.value === 'ALL' ? 'ALL' : Number(e.target.value)
|
options={[
|
||||||
)
|
{ value: 'ALL', label: 'Todos os Projetos' },
|
||||||
}
|
...projects.map((p) => ({
|
||||||
className="pl-7 pr-3.5 py-2 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand cursor-pointer"
|
value: p.id,
|
||||||
>
|
label: p.name,
|
||||||
<option value="ALL">Todos os Projetos</option>
|
color: p.color || '#0f766e',
|
||||||
{projects.map((p) => (
|
})),
|
||||||
<option key={p.id} value={p.id}>
|
]}
|
||||||
{p.name}
|
/>
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<Folder className="w-3.5 h-3.5 text-brand absolute left-2.5 top-1/2 -translate-y-1/2 pointer-events-none" />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Status Filter */}
|
{/* Status Filter */}
|
||||||
<select
|
<div className="w-44">
|
||||||
value={selectedStatus}
|
<CustomSelect
|
||||||
onChange={(e) => setSelectedStatus(e.target.value as TaskStatus | 'ALL')}
|
value={selectedStatus}
|
||||||
className="px-3.5 py-2 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand cursor-pointer"
|
onChange={(val) => setSelectedStatus(val as TaskStatus | 'ALL')}
|
||||||
>
|
options={[
|
||||||
<option value="ALL">Todos os Status</option>
|
{ value: 'ALL', label: 'Todos os Status' },
|
||||||
<option value="NOT_STARTED">A Fazer</option>
|
{ value: 'NOT_STARTED', label: 'A Fazer', color: '#71717a' },
|
||||||
<option value="IN_PROGRESS">Em Andamento</option>
|
{ value: 'IN_PROGRESS', label: 'Em Andamento', color: '#14b8a6' },
|
||||||
<option value="COMPLETED">Concluído</option>
|
{ value: 'COMPLETED', label: 'Concluído', color: '#10b981' },
|
||||||
<option value="CANCELED">Cancelado</option>
|
{ value: 'CANCELED', label: 'Cancelado', color: '#f43f5e' },
|
||||||
</select>
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue