1
0

3 Commits c447f74c37 ... 3cba6400e0

Autor SHA1 Nachricht Datum
  gdias 3cba6400e0 comments only vor 1 Monat
  gdias 94f0aea5a5 FEATURE DONT FINISHE: implement the new trading screen vor 1 Monat
  gdias 0fd3533802 FEATURE NOT COMPLETED: implent the new trading screen vor 1 Monat

Datei-Diff unterdrückt, da er zu groß ist
+ 527 - 60
package-lock.json


+ 3 - 0
package.json

@@ -24,5 +24,8 @@
 		"svelte": "^5.0.0",
 		"tailwindcss": "^3.4.0",
 		"vite": "^7.0.4"
+	},
+	"dependencies": {
+		"layerchart": "^1.0.12"
 	}
 }

+ 22 - 39
src/lib/components/commodities/RegisterCommodity.svelte

@@ -4,6 +4,8 @@
     let descricao = "";
     let quantidade = "";
     let preco = "";
+    let nome = "";
+    let registro = "";
     let vencimentoPagamento = "";
     let dataLimiteEntrega = "";
     let cpr = false;
@@ -68,66 +70,47 @@
 
     <!-- Descrição, Quantidade, Preço -->
     <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
-      <div>
-        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Descrição</label>
+            <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Nome *</label>
         <input
-          bind:value={descricao}
+          type="text"
+          bind:value={nome}
           class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-400 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
-          placeholder="Descreva a commodity"
+          placeholder="Nome da commodity"
         />
       </div>
       <div>
-        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Quantidade</label>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Descrição</label>
         <input
-          type="number"
-          min="0"
-          step="1"
-          bind:value={quantidade}
+          bind:value={descricao}
           class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-400 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
-          placeholder="0"
+          placeholder="Descreva a commodity"
         />
       </div>
-      <div>
-        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Preço (R$)</label>
+      
+    </div>
+          <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Numero de registro</label>
         <input
-          type="number"
-          min="0"
-          step="0.01"
-          bind:value={preco}
-          class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-400 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
-          placeholder="0,00"
+          bind:value={registro}
+          class="mt-1 block w-1/3 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-400 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
+          placeholder="Numero de registro da commodity"
         />
       </div>
-    </div>
 
     <!-- Vencimento do Pagamento, Data Limite da Entrega, CPR -->
     <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
-      <div>
-        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Vencimento do Pagamento</label>
-        <input
-          type="date"
-          bind:value={vencimentoPagamento}
-          class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-400 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
-        />
-      </div>
-      <div>
-        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Data Limite da Entrega</label>
-        <input
-          type="date"
-          bind:value={dataLimiteEntrega}
-          class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-400 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
-        />
-      </div>
+      
       <div class="flex items-end">
-        <label class="flex items-center gap-2 text-gray-700 dark:text-gray-300">
+        <!-- <label class="flex items-center gap-2 text-gray-700 dark:text-gray-300">
           <input type="checkbox" bind:checked={cpr} class="accent-blue-600" />
           CPR
-        </label>
+        </label> -->
       </div>
     </div>
 
     <!-- Anexos / Arquivos -->
-    <div>
+    <!-- <div>
       <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Anexos</label>
       <div
         class="mt-1 rounded border-2 border-dashed border-gray-300 dark:border-gray-600 p-6 text-center hover:border-blue-400 transition-colors"
@@ -169,7 +152,7 @@
           {/each}
         </ul>
       {/if}
-    </div>
+    </div> -->
 
     <div class="mt-6 flex justify-end">
       <button

+ 66 - 0
src/lib/components/trading/BoletaCompra.svelte

