BoletaCompra.svelte 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. <script>
  2. import ModalBase from './ModalBase.svelte';
  3. import flatpickr from 'flatpickr';
  4. import 'flatpickr/dist/flatpickr.min.css';
  5. import { Portuguese } from 'flatpickr/dist/l10n/pt.js';
  6. import { createEventDispatcher } from 'svelte';
  7. export let visible = false;
  8. export let onClose = () => {};
  9. export let state = '';
  10. export let commodity = '';
  11. export let prefill = null;
  12. const dispatch = createEventDispatcher();
  13. let valorSaca = '';
  14. let quantidade = '';
  15. let referencia = '';
  16. let validade = new Date();
  17. let validadeInput = validade.toISOString().slice(0, 10);
  18. $: total = (Number(valorSaca) || 0) * (Number(quantidade) || 0);
  19. $: if (visible && prefill) {
  20. if (prefill.valorSaca != null || prefill.valor != null) {
  21. valorSaca = String(prefill.valorSaca ?? prefill.valor ?? '');
  22. }
  23. if (prefill.quantidade != null) {
  24. quantidade = String(prefill.quantidade ?? '');
  25. }
  26. }
  27. let calendarEl;
  28. // Inicializa Flatpickr
  29. function initCalendar() {
  30. flatpickr(calendarEl, {
  31. inline: true,
  32. appendTo: calendarEl,
  33. defaultDate: validade,
  34. locale: Portuguese,
  35. dateFormat: 'Y-m-d',
  36. onChange: (selectedDates) => {
  37. validade = selectedDates[0];
  38. validadeInput = validade.toISOString().slice(0, 10);
  39. }
  40. });
  41. }
  42. function handleValidadeInput(event) {
  43. const str = event.target.value;
  44. validade = str ? new Date(str) : new Date();
  45. if (calendarEl._flatpickr) {
  46. calendarEl._flatpickr.setDate(validade);
  47. }
  48. }
  49. function confirmar() {
  50. const dados = {
  51. tipo: 'compra',
  52. valorSaca: Number(valorSaca) || 0,
  53. quantidade: Number(quantidade) || 0,
  54. referencia,
  55. total,
  56. validade: validadeInput,
  57. estado: state,
  58. commodity
  59. };
  60. //console.log('BoletaCompra confirmada', dados);
  61. dispatch('confirm', dados);
  62. try { onClose?.(); } catch {}
  63. }
  64. function formatBRL(n) {
  65. return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(Number(n || 0));
  66. }
  67. </script>
  68. <ModalBase title="Boleta de Compra" {visible} {onClose}>
  69. <div class="space-y-4">
  70. <!-- VALOR E QUANTIDADE -->
  71. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  72. <div>
  73. <label class="block text-sm font-medium text-gray-700 dark:text-gray-300" for="buy-valor-saca">Valor/saca</label>
  74. <input id="buy-valor-saca" type="number" min="0" step="0.01" bind:value={valorSaca}
  75. 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" />
  76. </div>
  77. <div>
  78. <label class="block text-sm font-medium text-gray-700 dark:text-gray-300" for="buy-quantidade">Quantidade em saca</label>
  79. <input id="buy-quantidade" type="number" min="0" step="1" bind:value={quantidade}
  80. 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" />
  81. </div>
  82. </div>
  83. <!-- REFERÊNCIA E TOTAL -->
  84. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  85. <div>
  86. <p class="block text-sm font-medium text-gray-700 dark:text-gray-300">eBRL: {referencia}</p>
  87. </div>
  88. <div>
  89. <p class="block text-sm font-medium text-gray-700 dark:text-gray-300">Valor total: {formatBRL(total)}</p>
  90. </div>
  91. </div>
  92. <!-- VALIDADE E CALENDÁRIO -->
  93. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  94. <!-- input de validade -->
  95. <div>
  96. <label class="block text-sm font-medium text-gray-700 dark:text-gray-300" for="buy-validade">Validade</label>
  97. <input id="buy-validade" type="date" bind:value={validadeInput} on:input={handleValidadeInput}
  98. 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" />
  99. <!-- ESTADO E COMMODITY -->
  100. <div class="mt-4">
  101. <label class="block text-sm font-medium text-gray-700 dark:text-gray-300" for="buy-estado">Estado</label>
  102. <input id="buy-estado" value={state} readonly
  103. 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" />
  104. </div>
  105. <div class="mt-4">
  106. <label class="block text-sm font-medium text-gray-700 dark:text-gray-300" for="buy-commodity">Commodity</label>
  107. <input id="buy-commodity" value={commodity} readonly
  108. 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" />
  109. </div>
  110. </div>
  111. <!-- Calendário inline -->
  112. <div>
  113. <p class="block text-sm font-medium text-gray-700 dark:text-gray-300">Calendário</p>
  114. <div bind:this={calendarEl} class="mt-1 w-56 text-sm calendar-override rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700/70 text-gray-900 dark:text-gray-200 p-2" use:initCalendar></div>
  115. </div>
  116. </div>
  117. <!-- BOTÃO CONFIRMAR -->
  118. <div class="flex justify-end pt-2">
  119. <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"
  120. on:click={confirmar}>
  121. Confirmar Compra
  122. </button>
  123. </div>
  124. </div>
  125. </ModalBase>
  126. <style>
  127. :global(.calendar-override .flatpickr-calendar) {
  128. position: static !important;
  129. background: transparent !important;
  130. border: 0 !important;
  131. box-shadow: none !important;
  132. }
  133. :global(.calendar-override .flatpickr-months) {
  134. border-bottom: 1px solid #e5e7eb;
  135. }
  136. :global(.dark .calendar-override .flatpickr-months) {
  137. border-color: #374151;
  138. }
  139. :global(.calendar-override .flatpickr-current-month .cur-month),
  140. :global(.calendar-override .flatpickr-current-month .numInputWrapper input) {
  141. color: #111827;
  142. font-weight: 600;
  143. }
  144. :global(.dark .calendar-override .flatpickr-current-month .cur-month),
  145. :global(.dark .calendar-override .flatpickr-current-month .numInputWrapper input) {
  146. color: #e5e7eb;
  147. }
  148. :global(.calendar-override .flatpickr-weekday) {
  149. color: #6b7280;
  150. font-weight: 500;
  151. }
  152. :global(.dark .calendar-override .flatpickr-weekday) {
  153. color: #9ca3af;
  154. }
  155. :global(.calendar-override .flatpickr-day) {
  156. color: #111827;
  157. border-radius: 0.375rem;
  158. }
  159. :global(.dark .calendar-override .flatpickr-day) {
  160. color: #e5e7eb;
  161. }
  162. :global(.calendar-override .flatpickr-day:hover) {
  163. background: #f3f4f6;
  164. }
  165. :global(.dark .calendar-override .flatpickr-day:hover) {
  166. background: #374151;
  167. }
  168. :global(.calendar-override .flatpickr-day.selected),
  169. :global(.calendar-override .flatpickr-day.startRange),
  170. :global(.calendar-override .flatpickr-day.endRange) {
  171. background: #16a34a !important; /* green-600 */
  172. color: #ffffff !important;
  173. border-color: transparent !important;
  174. }
  175. :global(.calendar-override .flatpickr-day.selected:hover),
  176. :global(.calendar-override .flatpickr-day.startRange:hover),
  177. :global(.calendar-override .flatpickr-day.endRange:hover) {
  178. background: #15803d !important; /* green-700 */
  179. }
  180. :global(.calendar-override .flatpickr-day.today) {
  181. box-shadow: inset 0 -2px 0 #16a34a;
  182. }
  183. :global(.calendar-override .flatpickr-day.disabled),
  184. :global(.calendar-override .flatpickr-day.prevMonthDay),
  185. :global(.calendar-override .flatpickr-day.nextMonthDay) {
  186. color: #9ca3af;
  187. }
  188. :global(.dark .calendar-override .flatpickr-day.disabled),
  189. :global(.dark .calendar-override .flatpickr-day.prevMonthDay),
  190. :global(.dark .calendar-override .flatpickr-day.nextMonthDay) {
  191. color: #6b7280;
  192. }
  193. :global(.calendar-override .flatpickr-prev-month svg),
  194. :global(.calendar-override .flatpickr-next-month svg) {
  195. fill: #6b7280;
  196. }
  197. :global(.calendar-override .flatpickr-prev-month:hover svg),
  198. :global(.calendar-override .flatpickr-next-month:hover svg) {
  199. fill: #111827;
  200. }
  201. :global(.dark .calendar-override .flatpickr-prev-month svg),
  202. :global(.dark .calendar-override .flatpickr-next-month svg) {
  203. fill: #9ca3af;
  204. }
  205. :global(.dark .calendar-override .flatpickr-prev-month:hover svg),
  206. :global(.dark .calendar-override .flatpickr-next-month:hover svg) {
  207. fill: #e5e7eb;
  208. }
  209. :global(.calendar-override .flatpickr-calendar) {
  210. width: 100% !important;
  211. }
  212. :global(.calendar-override .flatpickr-months) {
  213. padding: 0.25rem 0.25rem;
  214. }
  215. :global(.calendar-override .flatpickr-current-month) {
  216. font-size: 0.875rem; /* text-sm */
  217. }
  218. :global(.calendar-override .flatpickr-weekday) {
  219. font-size: 0.75rem; /* text-xs */
  220. }
  221. :global(.calendar-override .dayContainer) {
  222. padding: 0.25rem !important;
  223. }
  224. :global(.calendar-override .flatpickr-day) {
  225. width: 1.75rem;
  226. height: 1.75rem;
  227. line-height: 1.75rem;
  228. max-width: 1.75rem;
  229. font-size: 0.875rem; /* text-sm */
  230. margin: 0.125rem;
  231. }
  232. :global(.calendar-override .flatpickr-prev-month),
  233. :global(.calendar-override .flatpickr-next-month) {
  234. top: 0.35rem;
  235. }
  236. :global(.calendar-override .flatpickr-current-month .numInputWrapper input) {
  237. max-width: 3.5rem;
  238. }
  239. /* Ensure smaller footprint and dark-friendly backgrounds */
  240. :global(.calendar-override .flatpickr-calendar) {
  241. max-width: none !important;
  242. min-width: 0 !important;
  243. }
  244. :global(.calendar-override .flatpickr-innerContainer),
  245. :global(.calendar-override .flatpickr-months),
  246. :global(.calendar-override .flatpickr-weekdays),
  247. :global(.calendar-override .flatpickr-days) {
  248. background: transparent !important;
  249. }
  250. :global(.calendar-override .flatpickr-days),
  251. :global(.calendar-override .dayContainer) {
  252. width: auto !important;
  253. min-width: 0 !important;
  254. }
  255. :global(.calendar-override .dayContainer) {
  256. padding: 0.125rem !important;
  257. }
  258. :global(.calendar-override .flatpickr-day) {
  259. width: 1.5rem;
  260. height: 1.5rem;
  261. line-height: 1.5rem;
  262. max-width: 1.5rem;
  263. font-size: 0.875rem; /* text-sm */
  264. margin: 0.1rem;
  265. }
  266. :global(.calendar-override .flatpickr-prev-month svg),
  267. :global(.calendar-override .flatpickr-next-month svg) {
  268. width: 0.875rem;
  269. height: 0.875rem;
  270. }
  271. /* Hide top navigation arrows */
  272. :global(.calendar-override .flatpickr-prev-month),
  273. :global(.calendar-override .flatpickr-next-month) {
  274. display: none !important;
  275. }
  276. </style>