@hed-hog/contact 0.0.328 → 0.0.329
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.
|
@@ -44,7 +44,13 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
44
44
|
import { ChevronsUpDown, Loader2, Plus, X } from 'lucide-react';
|
|
45
45
|
import { useTranslations } from 'next-intl';
|
|
46
46
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
47
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
type FieldValues,
|
|
49
|
+
type Path,
|
|
50
|
+
type UseFormReturn,
|
|
51
|
+
useForm,
|
|
52
|
+
useWatch,
|
|
53
|
+
} from 'react-hook-form';
|
|
48
54
|
import { z } from 'zod';
|
|
49
55
|
|
|
50
56
|
type PersonOption = {
|
|
@@ -66,6 +72,7 @@ type CreatePersonValues = {
|
|
|
66
72
|
};
|
|
67
73
|
|
|
68
74
|
type PersonTypeFilter = 'individual' | 'company' | 'all';
|
|
75
|
+
type FieldValueType = 'string' | 'number';
|
|
69
76
|
|
|
70
77
|
type ContactTypeLookup = {
|
|
71
78
|
code: string;
|
|
@@ -88,7 +95,7 @@ type PaginatedResponse<T> = {
|
|
|
88
95
|
data?: T[];
|
|
89
96
|
};
|
|
90
97
|
|
|
91
|
-
function
|
|
98
|
+
function usePersonPickerTranslations() {
|
|
92
99
|
const tRoot = useTranslations();
|
|
93
100
|
|
|
94
101
|
return (
|
|
@@ -138,7 +145,7 @@ function CreatePersonSheet({
|
|
|
138
145
|
}) {
|
|
139
146
|
const { request, showToastHandler, currentLocaleCode, getSettingValue } =
|
|
140
147
|
useApp();
|
|
141
|
-
const t =
|
|
148
|
+
const t = usePersonPickerTranslations();
|
|
142
149
|
const allowCompanyRegistration =
|
|
143
150
|
getSettingValue('contact-allow-company-registration') !== false;
|
|
144
151
|
|
|
@@ -575,7 +582,7 @@ export function PersonPicker({
|
|
|
575
582
|
disabled?: boolean;
|
|
576
583
|
}) {
|
|
577
584
|
const { request } = useApp();
|
|
578
|
-
const t =
|
|
585
|
+
const t = usePersonPickerTranslations();
|
|
579
586
|
const [personOpen, setPersonOpen] = useState(false);
|
|
580
587
|
const [personSearch, setPersonSearch] = useState('');
|
|
581
588
|
const [debouncedPersonSearch, setDebouncedPersonSearch] = useState('');
|
|
@@ -948,3 +955,70 @@ export function PersonPicker({
|
|
|
948
955
|
</>
|
|
949
956
|
);
|
|
950
957
|
}
|
|
958
|
+
|
|
959
|
+
function parsePersonFieldValue(
|
|
960
|
+
personId: number | null,
|
|
961
|
+
valueType: FieldValueType
|
|
962
|
+
) {
|
|
963
|
+
if (personId === null) {
|
|
964
|
+
return valueType === 'number' ? null : '';
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
return valueType === 'number' ? personId : String(personId);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
export function PersonPickerField<TFieldValues extends FieldValues>({
|
|
971
|
+
form,
|
|
972
|
+
name,
|
|
973
|
+
label,
|
|
974
|
+
entityLabel,
|
|
975
|
+
selectPlaceholder,
|
|
976
|
+
personTypeFilter = 'all',
|
|
977
|
+
createType = 'individual',
|
|
978
|
+
lockCreateType = false,
|
|
979
|
+
valueType = 'string',
|
|
980
|
+
initialSelectedLabel = '',
|
|
981
|
+
disabled = false,
|
|
982
|
+
}: {
|
|
983
|
+
form: UseFormReturn<TFieldValues>;
|
|
984
|
+
name: Path<TFieldValues>;
|
|
985
|
+
label: string;
|
|
986
|
+
entityLabel: string;
|
|
987
|
+
selectPlaceholder: string;
|
|
988
|
+
personTypeFilter?: PersonTypeFilter;
|
|
989
|
+
createType?: Exclude<PersonTypeFilter, 'all'>;
|
|
990
|
+
lockCreateType?: boolean;
|
|
991
|
+
valueType?: FieldValueType;
|
|
992
|
+
initialSelectedLabel?: string;
|
|
993
|
+
disabled?: boolean;
|
|
994
|
+
}) {
|
|
995
|
+
const fieldValue = useWatch({
|
|
996
|
+
control: form.control,
|
|
997
|
+
name,
|
|
998
|
+
}) as string | number | null | undefined;
|
|
999
|
+
|
|
1000
|
+
return (
|
|
1001
|
+
<PersonPicker
|
|
1002
|
+
label={label}
|
|
1003
|
+
entityLabel={entityLabel}
|
|
1004
|
+
value={fieldValue ?? null}
|
|
1005
|
+
onChange={(personId) => {
|
|
1006
|
+
form.setValue(
|
|
1007
|
+
name,
|
|
1008
|
+
parsePersonFieldValue(personId, valueType) as never,
|
|
1009
|
+
{
|
|
1010
|
+
shouldValidate: false,
|
|
1011
|
+
shouldDirty: true,
|
|
1012
|
+
shouldTouch: true,
|
|
1013
|
+
}
|
|
1014
|
+
);
|
|
1015
|
+
}}
|
|
1016
|
+
selectPlaceholder={selectPlaceholder}
|
|
1017
|
+
personTypeFilter={personTypeFilter}
|
|
1018
|
+
createType={createType}
|
|
1019
|
+
lockCreateType={lockCreateType}
|
|
1020
|
+
initialSelectedLabel={initialSelectedLabel}
|
|
1021
|
+
disabled={disabled}
|
|
1022
|
+
/>
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { PersonPickerField } from '@/app/(app)/(libraries)/contact/_components/person-picker';
|
|
4
4
|
import {
|
|
5
5
|
AlertDialog,
|
|
6
6
|
AlertDialogContent,
|
|
@@ -2402,7 +2402,7 @@ export function PersonFormSheet({
|
|
|
2402
2402
|
|
|
2403
2403
|
{allowCompanyRegistration ? (
|
|
2404
2404
|
<div className="space-y-1.5">
|
|
2405
|
-
<
|
|
2405
|
+
<PersonPickerField<PersonFormValues>
|
|
2406
2406
|
form={form}
|
|
2407
2407
|
name="employer_company_id"
|
|
2408
2408
|
label={t('employerCompany')}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/contact",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.329",
|
|
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/api": "0.0.8",
|
|
13
12
|
"@hed-hog/api-locale": "0.0.14",
|
|
14
13
|
"@hed-hog/api-mail": "0.0.9",
|
|
15
|
-
"@hed-hog/
|
|
16
|
-
"@hed-hog/
|
|
17
|
-
"@hed-hog/
|
|
18
|
-
"@hed-hog/
|
|
14
|
+
"@hed-hog/api-pagination": "0.0.7",
|
|
15
|
+
"@hed-hog/core": "0.0.329",
|
|
16
|
+
"@hed-hog/api": "0.0.8",
|
|
17
|
+
"@hed-hog/address": "0.0.329",
|
|
18
|
+
"@hed-hog/api-prisma": "0.0.6"
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
21
21
|
".": {
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { CrmNav } from './crm-nav';
|
|
4
|
-
import { Page, PageHeader } from '@/components/entity-list';
|
|
5
|
-
import { Badge } from '@/components/ui/badge';
|
|
6
|
-
import { Button } from '@/components/ui/button';
|
|
7
|
-
import {
|
|
8
|
-
Card,
|
|
9
|
-
CardContent,
|
|
10
|
-
CardDescription,
|
|
11
|
-
CardHeader,
|
|
12
|
-
CardTitle,
|
|
13
|
-
} from '@/components/ui/card';
|
|
14
|
-
import type { LucideIcon } from 'lucide-react';
|
|
15
|
-
import { ArrowRight, Sparkles } from 'lucide-react';
|
|
16
|
-
import Link from 'next/link';
|
|
17
|
-
import { useTranslations } from 'next-intl';
|
|
18
|
-
|
|
19
|
-
type CrmComingSoonProps = {
|
|
20
|
-
currentHref: string;
|
|
21
|
-
titleKey: string;
|
|
22
|
-
descriptionKey: string;
|
|
23
|
-
icon: LucideIcon;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export function CrmComingSoon({
|
|
27
|
-
currentHref,
|
|
28
|
-
titleKey,
|
|
29
|
-
descriptionKey,
|
|
30
|
-
icon: Icon,
|
|
31
|
-
}: CrmComingSoonProps) {
|
|
32
|
-
const t = useTranslations('contact.CrmMenu');
|
|
33
|
-
const common = useTranslations('contact.CrmFuturePage');
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<Page>
|
|
37
|
-
<PageHeader
|
|
38
|
-
breadcrumbs={[
|
|
39
|
-
{ label: common('breadcrumbs.home'), href: '/' },
|
|
40
|
-
{ label: common('breadcrumbs.crm'), href: '/contact/dashboard' },
|
|
41
|
-
{ label: t(`sections.${titleKey}.title`) },
|
|
42
|
-
]}
|
|
43
|
-
title={t(`sections.${titleKey}.title`)}
|
|
44
|
-
description={t(`sections.${descriptionKey}.description`)}
|
|
45
|
-
/>
|
|
46
|
-
|
|
47
|
-
<div className="min-w-0 space-y-6 overflow-x-hidden">
|
|
48
|
-
<CrmNav currentHref={currentHref} />
|
|
49
|
-
|
|
50
|
-
<Card className="overflow-hidden border-orange-200/70 bg-gradient-to-br from-orange-50 via-background to-amber-50 py-0">
|
|
51
|
-
<CardContent className="grid gap-6 px-6 py-8 lg:grid-cols-[minmax(0,1.2fr)_minmax(260px,0.8fr)]">
|
|
52
|
-
<div className="min-w-0 space-y-4">
|
|
53
|
-
<Badge className="w-fit rounded-full bg-orange-500/10 px-3 py-1 text-orange-700 hover:bg-orange-500/10">
|
|
54
|
-
<Sparkles className="mr-2 size-3.5" />
|
|
55
|
-
{common('badge')}
|
|
56
|
-
</Badge>
|
|
57
|
-
<div className="space-y-3">
|
|
58
|
-
<div className="flex items-center gap-3">
|
|
59
|
-
<div className="rounded-2xl bg-orange-500/10 p-3 text-orange-700">
|
|
60
|
-
<Icon className="size-5" />
|
|
61
|
-
</div>
|
|
62
|
-
<h2 className="text-3xl font-semibold tracking-tight">
|
|
63
|
-
{t(`sections.${titleKey}.title`)}
|
|
64
|
-
</h2>
|
|
65
|
-
</div>
|
|
66
|
-
<p className="max-w-2xl text-sm leading-6 text-muted-foreground">
|
|
67
|
-
{common('description')}
|
|
68
|
-
</p>
|
|
69
|
-
</div>
|
|
70
|
-
<div className="flex flex-wrap gap-3">
|
|
71
|
-
<Button asChild>
|
|
72
|
-
<Link href="/contact/dashboard">
|
|
73
|
-
{common('backToDashboard')}
|
|
74
|
-
</Link>
|
|
75
|
-
</Button>
|
|
76
|
-
<Button asChild variant="outline">
|
|
77
|
-
<Link href="/contact/pipeline">{common('seePipeline')}</Link>
|
|
78
|
-
</Button>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
|
|
82
|
-
<Card className="border-white/70 bg-white/80 shadow-sm">
|
|
83
|
-
<CardHeader>
|
|
84
|
-
<CardTitle>{common('nextStepTitle')}</CardTitle>
|
|
85
|
-
<CardDescription>
|
|
86
|
-
{common('nextStepDescription')}
|
|
87
|
-
</CardDescription>
|
|
88
|
-
</CardHeader>
|
|
89
|
-
<CardContent className="space-y-3">
|
|
90
|
-
{['stepOne', 'stepTwo', 'stepThree'].map((step) => (
|
|
91
|
-
<div
|
|
92
|
-
key={step}
|
|
93
|
-
className="flex items-start gap-3 rounded-2xl border border-border/70 bg-background px-4 py-3"
|
|
94
|
-
>
|
|
95
|
-
<div className="mt-0.5 rounded-full bg-orange-500/10 p-1.5 text-orange-700">
|
|
96
|
-
<ArrowRight className="size-3.5" />
|
|
97
|
-
</div>
|
|
98
|
-
<p className="text-sm text-muted-foreground">
|
|
99
|
-
{common(step)}
|
|
100
|
-
</p>
|
|
101
|
-
</div>
|
|
102
|
-
))}
|
|
103
|
-
</CardContent>
|
|
104
|
-
</Card>
|
|
105
|
-
</CardContent>
|
|
106
|
-
</Card>
|
|
107
|
-
</div>
|
|
108
|
-
</Page>
|
|
109
|
-
);
|
|
110
|
-
}
|