feat: Redesign cost center modal + document modal pattern
- Completely redesigned Cost Center create/edit modal with elegant wizard-style UI - Added preview card, visual settings section, keyword tags with auto-assign badge - Added missing i18n translations for costCenters (namePlaceholder, descPlaceholder, etc.) - Documented modal design pattern in copilot-instructions.md for future reference - Pattern includes: colors, structure, labels, cards, tags, switch components
This commit is contained in:
parent
3ebb19e9c6
commit
a90ff9d013
109
.github/copilot-instructions.md
vendored
109
.github/copilot-instructions.md
vendored
@ -117,6 +117,115 @@ sshpass -p 'Master9354' ssh -o StrictHostKeyChecking=no root@213.165.93.60 "mysq
|
|||||||
|
|
||||||
❌ NUNCA usar `ssh root@213.165.93.60` sem sshpass (vai travar esperando senha)
|
❌ NUNCA usar `ssh root@213.165.93.60` sem sshpass (vai travar esperando senha)
|
||||||
|
|
||||||
|
## 🎨 Padrão Visual de Modais
|
||||||
|
|
||||||
|
**TODOS os modais de formulário devem seguir este padrão elegante:**
|
||||||
|
|
||||||
|
### Estrutura Base
|
||||||
|
```jsx
|
||||||
|
<div className="modal show d-block" style={{ backgroundColor: 'rgba(0,0,0,0.8)' }}>
|
||||||
|
<div className="modal-dialog modal-dialog-centered modal-lg">
|
||||||
|
<div className="modal-content border-0" style={{ background: '#1e293b', maxHeight: '90vh' }}>
|
||||||
|
|
||||||
|
{/* Header sem borda */}
|
||||||
|
<div className="modal-header border-0 pb-0">
|
||||||
|
<div>
|
||||||
|
<h5 className="modal-title text-white mb-1">
|
||||||
|
<i className="bi bi-icon me-2 text-primary"></i>
|
||||||
|
Título
|
||||||
|
</h5>
|
||||||
|
<p className="text-slate-400 mb-0 small">Subtítulo</p>
|
||||||
|
</div>
|
||||||
|
<button className="btn-close btn-close-white" onClick={onClose}></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Body com scroll */}
|
||||||
|
<div className="modal-body pt-3" style={{ maxHeight: '65vh', overflowY: 'auto' }}>
|
||||||
|
{/* Preview Card - SEMPRE no topo */}
|
||||||
|
<div className="mb-4 p-3 rounded-3" style={{ background: '#0f172a' }}>
|
||||||
|
{/* Preview visual do item sendo criado/editado */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Campos em cards com background #0f172a */}
|
||||||
|
{/* Labels com ícones coloridos */}
|
||||||
|
{/* Badges "Opcional" quando necessário */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer sem borda */}
|
||||||
|
<div className="modal-footer border-0">
|
||||||
|
<button className="btn btn-outline-secondary px-4">Cancelar</button>
|
||||||
|
<button className="btn btn-primary px-4">Salvar</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cores do Sistema
|
||||||
|
- **Background modal**: `#1e293b`
|
||||||
|
- **Background campos/cards**: `#0f172a`
|
||||||
|
- **Texto principal**: `text-white`
|
||||||
|
- **Texto secundário**: `text-slate-400`
|
||||||
|
- **Texto desabilitado**: `text-slate-500`
|
||||||
|
|
||||||
|
### Labels com Ícones
|
||||||
|
```jsx
|
||||||
|
<label className="form-label text-white fw-medium mb-2">
|
||||||
|
<i className="bi bi-type me-2 text-primary"></i>
|
||||||
|
Nome *
|
||||||
|
</label>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Badge Opcional
|
||||||
|
```jsx
|
||||||
|
<span className="badge bg-secondary ms-2" style={{ fontSize: '0.65rem' }}>
|
||||||
|
{t('common.optional')}
|
||||||
|
</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Seleção Visual com Cards
|
||||||
|
Para seleções (categorias, ícones), usar cards clicáveis:
|
||||||
|
```jsx
|
||||||
|
<div
|
||||||
|
onClick={() => handleSelect(item)}
|
||||||
|
className="p-2 rounded text-center"
|
||||||
|
style={{
|
||||||
|
background: isSelected ? 'rgba(59, 130, 246, 0.15)' : '#0f172a',
|
||||||
|
cursor: 'pointer',
|
||||||
|
border: isSelected ? '2px solid #3b82f6' : '2px solid transparent'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className={`bi ${icon} d-block mb-1`} style={{ color }}></i>
|
||||||
|
<small className="text-white">{label}</small>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Seção de Keywords/Tags
|
||||||
|
```jsx
|
||||||
|
<div className="p-3 rounded" style={{ background: '#0f172a' }}>
|
||||||
|
<div className="input-group mb-2">
|
||||||
|
<input className="form-control bg-dark text-white border-0" />
|
||||||
|
<button className="btn btn-primary px-3">
|
||||||
|
<i className="bi bi-plus-lg"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex flex-wrap gap-2">
|
||||||
|
{/* Tags com cor do item */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Switch de Status
|
||||||
|
```jsx
|
||||||
|
<div className="form-check form-switch">
|
||||||
|
<input type="checkbox" className="form-check-input" role="switch" />
|
||||||
|
<label className="form-check-label text-white">
|
||||||
|
<i className={`bi ${isActive ? 'bi-check-circle text-success' : 'bi-x-circle text-secondary'} me-2`}></i>
|
||||||
|
{isActive ? 'Activo' : 'Inactivo'}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
## Documentação
|
## Documentação
|
||||||
|
|
||||||
Consulte `.DIRETRIZES_DESENVOLVIMENTO_v5` para regras completas.
|
Consulte `.DIRETRIZES_DESENVOLVIMENTO_v5` para regras completas.
|
||||||
|
|||||||
@ -412,8 +412,13 @@
|
|||||||
"budget": "Budget",
|
"budget": "Budget",
|
||||||
"keywords": "Keywords",
|
"keywords": "Keywords",
|
||||||
"addKeyword": "Add Keyword",
|
"addKeyword": "Add Keyword",
|
||||||
"keywordPlaceholder": "Type a keyword",
|
"keywordPlaceholder": "Type and press Enter...",
|
||||||
"keywordHelp": "Keywords help automatically assign transactions",
|
"keywordHelp": "E.g.: \"UBER\", \"iFood\" - Transactions with these words are auto-assigned",
|
||||||
|
"noKeywords": "No keywords. Transactions will be assigned manually.",
|
||||||
|
"namePlaceholder": "E.g.: Project Alpha, Marketing Dept...",
|
||||||
|
"descPlaceholder": "Describe the purpose of this cost center...",
|
||||||
|
"visualSettings": "Appearance",
|
||||||
|
"autoAssignLabel": "Auto-assign",
|
||||||
"createSuccess": "Cost center created successfully",
|
"createSuccess": "Cost center created successfully",
|
||||||
"updateSuccess": "Cost center updated successfully",
|
"updateSuccess": "Cost center updated successfully",
|
||||||
"deleteSuccess": "Cost center deleted successfully",
|
"deleteSuccess": "Cost center deleted successfully",
|
||||||
|
|||||||
@ -415,8 +415,13 @@
|
|||||||
"budget": "Presupuesto",
|
"budget": "Presupuesto",
|
||||||
"keywords": "Palabras Clave",
|
"keywords": "Palabras Clave",
|
||||||
"addKeyword": "Agregar Palabra Clave",
|
"addKeyword": "Agregar Palabra Clave",
|
||||||
"keywordPlaceholder": "Escribe una palabra clave",
|
"keywordPlaceholder": "Escribe y presiona Enter...",
|
||||||
"keywordHelp": "Las palabras clave ayudan a asignar transacciones automáticamente",
|
"keywordHelp": "Ej: \"UBER\", \"iFood\" - Transacciones con estas palabras se asignan automáticamente",
|
||||||
|
"noKeywords": "Sin palabras clave. Las transacciones se asignarán manualmente.",
|
||||||
|
"namePlaceholder": "Ej: Proyecto Alpha, Dpto. Marketing...",
|
||||||
|
"descPlaceholder": "Describe el propósito de este centro de costo...",
|
||||||
|
"visualSettings": "Apariencia",
|
||||||
|
"autoAssignLabel": "Auto-asignación",
|
||||||
"createSuccess": "Centro de costo creado correctamente",
|
"createSuccess": "Centro de costo creado correctamente",
|
||||||
"updateSuccess": "Centro de costo actualizado correctamente",
|
"updateSuccess": "Centro de costo actualizado correctamente",
|
||||||
"deleteSuccess": "Centro de costo eliminado correctamente",
|
"deleteSuccess": "Centro de costo eliminado correctamente",
|
||||||
|
|||||||
@ -417,8 +417,13 @@
|
|||||||
"budget": "Orçamento",
|
"budget": "Orçamento",
|
||||||
"keywords": "Palavras-chave",
|
"keywords": "Palavras-chave",
|
||||||
"addKeyword": "Adicionar Palavra-chave",
|
"addKeyword": "Adicionar Palavra-chave",
|
||||||
"keywordPlaceholder": "Digite uma palavra-chave",
|
"keywordPlaceholder": "Digite e pressione Enter...",
|
||||||
"keywordHelp": "Palavras-chave ajudam a atribuir transações automaticamente",
|
"keywordHelp": "Ex: \"UBER\", \"iFood\" - Transações com essas palavras são atribuídas automaticamente",
|
||||||
|
"noKeywords": "Sem palavras-chave. Transações serão atribuídas manualmente.",
|
||||||
|
"namePlaceholder": "Ex: Projeto Alpha, Dpto. Marketing...",
|
||||||
|
"descPlaceholder": "Descreva o propósito deste centro de custo...",
|
||||||
|
"visualSettings": "Aparência",
|
||||||
|
"autoAssignLabel": "Auto-atribuição",
|
||||||
"createSuccess": "Centro de custo criado com sucesso",
|
"createSuccess": "Centro de custo criado com sucesso",
|
||||||
"updateSuccess": "Centro de custo atualizado com sucesso",
|
"updateSuccess": "Centro de custo atualizado com sucesso",
|
||||||
"deleteSuccess": "Centro de custo excluído com sucesso",
|
"deleteSuccess": "Centro de custo excluído com sucesso",
|
||||||
|
|||||||
@ -357,170 +357,242 @@ const CostCenters = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Modal de Criar/Editar */}
|
{/* Modal de Criar/Editar - Design Elegante */}
|
||||||
{showModal && (
|
{showModal && (
|
||||||
<div className="modal show d-block" style={{ backgroundColor: 'rgba(0,0,0,0.7)' }}>
|
<div className="modal show d-block" style={{ backgroundColor: 'rgba(0,0,0,0.8)' }}>
|
||||||
<div className="modal-dialog modal-lg modal-dialog-centered">
|
<div className="modal-dialog modal-dialog-centered modal-lg">
|
||||||
<div className="modal-content" style={{ background: '#1e293b' }}>
|
<div className="modal-content border-0" style={{ background: '#1e293b', maxHeight: '90vh' }}>
|
||||||
<div className="modal-header border-bottom" style={{ borderColor: '#334155 !important' }}>
|
{/* Header elegante */}
|
||||||
<h5 className="modal-title text-white">
|
<div className="modal-header border-0 pb-0">
|
||||||
<i className={`bi ${selectedItem ? 'bi-pencil' : 'bi-plus-circle'} me-2`}></i>
|
<div>
|
||||||
{selectedItem ? t('costCenters.editCostCenter') : t('costCenters.newCostCenter')}
|
<h5 className="modal-title text-white mb-1">
|
||||||
</h5>
|
<i className={`bi ${selectedItem ? 'bi-pencil-square' : 'bi-plus-circle-dotted'} me-2 text-success`}></i>
|
||||||
|
{selectedItem ? t('costCenters.editCostCenter') : t('costCenters.newCostCenter')}
|
||||||
|
</h5>
|
||||||
|
<p className="text-slate-400 mb-0 small">
|
||||||
|
{t('costCenters.title')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<button type="button" className="btn-close btn-close-white" onClick={handleCloseModal}></button>
|
<button type="button" className="btn-close btn-close-white" onClick={handleCloseModal}></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="modal-body">
|
<div className="modal-body pt-3" style={{ maxHeight: '65vh', overflowY: 'auto' }}>
|
||||||
<div className="row g-3">
|
|
||||||
{/* Nome */}
|
{/* Preview Card */}
|
||||||
|
<div className="mb-4 p-3 rounded-3" style={{ background: '#0f172a' }}>
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<div
|
||||||
|
className="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||||
|
style={{
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
background: `${formData.color}25`,
|
||||||
|
border: `2px solid ${formData.color}`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className={`bi ${formData.icon}`} style={{ fontSize: '1.3rem', color: formData.color }}></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h6 className="text-white mb-0">{formData.name || t('costCenters.newCostCenter')}</h6>
|
||||||
|
<small className="text-slate-400">
|
||||||
|
{formData.code ? `${t('costCenters.code')}: ${formData.code}` : t('common.description')}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div className="ms-auto">
|
||||||
|
{formData.is_active ? (
|
||||||
|
<span className="badge bg-success bg-opacity-25 text-success">{t('common.active')}</span>
|
||||||
|
) : (
|
||||||
|
<span className="badge bg-secondary bg-opacity-25 text-secondary">{t('common.inactive')}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Nome e Código - Linha principal */}
|
||||||
|
<div className="row g-3 mb-4">
|
||||||
<div className="col-md-8">
|
<div className="col-md-8">
|
||||||
<label className="form-label text-slate-300">{t('common.name')} *</label>
|
<label className="form-label text-white fw-medium mb-2">
|
||||||
|
<i className="bi bi-type me-2 text-primary"></i>
|
||||||
|
{t('common.name')} *
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control bg-dark text-white border-secondary"
|
className="form-control bg-dark text-white border-0"
|
||||||
|
style={{ background: '#0f172a' }}
|
||||||
name="name"
|
name="name"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="Ex: Projeto Alpha, Departamento RH..."
|
placeholder={t('costCenters.namePlaceholder') || 'Ej: Proyecto Alpha, Dpto. Marketing...'}
|
||||||
required
|
required
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Código */}
|
|
||||||
<div className="col-md-4">
|
<div className="col-md-4">
|
||||||
<label className="form-label text-slate-300">{t('costCenters.code')}</label>
|
<label className="form-label text-white fw-medium mb-2">
|
||||||
|
<i className="bi bi-hash me-2 text-warning"></i>
|
||||||
|
{t('costCenters.code')}
|
||||||
|
<span className="badge bg-secondary ms-2" style={{ fontSize: '0.65rem' }}>{t('common.optional')}</span>
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control bg-dark text-white border-secondary"
|
className="form-control bg-dark text-white border-0"
|
||||||
|
style={{ background: '#0f172a' }}
|
||||||
name="code"
|
name="code"
|
||||||
value={formData.code}
|
value={formData.code}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="Ex: CC001"
|
placeholder="CC001"
|
||||||
maxLength="20"
|
maxLength="20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Cor */}
|
{/* Visual - Cor e Ícone */}
|
||||||
<div className="col-md-3">
|
<div className="mb-4">
|
||||||
<label className="form-label text-slate-300">{t('common.color')}</label>
|
<label className="form-label text-white fw-medium mb-2">
|
||||||
<input
|
<i className="bi bi-palette me-2 text-success"></i>
|
||||||
type="color"
|
{t('costCenters.visualSettings') || t('categories.visualSettings') || 'Apariencia'}
|
||||||
className="form-control form-control-color bg-dark border-secondary w-100"
|
</label>
|
||||||
name="color"
|
<div className="row g-3">
|
||||||
value={formData.color}
|
<div className="col-4">
|
||||||
onChange={handleChange}
|
<div className="p-3 rounded text-center" style={{ background: '#0f172a' }}>
|
||||||
/>
|
<label className="text-slate-400 small d-block mb-2">{t('common.color')}</label>
|
||||||
</div>
|
<input
|
||||||
|
type="color"
|
||||||
{/* Ícone */}
|
className="form-control form-control-color mx-auto border-0"
|
||||||
<div className="col-md-5">
|
style={{ width: 50, height: 50, cursor: 'pointer', background: 'transparent' }}
|
||||||
<label className="form-label text-slate-300">{t('common.icon')}</label>
|
name="color"
|
||||||
<IconSelector
|
value={formData.color}
|
||||||
value={formData.icon}
|
onChange={handleChange}
|
||||||
onChange={(icon) => setFormData(prev => ({ ...prev, icon }))}
|
/>
|
||||||
type="costCenter"
|
</div>
|
||||||
/>
|
</div>
|
||||||
</div>
|
<div className="col-8">
|
||||||
|
<div className="p-3 rounded h-100" style={{ background: '#0f172a' }}>
|
||||||
{/* Status */}
|
<label className="text-slate-400 small d-block mb-2">{t('common.icon')}</label>
|
||||||
<div className="col-md-4">
|
<IconSelector
|
||||||
<label className="form-label text-slate-300"> </label>
|
value={formData.icon}
|
||||||
<div className="form-check mt-2">
|
onChange={(icon) => setFormData(prev => ({ ...prev, icon }))}
|
||||||
<input
|
type="costCenter"
|
||||||
type="checkbox"
|
/>
|
||||||
className="form-check-input"
|
</div>
|
||||||
id="is_active"
|
|
||||||
name="is_active"
|
|
||||||
checked={formData.is_active}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
<label className="form-check-label text-slate-300" htmlFor="is_active">
|
|
||||||
{t('common.active')}
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Descrição */}
|
{/* Descrição */}
|
||||||
<div className="col-12">
|
<div className="mb-4">
|
||||||
<label className="form-label text-slate-300">{t('common.description')}</label>
|
<label className="form-label text-white fw-medium mb-2">
|
||||||
<textarea
|
<i className="bi bi-text-paragraph me-2 text-secondary"></i>
|
||||||
className="form-control bg-dark text-white border-secondary"
|
{t('common.description')}
|
||||||
name="description"
|
<span className="badge bg-secondary ms-2" style={{ fontSize: '0.65rem' }}>{t('common.optional')}</span>
|
||||||
value={formData.description}
|
</label>
|
||||||
onChange={handleChange}
|
<textarea
|
||||||
rows="2"
|
className="form-control bg-dark text-white border-0"
|
||||||
placeholder="Descreva o propósito deste centro de custo..."
|
style={{ background: '#0f172a' }}
|
||||||
></textarea>
|
name="description"
|
||||||
</div>
|
value={formData.description}
|
||||||
|
onChange={handleChange}
|
||||||
|
rows="2"
|
||||||
|
placeholder={t('costCenters.descPlaceholder') || 'Describe el propósito de este centro de costo...'}
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Palavras-chave */}
|
{/* Palavras-chave - Seção destacada */}
|
||||||
<div className="col-12">
|
<div className="mb-3">
|
||||||
<label className="form-label text-slate-300">
|
<label className="form-label text-white fw-medium mb-2">
|
||||||
<i className="bi bi-key me-1"></i>
|
<i className="bi bi-key me-2 text-warning"></i>
|
||||||
{t('costCenters.keywordHelp')}
|
{t('costCenters.keywords')}
|
||||||
</label>
|
<span className="badge bg-warning text-dark ms-2" style={{ fontSize: '0.65rem' }}>
|
||||||
|
{t('costCenters.autoAssignLabel') || 'Auto-asignación'}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div className="p-3 rounded" style={{ background: '#0f172a' }}>
|
||||||
<div className="input-group mb-2">
|
<div className="input-group mb-2">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control bg-dark text-white border-secondary"
|
className="form-control bg-dark text-white border-0"
|
||||||
|
style={{ background: '#1e293b' }}
|
||||||
value={newKeyword}
|
value={newKeyword}
|
||||||
onChange={(e) => setNewKeyword(e.target.value)}
|
onChange={(e) => setNewKeyword(e.target.value)}
|
||||||
onKeyPress={handleKeywordKeyPress}
|
onKeyPress={handleKeywordKeyPress}
|
||||||
placeholder="Digite uma palavra-chave e pressione Enter..."
|
placeholder={t('costCenters.keywordPlaceholder') || 'Escribe y presiona Enter...'}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-outline-success"
|
className="btn btn-success px-3"
|
||||||
onClick={handleAddKeyword}
|
onClick={handleAddKeyword}
|
||||||
>
|
>
|
||||||
<i className="bi bi-plus"></i>
|
<i className="bi bi-plus-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex flex-wrap gap-2">
|
<div className="d-flex flex-wrap gap-2">
|
||||||
{formData.keywords.map((keyword, index) => (
|
{formData.keywords.map((keyword, index) => (
|
||||||
<span
|
<span
|
||||||
key={index}
|
key={index}
|
||||||
className="badge d-flex align-items-center"
|
className="badge d-flex align-items-center py-2 px-3"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: formData.color + '25',
|
backgroundColor: formData.color + '25',
|
||||||
color: formData.color,
|
color: formData.color,
|
||||||
|
fontSize: '0.85rem'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{keyword}
|
{keyword}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn-close btn-close-white ms-2"
|
className="btn-close ms-2"
|
||||||
style={{ fontSize: '8px' }}
|
style={{ fontSize: '8px', filter: 'brightness(1.5)' }}
|
||||||
onClick={() => handleRemoveKeyword(keyword)}
|
onClick={() => handleRemoveKeyword(keyword)}
|
||||||
></button>
|
></button>
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
{formData.keywords.length === 0 && (
|
{formData.keywords.length === 0 && (
|
||||||
<small className="text-slate-500">
|
<small className="text-slate-500">
|
||||||
{t('common.noData')}
|
<i className="bi bi-info-circle me-1"></i>
|
||||||
|
{t('costCenters.noKeywords') || 'Sin palabras clave. Las transacciones se asignarán manualmente.'}
|
||||||
</small>
|
</small>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<small className="text-slate-500 mt-2 d-block">
|
|
||||||
Ex: "UBER", "iFood", "Supermercado XYZ" - Quando estas palavras aparecerem na
|
|
||||||
descrição de uma transação, este centro de custo será sugerido automaticamente.
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
<small className="text-slate-500 mt-2 d-block">
|
||||||
|
<i className="bi bi-lightbulb me-1"></i>
|
||||||
|
{t('costCenters.keywordHelp')}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status */}
|
||||||
|
<div className="form-check form-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
id="is_active"
|
||||||
|
name="is_active"
|
||||||
|
checked={formData.is_active}
|
||||||
|
onChange={handleChange}
|
||||||
|
role="switch"
|
||||||
|
/>
|
||||||
|
<label className="form-check-label text-white" htmlFor="is_active">
|
||||||
|
<i className={`bi ${formData.is_active ? 'bi-check-circle text-success' : 'bi-x-circle text-secondary'} me-2`}></i>
|
||||||
|
{formData.is_active ? t('common.active') : t('common.inactive')}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-footer border-top" style={{ borderColor: '#334155 !important' }}>
|
|
||||||
<button type="button" className="btn btn-outline-light" onClick={handleCloseModal}>
|
{/* Footer elegante */}
|
||||||
|
<div className="modal-footer border-0">
|
||||||
|
<button type="button" className="btn btn-outline-secondary px-4" onClick={handleCloseModal}>
|
||||||
|
<i className="bi bi-x-lg me-2"></i>
|
||||||
{t('common.cancel')}
|
{t('common.cancel')}
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" className="btn btn-success" disabled={saving}>
|
<button type="submit" className="btn btn-success px-4" disabled={saving || !formData.name.trim()}>
|
||||||
{saving ? (
|
{saving ? (
|
||||||
<>
|
<>
|
||||||
<span className="spinner-border spinner-border-sm me-2"></span>
|
<span className="spinner-border spinner-border-sm me-2"></span>
|
||||||
{t('common.loading')}
|
{t('common.saving')}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<i className="bi bi-check-lg me-2"></i>
|
<i className={`bi ${selectedItem ? 'bi-check-lg' : 'bi-plus-lg'} me-2`}></i>
|
||||||
{selectedItem ? t('common.save') : t('common.create')}
|
{selectedItem ? t('common.save') : t('common.create')}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user