|
|
@@ -21,9 +21,12 @@
|
|
|
|
|
|
const STATE_FIELD = 'cpr_deliveryPlace_state_acronym';
|
|
|
const CITY_FIELD = 'cpr_deliveryPlace_city_name';
|
|
|
+ const CITY_IBGE_FIELD = 'cpr_deliveryPlace_ibge_code';
|
|
|
const ISSUER_STATE_FIELD = 'cpr_issuers_state_acronym';
|
|
|
const ISSUER_CITY_FIELD = 'cpr_issuers_city_name';
|
|
|
- const CIDADES_ESTADOS_SRC = 'https://cdn.jsdelivr.net/npm/cidades-estados@1.4.1/cidades-estados.js';
|
|
|
+ const BRASIL_API_BASE = 'https://brasilapi.com.br/api';
|
|
|
+ const UF_ENDPOINT = `${BRASIL_API_BASE}/ibge/uf/v1`;
|
|
|
+ const MUNICIPIOS_ENDPOINT = `${BRASIL_API_BASE}/ibge/municipios/v1`;
|
|
|
const isBrowser = typeof window !== 'undefined';
|
|
|
const apiUrl = import.meta.env.VITE_API_URL;
|
|
|
|
|
|
@@ -34,6 +37,8 @@
|
|
|
let cidadesPorEstado = {};
|
|
|
let estadosCarregando = false;
|
|
|
let estadosErro = '';
|
|
|
+ let cidadesCarregando = false;
|
|
|
+ let cidadesErro = '';
|
|
|
let selectedStateValue = '';
|
|
|
let selectedCityValue = '';
|
|
|
let availableCities = [];
|
|
|
@@ -44,6 +49,12 @@
|
|
|
let currentProductValue = '';
|
|
|
let selectedCommodityOption = null;
|
|
|
let selectedCommodityId = '';
|
|
|
+ let harvestOptions = [];
|
|
|
+ let harvestLoading = false;
|
|
|
+ let harvestError = '';
|
|
|
+ let lastSelectedState = '';
|
|
|
+ let pendingIbgeValue = '';
|
|
|
+ let deliveryIbgeValue = '';
|
|
|
|
|
|
const sections = [
|
|
|
{
|
|
|
@@ -64,7 +75,7 @@
|
|
|
{ key: 'cpr_document_deadline_days_number', label: 'Prazo para documentos (dias)' },
|
|
|
{ key: STATE_FIELD, label: 'Estado da entrega' },
|
|
|
{ key: CITY_FIELD, label: 'Cidade da entrega' },
|
|
|
- // { key: 'cpr_deliveryPlace_ibge_code', label: 'IBGE da entrega' }
|
|
|
+ { key: CITY_IBGE_FIELD, label: 'IBGE da entrega' }
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
@@ -75,10 +86,44 @@
|
|
|
{ key: 'cpr_product_name', label: 'Produto' },
|
|
|
{ key: 'cpr_product_harvest', label: 'Safra' },
|
|
|
{ key: 'cpr_product_quantity', label: 'Quantidade' },
|
|
|
- { key: 'cpr_measure_unit_name', label: 'Unidade de medida' },
|
|
|
- { key: 'cpr_packaging_way_name', label: 'Empacotamento' },
|
|
|
- { key: 'cpr_product_status_code', label: 'Status do produto' },
|
|
|
- { key: 'cpr_production_type_code', label: 'Tipo de produção' }
|
|
|
+ {
|
|
|
+ key: 'cpr_measure_unit_name',
|
|
|
+ label: 'Unidade de medida',
|
|
|
+ type: 'select',
|
|
|
+ options: [
|
|
|
+ { label: 'Selecione...', value: '' },
|
|
|
+ { label: 'QUILO', value: 'QUILO' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'cpr_packaging_way_name',
|
|
|
+ label: 'Empacotamento',
|
|
|
+ type: 'select',
|
|
|
+ options: [
|
|
|
+ { label: 'Selecione...', value: '' },
|
|
|
+ { label: 'SACA (60 KG)', value: 'SACA(60 KG)' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'cpr_product_status_code',
|
|
|
+ label: 'Status do produto',
|
|
|
+ type: 'select',
|
|
|
+ options: [
|
|
|
+ { label: 'Selecione...', value: '' },
|
|
|
+ { label: 'A PRODUZIR', value: '0' },
|
|
|
+ { label: 'PRODUZIDO', value: '1' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'cpr_production_type_code',
|
|
|
+ label: 'Tipo de produção',
|
|
|
+ type: 'select',
|
|
|
+ options: [
|
|
|
+ { label: 'Selecione...', value: '' },
|
|
|
+ { label: 'PRÓPRIA', value: '0' },
|
|
|
+ { label: 'TERCEIROS', value: '1' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
],
|
|
|
textarea: { key: 'cpr_product_description', label: 'Descrição do produto' }
|
|
|
},
|
|
|
@@ -96,16 +141,99 @@
|
|
|
// }
|
|
|
];
|
|
|
|
|
|
+ function synchronizeDeliveryIbge(stateValue, cityValue, derivedValue = '') {
|
|
|
+ const ibgeCode =
|
|
|
+ stateValue && cityValue ? findCityIbgeCode(stateValue, cityValue) : derivedValue || '';
|
|
|
+ if (formData?.[CITY_IBGE_FIELD] !== (ibgeCode ?? '')) {
|
|
|
+ onFieldChange(CITY_IBGE_FIELD, ibgeCode ?? '');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function findCityIbgeCode(stateValue, cityName) {
|
|
|
+ if (!stateValue || !cityName) return '';
|
|
|
+ const list = cidadesPorEstado[stateValue] ?? [];
|
|
|
+ const normalized = normalizeCityName(cityName);
|
|
|
+ const match = list.find((item) => item.normalizedName === normalized || item.name === cityName);
|
|
|
+ return match?.ibgeCode ?? '';
|
|
|
+ }
|
|
|
+
|
|
|
+ function normalizeCityName(name) {
|
|
|
+ if (!name) return '';
|
|
|
+ return name
|
|
|
+ .normalize('NFD')
|
|
|
+ .replace(/\p{Diacritic}/gu, '')
|
|
|
+ .trim()
|
|
|
+ .toUpperCase();
|
|
|
+ }
|
|
|
+
|
|
|
+ async function fetchHarvestOptions() {
|
|
|
+ if (!apiUrl) {
|
|
|
+ harvestError = 'URL da API não configurada.';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ harvestLoading = true;
|
|
|
+ harvestError = '';
|
|
|
+ try {
|
|
|
+ const token = resolveAuthToken();
|
|
|
+ if (!token) {
|
|
|
+ throw new Error('Sessão expirada. Faça login novamente.');
|
|
|
+ }
|
|
|
+
|
|
|
+ const res = await fetch(`${apiUrl}/harvest/list`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'content-type': 'application/json',
|
|
|
+ Authorization: `Bearer ${token}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ let payload = null;
|
|
|
+ try {
|
|
|
+ payload = await res.json();
|
|
|
+ } catch (err) {
|
|
|
+ console.error('Falha ao interpretar resposta de /harvest/list:', err);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (res.status === 204) {
|
|
|
+ harvestOptions = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!res.ok || payload?.status !== 'ok') {
|
|
|
+ throw new Error(payload?.msg ?? 'Falha ao carregar safras.');
|
|
|
+ }
|
|
|
+
|
|
|
+ harvestOptions = Array.isArray(payload?.data)
|
|
|
+ ? payload.data
|
|
|
+ .map((item) => ({
|
|
|
+ value: item?.harvest_code != null ? String(item.harvest_code).trim() : '',
|
|
|
+ label: (item?.harvest_name ?? '').trim()
|
|
|
+ }))
|
|
|
+ .filter((item) => item.value && item.label)
|
|
|
+ : [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Erro ao carregar safras (CPR):', error);
|
|
|
+ harvestError = error?.message ?? 'Falha ao carregar safras.';
|
|
|
+ harvestOptions = [];
|
|
|
+ } finally {
|
|
|
+ harvestLoading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
onMount(() => {
|
|
|
if (!isBrowser) return;
|
|
|
- hydrateCidadesEstadosOptions();
|
|
|
- void fetchCommodityOptions();
|
|
|
+ hydrateEstadosOptions();
|
|
|
+ void Promise.all([fetchCommodityOptions(), fetchHarvestOptions()]);
|
|
|
});
|
|
|
|
|
|
$: selectedStateValue = formData?.[STATE_FIELD] ?? '';
|
|
|
$: selectedCityValue = formData?.[CITY_FIELD] ?? '';
|
|
|
+ $: if ((selectedStateValue ?? '') !== lastSelectedState) {
|
|
|
+ lastSelectedState = selectedStateValue ?? '';
|
|
|
+ }
|
|
|
$: availableCities =
|
|
|
- selectedStateValue && cidadesPorEstado[selectedStateValue]
|
|
|
+ selectedStateValue && Array.isArray(cidadesPorEstado[selectedStateValue])
|
|
|
? cidadesPorEstado[selectedStateValue]
|
|
|
: [];
|
|
|
$: currentProductValue = formData?.[PRODUCT_NAME_FIELD] ?? '';
|
|
|
@@ -113,31 +241,86 @@
|
|
|
commodityOptions.find((item) => item.name === currentProductValue) ?? null;
|
|
|
$: selectedCommodityId = selectedCommodityOption?.id ?? '';
|
|
|
$: synchronizeProductCommodityFields(selectedCommodityOption);
|
|
|
-
|
|
|
- async function hydrateCidadesEstadosOptions() {
|
|
|
+ $: if (isBrowser && selectedStateValue) {
|
|
|
+ void loadCitiesForState(selectedStateValue);
|
|
|
+ }
|
|
|
+ $: stateCitiesDependency = cidadesPorEstado[lastSelectedState] ?? [];
|
|
|
+ $: deliveryIbgeValue =
|
|
|
+ formData?.[CITY_IBGE_FIELD] && formData[CITY_IBGE_FIELD] !== ''
|
|
|
+ ? formData[CITY_IBGE_FIELD]
|
|
|
+ : pendingIbgeValue || (lastSelectedState && selectedCityValue && stateCitiesDependency
|
|
|
+ ? findCityIbgeCode(lastSelectedState, selectedCityValue)
|
|
|
+ : '');
|
|
|
+ $: synchronizeDeliveryIbge(lastSelectedState, selectedCityValue, deliveryIbgeValue);
|
|
|
+
|
|
|
+ async function hydrateEstadosOptions() {
|
|
|
+ if (!isBrowser) return;
|
|
|
+ estadosCarregando = true;
|
|
|
+ estadosErro = '';
|
|
|
try {
|
|
|
- estadosCarregando = true;
|
|
|
- const ctor = await loadCidadesEstadosScript();
|
|
|
- const proto = ctor?.prototype;
|
|
|
- if (!proto?.estados || !proto?.cidades) {
|
|
|
- throw new Error('Estrutura inválida da biblioteca cidades-estados.');
|
|
|
+ const res = await fetch(UF_ENDPOINT);
|
|
|
+ if (!res.ok) {
|
|
|
+ throw new Error('Não foi possível carregar estados.');
|
|
|
}
|
|
|
-
|
|
|
+ const data = await res.json();
|
|
|
+ estadosOptions = Array.isArray(data)
|
|
|
+ ? data
|
|
|
+ .map((item) => ({
|
|
|
+ value: (item?.sigla ?? '').trim(),
|
|
|
+ label: (item?.nome ?? '').trim()
|
|
|
+ }))
|
|
|
+ .filter((item) => item.value && item.label)
|
|
|
+ .sort((a, b) => a.label.localeCompare(b.label, 'pt-BR'))
|
|
|
+ : [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Erro ao carregar estados (BrasilAPI):', error);
|
|
|
estadosOptions = [];
|
|
|
- cidadesPorEstado = {};
|
|
|
+ estadosErro = error?.message ?? 'Não foi possível carregar estados.';
|
|
|
+ } finally {
|
|
|
+ estadosCarregando = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- proto.estados.forEach(([value, label], index) => {
|
|
|
- if (!value) return;
|
|
|
- estadosOptions.push({ value, label });
|
|
|
- const cidades = proto.cidades?.[index] ?? [];
|
|
|
- cidadesPorEstado[value] = cidades.filter(Boolean);
|
|
|
- });
|
|
|
- estadosErro = '';
|
|
|
+ async function loadCitiesForState(stateAcronym) {
|
|
|
+ if (!isBrowser || !stateAcronym) return;
|
|
|
+ if (Array.isArray(cidadesPorEstado[stateAcronym]) && cidadesPorEstado[stateAcronym].length) {
|
|
|
+ cidadesErro = '';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ cidadesCarregando = true;
|
|
|
+ cidadesErro = '';
|
|
|
+ try {
|
|
|
+ const res = await fetch(`${MUNICIPIOS_ENDPOINT}/${stateAcronym}`);
|
|
|
+ if (!res.ok) {
|
|
|
+ throw new Error('Não foi possível carregar cidades.');
|
|
|
+ }
|
|
|
+ const data = await res.json();
|
|
|
+ const mapped = Array.isArray(data)
|
|
|
+ ? data
|
|
|
+ .map((item) => {
|
|
|
+ const name = (item?.nome ?? '').trim();
|
|
|
+ const ibgeCode =
|
|
|
+ item?.codigo_ibge != null ? String(item.codigo_ibge).trim() : '';
|
|
|
+ const normalizedName = normalizeCityName(name);
|
|
|
+ return { name, ibgeCode, normalizedName };
|
|
|
+ })
|
|
|
+ .filter((item) => item.name && item.ibgeCode)
|
|
|
+ .sort((a, b) => a.name.localeCompare(b.name, 'pt-BR'))
|
|
|
+ : [];
|
|
|
+ cidadesPorEstado = {
|
|
|
+ ...cidadesPorEstado,
|
|
|
+ [stateAcronym]: mapped
|
|
|
+ };
|
|
|
} catch (error) {
|
|
|
- console.error('Erro ao carregar cidades-estados:', error);
|
|
|
- estadosErro = error?.message ?? 'Não foi possível carregar estados e cidades.';
|
|
|
+ console.error('Erro ao carregar cidades (BrasilAPI):', error);
|
|
|
+ cidadesErro = error?.message ?? 'Não foi possível carregar cidades.';
|
|
|
+ cidadesPorEstado = {
|
|
|
+ ...cidadesPorEstado,
|
|
|
+ [stateAcronym]: []
|
|
|
+ };
|
|
|
} finally {
|
|
|
- estadosCarregando = false;
|
|
|
+ cidadesCarregando = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -204,38 +387,6 @@
|
|
|
return match ? decodeURIComponent(match[1]) : null;
|
|
|
}
|
|
|
|
|
|
- function loadCidadesEstadosScript() {
|
|
|
- if (!isBrowser) {
|
|
|
- return Promise.reject(new Error('Ambiente indisponível para carregar cidades-estados.'));
|
|
|
- }
|
|
|
-
|
|
|
- if (window.__cidadesEstadosPromise) {
|
|
|
- return window.__cidadesEstadosPromise;
|
|
|
- }
|
|
|
-
|
|
|
- window.__cidadesEstadosPromise = new Promise((resolve, reject) => {
|
|
|
- if (window.dgCidadesEstados) {
|
|
|
- resolve(window.dgCidadesEstados);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const script = document.createElement('script');
|
|
|
- script.src = CIDADES_ESTADOS_SRC;
|
|
|
- script.async = true;
|
|
|
- script.onload = () => {
|
|
|
- if (window.dgCidadesEstados) {
|
|
|
- resolve(window.dgCidadesEstados);
|
|
|
- } else {
|
|
|
- reject(new Error('Biblioteca cidades-estados não expôs dgCidadesEstados.'));
|
|
|
- }
|
|
|
- };
|
|
|
- script.onerror = () => reject(new Error('Falha ao carregar biblioteca cidades-estados.'));
|
|
|
- document.head.appendChild(script);
|
|
|
- });
|
|
|
-
|
|
|
- return window.__cidadesEstadosPromise;
|
|
|
- }
|
|
|
-
|
|
|
function getValue(key) {
|
|
|
return formData?.[key] ?? '';
|
|
|
}
|
|
|
@@ -260,13 +411,34 @@
|
|
|
return key === CITY_FIELD;
|
|
|
}
|
|
|
|
|
|
+ function isDeliveryIbgeField(key) {
|
|
|
+ return key === CITY_IBGE_FIELD;
|
|
|
+ }
|
|
|
+
|
|
|
function handleDeliveryStateChange(value) {
|
|
|
onFieldChange(STATE_FIELD, value);
|
|
|
onFieldChange(CITY_FIELD, '');
|
|
|
+ onFieldChange(CITY_IBGE_FIELD, '');
|
|
|
+ pendingIbgeValue = '';
|
|
|
+ lastSelectedState = value ?? '';
|
|
|
+ void loadCitiesForState(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleDeliveryCitySelect(event) {
|
|
|
+ const value = event?.currentTarget?.value ?? '';
|
|
|
+ const selectEl = event?.currentTarget;
|
|
|
+ const selectedOption = selectEl?.selectedOptions?.[0] ??
|
|
|
+ (selectEl && selectEl.options ? selectEl.options[selectEl.selectedIndex ?? -1] : null);
|
|
|
+ const ibgeFromOption = selectedOption?.dataset?.ibge ?? '';
|
|
|
+ handleDeliveryCityChange(value, ibgeFromOption);
|
|
|
}
|
|
|
|
|
|
- function handleDeliveryCityChange(value) {
|
|
|
+ function handleDeliveryCityChange(value, ibgeOverride = '') {
|
|
|
onFieldChange(CITY_FIELD, value);
|
|
|
+ const stateLookup = lastSelectedState || selectedStateValue;
|
|
|
+ const ibgeCode = ibgeOverride || findCityIbgeCode(stateLookup, value);
|
|
|
+ onFieldChange(CITY_IBGE_FIELD, ibgeCode ?? '');
|
|
|
+ pendingIbgeValue = ibgeCode ?? '';
|
|
|
}
|
|
|
|
|
|
function isIssuerStateField(key) {
|
|
|
@@ -279,11 +451,13 @@
|
|
|
|
|
|
function getCitiesForState(value) {
|
|
|
if (!value) return [];
|
|
|
- return cidadesPorEstado[value] ?? [];
|
|
|
+ const list = cidadesPorEstado[value] ?? [];
|
|
|
+ return list.map((item) => item.name);
|
|
|
}
|
|
|
|
|
|
function handleRepeatingStateChange(groupKey, index, stateField, cityField, value) {
|
|
|
onRepeatingFieldChange(groupKey, index, stateField, value);
|
|
|
+ void loadCitiesForState(value);
|
|
|
if (cityField) {
|
|
|
onRepeatingFieldChange(groupKey, index, cityField, '');
|
|
|
}
|
|
|
@@ -291,12 +465,18 @@
|
|
|
|
|
|
function handleRepeatingCityChange(groupKey, index, cityField, value) {
|
|
|
onRepeatingFieldChange(groupKey, index, cityField, value);
|
|
|
+ const ibgeCode = findCityIbgeCode(selectedStateValue, value);
|
|
|
+ onRepeatingFieldChange(groupKey, index, CITY_IBGE_FIELD, ibgeCode ?? '');
|
|
|
}
|
|
|
|
|
|
function isProductField(key) {
|
|
|
return key === PRODUCT_NAME_FIELD;
|
|
|
}
|
|
|
|
|
|
+ function isHarvestField(key) {
|
|
|
+ return key === 'cpr_product_harvest';
|
|
|
+ }
|
|
|
+
|
|
|
function synchronizeProductCommodityFields(option) {
|
|
|
if (!option) {
|
|
|
const hasClassValue = Boolean(formData?.[PRODUCT_CLASS_FIELD]);
|
|
|
@@ -362,16 +542,18 @@
|
|
|
{:else if isDeliveryCityField(field.key)}
|
|
|
<select
|
|
|
class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-100 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-60"
|
|
|
- on:change={(event) => handleDeliveryCityChange(event.currentTarget.value)}
|
|
|
+ on:change={handleDeliveryCitySelect}
|
|
|
bind:value={selectedCityValue}
|
|
|
required={requiredFields?.has(field.key)}
|
|
|
- disabled={!selectedStateValue || !availableCities.length || !!estadosErro}
|
|
|
+ disabled={!selectedStateValue || !!estadosErro || (!!cidadesErro && !availableCities.length)}
|
|
|
>
|
|
|
<option value="">
|
|
|
{#if !selectedStateValue}
|
|
|
Selecione um estado primeiro
|
|
|
- {:else if estadosCarregando}
|
|
|
+ {:else if cidadesCarregando}
|
|
|
Carregando cidades...
|
|
|
+ {:else if cidadesErro}
|
|
|
+ Não foi possível carregar cidades
|
|
|
{:else if !availableCities.length}
|
|
|
Nenhuma cidade disponível
|
|
|
{:else}
|
|
|
@@ -379,12 +561,23 @@
|
|
|
{/if}
|
|
|
</option>
|
|
|
{#each availableCities as cidade}
|
|
|
- <option value={cidade}>{cidade}</option>
|
|
|
+ <option value={cidade.name} data-ibge={cidade.ibgeCode}>{cidade.name}</option>
|
|
|
{/each}
|
|
|
</select>
|
|
|
{#if estadosErro}
|
|
|
<p class="text-xs text-red-500 mt-1">{estadosErro}</p>
|
|
|
+ {:else if cidadesErro}
|
|
|
+ <p class="text-xs text-red-500 mt-1">{cidadesErro}</p>
|
|
|
{/if}
|
|
|
+ {:else if isDeliveryIbgeField(field.key)}
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ class="w-full rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800/50 text-gray-900 dark:text-gray-100 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
+ value={deliveryIbgeValue}
|
|
|
+ readonly
|
|
|
+ tabindex="-1"
|
|
|
+ placeholder="IBGE vinculado à cidade"
|
|
|
+ />
|
|
|
{:else if isProductField(field.key)}
|
|
|
<select
|
|
|
class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-100 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-60"
|
|
|
@@ -414,6 +607,30 @@
|
|
|
{#if commoditiesError}
|
|
|
<p class="text-xs text-red-500 mt-1">{commoditiesError}</p>
|
|
|
{/if}
|
|
|
+ {:else if isHarvestField(field.key)}
|
|
|
+ <select
|
|
|
+ class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-100 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-60"
|
|
|
+ value={getValue(field.key)}
|
|
|
+ on:change={handleInput(field.key)}
|
|
|
+ required={requiredFields?.has(field.key)}
|
|
|
+ disabled={harvestLoading || (!!harvestError && !harvestOptions.length)}
|
|
|
+ >
|
|
|
+ <option value="">
|
|
|
+ {#if harvestLoading}
|
|
|
+ Carregando safras...
|
|
|
+ {:else if harvestError && !harvestOptions.length}
|
|
|
+ Não foi possível carregar
|
|
|
+ {:else}
|
|
|
+ Selecione uma safra
|
|
|
+ {/if}
|
|
|
+ </option>
|
|
|
+ {#each harvestOptions as option}
|
|
|
+ <option value={option.value}>{option.label}</option>
|
|
|
+ {/each}
|
|
|
+ </select>
|
|
|
+ {#if harvestError}
|
|
|
+ <p class="text-xs text-red-500 mt-1">{harvestError}</p>
|
|
|
+ {/if}
|
|
|
{:else if field.type === 'select'}
|
|
|
<select
|
|
|
class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-100 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|