solar-property-wizard / components /solar-integrator-form.js
fernando-bold's picture
'use client'
637ffd6 verified
class CustomSolarIntegratorForm extends HTMLElement {
constructor() {
super();
this.currentStep = 1;
this.totalSteps = 3;
this.formData = {
company_id: '',
integrator_type: 'RESIDENTIAL_INSTALLER',
certification_level: 'LEVEL_1',
license_number: '',
license_expiry_date: '',
years_of_experience: 0,
completed_projects: 0,
warranty_offered: '',
insurance_coverage: '',
specialization: [],
coverage_area: [],
certifications: [],
equipment_brands: [],
minimum_project_size: '',
maximum_project_size: '',
installation_time_frame: '',
warranty_period: 10,
maintenance_services: false,
emergency_support: false,
design_services: false,
project_management: false,
};
}
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.render();
}
handleInputChange(e) {
const { name, value, type, checked } = e.target;
if (type === 'checkbox') {
this.formData[name] = checked;
} else {
this.formData[name] = value;
}
this.render();
}
handleArrayChange(name, value) {
const currentArray = this.formData[name];
const newArray = currentArray.includes(value)
? currentArray.filter(item => item !== value)
: [...currentArray, value];
this.formData[name] = newArray;
this.render();
}
nextStep() {
if (this.currentStep < this.totalSteps) {
this.currentStep++;
this.render();
}
}
prevStep() {
if (this.currentStep > 1) {
this.currentStep--;
this.render();
}
}
renderStep1() {
return `
<div class="space-y-4">
<h3 class="text-lg font-semibold">Informações Básicas</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="text-sm font-medium text-gray-700">Tipo de Integrador *</label>
<select
name="integrator_type"
value="${this.formData.integrator_type}"
onchange="this.getRootNode().host.handleInputChange(event)"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
>
<option value="RESIDENTIAL_INSTALLER">Instalador Residencial</option>
<option value="COMMERCIAL_INSTALLER">Instalador Comercial</option>
<option value="UTILITY_INSTALLER">Instalador de Utilidade</option>
<option value="DESIGNER">Designer</option>
<option value="DISTRIBUTOR">Distribuidor</option>
</select>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Nível de Certificação</label>
<select
name="certification_level"
value="${this.formData.certification_level}"
onchange="this.getRootNode().host.handleInputChange(event)"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
>
<option value="LEVEL_1">Nível 1</option>
<option value="LEVEL_2">Nível 2</option>
<option value="LEVEL_3">Nível 3</option>
<option value="MASTER">Master</option>
</select>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="text-sm font-medium text-gray-700">Número da Licença</label>
<input
type="text"
name="license_number"
value="${this.formData.license_number}"
onchange="this.getRootNode().host.handleInputChange(event)"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Data de Expiração da Licença</label>
<input
type="date"
name="license_expiry_date"
value="${this.formData.license_expiry_date}"
onchange="this.getRootNode().host.handleInputChange(event)"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label class="text-sm font-medium text-gray-700">Anos de Experiência</label>
<input
type="number"
name="years_of_experience"
value="${this.formData.years_of_experience}"
onchange="this.getRootNode().host.handleInputChange(event)"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Projetos Completados</label>
<input
type="number"
name="completed_projects"
value="${this.formData.completed_projects}"
onchange="this.getRootNode().host.handleInputChange(event)"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Período de Garantia (anos)</label>
<input
type="number"
name="warranty_period"
value="${this.formData.warranty_period}"
onchange="this.getRootNode().host.handleInputChange(event)"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
</div>
`;
}
renderStep2() {
return `
<div class="space-y-4">
<h3 class="text-lg font-semibold">Serviços e Capabilities</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="text-sm font-medium text-gray-700">Áreas de Especialização</label>
<div class="mt-1 space-y-2">
${['roof_mount', 'ground_mount', 'tracking_systems', 'commercial', 'residential'].map(spec => `
<div key="${spec}" class="flex items-center">
<input
id="spec-${spec}"
name="specialization"
type="checkbox"
${this.formData.specialization.includes(spec) ? 'checked' : ''}
onchange="this.getRootNode().host.handleArrayChange('specialization', '${spec}')"
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
<label for="spec-${spec}" class="ml-2 block text-sm text-gray-900 capitalize">
${spec.replace('_', ' ')}
</label>
</div>
`).join('')}
</div>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Marcas de Equipamentos</label>
<textarea
name="equipment_brands"
value="${this.formData.equipment_brands.join(', ')}"
onchange="this.getRootNode().host.handleInputChange(event)"
placeholder="Digite marcas separadas por vírgula"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
></textarea>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="text-sm font-medium text-gray-700">Tamanho Mínimo do Projeto</label>
<input
type="text"
name="minimum_project_size"
value="${this.formData.minimum_project_size}"
onchange="this.getRootNode().host.handleInputChange(event)"
placeholder="ex: 5kW"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Tamanho Máximo do Projeto</label>
<input
type="text"
name="maximum_project_size"
value="${this.formData.maximum_project_size}"
onchange="this.getRootNode().host.handleInputChange(event)"
placeholder="ex: 1MW"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Prazo de Instalação</label>
<input
type="text"
name="installation_time_frame"
value="${this.formData.installation_time_frame}"
onchange="this.getRootNode().host.handleInputChange(event)"
placeholder="ex: 4-6 semanas"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="flex items-center">
<input
id="maintenance_services"
name="maintenance_services"
type="checkbox"
${this.formData.maintenance_services ? 'checked' : ''}
onchange="this.getRootNode().host.handleInputChange(event)"
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
<label for="maintenance_services" class="ml-2 block text-sm text-gray-900">
Serviços de Manutenção
</label>
</div>
<div class="flex items-center">
<input
id="emergency_support"
name="emergency_support"
type="checkbox"
${this.formData.emergency_support ? 'checked' : ''}
onchange="this.getRootNode().host.handleInputChange(event)"
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
<label for="emergency_support" class="ml-2 block text-sm text-gray-900">
Suporte de Emergência
</label>
</div>
<div class="flex items-center">
<input
id="design_services"
name="design_services"
type="checkbox"
${this.formData.design_services ? 'checked' : ''}
onchange="this.getRootNode().host.handleInputChange(event)"
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
<label for="design_services" class="ml-2 block text-sm text-gray-900">
Serviços de Design
</label>
</div>
<div class="flex items-center">
<input
id="project_management"
name="project_management"
type="checkbox"
${this.formData.project_management ? 'checked' : ''}
onchange="this.getRootNode().host.handleInputChange(event)"
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
<label for="project_management" class="ml-2 block text-sm text-gray-900">
Gerenciamento de Projetos
</label>
</div>
</div>
</div>
`;
}
renderStep3() {
return `
<div class="space-y-4">
<h3 class="text-lg font-semibold">Cobertura e Certificações</h3>
<div>
<label class="text-sm font-medium text-gray-700">Áreas de Cobertura</label>
<textarea
name="coverage_area"
value="${this.formData.coverage_area.join(', ')}"
onchange="this.getRootNode().host.handleInputChange(event)"
placeholder="Digite áreas separadas por vírgula, ex: São Paulo, Rio de Janeiro"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
></textarea>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Certificações</label>
<textarea
name="certifications"
value="${this.formData.certifications.join(', ')}"
onchange="this.getRootNode().host.handleInputChange(event)"
placeholder="Digite certificações separadas por vírgula, ex: NABCEP, UL, CSA"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
></textarea>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Cobertura de Seguro</label>
<input
type="text"
name="insurance_coverage"
value="${this.formData.insurance_coverage}"
onchange="this.getRootNode().host.handleInputChange(event)"
placeholder="ex: 500k de responsabilidade civil"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
<div>
<label class="text-sm font-medium text-gray-700">Garantia Oferecida</label>
<input
type="text"
name="warranty_offered"
value="${this.formData.warranty_offered}"
onchange="this.getRootNode().host.handleInputChange(event)"
placeholder="ex: 20 anos"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
`;
}
render() {
this.shadowRoot.innerHTML = `
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
:host {
display: block;
font-family: 'Inter', sans-serif;
}
.space-y-4 > * + * {
margin-top: 1rem;
}
.space-y-6 > * + * {
margin-top: 1.5rem;
}
.grid {
display: grid;
}
.gap-4 {
gap: 1rem;
}
.rounded-md {
border-radius: 0.375rem;
}
.border-gray-300 {
border-color: #d1d5db;
}
.focus\\:border-indigo-500:focus {
border-color: #6366f1;
}
.focus\\:ring-indigo-500:focus {
--tw-ring-color: #6366f1;
}
.shadow-sm {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
}
.text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
}
.font-semibold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.text-gray-700 {
color: #374151;
}
.text-gray-900 {
color: #111827;
}
.text-gray-500 {
color: #6b7280;
}
.text-indigo-600 {
color: #4f46e5;
}
.bg-gray-200 {
background-color: #e5e7eb;
}
.bg-blue-600 {
background-color: #2563eb;
}
.h-2\\.5 {
height: 0.625rem;
}
.rounded-full {
border-radius: 9999px;
}
.transition-all {
transition-property: all;
}
.duration-300 {
transition-duration: 300ms;
}
.w-full {
width: 100%;
}
.block {
display: block;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.mt-1 {
margin-top: 0.25rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mb-6 {
margin-bottom: 1.5rem;
}
.ml-2 {
margin-left: 0.5rem;
}
.pt-4 {
padding-top: 1rem;
}
.p-6 {
padding: 1.5rem;
}
.p-8 {
padding: 2rem;
}
.space-x-3 > * + * {
margin-left: 0.75rem;
}
@media (min-width: 768px) {
.md\\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.md\\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.md\\:grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
</style>
<form class="space-y-6">
<div class="mb-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-bold">Registro de Integrador Solar</h2>
<div class="text-sm text-gray-500">
Passo ${this.currentStep} de ${this.totalSteps}
</div>
</div>
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div
class="bg-blue-600 h-2.5 rounded-full transition-all duration-300"
style="width: ${(this.currentStep / this.totalSteps) * 100}%"
></div>
</div>
</div>
${this.currentStep === 1 ? this.renderStep1() : ''}
${this.currentStep === 2 ? this.renderStep2() : ''}
${this.currentStep === 3 ? this.renderStep3() : ''}
<div class="flex justify-between pt-4">
<div>
${this.currentStep > 1 ? `
<button
type="button"
onclick="this.getRootNode().host.prevStep()"
class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Voltar
</button>
` : ''}
</div>
<div class="flex space-x-3">
${this.currentStep < this.totalSteps ? `
<button
type="button"
onclick="this.getRootNode().host.nextStep()"
class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Próximo
</button>
` : `
<button
type="button"
onclick="this.getRootNode().host.dispatchEvent(new CustomEvent('cancel'))"
class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Cancelar
</button>
<button
type="button"
onclick="this.getRootNode().host.dispatchEvent(new CustomEvent('submit', { detail: this.getRootNode().host.formData }))"
class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Cadastrar
</button>
`}
</div>
</div>
</form>
`;
}
}
customElements.define('custom-solar-integrator-form', CustomSolarIntegratorForm);