🚀 Initial commit: Versão atual do TrackSteel APP
This commit is contained in:
@@ -0,0 +1,849 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { toast } from 'sonner';
|
||||
import { Save, FileText, User, MapPin, Settings2, Eye, Download, Search, Building2 } from 'lucide-react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { AlteracoesPopup } from '@/components/forms/AlteracoesPopup';
|
||||
import { FichaTecnicaPreview } from '@/components/forms/FichaTecnicaPreview';
|
||||
import { useThemeConfig } from '@/hooks/useThemeConfig';
|
||||
import { useFichaTecnica, FichaTecnicaData } from '@/hooks/useFichaTecnica';
|
||||
import { useUserManagement } from '@/hooks/useUserManagement';
|
||||
|
||||
interface InfoCalculoEstrutural {
|
||||
e: boolean;
|
||||
c: boolean;
|
||||
na: boolean;
|
||||
info: string;
|
||||
}
|
||||
|
||||
interface FormData extends FichaTecnicaData {
|
||||
data_inicio: string;
|
||||
data_termino_prev: string;
|
||||
status: string;
|
||||
prioridade: string;
|
||||
}
|
||||
|
||||
const initialFormData: FormData = {
|
||||
of_number: '',
|
||||
gestor: '',
|
||||
projetista: '',
|
||||
revisao: '0',
|
||||
quantidade: 0,
|
||||
data_inicio: '',
|
||||
data_termino_prev: '',
|
||||
status: 'aberta',
|
||||
prioridade: 'media',
|
||||
|
||||
// Dados do Cliente
|
||||
cliente: '',
|
||||
cnpj: '',
|
||||
ie: '',
|
||||
endereco: '',
|
||||
cidade: '',
|
||||
estado: '',
|
||||
cep: '',
|
||||
contato_contrato: '',
|
||||
fone_contrato: '',
|
||||
cel_contrato: '',
|
||||
email_contrato: '',
|
||||
contato_obra: '',
|
||||
fone_obra: '',
|
||||
cel_obra: '',
|
||||
email_obra: '',
|
||||
contato_qualid: '',
|
||||
fone_qualid: '',
|
||||
cel_qualid: '',
|
||||
email_qualid: '',
|
||||
|
||||
// Dados da Obra
|
||||
eng_responsavel: '',
|
||||
telefone_obra: '',
|
||||
email_obra_responsavel: '',
|
||||
endereco_obra: '',
|
||||
cep_obra: '',
|
||||
bairro_obra: '',
|
||||
cidade_obra: '',
|
||||
estado_obra: '',
|
||||
observacoes_obra: '',
|
||||
|
||||
// Dados do Projeto
|
||||
descricao_resumida: '',
|
||||
endereco_projeto: '',
|
||||
bairro_projeto: '',
|
||||
cep_projeto: '',
|
||||
cidade_projeto: '',
|
||||
estado_projeto: '',
|
||||
horarios_trabalho: '',
|
||||
condicoes_acesso: '',
|
||||
|
||||
// Tipos de Projeto
|
||||
tipo_estrutural: false,
|
||||
tipo_residencial: false,
|
||||
tipo_espacial: false,
|
||||
tipo_comercial: false,
|
||||
tipo_grades: false,
|
||||
tipo_industrial: false,
|
||||
tipo_cobertura: false,
|
||||
tipo_com_montagem: false,
|
||||
|
||||
// Documentos Fornecidos
|
||||
doc_calculo: false,
|
||||
doc_projeto: false,
|
||||
doc_detalhamento: false,
|
||||
doc_cronograma: false,
|
||||
doc_normas: false,
|
||||
doc_especif_tecnicas: false,
|
||||
doc_catalogo: false,
|
||||
doc_fotos: false,
|
||||
|
||||
// Informações Técnicas
|
||||
info_calculo_estrutural: { e: false, c: false, na: false, info: '' },
|
||||
info_projeto_basico: { e: false, c: false, na: false, info: '' },
|
||||
info_detalhamento: { e: false, c: false, na: false, info: '' },
|
||||
info_materia_prima: { e: false, c: false, na: false, info: '' },
|
||||
info_fabricacao: { e: false, c: false, na: false, info: '' },
|
||||
info_grades_piso: { e: false, c: false, na: false, info: '' },
|
||||
info_jateamento: { e: false, c: false, na: false, info: '' },
|
||||
info_pintura_base: { e: false, c: false, na: false, info: '' },
|
||||
info_pintura_inter: { e: false, c: false, na: false, info: '' },
|
||||
info_pintura_acabamento: { e: false, c: false, na: false, info: '' },
|
||||
info_galvanizacao: { e: false, c: false, na: false, info: '' },
|
||||
info_embalagem: { e: false, c: false, na: false, info: '' },
|
||||
info_transporte: { e: false, c: false, na: false, info: '' },
|
||||
info_inspecao: { e: false, c: false, na: false, info: '' },
|
||||
info_ensaios_lab: { e: false, c: false, na: false, info: '' },
|
||||
info_databook: { e: false, c: false, na: false, info: '' },
|
||||
info_pre_montagem: { e: false, c: false, na: false, info: '' },
|
||||
info_placa_engenetal: { e: false, c: false, na: false, info: '' },
|
||||
info_parafusos: { e: false, c: false, na: false, info: '' },
|
||||
info_chumbadores: { e: false, c: false, na: false, info: '' },
|
||||
info_stud_bolt: { e: false, c: false, na: false, info: '' },
|
||||
info_fornec_telhas: { e: false, c: false, na: false, info: '' },
|
||||
info_montagem_telhas: { e: false, c: false, na: false, info: '' },
|
||||
info_forn_calhas: { e: false, c: false, na: false, info: '' },
|
||||
info_mont_calhas: { e: false, c: false, na: false, info: '' },
|
||||
info_steel_deck: { e: false, c: false, na: false, info: '' },
|
||||
info_fornec_wall: { e: false, c: false, na: false, info: '' },
|
||||
info_mont_wall: { e: false, c: false, na: false, info: '' },
|
||||
info_outros_materiais: { e: false, c: false, na: false, info: '' },
|
||||
|
||||
// Validação
|
||||
necessita_validacao_pos_detalh: false,
|
||||
|
||||
// Grades
|
||||
grades_modelo: '',
|
||||
grades_padrao_comercial: false,
|
||||
grades_padrao_sa2: false,
|
||||
grades_padrao_sa2_meio: false,
|
||||
grades_padrao_sa3: false,
|
||||
|
||||
// Requisitos
|
||||
req_ambientais_existem: false,
|
||||
req_ambientais_quais: '',
|
||||
req_saude_seguranca_existem: false,
|
||||
req_saude_seguranca_quais: '',
|
||||
|
||||
// Alterações
|
||||
alteracao_descritivo: '',
|
||||
alteracao_motivo: '',
|
||||
alteracao_impacto: '',
|
||||
alteracao_custo: 0,
|
||||
alteracao_cronograma: '',
|
||||
alteracao_pecas_prontas: '',
|
||||
alteracao_detalh_projeto: '',
|
||||
|
||||
// Cronograma
|
||||
cronograma_semanas: {},
|
||||
|
||||
// Vistos
|
||||
visto_gestor: '',
|
||||
visto_pcp: '',
|
||||
visto_eng: '',
|
||||
visto_fab: '',
|
||||
visto_exp: '',
|
||||
visto_qual: '',
|
||||
visto_colunas: { '1': false, '2': false, '3': false, '4': false },
|
||||
};
|
||||
|
||||
const CadastroOF = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const ofId = searchParams.get('id');
|
||||
const isEditing = Boolean(ofId);
|
||||
const { themeConfig } = useThemeConfig();
|
||||
const { buscarFichaTecnica, salvarFichaTecnica, loading: fichaTecnicaLoading } = useFichaTecnica();
|
||||
const { users } = useUserManagement();
|
||||
|
||||
const [formData, setFormData] = useState<FormData>(initialFormData);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
const [searchOfNumber, setSearchOfNumber] = useState('');
|
||||
|
||||
// Apply theme config on component mount
|
||||
useEffect(() => {
|
||||
if (themeConfig) {
|
||||
const root = document.documentElement;
|
||||
const isDark = root.classList.contains('dark');
|
||||
const theme = isDark ? themeConfig.dark_theme : themeConfig.light_theme;
|
||||
|
||||
Object.entries(theme).forEach(([key, value]) => {
|
||||
root.style.setProperty(`--${key}`, value);
|
||||
});
|
||||
}
|
||||
}, [themeConfig]);
|
||||
|
||||
const handleLoadOF = async () => {
|
||||
if (!searchOfNumber.trim()) {
|
||||
toast.error('Digite o número da OF para carregar');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await buscarFichaTecnica(searchOfNumber.trim());
|
||||
if (data) {
|
||||
const parseJsonField = (field: any) => {
|
||||
if (typeof field === 'string') {
|
||||
try {
|
||||
return JSON.parse(field);
|
||||
} catch {
|
||||
return { e: false, c: false, na: false, info: '' };
|
||||
}
|
||||
}
|
||||
return field || { e: false, c: false, na: false, info: '' };
|
||||
};
|
||||
|
||||
setFormData({
|
||||
...initialFormData,
|
||||
...data,
|
||||
data_inicio: data.data_inicio || '',
|
||||
data_termino_prev: data.data_termino_prev || '',
|
||||
status: 'aberta',
|
||||
prioridade: 'media',
|
||||
quantidade: data.quantidade || 0,
|
||||
info_calculo_estrutural: parseJsonField(data.info_calculo_estrutural),
|
||||
info_projeto_basico: parseJsonField(data.info_projeto_basico),
|
||||
info_detalhamento: parseJsonField(data.info_detalhamento),
|
||||
info_materia_prima: parseJsonField(data.info_materia_prima),
|
||||
info_fabricacao: parseJsonField(data.info_fabricacao),
|
||||
info_grades_piso: parseJsonField(data.info_grades_piso),
|
||||
info_jateamento: parseJsonField(data.info_jateamento),
|
||||
info_pintura_base: parseJsonField(data.info_pintura_base),
|
||||
info_pintura_inter: parseJsonField(data.info_pintura_inter),
|
||||
info_pintura_acabamento: parseJsonField(data.info_pintura_acabamento),
|
||||
info_galvanizacao: parseJsonField(data.info_galvanizacao),
|
||||
info_embalagem: parseJsonField(data.info_embalagem),
|
||||
info_transporte: parseJsonField(data.info_transporte),
|
||||
info_inspecao: parseJsonField(data.info_inspecao),
|
||||
info_ensaios_lab: parseJsonField(data.info_ensaios_lab),
|
||||
info_databook: parseJsonField(data.info_databook),
|
||||
info_pre_montagem: parseJsonField(data.info_pre_montagem),
|
||||
info_placa_engenetal: parseJsonField(data.info_placa_engenetal),
|
||||
info_parafusos: parseJsonField(data.info_parafusos),
|
||||
info_chumbadores: parseJsonField(data.info_chumbadores),
|
||||
info_stud_bolt: parseJsonField(data.info_stud_bolt),
|
||||
info_fornec_telhas: parseJsonField(data.info_fornec_telhas),
|
||||
info_montagem_telhas: parseJsonField(data.info_montagem_telhas),
|
||||
info_forn_calhas: parseJsonField(data.info_forn_calhas),
|
||||
info_mont_calhas: parseJsonField(data.info_mont_calhas),
|
||||
info_steel_deck: parseJsonField(data.info_steel_deck),
|
||||
info_fornec_wall: parseJsonField(data.info_fornec_wall),
|
||||
info_mont_wall: parseJsonField(data.info_mont_wall),
|
||||
info_outros_materiais: parseJsonField(data.info_outros_materiais),
|
||||
});
|
||||
toast.success('OF carregada com sucesso!');
|
||||
} else {
|
||||
toast.error('OF não encontrada');
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Erro ao carregar OF');
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (field: keyof FormData, value: any) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleTechnicalInfoChange = (field: keyof FormData, key: keyof InfoCalculoEstrutural, value: any) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[field]: {
|
||||
...(prev[field] as InfoCalculoEstrutural),
|
||||
[key]: value,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!formData.of_number) {
|
||||
toast.error('Número da OF é obrigatório');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSaving(true);
|
||||
|
||||
try {
|
||||
const success = await salvarFichaTecnica(formData);
|
||||
|
||||
if (success && !isEditing) {
|
||||
setFormData(initialFormData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao salvar:', error);
|
||||
toast.error('Erro ao salvar a OF');
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const renderTechnicalInfoField = (
|
||||
label: string,
|
||||
fieldName: keyof FormData,
|
||||
info: InfoCalculoEstrutural
|
||||
) => (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-sm font-medium text-muted-foreground">{label}</Label>
|
||||
<div className="flex gap-2 mb-2">
|
||||
<div className="flex items-center space-x-1">
|
||||
<Checkbox
|
||||
checked={info.e}
|
||||
onCheckedChange={(checked) =>
|
||||
handleTechnicalInfoChange(fieldName, 'e', checked as boolean)
|
||||
}
|
||||
/>
|
||||
<Label className="text-xs">E</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1">
|
||||
<Checkbox
|
||||
checked={info.c}
|
||||
onCheckedChange={(checked) =>
|
||||
handleTechnicalInfoChange(fieldName, 'c', checked as boolean)
|
||||
}
|
||||
/>
|
||||
<Label className="text-xs">C</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1">
|
||||
<Checkbox
|
||||
checked={info.na}
|
||||
onCheckedChange={(checked) =>
|
||||
handleTechnicalInfoChange(fieldName, 'na', checked as boolean)
|
||||
}
|
||||
/>
|
||||
<Label className="text-xs">N/A</Label>
|
||||
</div>
|
||||
</div>
|
||||
<Textarea
|
||||
value={info.info}
|
||||
onChange={(e) =>
|
||||
handleTechnicalInfoChange(fieldName, 'info', e.target.value)
|
||||
}
|
||||
placeholder="Informações adicionais"
|
||||
className="min-h-[60px] text-xs bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4 space-y-4 bg-background text-foreground">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h1 className="text-2xl font-bold text-primary flex items-center gap-2">
|
||||
<FileText className="w-6 h-6" />
|
||||
Ficha Técnica da OF
|
||||
</h1>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={isSaving}
|
||||
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||
>
|
||||
<Save className="w-4 h-4 mr-2" />
|
||||
{isSaving ? 'Salvando...' : 'Salvar'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-border"
|
||||
onClick={() => setShowPreview(true)}
|
||||
disabled={!formData.of_number}
|
||||
>
|
||||
<Eye className="w-4 h-4 mr-2" />
|
||||
Preview
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Seção para Carregar OF */}
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg flex items-center gap-2 text-card-foreground">
|
||||
<Search className="w-5 h-5" />
|
||||
Carregar OF Existente
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
placeholder="Digite o número da OF"
|
||||
value={searchOfNumber}
|
||||
onChange={(e) => setSearchOfNumber(e.target.value)}
|
||||
className="bg-input border-border text-foreground"
|
||||
/>
|
||||
<Button
|
||||
onClick={handleLoadOF}
|
||||
disabled={fichaTecnicaLoading}
|
||||
variant="outline"
|
||||
className="border-border"
|
||||
>
|
||||
<Search className="w-4 h-4 mr-2" />
|
||||
{fichaTecnicaLoading ? 'Carregando...' : 'Carregar OF'}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Informações Básicas */}
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg flex items-center gap-2 text-card-foreground">
|
||||
<FileText className="w-5 h-5" />
|
||||
Informações Básicas
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<div>
|
||||
<Label htmlFor="of_number" className="text-sm font-medium text-muted-foreground">Número OF</Label>
|
||||
<Input
|
||||
id="of_number"
|
||||
value={formData.of_number}
|
||||
onChange={(e) => handleChange('of_number', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="gestor" className="text-sm font-medium text-muted-foreground">Gestor da OF</Label>
|
||||
<Select value={formData.gestor || ''} onValueChange={(value) => handleChange('gestor', value)}>
|
||||
<SelectTrigger className="mt-1 bg-input border-border text-foreground">
|
||||
<SelectValue placeholder="Selecione o gestor" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-popover border-border">
|
||||
{users.filter(user => user.status === 'active').map((user) => (
|
||||
<SelectItem key={user.id} value={user.full_name || user.email || ''}>
|
||||
{user.full_name || user.email}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="projetista" className="text-sm font-medium text-muted-foreground">Projetista da OF</Label>
|
||||
<Select value={formData.projetista || ''} onValueChange={(value) => handleChange('projetista', value)}>
|
||||
<SelectTrigger className="mt-1 bg-input border-border text-foreground">
|
||||
<SelectValue placeholder="Selecione o projetista" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-popover border-border">
|
||||
{users.filter(user => user.status === 'active').map((user) => (
|
||||
<SelectItem key={user.id} value={user.full_name || user.email || ''}>
|
||||
{user.full_name || user.email}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="revisao" className="text-sm font-medium text-muted-foreground">Revisão</Label>
|
||||
<Input
|
||||
id="revisao"
|
||||
value={formData.revisao || '0'}
|
||||
onChange={(e) => handleChange('revisao', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="data_inicio" className="text-sm font-medium text-muted-foreground">Data Início</Label>
|
||||
<Input
|
||||
id="data_inicio"
|
||||
type="date"
|
||||
value={formData.data_inicio}
|
||||
onChange={(e) => handleChange('data_inicio', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="data_termino_prev" className="text-sm font-medium text-muted-foreground">Data Prazo</Label>
|
||||
<Input
|
||||
id="data_termino_prev"
|
||||
type="date"
|
||||
value={formData.data_termino_prev}
|
||||
onChange={(e) => handleChange('data_termino_prev', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="quantidade" className="text-sm font-medium text-muted-foreground">Quantidade (t)</Label>
|
||||
<Input
|
||||
id="quantidade"
|
||||
type="number"
|
||||
value={formData.quantidade || ''}
|
||||
onChange={(e) => handleChange('quantidade', parseFloat(e.target.value) || 0)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="descricao_resumida" className="text-sm font-medium text-muted-foreground">Descrição Resumida</Label>
|
||||
<Input
|
||||
id="descricao_resumida"
|
||||
value={formData.descricao_resumida || ''}
|
||||
onChange={(e) => handleChange('descricao_resumida', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Status e Prioridade */}
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg flex items-center gap-2 text-card-foreground">
|
||||
<Settings2 className="w-5 h-5" />
|
||||
Status
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<div>
|
||||
<Label className="text-sm font-medium text-muted-foreground">Status Atual</Label>
|
||||
<Select value={formData.status} onValueChange={(value) => handleChange('status', value)}>
|
||||
<SelectTrigger className="mt-1 bg-input border-border text-foreground">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-popover border-border">
|
||||
<SelectItem value="aberta">Aberta</SelectItem>
|
||||
<SelectItem value="em_andamento">Em Andamento</SelectItem>
|
||||
<SelectItem value="concluida">Concluída</SelectItem>
|
||||
<SelectItem value="arquivada">Arquivada</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-sm font-medium text-muted-foreground">Prioridade</Label>
|
||||
<Select value={formData.prioridade} onValueChange={(value) => handleChange('prioridade', value)}>
|
||||
<SelectTrigger className="mt-1 bg-input border-border text-foreground">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-popover border-border">
|
||||
<SelectItem value="baixa">Baixa</SelectItem>
|
||||
<SelectItem value="media">Média</SelectItem>
|
||||
<SelectItem value="alta">Alta</SelectItem>
|
||||
<SelectItem value="urgente">Urgente</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Informações do Cliente */}
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg flex items-center gap-2 text-card-foreground">
|
||||
<User className="w-5 h-5" />
|
||||
Informações do Cliente
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="cliente" className="text-sm font-medium text-muted-foreground">Cliente</Label>
|
||||
<Input
|
||||
id="cliente"
|
||||
value={formData.cliente || ''}
|
||||
onChange={(e) => handleChange('cliente', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="cnpj" className="text-sm font-medium text-muted-foreground">CNPJ</Label>
|
||||
<Input
|
||||
id="cnpj"
|
||||
value={formData.cnpj || ''}
|
||||
onChange={(e) => handleChange('cnpj', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="ie" className="text-sm font-medium text-muted-foreground">IE</Label>
|
||||
<Input
|
||||
id="ie"
|
||||
value={formData.ie || ''}
|
||||
onChange={(e) => handleChange('ie', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="endereco" className="text-sm font-medium text-muted-foreground">Endereço</Label>
|
||||
<Input
|
||||
id="endereco"
|
||||
value={formData.endereco || ''}
|
||||
onChange={(e) => handleChange('endereco', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="cidade" className="text-sm font-medium text-muted-foreground">Cidade</Label>
|
||||
<Input
|
||||
id="cidade"
|
||||
value={formData.cidade || ''}
|
||||
onChange={(e) => handleChange('cidade', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="estado" className="text-sm font-medium text-muted-foreground">Estado</Label>
|
||||
<Input
|
||||
id="estado"
|
||||
value={formData.estado || ''}
|
||||
onChange={(e) => handleChange('estado', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Informações da Obra */}
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg flex items-center gap-2 text-card-foreground">
|
||||
<Building2 className="w-5 h-5" />
|
||||
Informações da Obra
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<div>
|
||||
<Label htmlFor="eng_responsavel" className="text-sm font-medium text-muted-foreground">Eng. Responsável</Label>
|
||||
<Input
|
||||
id="eng_responsavel"
|
||||
value={formData.eng_responsavel || ''}
|
||||
onChange={(e) => handleChange('eng_responsavel', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="telefone_obra" className="text-sm font-medium text-muted-foreground">Telefone</Label>
|
||||
<Input
|
||||
id="telefone_obra"
|
||||
value={formData.telefone_obra || ''}
|
||||
onChange={(e) => handleChange('telefone_obra', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="email_obra_responsavel" className="text-sm font-medium text-muted-foreground">E-mail</Label>
|
||||
<Input
|
||||
id="email_obra_responsavel"
|
||||
type="email"
|
||||
value={formData.email_obra_responsavel || ''}
|
||||
onChange={(e) => handleChange('email_obra_responsavel', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="endereco_obra" className="text-sm font-medium text-muted-foreground">Endereço da Obra</Label>
|
||||
<Input
|
||||
id="endereco_obra"
|
||||
value={formData.endereco_obra || ''}
|
||||
onChange={(e) => handleChange('endereco_obra', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="cep_obra" className="text-sm font-medium text-muted-foreground">CEP</Label>
|
||||
<Input
|
||||
id="cep_obra"
|
||||
value={formData.cep_obra || ''}
|
||||
onChange={(e) => handleChange('cep_obra', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="bairro_obra" className="text-sm font-medium text-muted-foreground">Bairro</Label>
|
||||
<Input
|
||||
id="bairro_obra"
|
||||
value={formData.bairro_obra || ''}
|
||||
onChange={(e) => handleChange('bairro_obra', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="cidade_obra" className="text-sm font-medium text-muted-foreground">Cidade</Label>
|
||||
<Input
|
||||
id="cidade_obra"
|
||||
value={formData.cidade_obra || ''}
|
||||
onChange={(e) => handleChange('cidade_obra', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="estado_obra" className="text-sm font-medium text-muted-foreground">Estado (UF)</Label>
|
||||
<Input
|
||||
id="estado_obra"
|
||||
value={formData.estado_obra || ''}
|
||||
onChange={(e) => handleChange('estado_obra', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-4">
|
||||
<Label htmlFor="observacoes_obra" className="text-sm font-medium text-muted-foreground">Observações Gerais</Label>
|
||||
<Textarea
|
||||
id="observacoes_obra"
|
||||
value={formData.observacoes_obra || ''}
|
||||
onChange={(e) => handleChange('observacoes_obra', e.target.value)}
|
||||
className="mt-1 bg-input border-border text-foreground"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Tipos de Projetos */}
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg flex items-center gap-2 text-card-foreground">
|
||||
<Settings2 className="w-5 h-5" />
|
||||
Tipos de Projetos
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{[
|
||||
{ key: 'tipo_estrutural', label: 'Estrutural' },
|
||||
{ key: 'tipo_residencial', label: 'Residencial' },
|
||||
{ key: 'tipo_espacial', label: 'Espacial' },
|
||||
{ key: 'tipo_comercial', label: 'Comercial' },
|
||||
{ key: 'tipo_grades', label: 'Grades' },
|
||||
{ key: 'tipo_industrial', label: 'Industrial' },
|
||||
{ key: 'tipo_cobertura', label: 'Cobertura' },
|
||||
{ key: 'tipo_com_montagem', label: 'Com Montagem' },
|
||||
].map(({ key, label }) => (
|
||||
<div key={key} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={formData[key as keyof FormData] as boolean || false}
|
||||
onCheckedChange={(checked) => handleChange(key as keyof FormData, checked)}
|
||||
/>
|
||||
<Label className="text-sm">{label}</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Documentos Fornecidos */}
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg flex items-center gap-2 text-card-foreground">
|
||||
<FileText className="w-5 h-5" />
|
||||
Documentos Fornecidos
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{[
|
||||
{ key: 'doc_calculo', label: 'Cálculo' },
|
||||
{ key: 'doc_projeto', label: 'Projeto' },
|
||||
{ key: 'doc_detalhamento', label: 'Detalhamento' },
|
||||
{ key: 'doc_cronograma', label: 'Cronograma' },
|
||||
{ key: 'doc_normas', label: 'Normas' },
|
||||
{ key: 'doc_especif_tecnicas', label: 'Especif. Técnicas' },
|
||||
{ key: 'doc_catalogo', label: 'Catálogo' },
|
||||
{ key: 'doc_fotos', label: 'Fotos' },
|
||||
].map(({ key, label }) => (
|
||||
<div key={key} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={formData[key as keyof FormData] as boolean || false}
|
||||
onCheckedChange={(checked) => handleChange(key as keyof FormData, checked)}
|
||||
/>
|
||||
<Label className="text-sm">{label}</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Alterações - Como botão popup */}
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg flex items-center gap-2 text-card-foreground">
|
||||
<Settings2 className="w-5 h-5" />
|
||||
Alterações da OF
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<AlteracoesPopup formData={formData} setFormData={setFormData} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Informações Técnicas Completas */}
|
||||
<Card className="bg-card border-border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg flex items-center gap-2 text-card-foreground">
|
||||
<Settings2 className="w-5 h-5" />
|
||||
Informações Técnicas
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{renderTechnicalInfoField('Cálculo Estrutural', 'info_calculo_estrutural', formData.info_calculo_estrutural)}
|
||||
{renderTechnicalInfoField('Projeto Básico', 'info_projeto_basico', formData.info_projeto_basico)}
|
||||
{renderTechnicalInfoField('Detalhamento', 'info_detalhamento', formData.info_detalhamento)}
|
||||
{renderTechnicalInfoField('Matéria Prima', 'info_materia_prima', formData.info_materia_prima)}
|
||||
{renderTechnicalInfoField('Fabricação', 'info_fabricacao', formData.info_fabricacao)}
|
||||
{renderTechnicalInfoField('Grades de Piso', 'info_grades_piso', formData.info_grades_piso)}
|
||||
{renderTechnicalInfoField('Jateamento', 'info_jateamento', formData.info_jateamento)}
|
||||
{renderTechnicalInfoField('Pintura Base', 'info_pintura_base', formData.info_pintura_base)}
|
||||
{renderTechnicalInfoField('Pintura Intermediária', 'info_pintura_inter', formData.info_pintura_inter)}
|
||||
{renderTechnicalInfoField('Pintura Acabamento', 'info_pintura_acabamento', formData.info_pintura_acabamento)}
|
||||
{renderTechnicalInfoField('Galvanização', 'info_galvanizacao', formData.info_galvanizacao)}
|
||||
{renderTechnicalInfoField('Embalagem', 'info_embalagem', formData.info_embalagem)}
|
||||
{renderTechnicalInfoField('Transporte', 'info_transporte', formData.info_transporte)}
|
||||
{renderTechnicalInfoField('Inspeção', 'info_inspecao', formData.info_inspecao)}
|
||||
{renderTechnicalInfoField('Ensaios de Laboratório', 'info_ensaios_lab', formData.info_ensaios_lab)}
|
||||
{renderTechnicalInfoField('Databook', 'info_databook', formData.info_databook)}
|
||||
{renderTechnicalInfoField('Pré-montagem', 'info_pre_montagem', formData.info_pre_montagem)}
|
||||
{renderTechnicalInfoField('Placa Engenetal', 'info_placa_engenetal', formData.info_placa_engenetal)}
|
||||
{renderTechnicalInfoField('Parafusos', 'info_parafusos', formData.info_parafusos)}
|
||||
{renderTechnicalInfoField('Chumbadores', 'info_chumbadores', formData.info_chumbadores)}
|
||||
{renderTechnicalInfoField('Stud Bolt', 'info_stud_bolt', formData.info_stud_bolt)}
|
||||
{renderTechnicalInfoField('Fornecimento Telhas', 'info_fornec_telhas', formData.info_fornec_telhas)}
|
||||
{renderTechnicalInfoField('Montagem Telhas', 'info_montagem_telhas', formData.info_montagem_telhas)}
|
||||
{renderTechnicalInfoField('Fornecimento Calhas', 'info_forn_calhas', formData.info_forn_calhas)}
|
||||
{renderTechnicalInfoField('Montagem Calhas', 'info_mont_calhas', formData.info_mont_calhas)}
|
||||
{renderTechnicalInfoField('Steel Deck', 'info_steel_deck', formData.info_steel_deck)}
|
||||
{renderTechnicalInfoField('Fornecimento Wall', 'info_fornec_wall', formData.info_fornec_wall)}
|
||||
{renderTechnicalInfoField('Montagem Wall', 'info_mont_wall', formData.info_mont_wall)}
|
||||
{renderTechnicalInfoField('Outros Materiais', 'info_outros_materiais', formData.info_outros_materiais)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Preview Modal */}
|
||||
<FichaTecnicaPreview
|
||||
isOpen={showPreview}
|
||||
onClose={() => setShowPreview(false)}
|
||||
data={formData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CadastroOF;
|
||||
Reference in New Issue
Block a user