@@ -0,0 +1,66 @@
+<script>
+  import ModalBase from './ModalBase.svelte';
+  export let visible = false;
+  export let onClose = () => {};
+  export let state = '';
+  export let commodity = '';
+  let valorSaca = '';
+  let quantidade = '';
+  let referencia = 'BRL : 12';
+  let validade = '';
+  let calendario = '';
+  $: total = (Number(valorSaca) || 0) * (Number(quantidade) || 0);
+  function confirmar() {
+    const dados = { tipo: 'compra', valorSaca: Number(valorSaca)||0, quantidade: Number(quantidade)||0, referencia, total, validade, calendario, estado: state, commodity };
+    console.log('BoletaCompra confirmada', dados);
+    try { onClose?.(); } catch {}
+  }
+  function formatBRL(n) { return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(Number(n||0)); }
+</script>
+<ModalBase title="Boleta de Compra" {visible} {onClose}>
+  <div class="space-y-4">
+    <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Valor/saca</label>
+        <input type="number" min="0" step="0.01" bind:value={valorSaca} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-green-500" />
+      </div>
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Quantidade em saca</label>
+        <input type="number" min="0" step="1" bind:value={quantidade} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-green-500" />
+      </div>
+    </div>
+    <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Referência</label>
+        <input type="text" bind:value={referencia} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-green-500" />
+      </div>
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Referência valor total</label>
+        <input value={formatBRL(total)} readonly class="mt-1 block w-full rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 text-gray-900 dark:text-gray-200 px-3 py-2" />
+      </div>
+    </div>
+    <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Validade</label>
+        <input type="date" bind:value={validade} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-green-500" />
+      </div>
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Calendário</label>
+        <input type="date" bind:value={calendario} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-green-500" />
+      </div>
+    </div>
+    <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Estado</label>
+        <input value={state} readonly class="mt-1 block w-full rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 text-gray-900 dark:text-gray-200 px-3 py-2" />
+      </div>
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Commodity</label>
+        <input value={commodity} readonly class="mt-1 block w-full rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 text-gray-900 dark:text-gray-200 px-3 py-2" />
+      </div>
+    </div>
+    <div class="flex justify-end">
+      <button class="px-4 py-2 rounded bg-green-600 hover:bg-green-700 text-white focus:outline-none focus:ring-2 focus:ring-green-500" on:click={confirmar}>Confirmar Compra</button>
+    </div>
+  </div>
+</ModalBase>

+ 66 - 0
src/lib/components/trading/BoletaVenda.svelte

@@ -0,0 +1,66 @@
+<script>
+  import ModalBase from './ModalBase.svelte';
+  export let visible = false;
+  export let onClose = () => {};
+  export let state = '';
+  export let commodity = '';
+  let valorSaca = '';
+  let quantidade = '';
+  let referencia = 'BRL : 12';
+  let validade = '';
+  let calendario = '';
+  $: total = (Number(valorSaca) || 0) * (Number(quantidade) || 0);
+  function confirmar() {
+    const dados = { tipo: 'venda', valorSaca: Number(valorSaca)||0, quantidade: Number(quantidade)||0, referencia, total, validade, calendario, estado: state, commodity };
+    console.log('BoletaVenda confirmada', dados);
+    try { onClose?.(); } catch {}
+  }
+  function formatBRL(n) { return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(Number(n||0)); }
+</script>
+<ModalBase title="Boleta de Venda" {visible} {onClose}>
+  <div class="space-y-4">
+    <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Valor/saca</label>
+        <input type="number" min="0" step="0.01" bind:value={valorSaca} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-red-500" />
+      </div>
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Quantidade em saca</label>
+        <input type="number" min="0" step="1" bind:value={quantidade} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-red-500" />
+      </div>
+    </div>
+    <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Referência</label>
+        <input type="text" bind:value={referencia} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-red-500" />
+      </div>
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Referência valor total</label>
+        <input value={formatBRL(total)} readonly class="mt-1 block w-full rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 text-gray-900 dark:text-gray-200 px-3 py-2" />
+      </div>
+    </div>
+    <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Validade</label>
+        <input type="date" bind:value={validade} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-red-500" />
+      </div>
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Calendário</label>
+        <input type="date" bind:value={calendario} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-red-500" />
+      </div>
+    </div>
+    <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Estado</label>
+        <input value={state} readonly class="mt-1 block w-full rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 text-gray-900 dark:text-gray-200 px-3 py-2" />
+      </div>
+      <div>
+        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Commodity</label>
+        <input value={commodity} readonly class="mt-1 block w-full rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 text-gray-900 dark:text-gray-200 px-3 py-2" />
+      </div>
+    </div>
+    <div class="flex justify-end">
+      <button class="px-4 py-2 rounded bg-red-600 hover:bg-red-700 text-white focus:outline-none focus:ring-2 focus:ring-red-500" on:click={confirmar}>Confirmar Venda</button>
+    </div>
+  </div>
+</ModalBase>

+ 1 - 0
src/lib/components/trading/BuyPanel.svelte

@@ -1,3 +1,4 @@
+<!--NAO ESTA MAIS SENDO UTILIZADO-->
 <script>
   import { createEventDispatcher } from 'svelte';
   export let referencePrice;

+ 101 - 0
src/lib/components/trading/GraficoCandlestick.svelte

