@hed-hog/contact 0.0.309 → 0.0.310
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.
|
@@ -63,13 +63,30 @@ type ImportResult = {
|
|
|
63
63
|
|
|
64
64
|
type ColumnMapping = Record<string, string>;
|
|
65
65
|
|
|
66
|
-
type CompanyOption = {
|
|
67
|
-
id: number;
|
|
68
|
-
name: string;
|
|
69
|
-
trade_name?: string | null;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
type WizardStep = 'upload' | 'preview' | 'mapping' | 'confirm' | 'result';
|
|
66
|
+
type CompanyOption = {
|
|
67
|
+
id: number;
|
|
68
|
+
name: string;
|
|
69
|
+
trade_name?: string | null;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
type WizardStep = 'upload' | 'preview' | 'mapping' | 'confirm' | 'result';
|
|
73
|
+
|
|
74
|
+
function getImportErrorMessage(error: unknown, fallback: string) {
|
|
75
|
+
if (typeof error === 'object' && error !== null) {
|
|
76
|
+
const response = 'response' in error ? error.response : undefined;
|
|
77
|
+
if (typeof response === 'object' && response !== null && 'data' in response) {
|
|
78
|
+
const data = response.data;
|
|
79
|
+
if (typeof data === 'object' && data !== null && 'message' in data) {
|
|
80
|
+
const message = data.message;
|
|
81
|
+
if (typeof message === 'string') return message;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if ('message' in error && typeof error.message === 'string') {
|
|
85
|
+
return error.message;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return fallback;
|
|
89
|
+
}
|
|
73
90
|
|
|
74
91
|
// ─── CRM Field Definitions ───────────────────────────────────────────────────
|
|
75
92
|
|
|
@@ -147,7 +164,7 @@ function StepIndicator({ current }: { current: WizardStep }) {
|
|
|
147
164
|
<Icon className="size-3.5" />
|
|
148
165
|
)}
|
|
149
166
|
<span className="text-[10px] font-medium leading-tight hidden sm:block">
|
|
150
|
-
{t(step.labelKey as
|
|
167
|
+
{t(step.labelKey as never)}
|
|
151
168
|
</span>
|
|
152
169
|
</div>
|
|
153
170
|
);
|
|
@@ -419,7 +436,7 @@ function MappingStep({
|
|
|
419
436
|
{duplicateFields
|
|
420
437
|
.map((field) => {
|
|
421
438
|
const fieldDef = CRM_FIELDS.find((f) => f.value === field);
|
|
422
|
-
const label = fieldDef ? t(fieldDef.labelKey as
|
|
439
|
+
const label = fieldDef ? t(fieldDef.labelKey as never) : field;
|
|
423
440
|
return t('importMappingDuplicateWarning', { field: label });
|
|
424
441
|
})
|
|
425
442
|
.join(' ')}
|
|
@@ -481,7 +498,7 @@ function MappingStep({
|
|
|
481
498
|
value={field.value}
|
|
482
499
|
className="text-xs"
|
|
483
500
|
>
|
|
484
|
-
{t(field.labelKey as
|
|
501
|
+
{t(field.labelKey as never)}
|
|
485
502
|
</SelectItem>
|
|
486
503
|
))}
|
|
487
504
|
</SelectContent>
|
|
@@ -564,7 +581,7 @@ function ConfirmStep({
|
|
|
564
581
|
variant="outline"
|
|
565
582
|
className="text-[10px] border-primary/30 bg-primary/5 text-primary px-1.5 py-0"
|
|
566
583
|
>
|
|
567
|
-
{fieldDef ? t(fieldDef.labelKey as
|
|
584
|
+
{fieldDef ? t(fieldDef.labelKey as never) : field}
|
|
568
585
|
</Badge>
|
|
569
586
|
</div>
|
|
570
587
|
);
|
|
@@ -588,9 +605,11 @@ function ConfirmStep({
|
|
|
588
605
|
onChange={(val) => {
|
|
589
606
|
onCompanyChange(val ? Number(val) : null);
|
|
590
607
|
}}
|
|
591
|
-
getOptionValue={(opt) => (opt as
|
|
592
|
-
getOptionLabel={(opt) => (opt as
|
|
593
|
-
getOptionDescription={(opt) =>
|
|
608
|
+
getOptionValue={(opt) => (opt as CompanyOption).id}
|
|
609
|
+
getOptionLabel={(opt) => (opt as CompanyOption).name ?? ''}
|
|
610
|
+
getOptionDescription={(opt) =>
|
|
611
|
+
(opt as CompanyOption).trade_name ?? undefined
|
|
612
|
+
}
|
|
594
613
|
loadOptions={async ({ page, pageSize, search }) => {
|
|
595
614
|
const params = new URLSearchParams({
|
|
596
615
|
page: String(page),
|
|
@@ -797,19 +816,29 @@ function ResultStep({
|
|
|
797
816
|
|
|
798
817
|
// ─── Main Sheet ──────────────────────────────────────────────────────────────
|
|
799
818
|
|
|
800
|
-
export type PersonImportSheetProps = {
|
|
801
|
-
open: boolean;
|
|
802
|
-
onOpenChange: (open: boolean) => void;
|
|
803
|
-
onSuccess: () => void;
|
|
804
|
-
initialCompanyId?: number | null;
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
819
|
+
export type PersonImportSheetProps = {
|
|
820
|
+
open: boolean;
|
|
821
|
+
onOpenChange: (open: boolean) => void;
|
|
822
|
+
onSuccess: (result?: ImportResult) => void;
|
|
823
|
+
initialCompanyId?: number | null;
|
|
824
|
+
previewUrl?: string;
|
|
825
|
+
importUrl?: string;
|
|
826
|
+
title?: string;
|
|
827
|
+
description?: string;
|
|
828
|
+
nameRequired?: boolean;
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
export function PersonImportSheet({
|
|
832
|
+
open,
|
|
833
|
+
onOpenChange,
|
|
834
|
+
onSuccess,
|
|
835
|
+
initialCompanyId = null,
|
|
836
|
+
previewUrl = '/person/import/preview',
|
|
837
|
+
importUrl = '/person/import',
|
|
838
|
+
title,
|
|
839
|
+
description,
|
|
840
|
+
nameRequired = true,
|
|
841
|
+
}: PersonImportSheetProps) {
|
|
813
842
|
const t = useTranslations('contact.ContactPage');
|
|
814
843
|
const { request } = useApp();
|
|
815
844
|
|
|
@@ -857,8 +886,8 @@ export function PersonImportSheet({
|
|
|
857
886
|
}
|
|
858
887
|
onOpenChange(nextOpen);
|
|
859
888
|
},
|
|
860
|
-
[onOpenChange]
|
|
861
|
-
);
|
|
889
|
+
[initialCompanyId, onOpenChange]
|
|
890
|
+
);
|
|
862
891
|
|
|
863
892
|
// ── Auto-initialise mapping from columns ──
|
|
864
893
|
const initMapping = useCallback((columns: string[]) => {
|
|
@@ -870,15 +899,16 @@ export function PersonImportSheet({
|
|
|
870
899
|
}, []);
|
|
871
900
|
|
|
872
901
|
// ── Navigation ──
|
|
873
|
-
const canGoNext = (): boolean => {
|
|
874
|
-
if (step === 'upload') return !!file;
|
|
875
|
-
if (step === 'preview') return !!preview && !previewError;
|
|
876
|
-
if (step === 'mapping') {
|
|
877
|
-
const hasName = Object.values(mapping).includes('name');
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
return
|
|
902
|
+
const canGoNext = (): boolean => {
|
|
903
|
+
if (step === 'upload') return !!file;
|
|
904
|
+
if (step === 'preview') return !!preview && !previewError;
|
|
905
|
+
if (step === 'mapping') {
|
|
906
|
+
const hasName = Object.values(mapping).includes('name');
|
|
907
|
+
const hasEmail = Object.values(mapping).includes('email');
|
|
908
|
+
return nameRequired ? hasName : hasEmail;
|
|
909
|
+
}
|
|
910
|
+
if (step === 'confirm') return true;
|
|
911
|
+
return false;
|
|
882
912
|
};
|
|
883
913
|
|
|
884
914
|
const handleNext = async () => {
|
|
@@ -896,14 +926,19 @@ export function PersonImportSheet({
|
|
|
896
926
|
await fetchPreview();
|
|
897
927
|
} else if (step === 'preview') {
|
|
898
928
|
setStep('mapping');
|
|
899
|
-
} else if (step === 'mapping') {
|
|
900
|
-
const hasName = Object.values(mapping).includes('name');
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
929
|
+
} else if (step === 'mapping') {
|
|
930
|
+
const hasName = Object.values(mapping).includes('name');
|
|
931
|
+
const hasEmail = Object.values(mapping).includes('email');
|
|
932
|
+
if (nameRequired && !hasName) {
|
|
933
|
+
setMappingError(t('importMappingNameRequired'));
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
if (!nameRequired && !hasEmail) {
|
|
937
|
+
setMappingError('Mapeie uma coluna para Email.');
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
setMappingError(null);
|
|
941
|
+
setStep('confirm');
|
|
907
942
|
} else if (step === 'confirm') {
|
|
908
943
|
await runImport();
|
|
909
944
|
}
|
|
@@ -926,19 +961,17 @@ export function PersonImportSheet({
|
|
|
926
961
|
const formData = new FormData();
|
|
927
962
|
formData.append('file', file);
|
|
928
963
|
|
|
929
|
-
const res = await request<ImportPreview>({
|
|
930
|
-
url:
|
|
931
|
-
method: 'POST',
|
|
932
|
-
data: formData,
|
|
964
|
+
const res = await request<ImportPreview>({
|
|
965
|
+
url: previewUrl,
|
|
966
|
+
method: 'POST',
|
|
967
|
+
data: formData,
|
|
933
968
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
934
969
|
});
|
|
935
970
|
|
|
936
971
|
setPreview(res.data);
|
|
937
972
|
initMapping(res.data.columns);
|
|
938
|
-
} catch (err:
|
|
939
|
-
|
|
940
|
-
err?.response?.data?.message ?? err?.message ?? t('importErrorGeneric');
|
|
941
|
-
setPreviewError(msg);
|
|
973
|
+
} catch (err: unknown) {
|
|
974
|
+
setPreviewError(getImportErrorMessage(err, t('importErrorGeneric')));
|
|
942
975
|
} finally {
|
|
943
976
|
setPreviewLoading(false);
|
|
944
977
|
}
|
|
@@ -957,19 +990,17 @@ export function PersonImportSheet({
|
|
|
957
990
|
formData.append('mapping', JSON.stringify(mapping));
|
|
958
991
|
if (companyId) formData.append('company_id', String(companyId));
|
|
959
992
|
|
|
960
|
-
const res = await request<ImportResult>({
|
|
961
|
-
url:
|
|
962
|
-
method: 'POST',
|
|
963
|
-
data: formData,
|
|
993
|
+
const res = await request<ImportResult>({
|
|
994
|
+
url: importUrl,
|
|
995
|
+
method: 'POST',
|
|
996
|
+
data: formData,
|
|
964
997
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
965
998
|
});
|
|
966
|
-
|
|
967
|
-
setResult(res.data);
|
|
968
|
-
onSuccess();
|
|
969
|
-
} catch (err:
|
|
970
|
-
|
|
971
|
-
err?.response?.data?.message ?? err?.message ?? t('importErrorGeneric');
|
|
972
|
-
setImportError(msg);
|
|
999
|
+
|
|
1000
|
+
setResult(res.data);
|
|
1001
|
+
onSuccess(res.data);
|
|
1002
|
+
} catch (err: unknown) {
|
|
1003
|
+
setImportError(getImportErrorMessage(err, t('importErrorGeneric')));
|
|
973
1004
|
} finally {
|
|
974
1005
|
setImportLoading(false);
|
|
975
1006
|
}
|
|
@@ -988,12 +1019,12 @@ export function PersonImportSheet({
|
|
|
988
1019
|
<Upload className="h-4 w-4 text-primary" />
|
|
989
1020
|
</div>
|
|
990
1021
|
<div>
|
|
991
|
-
<SheetTitle className="text-base">
|
|
992
|
-
{t('importSheetTitle')}
|
|
993
|
-
</SheetTitle>
|
|
994
|
-
<SheetDescription className="text-xs">
|
|
995
|
-
{t('importSheetDescription')}
|
|
996
|
-
</SheetDescription>
|
|
1022
|
+
<SheetTitle className="text-base">
|
|
1023
|
+
{title ?? t('importSheetTitle')}
|
|
1024
|
+
</SheetTitle>
|
|
1025
|
+
<SheetDescription className="text-xs">
|
|
1026
|
+
{description ?? t('importSheetDescription')}
|
|
1027
|
+
</SheetDescription>
|
|
997
1028
|
</div>
|
|
998
1029
|
</div>
|
|
999
1030
|
</SheetHeader>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/contact",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.310",
|
|
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/core": "0.0.
|
|
13
|
-
"@hed-hog/api-mail": "0.0.9",
|
|
14
|
-
"@hed-hog/address": "0.0.309",
|
|
15
|
-
"@hed-hog/api": "0.0.6",
|
|
12
|
+
"@hed-hog/core": "0.0.310",
|
|
16
13
|
"@hed-hog/api-prisma": "0.0.6",
|
|
14
|
+
"@hed-hog/api-mail": "0.0.9",
|
|
17
15
|
"@hed-hog/api-pagination": "0.0.7",
|
|
18
|
-
"@hed-hog/api-locale": "0.0.14"
|
|
16
|
+
"@hed-hog/api-locale": "0.0.14",
|
|
17
|
+
"@hed-hog/address": "0.0.310",
|
|
18
|
+
"@hed-hog/api": "0.0.6"
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
21
21
|
".": {
|