@hed-hog/contact 0.0.285 → 0.0.291
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.
- package/dist/contact.service.d.ts +2 -148
- package/dist/contact.service.d.ts.map +1 -1
- package/hedhog/frontend/app/_lib/crm-sections.tsx.ejs +2 -2
- package/hedhog/frontend/app/accounts/page.tsx.ejs +24 -18
- package/hedhog/frontend/app/activities/page.tsx.ejs +805 -8
- package/hedhog/frontend/app/dashboard/page.tsx.ejs +32 -47
- package/hedhog/frontend/app/follow-ups/page.tsx.ejs +689 -8
- package/hedhog/frontend/app/person/_components/person-field-with-create.tsx.ejs +28 -11
- package/hedhog/frontend/app/person/page.tsx.ejs +18 -14
- package/hedhog/frontend/app/pipeline/page.tsx.ejs +39 -66
- package/hedhog/frontend/messages/en.json +161 -0
- package/hedhog/frontend/messages/pt.json +127 -0
- package/package.json +6 -6
- package/src/contact.service.ts +2 -2
|
@@ -100,11 +100,25 @@ function usePersonFieldWithCreateTranslations() {
|
|
|
100
100
|
values?: Record<string, string | number | boolean | null | undefined>
|
|
101
101
|
) => {
|
|
102
102
|
const contactKey = `contact.PersonFieldWithCreate.${key}`;
|
|
103
|
+
// Filter values to only include types compatible with next-intl
|
|
104
|
+
const filteredValues = values
|
|
105
|
+
? Object.fromEntries(
|
|
106
|
+
Object.entries(values).filter(
|
|
107
|
+
([_, v]) => typeof v === 'string' || typeof v === 'number'
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
: undefined;
|
|
103
111
|
if (tRoot.has(contactKey)) {
|
|
104
|
-
return tRoot(
|
|
112
|
+
return tRoot(
|
|
113
|
+
contactKey,
|
|
114
|
+
filteredValues as Record<string, string | number> | undefined
|
|
115
|
+
);
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
return tRoot(
|
|
118
|
+
return tRoot(
|
|
119
|
+
`finance.PersonFieldWithCreate.${key}`,
|
|
120
|
+
filteredValues as Record<string, string | number> | undefined
|
|
121
|
+
);
|
|
108
122
|
};
|
|
109
123
|
}
|
|
110
124
|
|
|
@@ -643,20 +657,22 @@ export function PersonFieldWithCreate<TFieldValues extends FieldValues>({
|
|
|
643
657
|
params.set('search', debouncedPersonSearch.trim());
|
|
644
658
|
}
|
|
645
659
|
|
|
646
|
-
const response = await request<
|
|
660
|
+
const response = await request<
|
|
661
|
+
PaginatedResponse<PersonOption> | PersonOption[]
|
|
662
|
+
>({
|
|
647
663
|
url: `/person?${params.toString()}`,
|
|
648
664
|
method: 'GET',
|
|
649
665
|
});
|
|
650
666
|
|
|
651
667
|
const payload = response?.data;
|
|
652
|
-
if (Array.isArray(payload?.data)) {
|
|
653
|
-
return payload.data as PersonOption[];
|
|
654
|
-
}
|
|
655
|
-
|
|
656
668
|
if (Array.isArray(payload)) {
|
|
657
669
|
return payload as PersonOption[];
|
|
658
670
|
}
|
|
659
671
|
|
|
672
|
+
if (payload && 'data' in payload && Array.isArray(payload.data)) {
|
|
673
|
+
return payload.data as PersonOption[];
|
|
674
|
+
}
|
|
675
|
+
|
|
660
676
|
return [];
|
|
661
677
|
},
|
|
662
678
|
placeholderData: (old) => old ?? [],
|
|
@@ -670,7 +686,10 @@ export function PersonFieldWithCreate<TFieldValues extends FieldValues>({
|
|
|
670
686
|
return (
|
|
671
687
|
<>
|
|
672
688
|
<div className="grid gap-2">
|
|
673
|
-
<Label
|
|
689
|
+
<Label
|
|
690
|
+
data-error={!!fieldState.error}
|
|
691
|
+
className="data-[error=true]:text-destructive"
|
|
692
|
+
>
|
|
674
693
|
{label}
|
|
675
694
|
</Label>
|
|
676
695
|
<Controller
|
|
@@ -798,9 +817,7 @@ export function PersonFieldWithCreate<TFieldValues extends FieldValues>({
|
|
|
798
817
|
}}
|
|
799
818
|
/>
|
|
800
819
|
{fieldState.error?.message ? (
|
|
801
|
-
<p className="text-destructive text-sm">
|
|
802
|
-
{fieldState.error.message}
|
|
803
|
-
</p>
|
|
820
|
+
<p className="text-destructive text-sm">{fieldState.error.message}</p>
|
|
804
821
|
) : null}
|
|
805
822
|
</div>
|
|
806
823
|
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
PaginationFooter,
|
|
9
9
|
SearchBar,
|
|
10
10
|
type SearchBarControl,
|
|
11
|
-
StatsCards,
|
|
12
11
|
} from '@/components/entity-list';
|
|
13
12
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
|
14
13
|
import { Badge } from '@/components/ui/badge';
|
|
@@ -21,6 +20,7 @@ import {
|
|
|
21
20
|
DropdownMenuSeparator,
|
|
22
21
|
DropdownMenuTrigger,
|
|
23
22
|
} from '@/components/ui/dropdown-menu';
|
|
23
|
+
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
24
24
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
25
25
|
import {
|
|
26
26
|
Table,
|
|
@@ -714,32 +714,36 @@ export default function PeoplePage() {
|
|
|
714
714
|
|
|
715
715
|
const statsCards = [
|
|
716
716
|
{
|
|
717
|
+
key: 'total',
|
|
717
718
|
title: t('statsTotal'),
|
|
718
719
|
value: stats.total,
|
|
719
|
-
icon:
|
|
720
|
-
|
|
721
|
-
|
|
720
|
+
icon: Users,
|
|
721
|
+
accentClassName: 'from-slate-500/20 via-slate-400/10 to-transparent',
|
|
722
|
+
iconContainerClassName: 'bg-slate-100 text-slate-700',
|
|
722
723
|
},
|
|
723
724
|
{
|
|
725
|
+
key: 'active',
|
|
724
726
|
title: t('statsActive'),
|
|
725
727
|
value: stats.active,
|
|
726
|
-
icon:
|
|
727
|
-
|
|
728
|
-
|
|
728
|
+
icon: CheckCircle2,
|
|
729
|
+
accentClassName: 'from-green-500/20 via-emerald-500/10 to-transparent',
|
|
730
|
+
iconContainerClassName: 'bg-green-50 text-green-600',
|
|
729
731
|
},
|
|
730
732
|
{
|
|
733
|
+
key: 'individual',
|
|
731
734
|
title: t('statsIndividual'),
|
|
732
735
|
value: stats.individual,
|
|
733
|
-
icon:
|
|
734
|
-
|
|
735
|
-
|
|
736
|
+
icon: UserCheck,
|
|
737
|
+
accentClassName: 'from-blue-500/20 via-cyan-500/10 to-transparent',
|
|
738
|
+
iconContainerClassName: 'bg-blue-50 text-blue-600',
|
|
736
739
|
},
|
|
737
740
|
{
|
|
741
|
+
key: 'company',
|
|
738
742
|
title: t('statsCompany'),
|
|
739
743
|
value: stats.company,
|
|
740
|
-
icon:
|
|
741
|
-
|
|
742
|
-
|
|
744
|
+
icon: Building2,
|
|
745
|
+
accentClassName: 'from-amber-500/20 via-orange-500/10 to-transparent',
|
|
746
|
+
iconContainerClassName: 'bg-amber-50 text-amber-600',
|
|
743
747
|
},
|
|
744
748
|
];
|
|
745
749
|
|
|
@@ -807,7 +811,7 @@ export default function PeoplePage() {
|
|
|
807
811
|
]}
|
|
808
812
|
/>
|
|
809
813
|
|
|
810
|
-
<
|
|
814
|
+
<KpiCardsGrid items={statsCards} />
|
|
811
815
|
|
|
812
816
|
<div className="flex flex-col gap-4 xl:flex-row xl:items-center">
|
|
813
817
|
<div className="flex-1">
|
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
closestCenter,
|
|
5
|
-
DndContext,
|
|
6
|
-
DragOverlay,
|
|
7
|
-
PointerSensor,
|
|
8
|
-
useDraggable,
|
|
9
|
-
useDroppable,
|
|
10
|
-
useSensor,
|
|
11
|
-
useSensors,
|
|
12
|
-
type DragEndEvent,
|
|
13
|
-
type DragStartEvent,
|
|
14
|
-
type UniqueIdentifier,
|
|
15
|
-
} from '@dnd-kit/core';
|
|
16
|
-
import { CSS } from '@dnd-kit/utilities';
|
|
17
3
|
import {
|
|
18
4
|
Page,
|
|
19
5
|
PageHeader,
|
|
@@ -21,7 +7,6 @@ import {
|
|
|
21
7
|
type SearchBarControl,
|
|
22
8
|
} from '@/components/entity-list';
|
|
23
9
|
import { Badge } from '@/components/ui/badge';
|
|
24
|
-
import { Button } from '@/components/ui/button';
|
|
25
10
|
import {
|
|
26
11
|
Card,
|
|
27
12
|
CardContent,
|
|
@@ -34,6 +19,7 @@ import {
|
|
|
34
19
|
CollapsibleContent,
|
|
35
20
|
CollapsibleTrigger,
|
|
36
21
|
} from '@/components/ui/collapsible';
|
|
22
|
+
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
37
23
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
38
24
|
import {
|
|
39
25
|
Tooltip,
|
|
@@ -42,6 +28,20 @@ import {
|
|
|
42
28
|
} from '@/components/ui/tooltip';
|
|
43
29
|
import { useDebounce } from '@/hooks/use-debounce';
|
|
44
30
|
import { cn } from '@/lib/utils';
|
|
31
|
+
import {
|
|
32
|
+
closestCenter,
|
|
33
|
+
DndContext,
|
|
34
|
+
DragOverlay,
|
|
35
|
+
PointerSensor,
|
|
36
|
+
useDraggable,
|
|
37
|
+
useDroppable,
|
|
38
|
+
useSensor,
|
|
39
|
+
useSensors,
|
|
40
|
+
type DragEndEvent,
|
|
41
|
+
type DragStartEvent,
|
|
42
|
+
type UniqueIdentifier,
|
|
43
|
+
} from '@dnd-kit/core';
|
|
44
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
45
45
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
46
46
|
import {
|
|
47
47
|
CalendarCheck,
|
|
@@ -71,8 +71,9 @@ import {
|
|
|
71
71
|
type ReactNode,
|
|
72
72
|
} from 'react';
|
|
73
73
|
import { toast } from 'sonner';
|
|
74
|
-
import { crmImplementedSections } from '../_lib/crm-sections';
|
|
75
74
|
import { crmStageOrder, type CrmLead } from '../_lib/crm-mocks';
|
|
75
|
+
import { crmImplementedSections } from '../_lib/crm-sections';
|
|
76
|
+
import { PersonFormSheet } from '../person/_components/person-form-sheet';
|
|
76
77
|
import type {
|
|
77
78
|
ContactTypeOption,
|
|
78
79
|
DocumentTypeOption,
|
|
@@ -81,7 +82,6 @@ import type {
|
|
|
81
82
|
PersonLifecycleStage,
|
|
82
83
|
UserOption,
|
|
83
84
|
} from '../person/_components/person-types';
|
|
84
|
-
import { PersonFormSheet } from '../person/_components/person-form-sheet';
|
|
85
85
|
import { LeadDetailSheet } from './_components/lead-detail-sheet';
|
|
86
86
|
|
|
87
87
|
const PIPELINE_EXPANDED_KEY = 'contact:crm-pipeline:expanded-leads';
|
|
@@ -154,8 +154,6 @@ function DraggableLeadCard({
|
|
|
154
154
|
'touch-none',
|
|
155
155
|
isDragging && 'z-30 opacity-60 shadow-lg ring-1 ring-primary/30'
|
|
156
156
|
)}
|
|
157
|
-
role="button"
|
|
158
|
-
tabIndex={0}
|
|
159
157
|
onClick={onClick}
|
|
160
158
|
onKeyDown={onKeyDown}
|
|
161
159
|
{...listeners}
|
|
@@ -458,7 +456,24 @@ export default function CrmPipelinePage() {
|
|
|
458
456
|
),
|
|
459
457
|
icon: CircleDollarSign,
|
|
460
458
|
},
|
|
461
|
-
]
|
|
459
|
+
].map((item, index) => {
|
|
460
|
+
const sectionStyle =
|
|
461
|
+
crmImplementedSections[index % crmImplementedSections.length] ??
|
|
462
|
+
crmImplementedSections[0];
|
|
463
|
+
|
|
464
|
+
return {
|
|
465
|
+
key: item.key,
|
|
466
|
+
title: t(`summary.${item.key}.title`),
|
|
467
|
+
value: item.value,
|
|
468
|
+
description: t(`summary.${item.key}.description`, {
|
|
469
|
+
count: nextActions,
|
|
470
|
+
}),
|
|
471
|
+
icon: item.icon,
|
|
472
|
+
accentClassName: sectionStyle?.colorClass ?? fallbackKpiAccentClass,
|
|
473
|
+
iconContainerClassName:
|
|
474
|
+
sectionStyle?.glowClass ?? 'bg-sky-500/10 text-sky-700',
|
|
475
|
+
};
|
|
476
|
+
});
|
|
462
477
|
|
|
463
478
|
const allExpanded =
|
|
464
479
|
visibleLeads.length > 0 && visibleLeads.every((l) => expandedIds.has(l.id));
|
|
@@ -549,7 +564,8 @@ export default function CrmPipelinePage() {
|
|
|
549
564
|
const sourceLead = visibleLeadsById.get(sourceLeadId);
|
|
550
565
|
if (!sourceLead || sourceLead.lifecycle_stage === targetStage) return;
|
|
551
566
|
|
|
552
|
-
const previousStage =
|
|
567
|
+
const previousStage: PersonLifecycleStage =
|
|
568
|
+
sourceLead.lifecycle_stage ?? 'new';
|
|
553
569
|
|
|
554
570
|
setStageOverrides((prev) => ({ ...prev, [sourceLeadId]: targetStage }));
|
|
555
571
|
setSelectedLead((prev) =>
|
|
@@ -633,51 +649,8 @@ export default function CrmPipelinePage() {
|
|
|
633
649
|
]}
|
|
634
650
|
/>
|
|
635
651
|
|
|
636
|
-
<div className="min-w-0 space-y-
|
|
637
|
-
<
|
|
638
|
-
{kpiCards.map((item, index) => {
|
|
639
|
-
const sectionStyle =
|
|
640
|
-
crmImplementedSections[index % crmImplementedSections.length] ??
|
|
641
|
-
crmImplementedSections[0];
|
|
642
|
-
|
|
643
|
-
return (
|
|
644
|
-
<Card
|
|
645
|
-
key={item.key}
|
|
646
|
-
className="min-w-0 overflow-hidden border-border/70 py-0"
|
|
647
|
-
>
|
|
648
|
-
<div
|
|
649
|
-
className={cn(
|
|
650
|
-
'h-1 w-full bg-linear-to-r',
|
|
651
|
-
sectionStyle?.colorClass ?? fallbackKpiAccentClass
|
|
652
|
-
)}
|
|
653
|
-
/>
|
|
654
|
-
<CardContent className="flex items-start justify-between gap-3 px-6 py-5">
|
|
655
|
-
<div className="min-w-0">
|
|
656
|
-
<p className="text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
|
657
|
-
{t(`summary.${item.key}.title`)}
|
|
658
|
-
</p>
|
|
659
|
-
<p className="mt-2 text-3xl font-semibold tracking-tight">
|
|
660
|
-
{item.value}
|
|
661
|
-
</p>
|
|
662
|
-
<p className="mt-1 text-sm text-muted-foreground">
|
|
663
|
-
{t(`summary.${item.key}.description`, {
|
|
664
|
-
count: nextActions,
|
|
665
|
-
})}
|
|
666
|
-
</p>
|
|
667
|
-
</div>
|
|
668
|
-
<div
|
|
669
|
-
className={cn(
|
|
670
|
-
'rounded-2xl p-3',
|
|
671
|
-
sectionStyle?.glowClass ?? 'bg-sky-500/10 text-sky-700'
|
|
672
|
-
)}
|
|
673
|
-
>
|
|
674
|
-
<item.icon className="size-5" />
|
|
675
|
-
</div>
|
|
676
|
-
</CardContent>
|
|
677
|
-
</Card>
|
|
678
|
-
);
|
|
679
|
-
})}
|
|
680
|
-
</div>
|
|
652
|
+
<div className="min-w-0 space-y-4 overflow-x-hidden">
|
|
653
|
+
<KpiCardsGrid items={kpiCards} />
|
|
681
654
|
|
|
682
655
|
<SearchBar
|
|
683
656
|
searchQuery={searchTerm}
|
|
@@ -184,9 +184,43 @@
|
|
|
184
184
|
"duplicateMergeError": "Failed to merge records.",
|
|
185
185
|
"owner": "Owner",
|
|
186
186
|
"unassigned": "Unassigned",
|
|
187
|
+
"source": "Source",
|
|
188
|
+
"sourceReferral": "Referral",
|
|
189
|
+
"sourceWebsite": "Website",
|
|
190
|
+
"sourceSocial": "Social",
|
|
191
|
+
"sourceInbound": "Inbound",
|
|
192
|
+
"sourceOutbound": "Outbound",
|
|
193
|
+
"sourceOther": "Other",
|
|
194
|
+
"lifecycleStage": "Stage",
|
|
195
|
+
"lifecycleNew": "New",
|
|
196
|
+
"lifecycleContacted": "Contacted",
|
|
197
|
+
"lifecycleQualified": "Qualified",
|
|
198
|
+
"lifecycleProposal": "Proposal",
|
|
199
|
+
"lifecycleNegotiation": "Negotiation",
|
|
200
|
+
"lifecycleCustomer": "Customer",
|
|
201
|
+
"lifecycleLost": "Lost",
|
|
202
|
+
"nextActionAt": "Next action",
|
|
203
|
+
"nextActionEmpty": "No follow-up",
|
|
187
204
|
"registerInteraction": "Register Interaction",
|
|
205
|
+
"registerInteractionDescription": "Register the latest contact made with this person.",
|
|
188
206
|
"scheduleFollowup": "Schedule Follow-up",
|
|
207
|
+
"scheduleFollowupDescription": "Set the date and owner for the next action.",
|
|
189
208
|
"interactionType": "Interaction Type",
|
|
209
|
+
"interactionTypeCall": "Call",
|
|
210
|
+
"interactionTypeEmail": "Email",
|
|
211
|
+
"interactionTypeWhatsapp": "WhatsApp",
|
|
212
|
+
"interactionTypeMeeting": "Meeting",
|
|
213
|
+
"interactionTypeNote": "Note",
|
|
214
|
+
"interactionDate": "Interaction date",
|
|
215
|
+
"interactionOutcome": "Outcome",
|
|
216
|
+
"interactionOutcomePlaceholder": "E.g. Interested, asked for a proposal...",
|
|
217
|
+
"interactionNotesPlaceholder": "Write a quick summary...",
|
|
218
|
+
"interactionCreateSuccess": "Interaction recorded successfully.",
|
|
219
|
+
"interactionCreateError": "Failed to record interaction.",
|
|
220
|
+
"nextActionRequired": "The next action date is required.",
|
|
221
|
+
"schedule": "Schedule",
|
|
222
|
+
"saveAndNew": "Save and new",
|
|
223
|
+
"formShortcutsHint": "Shortcuts: Ctrl/Cmd+S to save, Esc to close.",
|
|
190
224
|
"interactionType_call": "Call",
|
|
191
225
|
"interactionType_email": "Email",
|
|
192
226
|
"interactionType_whatsapp": "WhatsApp",
|
|
@@ -610,6 +644,133 @@
|
|
|
610
644
|
"age": "Days since first contact with this lead."
|
|
611
645
|
}
|
|
612
646
|
},
|
|
647
|
+
"CrmFollowups": {
|
|
648
|
+
"title": "Follow-ups",
|
|
649
|
+
"description": "Track upcoming actions, overdue items, and owner commitments in one operational list.",
|
|
650
|
+
"newFollowup": "New Follow-up",
|
|
651
|
+
"reschedule": "Reschedule",
|
|
652
|
+
"unassigned": "Unassigned",
|
|
653
|
+
"stats": {
|
|
654
|
+
"total": "Total follow-ups",
|
|
655
|
+
"today": "Due today",
|
|
656
|
+
"overdue": "Overdue",
|
|
657
|
+
"upcoming": "Upcoming"
|
|
658
|
+
},
|
|
659
|
+
"filters": {
|
|
660
|
+
"searchPlaceholder": "Search by lead, contact, or account name...",
|
|
661
|
+
"statusPlaceholder": "Status",
|
|
662
|
+
"statusAll": "All statuses"
|
|
663
|
+
},
|
|
664
|
+
"status": {
|
|
665
|
+
"today": "Today",
|
|
666
|
+
"upcoming": "Upcoming",
|
|
667
|
+
"overdue": "Overdue"
|
|
668
|
+
},
|
|
669
|
+
"table": {
|
|
670
|
+
"person": "Person",
|
|
671
|
+
"owner": "Owner",
|
|
672
|
+
"nextAction": "Next action",
|
|
673
|
+
"lastInteraction": "Last interaction",
|
|
674
|
+
"status": "Status",
|
|
675
|
+
"actions": "Actions"
|
|
676
|
+
},
|
|
677
|
+
"empty": {
|
|
678
|
+
"title": "No follow-ups found",
|
|
679
|
+
"description": "Adjust your filters or create a new follow-up to start managing next actions."
|
|
680
|
+
},
|
|
681
|
+
"sheet": {
|
|
682
|
+
"title": "Schedule follow-up",
|
|
683
|
+
"description": "Define person, date, and notes for the next action.",
|
|
684
|
+
"submit": "Save follow-up"
|
|
685
|
+
},
|
|
686
|
+
"form": {
|
|
687
|
+
"person": "Person",
|
|
688
|
+
"personPlaceholder": "Select a person",
|
|
689
|
+
"date": "Follow-up date",
|
|
690
|
+
"notes": "Notes",
|
|
691
|
+
"notesPlaceholder": "Add context for the next interaction...",
|
|
692
|
+
"personRequired": "Select a person.",
|
|
693
|
+
"dateRequired": "Set a date and time."
|
|
694
|
+
},
|
|
695
|
+
"toasts": {
|
|
696
|
+
"scheduleSuccess": "Follow-up scheduled successfully",
|
|
697
|
+
"scheduleError": "Failed to schedule follow-up"
|
|
698
|
+
},
|
|
699
|
+
"errors": {
|
|
700
|
+
"invalidPerson": "Invalid person.",
|
|
701
|
+
"invalidDate": "Invalid date."
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
"CrmActivities": {
|
|
705
|
+
"title": "Activities",
|
|
706
|
+
"description": "Operational activity queue with calls, meetings, messages, and tasks tracked by owner and due date.",
|
|
707
|
+
"viewMode": "View",
|
|
708
|
+
"viewModeTable": "Table",
|
|
709
|
+
"viewModeTimeline": "Timeline",
|
|
710
|
+
"unassignedPerson": "Unassigned lead",
|
|
711
|
+
"unassignedOwner": "Unassigned",
|
|
712
|
+
"stats": {
|
|
713
|
+
"total": "Total activities",
|
|
714
|
+
"pending": "Pending",
|
|
715
|
+
"overdue": "Overdue",
|
|
716
|
+
"completed": "Completed"
|
|
717
|
+
},
|
|
718
|
+
"filters": {
|
|
719
|
+
"searchPlaceholder": "Search by subject, lead, owner, or notes...",
|
|
720
|
+
"statusPlaceholder": "Status",
|
|
721
|
+
"typePlaceholder": "Type",
|
|
722
|
+
"priorityPlaceholder": "Priority",
|
|
723
|
+
"allStatuses": "All statuses",
|
|
724
|
+
"allTypes": "All types",
|
|
725
|
+
"allPriorities": "All priorities"
|
|
726
|
+
},
|
|
727
|
+
"status": {
|
|
728
|
+
"pending": "Pending",
|
|
729
|
+
"overdue": "Overdue",
|
|
730
|
+
"completed": "Completed"
|
|
731
|
+
},
|
|
732
|
+
"type": {
|
|
733
|
+
"call": "Call",
|
|
734
|
+
"email": "Email",
|
|
735
|
+
"meeting": "Meeting",
|
|
736
|
+
"whatsapp": "WhatsApp",
|
|
737
|
+
"task": "Task",
|
|
738
|
+
"note": "Note"
|
|
739
|
+
},
|
|
740
|
+
"priority": {
|
|
741
|
+
"high": "High",
|
|
742
|
+
"medium": "Medium",
|
|
743
|
+
"low": "Low"
|
|
744
|
+
},
|
|
745
|
+
"table": {
|
|
746
|
+
"activity": "Activity",
|
|
747
|
+
"person": "Lead / Contact",
|
|
748
|
+
"owner": "Owner",
|
|
749
|
+
"dueAt": "Due at",
|
|
750
|
+
"createdAt": "Created",
|
|
751
|
+
"status": "Status",
|
|
752
|
+
"priority": "Priority",
|
|
753
|
+
"actions": "Actions"
|
|
754
|
+
},
|
|
755
|
+
"actions": {
|
|
756
|
+
"view": "View",
|
|
757
|
+
"complete": "Mark done"
|
|
758
|
+
},
|
|
759
|
+
"timeline": {
|
|
760
|
+
"description": "Chronological timeline view of activities in the current filter.",
|
|
761
|
+
"dueLabel": "Due",
|
|
762
|
+
"createdLabel": "Created"
|
|
763
|
+
},
|
|
764
|
+
"empty": {
|
|
765
|
+
"title": "No activities found",
|
|
766
|
+
"description": "Adjust filters to find activities or clear filters to return to the full queue.",
|
|
767
|
+
"resetFilters": "Clear filters"
|
|
768
|
+
},
|
|
769
|
+
"toasts": {
|
|
770
|
+
"markedAsCompleted": "Activity marked as completed.",
|
|
771
|
+
"openDetails": "Opening activity: {subject}"
|
|
772
|
+
}
|
|
773
|
+
},
|
|
613
774
|
"CrmFuturePage": {
|
|
614
775
|
"breadcrumbs": {
|
|
615
776
|
"home": "Home",
|
|
@@ -643,6 +643,133 @@
|
|
|
643
643
|
"age": "Dias desde o primeiro contato com este lead."
|
|
644
644
|
}
|
|
645
645
|
},
|
|
646
|
+
"CrmFollowups": {
|
|
647
|
+
"title": "Follow-ups",
|
|
648
|
+
"description": "Acompanhe próximas ações, vencidos e compromissos por responsável em uma lista operacional.",
|
|
649
|
+
"newFollowup": "Novo Follow-up",
|
|
650
|
+
"reschedule": "Reagendar",
|
|
651
|
+
"unassigned": "Não atribuído",
|
|
652
|
+
"stats": {
|
|
653
|
+
"total": "Total de follow-ups",
|
|
654
|
+
"today": "Vencem hoje",
|
|
655
|
+
"overdue": "Vencidos",
|
|
656
|
+
"upcoming": "Próximos"
|
|
657
|
+
},
|
|
658
|
+
"filters": {
|
|
659
|
+
"searchPlaceholder": "Buscar por lead, contato ou conta...",
|
|
660
|
+
"statusPlaceholder": "Status",
|
|
661
|
+
"statusAll": "Todos os status"
|
|
662
|
+
},
|
|
663
|
+
"status": {
|
|
664
|
+
"today": "Hoje",
|
|
665
|
+
"upcoming": "Próximos",
|
|
666
|
+
"overdue": "Vencidos"
|
|
667
|
+
},
|
|
668
|
+
"table": {
|
|
669
|
+
"person": "Pessoa",
|
|
670
|
+
"owner": "Responsável",
|
|
671
|
+
"nextAction": "Próxima ação",
|
|
672
|
+
"lastInteraction": "Última interação",
|
|
673
|
+
"status": "Status",
|
|
674
|
+
"actions": "Ações"
|
|
675
|
+
},
|
|
676
|
+
"empty": {
|
|
677
|
+
"title": "Nenhum follow-up encontrado",
|
|
678
|
+
"description": "Ajuste os filtros ou crie um novo follow-up para começar a gerir as próximas ações."
|
|
679
|
+
},
|
|
680
|
+
"sheet": {
|
|
681
|
+
"title": "Agendar follow-up",
|
|
682
|
+
"description": "Defina pessoa, data e notas para a próxima ação.",
|
|
683
|
+
"submit": "Salvar follow-up"
|
|
684
|
+
},
|
|
685
|
+
"form": {
|
|
686
|
+
"person": "Pessoa",
|
|
687
|
+
"personPlaceholder": "Selecione uma pessoa",
|
|
688
|
+
"date": "Data do follow-up",
|
|
689
|
+
"notes": "Notas",
|
|
690
|
+
"notesPlaceholder": "Adicione contexto para a próxima interação...",
|
|
691
|
+
"personRequired": "Selecione uma pessoa.",
|
|
692
|
+
"dateRequired": "Defina data e hora."
|
|
693
|
+
},
|
|
694
|
+
"toasts": {
|
|
695
|
+
"scheduleSuccess": "Follow-up agendado com sucesso",
|
|
696
|
+
"scheduleError": "Falha ao agendar follow-up"
|
|
697
|
+
},
|
|
698
|
+
"errors": {
|
|
699
|
+
"invalidPerson": "Pessoa inválida.",
|
|
700
|
+
"invalidDate": "Data inválida."
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
"CrmActivities": {
|
|
704
|
+
"title": "Atividades",
|
|
705
|
+
"description": "Fila operacional de atividades com ligações, reuniões, mensagens e tarefas por responsável e prazo.",
|
|
706
|
+
"viewMode": "Visualização",
|
|
707
|
+
"viewModeTable": "Tabela",
|
|
708
|
+
"viewModeTimeline": "Timeline",
|
|
709
|
+
"unassignedPerson": "Lead não atribuído",
|
|
710
|
+
"unassignedOwner": "Não atribuído",
|
|
711
|
+
"stats": {
|
|
712
|
+
"total": "Total de atividades",
|
|
713
|
+
"pending": "Pendentes",
|
|
714
|
+
"overdue": "Atrasadas",
|
|
715
|
+
"completed": "Concluídas"
|
|
716
|
+
},
|
|
717
|
+
"filters": {
|
|
718
|
+
"searchPlaceholder": "Buscar por assunto, lead, responsável ou notas...",
|
|
719
|
+
"statusPlaceholder": "Status",
|
|
720
|
+
"typePlaceholder": "Tipo",
|
|
721
|
+
"priorityPlaceholder": "Prioridade",
|
|
722
|
+
"allStatuses": "Todos os status",
|
|
723
|
+
"allTypes": "Todos os tipos",
|
|
724
|
+
"allPriorities": "Todas as prioridades"
|
|
725
|
+
},
|
|
726
|
+
"status": {
|
|
727
|
+
"pending": "Pendente",
|
|
728
|
+
"overdue": "Atrasada",
|
|
729
|
+
"completed": "Concluída"
|
|
730
|
+
},
|
|
731
|
+
"type": {
|
|
732
|
+
"call": "Ligação",
|
|
733
|
+
"email": "E-mail",
|
|
734
|
+
"meeting": "Reunião",
|
|
735
|
+
"whatsapp": "WhatsApp",
|
|
736
|
+
"task": "Tarefa",
|
|
737
|
+
"note": "Anotação"
|
|
738
|
+
},
|
|
739
|
+
"priority": {
|
|
740
|
+
"high": "Alta",
|
|
741
|
+
"medium": "Média",
|
|
742
|
+
"low": "Baixa"
|
|
743
|
+
},
|
|
744
|
+
"table": {
|
|
745
|
+
"activity": "Atividade",
|
|
746
|
+
"person": "Lead / Contato",
|
|
747
|
+
"owner": "Responsável",
|
|
748
|
+
"dueAt": "Prazo",
|
|
749
|
+
"createdAt": "Criada em",
|
|
750
|
+
"status": "Status",
|
|
751
|
+
"priority": "Prioridade",
|
|
752
|
+
"actions": "Ações"
|
|
753
|
+
},
|
|
754
|
+
"actions": {
|
|
755
|
+
"view": "Ver",
|
|
756
|
+
"complete": "Concluir"
|
|
757
|
+
},
|
|
758
|
+
"timeline": {
|
|
759
|
+
"description": "Visão cronológica das atividades considerando os filtros atuais.",
|
|
760
|
+
"dueLabel": "Prazo",
|
|
761
|
+
"createdLabel": "Criada"
|
|
762
|
+
},
|
|
763
|
+
"empty": {
|
|
764
|
+
"title": "Nenhuma atividade encontrada",
|
|
765
|
+
"description": "Ajuste os filtros para encontrar atividades ou limpe os filtros para voltar à fila completa.",
|
|
766
|
+
"resetFilters": "Limpar filtros"
|
|
767
|
+
},
|
|
768
|
+
"toasts": {
|
|
769
|
+
"markedAsCompleted": "Atividade marcada como concluída.",
|
|
770
|
+
"openDetails": "Abrindo atividade: {subject}"
|
|
771
|
+
}
|
|
772
|
+
},
|
|
646
773
|
"CrmFuturePage": {
|
|
647
774
|
"breadcrumbs": {
|
|
648
775
|
"home": "Home",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/contact",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.291",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
"@nestjs/core": "^11",
|
|
10
10
|
"@nestjs/jwt": "^11",
|
|
11
11
|
"@nestjs/mapped-types": "*",
|
|
12
|
-
"@hed-hog/
|
|
12
|
+
"@hed-hog/core": "0.0.291",
|
|
13
13
|
"@hed-hog/api-mail": "0.0.8",
|
|
14
|
-
"@hed-hog/core": "0.0.285",
|
|
15
|
-
"@hed-hog/api": "0.0.4",
|
|
16
|
-
"@hed-hog/api-pagination": "0.0.6",
|
|
17
14
|
"@hed-hog/api-prisma": "0.0.5",
|
|
18
|
-
"@hed-hog/api-locale": "0.0.13"
|
|
15
|
+
"@hed-hog/api-locale": "0.0.13",
|
|
16
|
+
"@hed-hog/address": "0.0.291",
|
|
17
|
+
"@hed-hog/api": "0.0.4",
|
|
18
|
+
"@hed-hog/api-pagination": "0.0.6"
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
21
21
|
".": {
|
package/src/contact.service.ts
CHANGED
|
@@ -16,7 +16,7 @@ export class ContactService {
|
|
|
16
16
|
private readonly jwtService: JwtService,
|
|
17
17
|
) {}
|
|
18
18
|
|
|
19
|
-
async getPerson(personId: number) {
|
|
19
|
+
async getPerson(personId: number): Promise<any> {
|
|
20
20
|
const person = await this.prismaService.person.findUnique({
|
|
21
21
|
where: {
|
|
22
22
|
id: personId,
|
|
@@ -52,7 +52,7 @@ export class ContactService {
|
|
|
52
52
|
phone: string,
|
|
53
53
|
cpf: string,
|
|
54
54
|
cnpj: string,
|
|
55
|
-
) {
|
|
55
|
+
): Promise<any> {
|
|
56
56
|
const findPersonByEmail = await this.prismaService.person.findFirst({
|
|
57
57
|
where: {
|
|
58
58
|
contact: {
|