@hed-hog/finance 0.0.236 → 0.0.237

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,6 +46,7 @@ import {
46
46
  import {
47
47
  DndContext,
48
48
  DragEndEvent,
49
+ DragOverEvent,
49
50
  PointerSensor,
50
51
  closestCenter,
51
52
  useSensor,
@@ -53,7 +54,6 @@ import {
53
54
  } from '@dnd-kit/core';
54
55
  import {
55
56
  SortableContext,
56
- arrayMove,
57
57
  useSortable,
58
58
  verticalListSortingStrategy,
59
59
  } from '@dnd-kit/sortable';
@@ -69,7 +69,7 @@ import {
69
69
  Trash2,
70
70
  } from 'lucide-react';
71
71
  import { useTranslations } from 'next-intl';
72
- import { useMemo, useState } from 'react';
72
+ import { useEffect, useMemo, useState } from 'react';
73
73
  import { useForm } from 'react-hook-form';
74
74
  import { z } from 'zod';
75
75
 
@@ -157,11 +157,8 @@ function CategoriaSheet({
157
157
  },
158
158
  });
159
159
 
160
- const handleOpenChange = (nextOpen: boolean) => {
161
- onOpenChange(nextOpen);
162
-
163
- if (!nextOpen) {
164
- setEditing(null);
160
+ useEffect(() => {
161
+ if (!open) {
165
162
  form.reset({ nome: '', natureza: 'despesa', parentId: 'root' });
166
163
  return;
167
164
  }
@@ -172,6 +169,18 @@ function CategoriaSheet({
172
169
  natureza: editing.natureza,
173
170
  parentId: editing.parentId || 'root',
174
171
  });
172
+ return;
173
+ }
174
+
175
+ form.reset({ nome: '', natureza: 'despesa', parentId: 'root' });
176
+ }, [open, editing, form]);
177
+
178
+ const handleOpenChange = (nextOpen: boolean) => {
179
+ onOpenChange(nextOpen);
180
+
181
+ if (!nextOpen) {
182
+ setEditing(null);
183
+ form.reset({ nome: '', natureza: 'despesa', parentId: 'root' });
175
184
  }
176
185
  };
177
186
 
@@ -254,7 +263,7 @@ function CategoriaSheet({
254
263
  <FormLabel>{t('sheet.fields.kind')}</FormLabel>
255
264
  <Select value={field.value} onValueChange={field.onChange}>
256
265
  <FormControl>
257
- <SelectTrigger>
266
+ <SelectTrigger className="w-full">
258
267
  <SelectValue placeholder={t('common.select')} />
259
268
  </SelectTrigger>
260
269
  </FormControl>
@@ -292,7 +301,7 @@ function CategoriaSheet({
292
301
  onValueChange={field.onChange}
293
302
  >
294
303
  <FormControl>
295
- <SelectTrigger>
304
+ <SelectTrigger className="w-full">
296
305
  <SelectValue placeholder={t('sheet.fields.noParent')} />
297
306
  </SelectTrigger>
298
307
  </FormControl>
@@ -339,6 +348,7 @@ function CategoryRow({
339
348
  setExpanded,
340
349
  onEdit,
341
350
  onDelete,
351
+ dropHint,
342
352
  t,
343
353
  }: {
344
354
  item: CategoryNode & { level: number };
@@ -346,6 +356,7 @@ function CategoryRow({
346
356
  setExpanded: (value: Set<string>) => void;
347
357
  onEdit: (item: FinanceCategory) => void;
348
358
  onDelete: (id: string) => void;
359
+ dropHint?: 'inside' | 'before' | 'after';
349
360
  t: ReturnType<typeof useTranslations>;
350
361
  }) {
351
362
  const sortable = useSortable({ id: item.id });
@@ -363,7 +374,11 @@ function CategoryRow({
363
374
  <div
364
375
  ref={sortable.setNodeRef}
365
376
  style={style}
366
- className="rounded-md border bg-background"
377
+ className={`rounded-md border bg-background ${
378
+ dropHint === 'inside' ? 'ring-1 ring-primary/50 bg-muted/40' : ''
379
+ } ${dropHint === 'before' ? 'border-t-2 border-t-primary' : ''} ${
380
+ dropHint === 'after' ? 'border-b-2 border-b-primary' : ''
381
+ }`}
367
382
  >
368
383
  <div
369
384
  className="flex items-center justify-between gap-2 p-3"
@@ -439,6 +454,10 @@ export default function CategoriesPage() {
439
454
  const [editing, setEditing] = useState<FinanceCategory | null>(null);
440
455
  const [deleteId, setDeleteId] = useState<string | null>(null);
441
456
  const [expanded, setExpanded] = useState<Set<string>>(new Set());
457
+ const [dragOverHint, setDragOverHint] = useState<{
458
+ overId: string;
459
+ mode: 'inside' | 'before' | 'after';
460
+ } | null>(null);
442
461
 
443
462
  const sensors = useSensors(
444
463
  useSensor(PointerSensor, { activationConstraint: { distance: 6 } })
@@ -492,27 +511,42 @@ export default function CategoriesPage() {
492
511
  const activeId = String(active.id);
493
512
  const overId = String(over.id);
494
513
 
495
- const activeIndex = flat.findIndex((item) => item.id === activeId);
496
- const overIndex = flat.findIndex((item) => item.id === overId);
514
+ const overRect = over.rect;
515
+ const activeTop =
516
+ active.rect.current.translated?.top ?? active.rect.current.initial?.top;
497
517
 
498
- if (activeIndex === -1 || overIndex === -1) {
499
- return;
500
- }
518
+ const isDropInsideTarget =
519
+ typeof activeTop === 'number' &&
520
+ activeTop > overRect.top + overRect.height * 0.25 &&
521
+ activeTop < overRect.top + overRect.height * 0.75;
501
522
 
502
- const reordered = arrayMove(flat, activeIndex, overIndex);
503
- const movedItem = reordered[overIndex];
504
- if (!movedItem) {
505
- return;
523
+ const overParentId = idToParent.get(overId) || null;
524
+ const targetParentId = isDropInsideTarget ? overId : overParentId;
525
+
526
+ let cursor: string | null = targetParentId;
527
+ while (cursor) {
528
+ if (cursor === activeId) {
529
+ return;
530
+ }
531
+ cursor = idToParent.get(cursor) || null;
506
532
  }
507
- const targetParentId = idToParent.get(overId) || null;
508
533
 
509
- const siblings = reordered.filter(
534
+ const siblings = categories.filter(
510
535
  (item) =>
511
- (idToParent.get(item.id) || null) === targetParentId &&
512
- item.id !== movedItem.id
536
+ (item.parentId || null) === targetParentId && item.id !== activeId
513
537
  );
514
538
 
515
- const position = siblings.findIndex((item) => item.id === overId);
539
+ let position = siblings.length;
540
+
541
+ if (!isDropInsideTarget) {
542
+ const overIndex = siblings.findIndex((item) => item.id === overId);
543
+ if (overIndex !== -1) {
544
+ const isDropAfter =
545
+ typeof activeTop === 'number' &&
546
+ activeTop > overRect.top + overRect.height / 2;
547
+ position = isDropAfter ? overIndex + 1 : overIndex;
548
+ }
549
+ }
516
550
 
517
551
  try {
518
552
  await request({
@@ -520,15 +554,57 @@ export default function CategoriesPage() {
520
554
  method: 'PATCH',
521
555
  data: {
522
556
  parent_id: targetParentId ? Number(targetParentId) : null,
523
- position: position < 0 ? siblings.length : position,
557
+ position,
524
558
  },
525
559
  });
526
560
 
527
561
  await refetch();
562
+ if (targetParentId) {
563
+ setExpanded((prev) => {
564
+ const next = new Set(prev);
565
+ next.add(targetParentId);
566
+ return next;
567
+ });
568
+ }
528
569
  showToastHandler?.('success', t('messages.moveSuccess'));
529
570
  } catch {
530
571
  showToastHandler?.('error', t('messages.moveError'));
572
+ } finally {
573
+ setDragOverHint(null);
574
+ }
575
+ };
576
+
577
+ const handleDragOver = (event: DragOverEvent) => {
578
+ const { active, over } = event;
579
+
580
+ if (!over || active.id === over.id) {
581
+ setDragOverHint(null);
582
+ return;
531
583
  }
584
+
585
+ const overRect = over.rect;
586
+ const activeTop =
587
+ active.rect.current.translated?.top ?? active.rect.current.initial?.top;
588
+
589
+ if (typeof activeTop !== 'number') {
590
+ setDragOverHint(null);
591
+ return;
592
+ }
593
+
594
+ const overId = String(over.id);
595
+ const inside =
596
+ activeTop > overRect.top + overRect.height * 0.25 &&
597
+ activeTop < overRect.top + overRect.height * 0.75;
598
+
599
+ if (inside) {
600
+ setDragOverHint({ overId, mode: 'inside' });
601
+ return;
602
+ }
603
+
604
+ setDragOverHint({
605
+ overId,
606
+ mode: activeTop > overRect.top + overRect.height / 2 ? 'after' : 'before',
607
+ });
532
608
  };
533
609
 
534
610
  return (
@@ -576,7 +652,9 @@ export default function CategoriesPage() {
576
652
  <DndContext
577
653
  sensors={sensors}
578
654
  collisionDetection={closestCenter}
655
+ onDragOver={handleDragOver}
579
656
  onDragEnd={handleDragEnd}
657
+ onDragCancel={() => setDragOverHint(null)}
580
658
  >
581
659
  <SortableContext
582
660
  items={flat.map((item) => item.id)}
@@ -589,6 +667,11 @@ export default function CategoriesPage() {
589
667
  item={item}
590
668
  expanded={expanded}
591
669
  setExpanded={setExpanded}
670
+ dropHint={
671
+ dragOverHint?.overId === item.id
672
+ ? dragOverHint.mode
673
+ : undefined
674
+ }
592
675
  onEdit={(category) => {
593
676
  setEditing(category);
594
677
  setSheetOpen(true);
@@ -40,7 +40,7 @@ import { useApp, useQuery } from '@hed-hog/next-app-provider';
40
40
  import { zodResolver } from '@hookform/resolvers/zod';
41
41
  import { Building2, Pencil, Plus, Trash2 } from 'lucide-react';
42
42
  import { useTranslations } from 'next-intl';
43
- import { useState } from 'react';
43
+ import { useEffect, useState } from 'react';
44
44
  import { useForm } from 'react-hook-form';
45
45
  import { z } from 'zod';
46
46
 
@@ -84,6 +84,20 @@ function CentroCustoSheet({
84
84
  },
85
85
  });
86
86
 
87
+ useEffect(() => {
88
+ if (!open) {
89
+ form.reset({ nome: '' });
90
+ return;
91
+ }
92
+
93
+ if (editingCostCenter) {
94
+ form.reset({ nome: editingCostCenter.nome });
95
+ return;
96
+ }
97
+
98
+ form.reset({ nome: '' });
99
+ }, [open, editingCostCenter, form]);
100
+
87
101
  const handleSubmit = async (values: CostCenterFormValues) => {
88
102
  try {
89
103
  if (editingCostCenter) {
@@ -130,13 +144,6 @@ function CentroCustoSheet({
130
144
  if (!nextOpen) {
131
145
  form.reset({ nome: '' });
132
146
  onEditingChange(null);
133
- return;
134
- }
135
-
136
- if (editingCostCenter) {
137
- form.reset({
138
- nome: editingCostCenter.nome,
139
- });
140
147
  }
141
148
  };
142
149
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/finance",
3
- "version": "0.0.236",
3
+ "version": "0.0.237",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -9,14 +9,14 @@
9
9
  "@nestjs/core": "^11",
10
10
  "@nestjs/jwt": "^11",
11
11
  "@nestjs/mapped-types": "*",
12
- "@hed-hog/api-locale": "0.0.11",
13
12
  "@hed-hog/api-prisma": "0.0.4",
13
+ "@hed-hog/tag": "0.0.237",
14
14
  "@hed-hog/api-pagination": "0.0.5",
15
15
  "@hed-hog/api-types": "0.0.1",
16
- "@hed-hog/tag": "0.0.236",
17
16
  "@hed-hog/api": "0.0.3",
18
- "@hed-hog/contact": "0.0.236",
19
- "@hed-hog/core": "0.0.236"
17
+ "@hed-hog/contact": "0.0.237",
18
+ "@hed-hog/core": "0.0.237",
19
+ "@hed-hog/api-locale": "0.0.11"
20
20
  },
21
21
  "exports": {
22
22
  ".": {