BoletaCompra.svelte 10 KB

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