@hed-hog/contact 0.0.347 → 0.0.350

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.
@@ -103,6 +103,8 @@ import type {
103
103
  type AccountFormData = {
104
104
  name: string;
105
105
  trade_name: string | null;
106
+ foundation_date: string | null;
107
+ legal_nature: string | null;
106
108
  status: 'active' | 'inactive';
107
109
  industry: string | null;
108
110
  website: string | null;
@@ -145,6 +147,24 @@ function emptyToNull(value: string | null | undefined) {
145
147
  return normalized ? normalized : null;
146
148
  }
147
149
 
150
+ function toDateInputValue(value: string | null | undefined) {
151
+ const normalized = String(value ?? '').trim();
152
+ if (!normalized) {
153
+ return null;
154
+ }
155
+
156
+ if (/^\d{4}-\d{2}-\d{2}$/.test(normalized)) {
157
+ return normalized;
158
+ }
159
+
160
+ const parsed = new Date(normalized);
161
+ if (Number.isNaN(parsed.getTime())) {
162
+ return null;
163
+ }
164
+
165
+ return parsed.toISOString().slice(0, 10);
166
+ }
167
+
148
168
  function createClientId(prefix: string) {
149
169
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
150
170
  }
@@ -341,6 +361,8 @@ export function AccountFormSheet({
341
361
  z.object({
342
362
  name: z.string().trim().min(2, t('form.validation.nameMinLength')),
343
363
  trade_name: z.string().nullable(),
364
+ foundation_date: z.string().nullable(),
365
+ legal_nature: z.string().nullable(),
344
366
  status: z.enum(['active', 'inactive']),
345
367
  industry: z.string().nullable(),
346
368
  website: z.string().nullable(),
@@ -372,6 +394,8 @@ export function AccountFormSheet({
372
394
  defaultValues: {
373
395
  name: '',
374
396
  trade_name: null,
397
+ foundation_date: null,
398
+ legal_nature: null,
375
399
  status: 'active',
376
400
  industry: null,
377
401
  website: null,
@@ -422,7 +446,7 @@ export function AccountFormSheet({
422
446
  });
423
447
 
424
448
  const additionalDetailsDefaultOpen = Boolean(
425
- account?.industry ||
449
+ account?.website ||
426
450
  account?.annual_revenue != null ||
427
451
  account?.employee_count != null
428
452
  );
@@ -530,6 +554,8 @@ export function AccountFormSheet({
530
554
  Boolean(
531
555
  (watchedFormValues?.name ?? '').trim() ||
532
556
  (watchedFormValues?.trade_name ?? '').trim() ||
557
+ (watchedFormValues?.legal_nature ?? '').trim() ||
558
+ (watchedFormValues?.foundation_date ?? '').trim() ||
533
559
  (watchedFormValues?.industry ?? '').trim() ||
534
560
  (watchedFormValues?.website ?? '').trim() ||
535
561
  watchedFormValues?.owner_user_id != null ||
@@ -560,6 +586,8 @@ export function AccountFormSheet({
560
586
  values: {
561
587
  name: watchedFormValues?.name ?? '',
562
588
  trade_name: watchedFormValues?.trade_name ?? null,
589
+ foundation_date: watchedFormValues?.foundation_date ?? null,
590
+ legal_nature: watchedFormValues?.legal_nature ?? null,
563
591
  status: watchedFormValues?.status ?? 'active',
564
592
  industry: watchedFormValues?.industry ?? null,
565
593
  website: watchedFormValues?.website ?? null,
@@ -895,6 +923,14 @@ export function AccountFormSheet({
895
923
  name: storedDraft?.payload.values?.name ?? account?.name ?? '',
896
924
  trade_name:
897
925
  storedDraft?.payload.values?.trade_name ?? account?.trade_name ?? null,
926
+ foundation_date:
927
+ storedDraft?.payload.values?.foundation_date ??
928
+ toDateInputValue(account?.foundation_date) ??
929
+ null,
930
+ legal_nature:
931
+ storedDraft?.payload.values?.legal_nature ??
932
+ account?.legal_nature ??
933
+ null,
898
934
  status:
899
935
  storedDraft?.payload.values?.status ?? account?.status ?? 'active',
900
936
  industry:
@@ -1258,6 +1294,8 @@ export function AccountFormSheet({
1258
1294
  {
1259
1295
  name: data.name.trim(),
1260
1296
  trade_name: emptyToNull(data.trade_name),
1297
+ foundation_date: emptyToNull(data.foundation_date),
1298
+ legal_nature: emptyToNull(data.legal_nature),
1261
1299
  status: data.status,
1262
1300
  industry: emptyToNull(data.industry),
1263
1301
  website: emptyToNull(data.website),
@@ -1356,7 +1394,7 @@ export function AccountFormSheet({
1356
1394
  icon={<Building2 className="h-4 w-4" />}
1357
1395
  />
1358
1396
 
1359
- <div className="grid grid-cols-2 gap-3">
1397
+ <div className="grid grid-cols-1 gap-3 md:grid-cols-2">
1360
1398
  <FormField
1361
1399
  control={form.control}
1362
1400
  name="name"
@@ -1398,9 +1436,30 @@ export function AccountFormSheet({
1398
1436
  </FormItem>
1399
1437
  )}
1400
1438
  />
1439
+
1440
+ <FormField
1441
+ control={form.control}
1442
+ name="industry"
1443
+ render={({ field }) => (
1444
+ <FormItem className="space-y-1.5 md:col-span-2">
1445
+ <FormLabel>{t('form.industry')}</FormLabel>
1446
+ <FormControl>
1447
+ <Input
1448
+ placeholder={t('form.placeholders.industry')}
1449
+ value={field.value ?? ''}
1450
+ onChange={(event) =>
1451
+ field.onChange(event.target.value || null)
1452
+ }
1453
+ disabled={isLoading}
1454
+ />
1455
+ </FormControl>
1456
+ <FormMessage />
1457
+ </FormItem>
1458
+ )}
1459
+ />
1401
1460
  </div>
1402
1461
 
1403
- <div className="flex items-start gap-3">
1462
+ <div className="flex flex-col items-start gap-3 sm:flex-row">
1404
1463
  <Avatar className="h-16 w-16 rounded-md border">
1405
1464
  <AvatarImage
1406
1465
  src={avatarPreviewUrl || undefined}
@@ -1423,7 +1482,7 @@ export function AccountFormSheet({
1423
1482
  void handleAvatarUpload(file);
1424
1483
  }}
1425
1484
  />
1426
- <div className="flex flex-wrap gap-2">
1485
+ <div className="flex w-full flex-wrap gap-2">
1427
1486
  <Button
1428
1487
  type="button"
1429
1488
  variant="outline"
@@ -1472,6 +1531,52 @@ export function AccountFormSheet({
1472
1531
  ) : null}
1473
1532
  </div>
1474
1533
  </div>
1534
+
1535
+ <div className="grid grid-cols-1 gap-3 lg:grid-cols-2">
1536
+ <FormField
1537
+ control={form.control}
1538
+ name="foundation_date"
1539
+ render={({ field }) => (
1540
+ <FormItem className="space-y-1.5">
1541
+ <FormLabel>{personT('foundationDate')}</FormLabel>
1542
+ <FormControl>
1543
+ <Input
1544
+ type="date"
1545
+ max={new Date().toISOString().slice(0, 10)}
1546
+ placeholder={personT('selectDate')}
1547
+ value={field.value ?? ''}
1548
+ onChange={(event) =>
1549
+ field.onChange(event.target.value || null)
1550
+ }
1551
+ disabled={isLoading}
1552
+ />
1553
+ </FormControl>
1554
+ <FormMessage />
1555
+ </FormItem>
1556
+ )}
1557
+ />
1558
+
1559
+ <FormField
1560
+ control={form.control}
1561
+ name="legal_nature"
1562
+ render={({ field }) => (
1563
+ <FormItem className="space-y-1.5">
1564
+ <FormLabel>{personT('legalNature')}</FormLabel>
1565
+ <FormControl>
1566
+ <Input
1567
+ placeholder={personT('legalNaturePlaceholder')}
1568
+ value={field.value ?? ''}
1569
+ onChange={(event) =>
1570
+ field.onChange(event.target.value || null)
1571
+ }
1572
+ disabled={isLoading}
1573
+ />
1574
+ </FormControl>
1575
+ <FormMessage />
1576
+ </FormItem>
1577
+ )}
1578
+ />
1579
+ </div>
1475
1580
  </section>
1476
1581
 
1477
1582
  <section className="space-y-3">
@@ -1481,7 +1586,7 @@ export function AccountFormSheet({
1481
1586
  icon={<Users className="h-4 w-4" />}
1482
1587
  />
1483
1588
 
1484
- <div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
1589
+ <div className="grid grid-cols-1 gap-3 md:grid-cols-2">
1485
1590
  <FormField
1486
1591
  control={form.control}
1487
1592
  name="status"
@@ -2207,29 +2312,6 @@ export function AccountFormSheet({
2207
2312
  )}
2208
2313
  />
2209
2314
 
2210
- <FormField
2211
- control={form.control}
2212
- name="industry"
2213
- render={({ field }) => (
2214
- <FormItem className="space-y-1.5 md:col-span-2 xl:col-span-2">
2215
- <FormLabel>{t('form.industry')}</FormLabel>
2216
- <FormControl>
2217
- <Input
2218
- placeholder={t(
2219
- 'form.placeholders.industry'
2220
- )}
2221
- value={field.value ?? ''}
2222
- onChange={(event) =>
2223
- field.onChange(event.target.value || null)
2224
- }
2225
- disabled={isLoading}
2226
- />
2227
- </FormControl>
2228
- <FormMessage />
2229
- </FormItem>
2230
- )}
2231
- />
2232
-
2233
2315
  <FormField
2234
2316
  control={form.control}
2235
2317
  name="annual_revenue"
@@ -66,6 +66,8 @@ export type Account = {
66
66
  id: number;
67
67
  name: string;
68
68
  trade_name?: string | null;
69
+ foundation_date?: string | null;
70
+ legal_nature?: string | null;
69
71
  status: 'active' | 'inactive';
70
72
  industry?: string | null;
71
73
  website?: string | null;
@@ -99,6 +101,8 @@ export type PaginatedResult<T> = {
99
101
  export type AccountFormValues = {
100
102
  name: string;
101
103
  trade_name?: string | null;
104
+ foundation_date?: string | null;
105
+ legal_nature?: string | null;
102
106
  status: 'active' | 'inactive';
103
107
  industry?: string | null;
104
108
  website?: string | null;
@@ -466,6 +466,8 @@ export default function AccountsPage() {
466
466
  id: savedId,
467
467
  name: data.name,
468
468
  trade_name: data.trade_name ?? null,
469
+ foundation_date: data.foundation_date ?? null,
470
+ legal_nature: data.legal_nature ?? null,
469
471
  status: data.status,
470
472
  industry: data.industry ?? null,
471
473
  website: data.website ?? null,
@@ -710,6 +710,28 @@ export function PersonFormSheet({
710
710
  const t = useTranslations('contact.ContactPage');
711
711
  const { request, currentLocaleCode, getSettingValue, user } = useApp();
712
712
  const isEditing = Boolean(person);
713
+ const normalizedContactTypes = useMemo<ContactTypeOption[]>(
714
+ () =>
715
+ contactTypes
716
+ .map((contactType) => ({
717
+ ...contactType,
718
+ contact_type_id: contactType.contact_type_id ?? contactType.id,
719
+ }))
720
+ .filter((contactType) => Number.isFinite(contactType.contact_type_id)),
721
+ [contactTypes]
722
+ );
723
+ const normalizedDocumentTypes = useMemo<DocumentTypeOption[]>(
724
+ () =>
725
+ documentTypes
726
+ .map((documentType) => ({
727
+ ...documentType,
728
+ document_type_id: documentType.document_type_id ?? documentType.id,
729
+ }))
730
+ .filter((documentType) =>
731
+ Number.isFinite(documentType.document_type_id)
732
+ ),
733
+ [documentTypes]
734
+ );
713
735
  const canEditLinkedUser = useMemo(
714
736
  () =>
715
737
  (user?.role_user ?? []).some((roleUser) => {
@@ -1332,14 +1354,14 @@ export function PersonFormSheet({
1332
1354
 
1333
1355
  const getContactTypeCode = (contactTypeId?: number | null) =>
1334
1356
  String(
1335
- contactTypes.find(
1357
+ normalizedContactTypes.find(
1336
1358
  (contactType) => contactType.contact_type_id === contactTypeId
1337
1359
  )?.code || ''
1338
1360
  ).toUpperCase();
1339
1361
 
1340
1362
  const getDocumentTypeCode = (documentTypeId?: number | null) =>
1341
1363
  String(
1342
- documentTypes.find(
1364
+ normalizedDocumentTypes.find(
1343
1365
  (documentType) => documentType.document_type_id === documentTypeId
1344
1366
  )?.code || ''
1345
1367
  ).toUpperCase();
@@ -1382,7 +1404,7 @@ export function PersonFormSheet({
1382
1404
  const resolveContactTypeIdByCodes = (codes: string[]) => {
1383
1405
  const normalizedCodes = codes.map((code) => code.toUpperCase());
1384
1406
  return (
1385
- contactTypes.find((contactType) =>
1407
+ normalizedContactTypes.find((contactType) =>
1386
1408
  normalizedCodes.includes(String(contactType.code || '').toUpperCase())
1387
1409
  )?.contact_type_id || null
1388
1410
  );
@@ -1391,7 +1413,7 @@ export function PersonFormSheet({
1391
1413
  const resolveDocumentTypeIdByCodes = (codes: string[]) => {
1392
1414
  const normalizedCodes = codes.map((code) => code.toUpperCase());
1393
1415
  return (
1394
- documentTypes.find((documentType) =>
1416
+ normalizedDocumentTypes.find((documentType) =>
1395
1417
  normalizedCodes.includes(String(documentType.code || '').toUpperCase())
1396
1418
  )?.document_type_id || null
1397
1419
  );
@@ -1883,7 +1905,7 @@ export function PersonFormSheet({
1883
1905
  contact: payload.contacts.map((contact) => ({
1884
1906
  ...contact,
1885
1907
  contact_type:
1886
- contactTypes.find(
1908
+ normalizedContactTypes.find(
1887
1909
  (item) => item.contact_type_id === contact.contact_type_id
1888
1910
  ) ?? undefined,
1889
1911
  })),
@@ -1891,7 +1913,7 @@ export function PersonFormSheet({
1891
1913
  document: payload.documents.map((document) => ({
1892
1914
  ...document,
1893
1915
  document_type:
1894
- documentTypes.find(
1916
+ normalizedDocumentTypes.find(
1895
1917
  (item) => item.document_type_id === document.document_type_id
1896
1918
  ) ?? undefined,
1897
1919
  })),
@@ -2206,75 +2228,73 @@ export function PersonFormSheet({
2206
2228
  </Select>
2207
2229
  </div>
2208
2230
  ) : null}
2209
- <div className="flex items-center gap-3">
2210
- <div className="space-y-1.5">
2211
- <Label className="text-xs font-medium">
2212
- {t('status')}
2213
- </Label>
2214
- <Select
2215
- value={watch('status')}
2216
- onValueChange={(value: 'active' | 'inactive') =>
2217
- setValue('status', value)
2218
- }
2219
- >
2220
- <SelectTrigger className="h-9 w-full min-w-0">
2221
- <SelectValue />
2222
- </SelectTrigger>
2223
- <SelectContent>
2224
- <SelectItem value="active">{t('active')}</SelectItem>
2225
- <SelectItem value="inactive">
2226
- {t('inactive')}
2227
- </SelectItem>
2228
- </SelectContent>
2229
- </Select>
2230
- </div>
2231
+ <div className="space-y-1.5">
2232
+ <Label className="text-xs font-medium">
2233
+ {t('status')}
2234
+ </Label>
2235
+ <Select
2236
+ value={watch('status')}
2237
+ onValueChange={(value: 'active' | 'inactive') =>
2238
+ setValue('status', value)
2239
+ }
2240
+ >
2241
+ <SelectTrigger className="h-9 w-full min-w-0">
2242
+ <SelectValue />
2243
+ </SelectTrigger>
2244
+ <SelectContent>
2245
+ <SelectItem value="active">{t('active')}</SelectItem>
2246
+ <SelectItem value="inactive">
2247
+ {t('inactive')}
2248
+ </SelectItem>
2249
+ </SelectContent>
2250
+ </Select>
2251
+ </div>
2231
2252
 
2232
- {watchType === 'individual' ? (
2233
- <>
2234
- <div className="space-y-1.5 mb-1.5">
2235
- <Label className="text-xs font-medium">
2236
- {t('birthDate')}
2237
- </Label>
2238
- <DatePickerWithYearMonth
2239
- date={watch('birth_date') || undefined}
2240
- onSelect={(date) =>
2241
- setValue('birth_date', date || null)
2242
- }
2243
- maxDate={new Date()}
2244
- placeholder={t('selectDate')}
2245
- localeCode={currentLocaleCode}
2246
- />
2247
- </div>
2253
+ {watchType === 'individual' ? (
2254
+ <>
2255
+ <div className="space-y-1.5">
2256
+ <Label className="text-xs font-medium">
2257
+ {t('birthDate')}
2258
+ </Label>
2259
+ <DatePickerWithYearMonth
2260
+ date={watch('birth_date') || undefined}
2261
+ onSelect={(date) =>
2262
+ setValue('birth_date', date || null)
2263
+ }
2264
+ maxDate={new Date()}
2265
+ placeholder={t('selectDate')}
2266
+ localeCode={currentLocaleCode}
2267
+ />
2268
+ </div>
2248
2269
 
2249
- <div className="space-y-1.5">
2250
- <Label className="text-xs font-medium">
2251
- {t('gender')}
2252
- </Label>
2253
- <Select
2254
- value={watch('gender') || ''}
2255
- onValueChange={(value: PersonGender) =>
2256
- setValue('gender', value)
2257
- }
2258
- >
2259
- <SelectTrigger className="h-9 w-full min-w-0">
2260
- <SelectValue placeholder={t('selectGender')} />
2261
- </SelectTrigger>
2262
- <SelectContent>
2263
- <SelectItem value="male">
2264
- {t('genderMale')}
2265
- </SelectItem>
2266
- <SelectItem value="female">
2267
- {t('genderFemale')}
2268
- </SelectItem>
2269
- <SelectItem value="other">
2270
- {t('genderOther')}
2271
- </SelectItem>
2272
- </SelectContent>
2273
- </Select>
2274
- </div>
2275
- </>
2276
- ) : null}
2277
- </div>
2270
+ <div className="space-y-1.5">
2271
+ <Label className="text-xs font-medium">
2272
+ {t('gender')}
2273
+ </Label>
2274
+ <Select
2275
+ value={watch('gender') || ''}
2276
+ onValueChange={(value: PersonGender) =>
2277
+ setValue('gender', value)
2278
+ }
2279
+ >
2280
+ <SelectTrigger className="h-9 w-full min-w-0">
2281
+ <SelectValue placeholder={t('selectGender')} />
2282
+ </SelectTrigger>
2283
+ <SelectContent>
2284
+ <SelectItem value="male">
2285
+ {t('genderMale')}
2286
+ </SelectItem>
2287
+ <SelectItem value="female">
2288
+ {t('genderFemale')}
2289
+ </SelectItem>
2290
+ <SelectItem value="other">
2291
+ {t('genderOther')}
2292
+ </SelectItem>
2293
+ </SelectContent>
2294
+ </Select>
2295
+ </div>
2296
+ </>
2297
+ ) : null}
2278
2298
  </div>
2279
2299
 
2280
2300
  <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
@@ -2823,7 +2843,7 @@ export function PersonFormSheet({
2823
2843
  />
2824
2844
  </SelectTrigger>
2825
2845
  <SelectContent>
2826
- {contactTypes.map((contactType) => (
2846
+ {normalizedContactTypes.map((contactType) => (
2827
2847
  <SelectItem
2828
2848
  key={contactType.contact_type_id}
2829
2849
  value={String(contactType.contact_type_id)}
@@ -3253,7 +3273,7 @@ export function PersonFormSheet({
3253
3273
  />
3254
3274
  </SelectTrigger>
3255
3275
  <SelectContent>
3256
- {documentTypes.map((documentType) => (
3276
+ {normalizedDocumentTypes.map((documentType) => (
3257
3277
  <SelectItem
3258
3278
  key={documentType.document_type_id}
3259
3279
  value={String(documentType.document_type_id)}