|
|
@@ -1,5 +1,8 @@
|
|
|
<script>
|
|
|
import { onMount } from 'svelte';
|
|
|
+ import { get } from 'svelte/store';
|
|
|
+
|
|
|
+ import { authToken } from '$lib/utils/stores';
|
|
|
|
|
|
export let formData = {};
|
|
|
export let onFieldChange = () => {};
|
|
|
@@ -22,6 +25,10 @@
|
|
|
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 isBrowser = typeof window !== 'undefined';
|
|
|
+ const apiUrl = import.meta.env.VITE_API_URL;
|
|
|
+
|
|
|
+ const PRODUCT_NAME_FIELD = 'cpr_product_name';
|
|
|
+ const PRODUCT_CLASS_FIELD = 'cpr_product_class_name';
|
|
|
|
|
|
let estadosOptions = [];
|
|
|
let cidadesPorEstado = {};
|
|
|
@@ -31,6 +38,13 @@
|
|
|
let selectedCityValue = '';
|
|
|
let availableCities = [];
|
|
|
|
|
|
+ let commodityOptions = [];
|
|
|
+ let commoditiesLoading = false;
|
|
|
+ let commoditiesError = '';
|
|
|
+ let currentProductValue = '';
|
|
|
+ let selectedCommodityOption = null;
|
|
|
+ let selectedCommodityId = '';
|
|
|
+
|
|
|
const sections = [
|
|
|
{
|
|
|
title: 'Emissão e Entrega',
|
|
|
@@ -59,7 +73,6 @@
|
|
|
columns: 3,
|
|
|
fields: [
|
|
|
{ key: 'cpr_product_name', label: 'Produto' },
|
|
|
- { key: 'cpr_product_class_name', label: 'Classe' },
|
|
|
{ key: 'cpr_product_harvest', label: 'Safra' },
|
|
|
{ key: 'cpr_product_quantity', label: 'Quantidade' },
|
|
|
{ key: 'cpr_measure_unit_name', label: 'Unidade de medida' },
|
|
|
@@ -86,6 +99,7 @@
|
|
|
onMount(() => {
|
|
|
if (!isBrowser) return;
|
|
|
hydrateCidadesEstadosOptions();
|
|
|
+ void fetchCommodityOptions();
|
|
|
});
|
|
|
|
|
|
$: selectedStateValue = formData?.[STATE_FIELD] ?? '';
|
|
|
@@ -94,6 +108,11 @@
|
|
|
selectedStateValue && cidadesPorEstado[selectedStateValue]
|
|
|
? cidadesPorEstado[selectedStateValue]
|
|
|
: [];
|
|
|
+ $: currentProductValue = formData?.[PRODUCT_NAME_FIELD] ?? '';
|
|
|
+ $: selectedCommodityOption =
|
|
|
+ commodityOptions.find((item) => item.name === currentProductValue) ?? null;
|
|
|
+ $: selectedCommodityId = selectedCommodityOption?.id ?? '';
|
|
|
+ $: synchronizeProductCommodityFields(selectedCommodityOption);
|
|
|
|
|
|
async function hydrateCidadesEstadosOptions() {
|
|
|
try {
|
|
|
@@ -122,6 +141,69 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ async function fetchCommodityOptions() {
|
|
|
+ if (!apiUrl) {
|
|
|
+ commoditiesError = 'URL da API não configurada.';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ commoditiesLoading = true;
|
|
|
+ commoditiesError = '';
|
|
|
+ try {
|
|
|
+ const token = resolveAuthToken();
|
|
|
+ if (!token) {
|
|
|
+ throw new Error('Sessão expirada. Faça login novamente.');
|
|
|
+ }
|
|
|
+ const res = await fetch(`${apiUrl}/commodities/get`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'content-type': 'application/json',
|
|
|
+ Authorization: `Bearer ${token}`
|
|
|
+ },
|
|
|
+ body: JSON.stringify({})
|
|
|
+ });
|
|
|
+
|
|
|
+ let payload = null;
|
|
|
+ try {
|
|
|
+ payload = await res.json();
|
|
|
+ } catch (err) {
|
|
|
+ console.error('Falha ao interpretar resposta de /commodities/get:', err);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!res.ok || payload?.status !== 'ok') {
|
|
|
+ throw new Error(payload?.msg ?? 'Falha ao carregar commodities.');
|
|
|
+ }
|
|
|
+
|
|
|
+ commodityOptions = Array.isArray(payload?.data)
|
|
|
+ ? payload.data
|
|
|
+ .map((item) => ({
|
|
|
+ id:
|
|
|
+ item?.commodities_id != null
|
|
|
+ ? String(item.commodities_id)
|
|
|
+ : item?.commodities_name ?? '',
|
|
|
+ name: (item?.commodities_name ?? '').trim(),
|
|
|
+ class: (item?.commodities_class ?? '').trim(),
|
|
|
+ raw: item
|
|
|
+ }))
|
|
|
+ .filter((item) => item.id && item.name)
|
|
|
+ : [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Erro ao carregar commodities (CPR):', error);
|
|
|
+ commoditiesError = error?.message ?? 'Falha ao carregar commodities.';
|
|
|
+ commodityOptions = [];
|
|
|
+ } finally {
|
|
|
+ commoditiesLoading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function resolveAuthToken() {
|
|
|
+ const storeToken = get(authToken);
|
|
|
+ if (storeToken) return storeToken;
|
|
|
+ if (!isBrowser) return null;
|
|
|
+ const match = document.cookie.match(/(?:^|; )auth_token=([^;]+)/);
|
|
|
+ return match ? decodeURIComponent(match[1]) : null;
|
|
|
+ }
|
|
|
+
|
|
|
function loadCidadesEstadosScript() {
|
|
|
if (!isBrowser) {
|
|
|
return Promise.reject(new Error('Ambiente indisponível para carregar cidades-estados.'));
|
|
|
@@ -210,6 +292,37 @@
|
|
|
function handleRepeatingCityChange(groupKey, index, cityField, value) {
|
|
|
onRepeatingFieldChange(groupKey, index, cityField, value);
|
|
|
}
|
|
|
+
|
|
|
+ function isProductField(key) {
|
|
|
+ return key === PRODUCT_NAME_FIELD;
|
|
|
+ }
|
|
|
+
|
|
|
+ function synchronizeProductCommodityFields(option) {
|
|
|
+ if (!option) {
|
|
|
+ const hasClassValue = Boolean(formData?.[PRODUCT_CLASS_FIELD]);
|
|
|
+ if (!currentProductValue && hasClassValue) {
|
|
|
+ onFieldChange(PRODUCT_CLASS_FIELD, '');
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const resolvedName = option.name ?? '';
|
|
|
+ if (resolvedName && formData?.[PRODUCT_NAME_FIELD] !== resolvedName) {
|
|
|
+ onFieldChange(PRODUCT_NAME_FIELD, resolvedName);
|
|
|
+ }
|
|
|
+
|
|
|
+ const resolvedClass = option.class ?? '';
|
|
|
+ if (formData?.[PRODUCT_CLASS_FIELD] !== resolvedClass) {
|
|
|
+ onFieldChange(PRODUCT_CLASS_FIELD, resolvedClass);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleProductSelect(event) {
|
|
|
+ const commodityId = event?.currentTarget?.value ?? '';
|
|
|
+ const selected = commodityOptions.find((item) => item.id === commodityId) ?? null;
|
|
|
+ onFieldChange(PRODUCT_NAME_FIELD, selected?.name ?? '');
|
|
|
+ onFieldChange(PRODUCT_CLASS_FIELD, selected?.class ?? '');
|
|
|
+ }
|
|
|
</script>
|
|
|
|
|
|
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm">
|
|
|
@@ -272,6 +385,35 @@
|
|
|
{#if estadosErro}
|
|
|
<p class="text-xs text-red-500 mt-1">{estadosErro}</p>
|
|
|
{/if}
|
|
|
+ {: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"
|
|
|
+ value={selectedCommodityId}
|
|
|
+ on:change={handleProductSelect}
|
|
|
+ required={requiredFields?.has(field.key)}
|
|
|
+ disabled={commoditiesLoading || !!commoditiesError}
|
|
|
+ >
|
|
|
+ <option value="">
|
|
|
+ {#if commoditiesLoading}
|
|
|
+ Carregando produtos...
|
|
|
+ {:else if commoditiesError}
|
|
|
+ Não foi possível carregar
|
|
|
+ {:else}
|
|
|
+ Selecione um produto
|
|
|
+ {/if}
|
|
|
+ </option>
|
|
|
+ {#each commodityOptions as commodity}
|
|
|
+ <option value={commodity.id}>
|
|
|
+ {commodity.name}
|
|
|
+ {#if commodity.class}
|
|
|
+ — Classe {commodity.class}
|
|
|
+ {/if}
|
|
|
+ </option>
|
|
|
+ {/each}
|
|
|
+ </select>
|
|
|
+ {#if commoditiesError}
|
|
|
+ <p class="text-xs text-red-500 mt-1">{commoditiesError}</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"
|