@hed-hog/contact 0.0.293 → 0.0.295
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/person/dto/account.dto.d.ts +28 -0
- package/dist/person/dto/account.dto.d.ts.map +1 -0
- package/dist/person/dto/account.dto.js +123 -0
- package/dist/person/dto/account.dto.js.map +1 -0
- package/dist/person/dto/activity.dto.d.ts +15 -0
- package/dist/person/dto/activity.dto.d.ts.map +1 -0
- package/dist/person/dto/activity.dto.js +65 -0
- package/dist/person/dto/activity.dto.js.map +1 -0
- package/dist/person/dto/dashboard-query.dto.d.ts +9 -0
- package/dist/person/dto/dashboard-query.dto.d.ts.map +1 -0
- package/dist/person/dto/dashboard-query.dto.js +40 -0
- package/dist/person/dto/dashboard-query.dto.js.map +1 -0
- package/dist/person/dto/followup-query.dto.d.ts +10 -0
- package/dist/person/dto/followup-query.dto.d.ts.map +1 -0
- package/dist/person/dto/followup-query.dto.js +45 -0
- package/dist/person/dto/followup-query.dto.js.map +1 -0
- package/dist/person/person.controller.d.ts +204 -0
- package/dist/person/person.controller.d.ts.map +1 -1
- package/dist/person/person.controller.js +138 -0
- package/dist/person/person.controller.js.map +1 -1
- package/dist/person/person.service.d.ts +234 -0
- package/dist/person/person.service.d.ts.map +1 -1
- package/dist/person/person.service.js +1367 -0
- package/dist/person/person.service.js.map +1 -1
- package/hedhog/data/menu.yaml +163 -163
- package/hedhog/data/route.yaml +41 -0
- package/hedhog/frontend/app/accounts/_components/account-form-sheet.tsx.ejs +210 -114
- package/hedhog/frontend/app/accounts/_components/account-types.ts.ejs +3 -0
- package/hedhog/frontend/app/accounts/page.tsx.ejs +323 -245
- package/hedhog/frontend/app/activities/_components/activity-detail-sheet.tsx.ejs +240 -0
- package/hedhog/frontend/app/activities/_components/activity-types.ts.ejs +66 -0
- package/hedhog/frontend/app/activities/page.tsx.ejs +165 -517
- package/hedhog/frontend/app/dashboard/_components/dashboard-types.ts.ejs +70 -0
- package/hedhog/frontend/app/dashboard/page.tsx.ejs +504 -356
- package/hedhog/frontend/app/follow-ups/page.tsx.ejs +242 -153
- package/hedhog/frontend/messages/en.json +91 -6
- package/hedhog/frontend/messages/pt.json +91 -6
- package/hedhog/table/crm_activity.yaml +68 -0
- package/hedhog/table/person_company.yaml +22 -0
- package/package.json +5 -5
- package/src/person/dto/account.dto.ts +100 -0
- package/src/person/dto/activity.dto.ts +54 -0
- package/src/person/dto/dashboard-query.dto.ts +25 -0
- package/src/person/dto/followup-query.dto.ts +25 -0
- package/src/person/person.controller.ts +116 -0
- package/src/person/person.service.ts +2139 -77
|
@@ -25,47 +25,42 @@ import {
|
|
|
25
25
|
SheetTitle,
|
|
26
26
|
} from '@/components/ui/sheet';
|
|
27
27
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
28
|
-
import {
|
|
28
|
+
import { useTranslations } from 'next-intl';
|
|
29
|
+
import { useEffect, useMemo } from 'react';
|
|
29
30
|
import { useForm } from 'react-hook-form';
|
|
30
|
-
import { toast } from 'sonner';
|
|
31
31
|
import { z } from 'zod';
|
|
32
32
|
import type { Account, AccountFormValues, UserOption } from './account-types';
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
name:
|
|
36
|
-
trade_name:
|
|
37
|
-
status:
|
|
38
|
-
industry:
|
|
39
|
-
website:
|
|
40
|
-
email:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
employee_count: z.number().optional().nullable(),
|
|
50
|
-
lifecycle_stage: z
|
|
51
|
-
.enum(['prospect', 'customer', 'churned', 'inactive'])
|
|
52
|
-
.optional()
|
|
53
|
-
.nullable(),
|
|
54
|
-
city: z.string().optional().nullable(),
|
|
55
|
-
state: z.string().optional().nullable(),
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
type AccountFormData = z.infer<typeof accountFormSchema>;
|
|
34
|
+
type AccountFormData = {
|
|
35
|
+
name: string;
|
|
36
|
+
trade_name: string | null;
|
|
37
|
+
status: 'active' | 'inactive';
|
|
38
|
+
industry: string | null;
|
|
39
|
+
website: string | null;
|
|
40
|
+
email: string | null;
|
|
41
|
+
phone: string | null;
|
|
42
|
+
owner_user_id: number | null;
|
|
43
|
+
annual_revenue: number | null;
|
|
44
|
+
employee_count: number | null;
|
|
45
|
+
lifecycle_stage: 'prospect' | 'customer' | 'churned' | 'inactive' | null;
|
|
46
|
+
city: string | null;
|
|
47
|
+
state: string | null;
|
|
48
|
+
};
|
|
59
49
|
|
|
60
50
|
interface AccountFormSheetProps {
|
|
61
51
|
open: boolean;
|
|
62
52
|
onOpenChange: (open: boolean) => void;
|
|
63
53
|
account?: Account | null;
|
|
64
54
|
owners: UserOption[];
|
|
65
|
-
onSubmit: (data: AccountFormValues) => void
|
|
55
|
+
onSubmit: (data: AccountFormValues) => Promise<void>;
|
|
66
56
|
isLoading?: boolean;
|
|
67
57
|
}
|
|
68
58
|
|
|
59
|
+
function emptyToNull(value: string | null | undefined) {
|
|
60
|
+
const normalized = String(value ?? '').trim();
|
|
61
|
+
return normalized ? normalized : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
69
64
|
export function AccountFormSheet({
|
|
70
65
|
open,
|
|
71
66
|
onOpenChange,
|
|
@@ -74,6 +69,55 @@ export function AccountFormSheet({
|
|
|
74
69
|
onSubmit,
|
|
75
70
|
isLoading = false,
|
|
76
71
|
}: AccountFormSheetProps) {
|
|
72
|
+
const t = useTranslations('contact.AccountsPage');
|
|
73
|
+
|
|
74
|
+
const accountFormSchema = useMemo(
|
|
75
|
+
() =>
|
|
76
|
+
z.object({
|
|
77
|
+
name: z.string().trim().min(2, t('form.validation.nameMinLength')),
|
|
78
|
+
trade_name: z.string().nullable(),
|
|
79
|
+
status: z.enum(['active', 'inactive']),
|
|
80
|
+
industry: z.string().nullable(),
|
|
81
|
+
website: z.string().nullable(),
|
|
82
|
+
email: z
|
|
83
|
+
.string()
|
|
84
|
+
.nullable()
|
|
85
|
+
.refine(
|
|
86
|
+
(value) => !value || z.string().email().safeParse(value).success,
|
|
87
|
+
t('form.validation.invalidEmail')
|
|
88
|
+
),
|
|
89
|
+
phone: z.string().nullable(),
|
|
90
|
+
owner_user_id: z.number().nullable(),
|
|
91
|
+
annual_revenue: z
|
|
92
|
+
.number()
|
|
93
|
+
.nullable()
|
|
94
|
+
.refine(
|
|
95
|
+
(value) => value == null || value >= 0,
|
|
96
|
+
t('form.validation.nonNegativeNumber')
|
|
97
|
+
),
|
|
98
|
+
employee_count: z
|
|
99
|
+
.number()
|
|
100
|
+
.int(t('form.validation.integerRequired'))
|
|
101
|
+
.nullable()
|
|
102
|
+
.refine(
|
|
103
|
+
(value) => value == null || value >= 0,
|
|
104
|
+
t('form.validation.nonNegativeNumber')
|
|
105
|
+
),
|
|
106
|
+
lifecycle_stage: z
|
|
107
|
+
.enum(['prospect', 'customer', 'churned', 'inactive'])
|
|
108
|
+
.nullable(),
|
|
109
|
+
city: z.string().nullable(),
|
|
110
|
+
state: z
|
|
111
|
+
.string()
|
|
112
|
+
.nullable()
|
|
113
|
+
.refine(
|
|
114
|
+
(value) => !value || value.trim().length <= 2,
|
|
115
|
+
t('form.validation.stateMaxLength')
|
|
116
|
+
),
|
|
117
|
+
}),
|
|
118
|
+
[t]
|
|
119
|
+
);
|
|
120
|
+
|
|
77
121
|
const form = useForm<AccountFormData>({
|
|
78
122
|
resolver: zodResolver(accountFormSchema),
|
|
79
123
|
defaultValues: {
|
|
@@ -87,13 +131,17 @@ export function AccountFormSheet({
|
|
|
87
131
|
owner_user_id: null,
|
|
88
132
|
annual_revenue: null,
|
|
89
133
|
employee_count: null,
|
|
90
|
-
lifecycle_stage: 'prospect'
|
|
134
|
+
lifecycle_stage: 'prospect',
|
|
91
135
|
city: null,
|
|
92
136
|
state: null,
|
|
93
137
|
},
|
|
94
138
|
});
|
|
95
139
|
|
|
96
140
|
useEffect(() => {
|
|
141
|
+
if (!open) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
97
145
|
if (account) {
|
|
98
146
|
form.reset({
|
|
99
147
|
name: account.name,
|
|
@@ -110,44 +158,53 @@ export function AccountFormSheet({
|
|
|
110
158
|
city: account.city ?? null,
|
|
111
159
|
state: account.state ?? null,
|
|
112
160
|
});
|
|
113
|
-
|
|
114
|
-
form.reset({
|
|
115
|
-
name: '',
|
|
116
|
-
trade_name: null,
|
|
117
|
-
status: 'active',
|
|
118
|
-
industry: null,
|
|
119
|
-
website: null,
|
|
120
|
-
email: null,
|
|
121
|
-
phone: null,
|
|
122
|
-
owner_user_id: null,
|
|
123
|
-
annual_revenue: null,
|
|
124
|
-
employee_count: null,
|
|
125
|
-
lifecycle_stage: 'prospect',
|
|
126
|
-
city: null,
|
|
127
|
-
state: null,
|
|
128
|
-
});
|
|
161
|
+
return;
|
|
129
162
|
}
|
|
163
|
+
|
|
164
|
+
form.reset({
|
|
165
|
+
name: '',
|
|
166
|
+
trade_name: null,
|
|
167
|
+
status: 'active',
|
|
168
|
+
industry: null,
|
|
169
|
+
website: null,
|
|
170
|
+
email: null,
|
|
171
|
+
phone: null,
|
|
172
|
+
owner_user_id: null,
|
|
173
|
+
annual_revenue: null,
|
|
174
|
+
employee_count: null,
|
|
175
|
+
lifecycle_stage: 'prospect',
|
|
176
|
+
city: null,
|
|
177
|
+
state: null,
|
|
178
|
+
});
|
|
130
179
|
}, [account, form, open]);
|
|
131
180
|
|
|
132
|
-
const handleSubmit = (data: AccountFormData) => {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
181
|
+
const handleSubmit = async (data: AccountFormData) => {
|
|
182
|
+
await onSubmit({
|
|
183
|
+
name: data.name.trim(),
|
|
184
|
+
trade_name: emptyToNull(data.trade_name),
|
|
185
|
+
status: data.status,
|
|
186
|
+
industry: emptyToNull(data.industry),
|
|
187
|
+
website: emptyToNull(data.website),
|
|
188
|
+
email: emptyToNull(data.email),
|
|
189
|
+
phone: emptyToNull(data.phone),
|
|
190
|
+
owner_user_id: data.owner_user_id ?? null,
|
|
191
|
+
annual_revenue: data.annual_revenue ?? null,
|
|
192
|
+
employee_count: data.employee_count ?? null,
|
|
193
|
+
lifecycle_stage: data.lifecycle_stage ?? null,
|
|
194
|
+
city: emptyToNull(data.city),
|
|
195
|
+
state: emptyToNull(data.state)?.toUpperCase() ?? null,
|
|
196
|
+
});
|
|
140
197
|
};
|
|
141
198
|
|
|
142
199
|
return (
|
|
143
200
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
144
201
|
<SheetContent className="max-h-screen overflow-y-auto">
|
|
145
202
|
<SheetHeader>
|
|
146
|
-
<SheetTitle>
|
|
203
|
+
<SheetTitle>
|
|
204
|
+
{account ? t('form.editTitle') : t('form.createTitle')}
|
|
205
|
+
</SheetTitle>
|
|
147
206
|
<SheetDescription>
|
|
148
|
-
{account
|
|
149
|
-
? 'Update account information and details'
|
|
150
|
-
: 'Create a new account record in the CRM'}
|
|
207
|
+
{account ? t('form.editDescription') : t('form.createDescription')}
|
|
151
208
|
</SheetDescription>
|
|
152
209
|
</SheetHeader>
|
|
153
210
|
|
|
@@ -156,16 +213,15 @@ export function AccountFormSheet({
|
|
|
156
213
|
onSubmit={form.handleSubmit(handleSubmit)}
|
|
157
214
|
className="space-y-4 py-4"
|
|
158
215
|
>
|
|
159
|
-
{/* Name and Trade Name */}
|
|
160
216
|
<FormField
|
|
161
217
|
control={form.control}
|
|
162
218
|
name="name"
|
|
163
219
|
render={({ field }) => (
|
|
164
220
|
<FormItem>
|
|
165
|
-
<FormLabel>
|
|
221
|
+
<FormLabel>{t('form.companyName')}</FormLabel>
|
|
166
222
|
<FormControl>
|
|
167
223
|
<Input
|
|
168
|
-
placeholder=
|
|
224
|
+
placeholder={t('form.placeholders.companyName')}
|
|
169
225
|
{...field}
|
|
170
226
|
disabled={isLoading}
|
|
171
227
|
/>
|
|
@@ -180,12 +236,14 @@ export function AccountFormSheet({
|
|
|
180
236
|
name="trade_name"
|
|
181
237
|
render={({ field }) => (
|
|
182
238
|
<FormItem>
|
|
183
|
-
<FormLabel>
|
|
239
|
+
<FormLabel>{t('form.tradeName')}</FormLabel>
|
|
184
240
|
<FormControl>
|
|
185
241
|
<Input
|
|
186
|
-
placeholder=
|
|
242
|
+
placeholder={t('form.placeholders.tradeName')}
|
|
187
243
|
value={field.value ?? ''}
|
|
188
|
-
onChange={(
|
|
244
|
+
onChange={(event) =>
|
|
245
|
+
field.onChange(event.target.value || null)
|
|
246
|
+
}
|
|
189
247
|
disabled={isLoading}
|
|
190
248
|
/>
|
|
191
249
|
</FormControl>
|
|
@@ -194,14 +252,13 @@ export function AccountFormSheet({
|
|
|
194
252
|
)}
|
|
195
253
|
/>
|
|
196
254
|
|
|
197
|
-
|
|
198
|
-
<div className="grid gap-4 grid-cols-2">
|
|
255
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
199
256
|
<FormField
|
|
200
257
|
control={form.control}
|
|
201
258
|
name="status"
|
|
202
259
|
render={({ field }) => (
|
|
203
260
|
<FormItem>
|
|
204
|
-
<FormLabel>
|
|
261
|
+
<FormLabel>{t('form.status')}</FormLabel>
|
|
205
262
|
<Select
|
|
206
263
|
value={field.value}
|
|
207
264
|
onValueChange={field.onChange}
|
|
@@ -213,8 +270,12 @@ export function AccountFormSheet({
|
|
|
213
270
|
</SelectTrigger>
|
|
214
271
|
</FormControl>
|
|
215
272
|
<SelectContent>
|
|
216
|
-
<SelectItem value="active">
|
|
217
|
-
|
|
273
|
+
<SelectItem value="active">
|
|
274
|
+
{t('status_active')}
|
|
275
|
+
</SelectItem>
|
|
276
|
+
<SelectItem value="inactive">
|
|
277
|
+
{t('status_inactive')}
|
|
278
|
+
</SelectItem>
|
|
218
279
|
</SelectContent>
|
|
219
280
|
</Select>
|
|
220
281
|
<FormMessage />
|
|
@@ -227,7 +288,7 @@ export function AccountFormSheet({
|
|
|
227
288
|
name="lifecycle_stage"
|
|
228
289
|
render={({ field }) => (
|
|
229
290
|
<FormItem>
|
|
230
|
-
<FormLabel>
|
|
291
|
+
<FormLabel>{t('form.lifecycleStage')}</FormLabel>
|
|
231
292
|
<Select
|
|
232
293
|
value={field.value ?? 'prospect'}
|
|
233
294
|
onValueChange={(value) => field.onChange(value || null)}
|
|
@@ -239,10 +300,18 @@ export function AccountFormSheet({
|
|
|
239
300
|
</SelectTrigger>
|
|
240
301
|
</FormControl>
|
|
241
302
|
<SelectContent>
|
|
242
|
-
<SelectItem value="prospect">
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
<SelectItem value="
|
|
303
|
+
<SelectItem value="prospect">
|
|
304
|
+
{t('stage_prospect')}
|
|
305
|
+
</SelectItem>
|
|
306
|
+
<SelectItem value="customer">
|
|
307
|
+
{t('stage_customer')}
|
|
308
|
+
</SelectItem>
|
|
309
|
+
<SelectItem value="churned">
|
|
310
|
+
{t('stage_churned')}
|
|
311
|
+
</SelectItem>
|
|
312
|
+
<SelectItem value="inactive">
|
|
313
|
+
{t('stage_inactive')}
|
|
314
|
+
</SelectItem>
|
|
246
315
|
</SelectContent>
|
|
247
316
|
</Select>
|
|
248
317
|
<FormMessage />
|
|
@@ -251,19 +320,20 @@ export function AccountFormSheet({
|
|
|
251
320
|
/>
|
|
252
321
|
</div>
|
|
253
322
|
|
|
254
|
-
{/* Contact Information */}
|
|
255
323
|
<FormField
|
|
256
324
|
control={form.control}
|
|
257
325
|
name="email"
|
|
258
326
|
render={({ field }) => (
|
|
259
327
|
<FormItem>
|
|
260
|
-
<FormLabel>
|
|
328
|
+
<FormLabel>{t('form.email')}</FormLabel>
|
|
261
329
|
<FormControl>
|
|
262
330
|
<Input
|
|
263
331
|
type="email"
|
|
264
|
-
placeholder=
|
|
332
|
+
placeholder={t('form.placeholders.email')}
|
|
265
333
|
value={field.value ?? ''}
|
|
266
|
-
onChange={(
|
|
334
|
+
onChange={(event) =>
|
|
335
|
+
field.onChange(event.target.value || null)
|
|
336
|
+
}
|
|
267
337
|
disabled={isLoading}
|
|
268
338
|
/>
|
|
269
339
|
</FormControl>
|
|
@@ -277,12 +347,14 @@ export function AccountFormSheet({
|
|
|
277
347
|
name="phone"
|
|
278
348
|
render={({ field }) => (
|
|
279
349
|
<FormItem>
|
|
280
|
-
<FormLabel>
|
|
350
|
+
<FormLabel>{t('form.phone')}</FormLabel>
|
|
281
351
|
<FormControl>
|
|
282
352
|
<Input
|
|
283
|
-
placeholder=
|
|
353
|
+
placeholder={t('form.placeholders.phone')}
|
|
284
354
|
value={field.value ?? ''}
|
|
285
|
-
onChange={(
|
|
355
|
+
onChange={(event) =>
|
|
356
|
+
field.onChange(event.target.value || null)
|
|
357
|
+
}
|
|
286
358
|
disabled={isLoading}
|
|
287
359
|
/>
|
|
288
360
|
</FormControl>
|
|
@@ -296,12 +368,14 @@ export function AccountFormSheet({
|
|
|
296
368
|
name="website"
|
|
297
369
|
render={({ field }) => (
|
|
298
370
|
<FormItem>
|
|
299
|
-
<FormLabel>
|
|
371
|
+
<FormLabel>{t('form.website')}</FormLabel>
|
|
300
372
|
<FormControl>
|
|
301
373
|
<Input
|
|
302
|
-
placeholder=
|
|
374
|
+
placeholder={t('form.placeholders.website')}
|
|
303
375
|
value={field.value ?? ''}
|
|
304
|
-
onChange={(
|
|
376
|
+
onChange={(event) =>
|
|
377
|
+
field.onChange(event.target.value || null)
|
|
378
|
+
}
|
|
305
379
|
disabled={isLoading}
|
|
306
380
|
/>
|
|
307
381
|
</FormControl>
|
|
@@ -310,18 +384,19 @@ export function AccountFormSheet({
|
|
|
310
384
|
)}
|
|
311
385
|
/>
|
|
312
386
|
|
|
313
|
-
{/* Business Information */}
|
|
314
387
|
<FormField
|
|
315
388
|
control={form.control}
|
|
316
389
|
name="industry"
|
|
317
390
|
render={({ field }) => (
|
|
318
391
|
<FormItem>
|
|
319
|
-
<FormLabel>
|
|
392
|
+
<FormLabel>{t('form.industry')}</FormLabel>
|
|
320
393
|
<FormControl>
|
|
321
394
|
<Input
|
|
322
|
-
placeholder=
|
|
395
|
+
placeholder={t('form.placeholders.industry')}
|
|
323
396
|
value={field.value ?? ''}
|
|
324
|
-
onChange={(
|
|
397
|
+
onChange={(event) =>
|
|
398
|
+
field.onChange(event.target.value || null)
|
|
399
|
+
}
|
|
325
400
|
disabled={isLoading}
|
|
326
401
|
/>
|
|
327
402
|
</FormControl>
|
|
@@ -330,21 +405,26 @@ export function AccountFormSheet({
|
|
|
330
405
|
)}
|
|
331
406
|
/>
|
|
332
407
|
|
|
333
|
-
<div className="grid gap-4 grid-cols-2">
|
|
408
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
334
409
|
<FormField
|
|
335
410
|
control={form.control}
|
|
336
411
|
name="annual_revenue"
|
|
337
412
|
render={({ field }) => (
|
|
338
413
|
<FormItem>
|
|
339
|
-
<FormLabel>
|
|
414
|
+
<FormLabel>{t('form.annualRevenue')}</FormLabel>
|
|
340
415
|
<FormControl>
|
|
341
416
|
<Input
|
|
342
417
|
type="number"
|
|
343
|
-
|
|
418
|
+
inputMode="decimal"
|
|
419
|
+
min="0"
|
|
420
|
+
step="0.01"
|
|
421
|
+
placeholder={t('form.placeholders.annualRevenue')}
|
|
344
422
|
value={field.value ?? ''}
|
|
345
|
-
onChange={(
|
|
423
|
+
onChange={(event) =>
|
|
346
424
|
field.onChange(
|
|
347
|
-
|
|
425
|
+
event.target.value
|
|
426
|
+
? Number(event.target.value)
|
|
427
|
+
: null
|
|
348
428
|
)
|
|
349
429
|
}
|
|
350
430
|
disabled={isLoading}
|
|
@@ -360,15 +440,20 @@ export function AccountFormSheet({
|
|
|
360
440
|
name="employee_count"
|
|
361
441
|
render={({ field }) => (
|
|
362
442
|
<FormItem>
|
|
363
|
-
<FormLabel>
|
|
443
|
+
<FormLabel>{t('form.employeeCount')}</FormLabel>
|
|
364
444
|
<FormControl>
|
|
365
445
|
<Input
|
|
366
446
|
type="number"
|
|
367
|
-
|
|
447
|
+
inputMode="numeric"
|
|
448
|
+
min="0"
|
|
449
|
+
step="1"
|
|
450
|
+
placeholder={t('form.placeholders.employeeCount')}
|
|
368
451
|
value={field.value ?? ''}
|
|
369
|
-
onChange={(
|
|
452
|
+
onChange={(event) =>
|
|
370
453
|
field.onChange(
|
|
371
|
-
|
|
454
|
+
event.target.value
|
|
455
|
+
? Number(event.target.value)
|
|
456
|
+
: null
|
|
372
457
|
)
|
|
373
458
|
}
|
|
374
459
|
disabled={isLoading}
|
|
@@ -380,19 +465,20 @@ export function AccountFormSheet({
|
|
|
380
465
|
/>
|
|
381
466
|
</div>
|
|
382
467
|
|
|
383
|
-
|
|
384
|
-
<div className="grid gap-4 grid-cols-2">
|
|
468
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
385
469
|
<FormField
|
|
386
470
|
control={form.control}
|
|
387
471
|
name="city"
|
|
388
472
|
render={({ field }) => (
|
|
389
473
|
<FormItem>
|
|
390
|
-
<FormLabel>
|
|
474
|
+
<FormLabel>{t('form.city')}</FormLabel>
|
|
391
475
|
<FormControl>
|
|
392
476
|
<Input
|
|
393
|
-
placeholder=
|
|
477
|
+
placeholder={t('form.placeholders.city')}
|
|
394
478
|
value={field.value ?? ''}
|
|
395
|
-
onChange={(
|
|
479
|
+
onChange={(event) =>
|
|
480
|
+
field.onChange(event.target.value || null)
|
|
481
|
+
}
|
|
396
482
|
disabled={isLoading}
|
|
397
483
|
/>
|
|
398
484
|
</FormControl>
|
|
@@ -406,13 +492,15 @@ export function AccountFormSheet({
|
|
|
406
492
|
name="state"
|
|
407
493
|
render={({ field }) => (
|
|
408
494
|
<FormItem>
|
|
409
|
-
<FormLabel>
|
|
495
|
+
<FormLabel>{t('form.state')}</FormLabel>
|
|
410
496
|
<FormControl>
|
|
411
497
|
<Input
|
|
412
|
-
placeholder=
|
|
498
|
+
placeholder={t('form.placeholders.state')}
|
|
413
499
|
maxLength={2}
|
|
414
500
|
value={field.value ?? ''}
|
|
415
|
-
onChange={(
|
|
501
|
+
onChange={(event) =>
|
|
502
|
+
field.onChange(event.target.value || null)
|
|
503
|
+
}
|
|
416
504
|
disabled={isLoading}
|
|
417
505
|
/>
|
|
418
506
|
</FormControl>
|
|
@@ -422,27 +510,32 @@ export function AccountFormSheet({
|
|
|
422
510
|
/>
|
|
423
511
|
</div>
|
|
424
512
|
|
|
425
|
-
{/* Owner */}
|
|
426
513
|
<FormField
|
|
427
514
|
control={form.control}
|
|
428
515
|
name="owner_user_id"
|
|
429
516
|
render={({ field }) => (
|
|
430
517
|
<FormItem>
|
|
431
|
-
<FormLabel>
|
|
518
|
+
<FormLabel>{t('form.owner')}</FormLabel>
|
|
432
519
|
<Select
|
|
433
|
-
value={
|
|
520
|
+
value={
|
|
521
|
+
field.value != null ? String(field.value) : 'unassigned'
|
|
522
|
+
}
|
|
434
523
|
onValueChange={(value) =>
|
|
435
|
-
field.onChange(
|
|
524
|
+
field.onChange(
|
|
525
|
+
value === 'unassigned' ? null : Number(value)
|
|
526
|
+
)
|
|
436
527
|
}
|
|
437
528
|
disabled={isLoading}
|
|
438
529
|
>
|
|
439
530
|
<FormControl>
|
|
440
531
|
<SelectTrigger>
|
|
441
|
-
<SelectValue placeholder=
|
|
532
|
+
<SelectValue placeholder={t('unassigned')} />
|
|
442
533
|
</SelectTrigger>
|
|
443
534
|
</FormControl>
|
|
444
535
|
<SelectContent>
|
|
445
|
-
<SelectItem value="">
|
|
536
|
+
<SelectItem value="unassigned">
|
|
537
|
+
{t('unassigned')}
|
|
538
|
+
</SelectItem>
|
|
446
539
|
{owners.map((owner) => (
|
|
447
540
|
<SelectItem key={owner.id} value={String(owner.id)}>
|
|
448
541
|
{owner.name}
|
|
@@ -455,18 +548,21 @@ export function AccountFormSheet({
|
|
|
455
548
|
)}
|
|
456
549
|
/>
|
|
457
550
|
|
|
458
|
-
|
|
459
|
-
<div className="flex justify-end gap-2 pt-4 border-t">
|
|
551
|
+
<div className="flex justify-end gap-2 border-t pt-4">
|
|
460
552
|
<Button
|
|
461
553
|
type="button"
|
|
462
554
|
variant="outline"
|
|
463
555
|
onClick={() => onOpenChange(false)}
|
|
464
556
|
disabled={isLoading}
|
|
465
557
|
>
|
|
466
|
-
|
|
558
|
+
{t('cancel')}
|
|
467
559
|
</Button>
|
|
468
560
|
<Button type="submit" disabled={isLoading}>
|
|
469
|
-
{isLoading
|
|
561
|
+
{isLoading
|
|
562
|
+
? t('form.saving')
|
|
563
|
+
: account
|
|
564
|
+
? t('form.updateSubmit')
|
|
565
|
+
: t('form.createSubmit')}
|
|
470
566
|
</Button>
|
|
471
567
|
</div>
|
|
472
568
|
</form>
|