@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 any)}
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 any) : field;
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 any)}
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 any) : field}
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 any).id}
592
- getOptionLabel={(opt) => (opt as any).name ?? ''}
593
- getOptionDescription={(opt) => (opt as any).trade_name ?? undefined}
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
- export function PersonImportSheet({
808
- open,
809
- onOpenChange,
810
- onSuccess,
811
- initialCompanyId = null,
812
- }: PersonImportSheetProps) {
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
- return hasName;
879
- }
880
- if (step === 'confirm') return true;
881
- return false;
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
- if (!hasName) {
902
- setMappingError(t('importMappingNameRequired'));
903
- return;
904
- }
905
- setMappingError(null);
906
- setStep('confirm');
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: '/person/import/preview',
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: any) {
939
- const msg =
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: '/person/import',
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: any) {
970
- const msg =
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.309",
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.309",
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
  ".": {