@@ -0,0 +1,101 @@
+<script>
+  import { Chart, Svg, Axis, Points, Bars, Highlight, Tooltip } from 'layerchart';
+  import { scaleBand, scaleOrdinal } from 'd3-scale';
+
+  export let dadosMock = [];
+
+  const xScale = scaleBand().paddingInner(0.5);
+
+  $: dataSeries = (dadosMock || [])
+    .map((d) => ({
+      date: d.date || d.time,
+      valor: Number(d.valor),
+      high: d.high != null ? Number(d.high) : undefined,
+      low: d.low != null ? Number(d.low) : undefined
+    })) 
+    .filter((d) => d.date && Number.isFinite(d.valor));
+
+  $: ohlc = dataSeries.map((d, i) => {
+    const open = i > 0 ? dataSeries[i - 1].valor : d.valor;
+    const close = d.valor;
+    const providedHigh = Number.isFinite(d.high) ? d.high : undefined;
+    const providedLow = Number.isFinite(d.low) ? d.low : undefined;
+    // Se vierem no mock, usa; senão faz fallback mínimo seguro
+    const high = providedHigh !== undefined ? Math.max(providedHigh, open, close) : Math.max(open, close);
+    const low = providedLow !== undefined ? Math.min(providedLow, open, close) : Math.min(open, close);
+    return { date: d.date, open, high, low, close };
+  });
+
+  function fmtDate(d) {
+    if (!d) return '';
+    if (typeof d === 'string') {
+      const m = d.match(/^(\d{4})-(\d{2})-(\d{2})/);
+      return m ? `${m[3]}/${m[2]}` : d;
+    }
+    const dt = d instanceof Date ? d : new Date(d);
+    return Number.isNaN(dt.getTime()) ? '' : dt.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' });
+  }
+
+  const tooltip = { mode: 'bisect-x' };
+  function fmtNum(n) {
+    return new Intl.NumberFormat('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(Number(n ?? 0));
+  }
+  function fmtTickY(v) {
+    const raw = typeof v === 'object' && v !== null && 'value' in v ? v.value : v;
+    const n = Number(raw);
+    if (!Number.isFinite(n)) return '';
+    return new Intl.NumberFormat('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(n);
+  }
+  // domínio Y: 0 até (max atual + 5%)
+  $: yMaxRaw = (ohlc && ohlc.length)
+    ? Math.max(...ohlc.map((d) => Math.max(Number(d.high ?? d.open ?? d.close ?? 0), Number(d.open ?? 0), Number(d.close ?? 0))))
+    : 0;
+  $: yDomain = [0, (yMaxRaw > 0 ? yMaxRaw * 1.05 : 1)];
+
+  const colorKey = (d) => (Number(d.close) < Number(d.open) ? 'desc' : 'asc');
+</script>
+
+<div class="h-[550px] p-4 border rounded bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 cursor-crosshair select-none">
+  <Chart
+    data={ohlc}
+    x="date"
+    xScale={xScale}
+    y={["high", "low"]}
+    yDomain={yDomain}
+    yNice
+    c={colorKey}
+    cScale={scaleOrdinal()}
+    cDomain={["desc", "asc"]}
+    cRange={["#e41a1c", "#16a34a"]}
+    padding={{ left: 20, bottom: 26 }}
+    tooltip={tooltip}
+  >
+    <Svg clip={false}>
+      <Axis
+  placement="left"
+  grid
+  rule
+  ticks={6}
+  format={fmtTickY}
+  tickPadding={10}
+  labelPadding={12}
+  class="text-xs fill-black dark:fill-white stroke-gray-400/50"
+  style="font-family: sans-serif;"
+/><Axis placement="bottom" rule ticks={Math.min(8, ohlc.length)} format={fmtDate} class="text-black dark:text-white fill-black dark:fill-white text-xs" />
+      <Points links r={0} c={colorKey} strokeWidth={1} strokeLinecap="round" strokeOpacity={0.8} />
+      <Bars y={(d) => [Number(d.open), Number(d.close)]} radius={0} c={colorKey} stroke="transparent" fillOpacity={1} />
+    </Svg>
+
+    <Tooltip.Root let:data>
+      <div class="pointer-events-none rounded-md bg-gray-900/95 text-white shadow-lg ring-1 ring-black/12 px-3 py-2 text-xs">
+        <div class="font-semibold mb-1">{fmtDate(data?.date)}</div>
+        <div class="grid grid-cols-2 gap-x-3 gap-y-0.5">
+          <div class="opacity-70">Open</div><div>{fmtNum(data?.open)}</div>
+          <div class="opacity-70">Close</div><div>{fmtNum(data?.close)}</div>
+          <div class="opacity-70">High</div><div>{fmtNum(data?.high)}</div>
+          <div class="opacity-70">Low</div><div>{fmtNum(data?.low)}</div>
+        </div>
+      </div>
+    </Tooltip.Root>
+  </Chart>
+</div>

+ 23 - 0
src/lib/components/trading/ModalBase.svelte

@@ -0,0 +1,23 @@
+<script>
+  import { createEventDispatcher } from 'svelte';
+  export let title = '';
+  export let visible = false;
+  export let onClose = () => {};
+  const dispatch = createEventDispatcher();
+  function close() { try { onClose?.(); } catch {} dispatch('close'); }
+  function overlayClick(e) { if (e.target === e.currentTarget) close(); }
+</script>
+{#if visible}
+<div class="fixed inset-0 z-50 flex items-center justify-center">
+  <div class="absolute inset-0 bg-black/50" on:click={overlayClick}></div>
+  <div class="relative bg-white dark:bg-gray-800 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700 w-full max-w-lg mx-4 max-h-[80vh] flex flex-col">
+    <div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
+      <h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">{title}</h3>
+      <button class="p-1 rounded text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" on:click={close}>✕</button>
+    </div>
+    <div class="p-4 flex-1 overflow-auto">
+      <slot />
+    </div>
+  </div>
+</div>
+{/if}

+ 6 - 17
src/lib/components/trading/SellPanel.svelte

@@ -1,3 +1,4 @@
+<!--NAO ESTA MAIS SENDO UTILIZADO-->
 <script>
   import { createEventDispatcher } from 'svelte';
   export let referencePrice;
@@ -5,9 +6,7 @@
 
   const dispatch = createEventDispatcher();
 
-  let tab = 'market';
   let amountFiat = '';
-  let limitPrice = '';
   let selectedTokenId = tokens?.[0]?.id ?? null;
 
   function formatBRL(n) {
@@ -19,19 +18,16 @@
 
   $: selectedToken = tokens.find(t => t.id === selectedTokenId);
   $: refPriceByToken = selectedToken?.price ?? referencePrice;
-  $: priceUsed = tab === 'limit' ? Number(limitPrice) : Number(refPriceByToken);
   $: numericFiat = Number(amountFiat) || 0;
-  $: computedTokensRaw = (numericFiat && priceUsed) ? numericFiat / priceUsed : 0;
-  $: computedTokens = Math.floor(computedTokensRaw);
+  const FIXED_AMOUNT = 1;
 
   function confirm() {
-    const price = priceUsed;
+    const price = numericFiat;
     const total = numericFiat;
-    const amount = computedTokens;
+    const amount = FIXED_AMOUNT;
     if (!price || !total || !amount) return;
-    dispatch('confirm', { type: tab, price, amount, total, tokenId: selectedToken?.id });
+    dispatch('confirm', { type: 'market', price, amount, total, tokenId: selectedToken?.id });
     amountFiat = '';
-    limitPrice = '';
   }
 </script>
 
@@ -52,13 +48,6 @@
       </select>
     </div>
 
-    {#if tab === 'limit'}
-      <div>
-        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Preço limite (R$)</label>
-        <input type="number" min="0" step="0.01" bind:value={limitPrice} class="mt-1 block w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" />
-      </div>
-    {/if}
-
     <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
       <div>
         <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">EasyCoin (R$)</label>
@@ -66,7 +55,7 @@
       </div>
       <div>
         <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">EasyToken (inteiros)</label>
-        <input value={formatEasyToken(computedTokens || 0)} readonly class="mt-1 block w-full rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 text-gray-900 dark:text-gray-200 px-3 py-2" />
+        <input value={formatEasyToken(FIXED_AMOUNT)} readonly class="mt-1 block w-full rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 text-gray-900 dark:text-gray-200 px-3 py-2" />
       </div>
     </div>
 

+ 108 - 0
src/lib/components/trading/TabelaOrdens.svelte

@@ -0,0 +1,108 @@
+<script>
+  export let ordensCompra = [];
+  export let ordensVenda = [];
+  export let ultimaVenda = { valor: 0, quantidade: 0 };
+  function formatBRL(n) { return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(Number(n || 0)); }
+  function formatQty(n) { return new Intl.NumberFormat('pt-BR', { minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(Number(n || 0)); }
+
+  $: listaCompra = (ordensCompra || []).map((d) => ({ valor: Number(d?.valor ?? 0), quantidade: Number(d?.quantidade ?? 0) }));
+  $: listaCompraView = (listaCompra || []).map((o, __idx) => ({ ...o, __idx })).slice().reverse();
+  $: listaVenda = (ordensVenda || []).map((d) => ({ valor: Number(d?.valor ?? 0), quantidade: Number(d?.quantidade ?? 0) }));
+  $: listaVendaView = (listaVenda || []).map((o, __idx) => ({ ...o, __idx })).slice().reverse();
+  // `ultimaVenda` agora vem por prop do pai
+
+  let flashCompra = new Set();
+  let flashVenda = new Set();
+  let prevCompraLen = listaCompra?.length || 0;
+  let prevVendaLen = listaVenda?.length || 0;
+
+  $: {
+    const curr = listaCompra?.length || 0;
+    if (curr > prevCompraLen) {
+      for (let i = prevCompraLen; i < curr; i++) {
+        flashCompra.add(i);
+        setTimeout(() => { flashCompra.delete(i); }, 1500);
+      }
+    }
+    prevCompraLen = curr;
+  }
+
+  $: {
+    const curr = listaVenda?.length || 0;
+    if (curr > prevVendaLen) {
+      for (let i = prevVendaLen; i < curr; i++) {
+        flashVenda.add(i);
+        setTimeout(() => { flashVenda.delete(i); }, 1500);
+      }
+    }
+    prevVendaLen = curr;
+  }
+</script>
+<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm h-full">
+  <div class="px-4 py-2 bg-yellow-100 dark:bg-yellow-900/40 border-b border-yellow-300 dark:border-yellow-700 text-sm text-yellow-900 dark:text-yellow-200">
+    <div class="flex items-center justify-between">
+      <span>Última Venda</span>
+      <span class="font-medium">{formatBRL(ultimaVenda?.valor)} · {formatQty(ultimaVenda?.quantidade)}</span>
+    </div>
+  </div>
+  <div class="h-[70vh] overflow-auto grid grid-cols-2 divide-x divide-gray-200 dark:divide-gray-700 scroll-ordens">
+    <div class="p-4">
+      <div class="flex flex-col items-center justify-between mb-2">
+        <h3 class="text-sm font-semibold text-green-600">Ordem de Compra</h3>
+        <div class="text-xs text-gray-500">VALOR · QUANTIDADE</div>
+      </div>
+      <div class="space-y-1">
+        {#each listaCompraView as o}
+        <div class={`flex items-center justify-between text-sm rounded px-1 py-0.5 transition-colors duration-700 ${flashCompra.has(o.__idx) ? 'bg-green-100 dark:bg-green-900/30' : ''}`}>
+          <span class="text-green-600">{formatBRL(o.valor)}</span>
+          <span class="text-gray-700 dark:text-gray-200">{formatQty(o.quantidade)}</span>
+        </div>
+        {/each}
+      </div>
+    </div>
+    <div class="p-4">
+      <div class="flex flex-col items-center justify-between mb-2">
+        <h3 class="text-sm font-semibold text-red-600">Ordem de Venda</h3>
+        <div class="text-xs text-gray-500">VALOR · QUANTIDADE</div>
+      </div>
+      <div class="space-y-1">
+        {#each listaVendaView as o}
+        <div class={`flex items-center justify-between text-sm rounded px-1 py-0.5 transition-colors duration-700 ${flashVenda.has(o.__idx) ? 'bg-red-100 dark:bg-red-900/30' : ''}`}>
+          <span class="text-red-600">{formatBRL(o.valor)}</span>
+          <span class="text-gray-700 dark:text-gray-200">{formatQty(o.quantidade)}</span>
+        </div>
+        {/each}
+      </div>
+    </div>
+  </div>
+</div>
+
+<style>
+  :global(.scroll-ordens) {
+    scrollbar-width: thin;                 /* Firefox */
+    scrollbar-color: #cbd5e1 #f3f4f6;      /* thumb track */
+  }
+  :global(.dark .scroll-ordens) {
+    scrollbar-color: #374151 #1f2937;      /* thumb track */
+  }
+
+  :global(.scroll-ordens::-webkit-scrollbar) {
+    width: 10px;
+    height: 10px;
+  }
+  :global(.scroll-ordens::-webkit-scrollbar-track) {
+    background: #f3f4f6;                   /* gray-100 */
+  }
+  :global(.scroll-ordens::-webkit-scrollbar-thumb) {
+    background-color: #cbd5e1;             /* slate-300 */
+    border-radius: 9999px;
+    border: 2px solid #f3f4f6;             /* match track */
+  }
+  :global(.dark .scroll-ordens::-webkit-scrollbar-track) {
+    background: #1f2937;                   /* gray-800 */
+  }
+  :global(.dark .scroll-ordens::-webkit-scrollbar-thumb) {
+    background-color: #374151;             /* gray-700 */
+    border-color: #1f2937;                 /* match dark track */
+  }
+</style>

+ 1 - 0
src/lib/layout/Header.svelte

@@ -19,6 +19,7 @@
     {/if}
   </div>
   <div class="flex items-center mt-2 md:mt-0 space-x-4">
+    <slot name="extra" />
     <div class="flex items-center text-gray-500 dark:text-gray-300 text-sm">
       {#each breadcrumb as item, i}
         <span class="{item.active ? 'text-gray-900 dark:text-gray-100 font-medium' : ''}">{item.label}</span>

+ 65 - 0
src/lib/mock/ordens.js

@@ -0,0 +1,65 @@
+export const ordensCompra = [
+  { valor: 109.98, quantidade: 10000 },
+  { valor: 109.99, quantidade: 20000 },
+  { valor: 110.0, quantidade: 10000 },
+
+];
+
+export const ordensVenda = [
+  { valor: 110.01, quantidade: 10000 },
+  { valor: 110.02, quantidade: 20000 },
+  { valor: 110.03, quantidade: 10000 },
+  
+];
+
+export const ultimaVenda = { valor: 10.0, quantidade: 8000 };
+
+export function startOrdensSimulator({ get, set, intervalMs = 1500, maxItems = 60, minItems = 8, qtyGenerator }) {
+  let timer = null;
+
+  const randQty = () => Math.floor(1000 + Math.random() * (10000 - 1000 + 1));
+  const pad = (n) => String(n).padStart(2, '0');
+  const fmtDate = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
+
+  function nextItem(prevArr) {
+    const last = prevArr[prevArr.length - 1];
+    let lastDate = last?.date ? new Date(last.date) : new Date();
+    if (Number.isNaN(lastDate.getTime())) lastDate = new Date();
+    const nextDate = new Date(lastDate.getTime() + 24 * 60 * 60 * 1000);
+
+    const rangeMin = 100;
+    const rangeMax = 200;
+    const lastValor = Number(last?.valor);
+    const base = Number.isFinite(lastValor) && lastValor >= rangeMin && lastValor <= rangeMax
+      ? lastValor
+      : (rangeMin + Math.random() * (rangeMax - rangeMin));
+
+    const delta = (Math.random() - 0.5) * 5; // +/- ~2.5 ao redor do base
+    const close = Math.min(rangeMax, Math.max(rangeMin, Number((base + delta).toFixed(2))));
+    const open = base;
+    const wick = Math.random() * 1.5 + 0.2; // 0.2 .. 1.7
+    const high = Math.min(rangeMax, +(Math.max(open, close) + wick).toFixed(2));
+    const low = Math.max(rangeMin, +(Math.min(open, close) - wick).toFixed(2));
+
+    return { date: fmtDate(nextDate), valor: close, high, low, quantidade: (typeof qtyGenerator === 'function' ? qtyGenerator({ last, close }) : randQty()) };
+  }
+
+  timer = setInterval(() => {
+    const curr = Array.isArray(get?.()) ? get() : [];
+    const shouldAdd = curr.length < minItems || (Math.random() < 0.6 && curr.length < maxItems);
+
+    let next = curr.slice();
+    if (shouldAdd) {
+      next.push(nextItem(curr));
+    } else if (next.length > minItems) {
+      const idx = Math.floor(Math.random() * next.length);
+      next.splice(idx, 1);
+    }
+
+    set?.(next);
+  }, intervalMs);
+
+  return () => {
+    if (timer) clearInterval(timer);
+  };
+}

+ 125 - 22
src/routes/trading/+page.svelte

@@ -1,11 +1,13 @@
 <script>
   import Header from '$lib/layout/Header.svelte';
-  import Tables from '$lib/components/Tables.svelte';
-  import BuyPanel from '$lib/components/trading/BuyPanel.svelte';
-  import SellPanel from '$lib/components/trading/SellPanel.svelte';
+  import TabelaOrdens from '$lib/components/trading/TabelaOrdens.svelte';
+  import GraficoCandlestick from '$lib/components/trading/GraficoCandlestick.svelte';
+  import BoletaCompra from '$lib/components/trading/BoletaCompra.svelte';
+  import BoletaVenda from '$lib/components/trading/BoletaVenda.svelte';
   import { writable } from 'svelte/store';
   import { onMount } from 'svelte';
   import { browser } from '$app/environment';
+  import { ordensCompra as ordensCompraMock, ordensVenda as ordensVendaMock, startOrdensSimulator } from '$lib/mock/ordens.js';
 
   const breadcrumb = [{ label: 'Início' }, { label: 'Trading', active: true }];
 
@@ -22,6 +24,27 @@
         if (saved) orders.set(JSON.parse(saved));
       } catch {}
     }
+
+    const stopSimCompra = startOrdensSimulator({
+      get: () => ordensCompra,
+      set: (next) => { ordensCompra = next; },
+      intervalMs: 1500,
+      maxItems: 60,
+      minItems: 8
+    });
+
+    const stopSimVenda = startOrdensSimulator({
+      get: () => ordensVenda,
+      set: (next) => { ordensVenda = next; },
+      intervalMs: 1500,
+      maxItems: 60,
+      minItems: 8
+    });
+
+    return () => {
+      stopSimCompra?.();
+      stopSimVenda?.();
+    };
   });
 
   function addOrder(order) {
@@ -84,14 +107,14 @@
     { key: 'status', label: 'Status' }
   ];
 
-$: pendingBuys = $orders.filter((o) => o.side === 'Compra' && o.type === 'limit' && o.status === 'Aberta');
-$: pendingSells = $orders.filter((o) => o.side === 'Venda' && o.type === 'limit' && o.status === 'Aberta');
+$: pendingBuys = $orders.filter((o) => o.side === 'Compra' && o.status === 'Aberta');
+$: pendingSells = $orders.filter((o) => o.side === 'Venda' && o.status === 'Aberta');
 
 $: displayPendingBuys = pendingBuys.map((o) => ({
   side: o.side,
-  price: formatEasyToken(o.price),
+  price: formatBRL(o.price),
   amount: formatEasyToken(o.amount),
-  total: formatEasyToken(o.total),
+  total: formatBRL(o.total),
   status: o.status
 }));
 
@@ -102,26 +125,106 @@ $: displayPendingSells = pendingSells.map((o) => ({
   total: formatBRL(o.total),
   status: o.status
 }));
+
+ const tokens = [
+    { id: 'TK-SOJA',  label: 'Soja (saca 60kg)',  price: 125.50 },
+    { id: 'TK-MILHO', label: 'Milho (saca 60kg)', price: 78.90 },
+    { id: 'TK-CAFE',  name:  'Café (saca 60kg)' },
+  ];
+
+  const stateOptions = ['SP','PR','RS','MG','MT','GO','MS','BA','SC','RO','PA'];
+  let selectedState = 'SP';
+  let selectedCommodity = tokens?.[0]?.id ?? '';
+
+  let showBuyModal = false;
+  let showSellModal = false;
+
+  $: selectedCommodityObj = tokens.find((t) => t.id === selectedCommodity);
+  $: selectedCommodityLabel = selectedCommodityObj?.label || selectedCommodityObj?.name;
+
+  let ordensCompra = ordensCompraMock.slice();
+  let ordensVenda = ordensVendaMock.slice();
+  $: ultimaVenda = (ordensVenda && ordensVenda.length)
+    ? { valor: Number(ordensVenda[ordensVenda.length - 1]?.valor ?? 0), quantidade: Number(ordensVenda[ordensVenda.length - 1]?.quantidade ?? 0) }
+    : { valor: 0, quantidade: 0 };
+
+  const dadosMock = [
+  { date: '2025-10-01', valor: 9.98, high: 10.05, low: 9.90 },
+  { date: '2025-10-02', valor: 10.12, high: 10.25, low: 9.95 },
+  { date: '2025-10-03', valor: 9.85, high: 9.92, low: 9.60 },
+  { date: '2025-10-04', valor: 10.30, high: 10.55, low: 10.10 },
+  { date: '2025-10-05', valor: 10.05, high: 10.20, low: 9.85 },
+  { date: '2025-10-06', valor: 9.70, high: 9.85, low: 9.45 },
+  { date: '2025-10-07', valor: 10.45, high: 10.75, low: 10.25 },
+  { date: '2025-10-08', valor: 10.60, high: 10.80, low: 10.40 },
+  { date: '2025-10-09', valor: 9.55, high: 9.70, low: 9.20 },   // forte queda
+  { date: '2025-10-10', valor: 9.30, high: 9.50, low: 9.00 },  // fundo do poço
+  { date: '2025-10-11', valor: 9.85, high: 10.00, low: 9.60 }, // leve recuperação
+  { date: '2025-10-12', valor: 10.50, high: 10.80, low: 10.30 }, // disparada
+  { date: '2025-10-13', valor: 10.75, high: 11.10, low: 10.60 },
+  { date: '2025-10-14', valor: 10.95, high: 11.20, low: 10.80 }, // pico máximo
+  { date: '2025-10-15', valor: 10.40, high: 10.60, low: 10.10 },
+  { date: '2025-10-16', valor: 10.10, high: 10.25, low: 9.90 },
+  { date: '2025-10-17', valor: 9.85, high: 9.95, low: 9.60 },
+  { date: '2025-10-18', valor: 9.20, high: 9.35, low: 8.95 },   // nova queda brusca
+  { date: '2025-10-19', valor: 8.85, high: 9.00, low: 8.55 },
+  { date: '2025-10-20', valor: 9.50, high: 9.70, low: 9.20 },
+  { date: '2025-10-21', valor: 9.95, high: 10.10, low: 9.80 },
+  { date: '2025-10-22', valor: 10.25, high: 10.50, low: 10.05 },
+  { date: '2025-10-23', valor: 10.90, high: 11.15, low: 10.75 }, // novo pico
+  { date: '2025-10-24', valor: 11.40, high: 11.75, low: 11.10 },
+  { date: '2025-10-25', valor: 13.75, high: 13.95, low: 13.50 },
+  { date: '2025-10-26', valor: 13.60, high: 13.80, low: 13.40 },
+  { date: '2025-10-27', valor: 9.30, high: 9.45, low: 9.00 },  // queda acentuada
+  { date: '2025-10-28', valor: 9.10, high: 9.25, low: 8.80 },
+  { date: '2025-10-29', valor: 9.60, high: 9.80, low: 9.40 },
+  { date: '2025-10-30', valor: 10.25, high: 10.50, low: 10.05 },
+  { date: '2025-10-31', valor: 10.80, high: 11.00, low: 10.65 }, // novo rally
+  { date: '2025-11-01', valor: 11.20, high: 11.45, low: 11.00 },
+  { date: '2025-11-02', valor: 10.95, high: 11.10, low: 10.70 },
+  { date: '2025-11-03', valor: 10.50, high: 10.65, low: 10.30 },
+  { date: '2025-11-04', valor: 9.80, high: 9.95, low: 9.55 },   // nova correção
+  { date: '2025-11-05', valor: 10.05, high: 10.30, low: 9.85 }
+  ];
 </script>
 
 <div>
-  <Header title="Trading" subtitle="Trading do sistema" breadcrumb={breadcrumb} />
+  <Header title="Trading" subtitle="Trading do sistema" breadcrumb={breadcrumb}>
+    <svelte:fragment slot="extra">
+      <div class="flex items-center gap-3">
+        <button class="px-3 py-1.5 rounded bg-green-600 hover:bg-green-700 text-white focus:outline-none focus:ring-2 focus:ring-green-500" on:click={() => (showBuyModal = true)}>COMPRAR</button>
+        <button class="px-3 py-1.5 rounded bg-red-600 hover:bg-red-700 text-white focus:outline-none focus:ring-2 focus:ring-red-500" on:click={() => (showSellModal = true)}>VENDER</button>
+        <div class="flex items-center gap-2">
+          <span class="text-xs text-gray-600 dark:text-gray-300">Estado</span>
+          <select bind:value={selectedState} class="block w-32 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500">
+            {#each stateOptions as uf}
+              <option value={uf}>{uf}</option>
+            {/each}
+          </select>
+        </div>
+        <div class="flex items-center gap-2">
+          <span class="text-xs text-gray-600 dark:text-gray-300">Commodity</span>
+          <select bind:value={selectedCommodity} class="block w-56 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500">
+            {#each tokens as t}
+              <option value={t.id}>{t.label || t.name}</option>
+            {/each}
+          </select>
+        </div>
+      </div>
+    </svelte:fragment>
+  </Header>
 
   <div class="p-4 space-y-4">
-    <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
-      <BuyPanel
-        referencePrice={currentPrice}
-        on:confirm={(e) => addOrder({ side: 'Compra', price: e.detail.price, amount: e.detail.amount, total: e.detail.total, status: 'Aberta', type: e.detail.type })}
-      />
-      <SellPanel
-        referencePrice={currentPrice}
-        on:confirm={(e) => addOrder({ side: 'Venda', price: e.detail.price, amount: e.detail.amount, total: e.detail.total, status: 'Aberta', type: e.detail.type })}
-      />
+    <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
+      <div class="md:col-span-1">
+        <TabelaOrdens {ordensCompra} {ordensVenda} {ultimaVenda} />
+      </div>
+      <div class="md:col-span-2">
+        <GraficoCandlestick {dadosMock} />
+      </div>
     </div>
-
-<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
-  <Tables title="Ordens de Compra (pendentes)" columns={orderColumns} data={displayPendingBuys} showActions={false} showAdd={false}/>
-  <Tables title="Ordens de Venda (pendentes)" columns={orderColumns} data={displayPendingSells} showActions={false} showAdd={false}/>
-</div>
   </div>
+
+  <BoletaCompra visible={showBuyModal} onClose={() => (showBuyModal = false)} state={selectedState} commodity={selectedCommodityLabel} />
+  <BoletaVenda visible={showSellModal} onClose={() => (showSellModal = false)} state={selectedState} commodity={selectedCommodityLabel} />
 </div>

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.