@open-mercato/core 0.6.6-develop.6386.1.391a1afb9e → 0.6.6-develop.6387.1.f4896ad565

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.
Files changed (57) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/customers/api/deals/map/route.js +433 -0
  3. package/dist/modules/customers/api/deals/map/route.js.map +7 -0
  4. package/dist/modules/customers/backend/customers/companies/create/page.js +2 -0
  5. package/dist/modules/customers/backend/customers/companies/create/page.js.map +2 -2
  6. package/dist/modules/customers/backend/customers/deals/map/components/DealsLocationPanel.js +159 -0
  7. package/dist/modules/customers/backend/customers/deals/map/components/DealsLocationPanel.js.map +7 -0
  8. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvas.js +26 -0
  9. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvas.js.map +7 -0
  10. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.js +321 -0
  11. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.js.map +7 -0
  12. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js +533 -0
  13. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js.map +7 -0
  14. package/dist/modules/customers/backend/customers/deals/map/page.js +55 -0
  15. package/dist/modules/customers/backend/customers/deals/map/page.js.map +7 -0
  16. package/dist/modules/customers/backend/customers/deals/map/page.meta.js +38 -0
  17. package/dist/modules/customers/backend/customers/deals/map/page.meta.js.map +7 -0
  18. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js +3 -3
  19. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js.map +2 -2
  20. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js +22 -1
  21. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js.map +2 -2
  22. package/dist/modules/customers/backend/customers/people/create/page.js +2 -0
  23. package/dist/modules/customers/backend/customers/people/create/page.js.map +2 -2
  24. package/dist/modules/customers/components/AddressEditor.js +34 -2
  25. package/dist/modules/customers/components/AddressEditor.js.map +2 -2
  26. package/dist/modules/customers/components/AddressTiles.js +43 -3
  27. package/dist/modules/customers/components/AddressTiles.js.map +2 -2
  28. package/dist/modules/customers/components/detail/AddressesSection.js +4 -1
  29. package/dist/modules/customers/components/detail/AddressesSection.js.map +2 -2
  30. package/dist/modules/customers/components/formConfig.js +10 -0
  31. package/dist/modules/customers/components/formConfig.js.map +2 -2
  32. package/dist/modules/customers/data/validators.js +3 -2
  33. package/dist/modules/customers/data/validators.js.map +2 -2
  34. package/dist/modules/customers/lib/dealsMapLocation.js +75 -0
  35. package/dist/modules/customers/lib/dealsMapLocation.js.map +7 -0
  36. package/package.json +11 -7
  37. package/src/modules/customers/api/deals/map/route.ts +549 -0
  38. package/src/modules/customers/backend/customers/companies/create/page.tsx +2 -0
  39. package/src/modules/customers/backend/customers/deals/map/components/DealsLocationPanel.tsx +213 -0
  40. package/src/modules/customers/backend/customers/deals/map/components/DealsMapCanvas.tsx +79 -0
  41. package/src/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.tsx +407 -0
  42. package/src/modules/customers/backend/customers/deals/map/components/DealsMapView.tsx +667 -0
  43. package/src/modules/customers/backend/customers/deals/map/page.meta.ts +36 -0
  44. package/src/modules/customers/backend/customers/deals/map/page.tsx +77 -0
  45. package/src/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.tsx +3 -3
  46. package/src/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.tsx +22 -1
  47. package/src/modules/customers/backend/customers/people/create/page.tsx +2 -0
  48. package/src/modules/customers/components/AddressEditor.tsx +35 -0
  49. package/src/modules/customers/components/AddressTiles.tsx +51 -1
  50. package/src/modules/customers/components/detail/AddressesSection.tsx +3 -0
  51. package/src/modules/customers/components/formConfig.tsx +10 -0
  52. package/src/modules/customers/data/validators.ts +13 -2
  53. package/src/modules/customers/i18n/de.json +24 -0
  54. package/src/modules/customers/i18n/en.json +24 -0
  55. package/src/modules/customers/i18n/es.json +24 -0
  56. package/src/modules/customers/i18n/pl.json +24 -0
  57. package/src/modules/customers/lib/dealsMapLocation.ts +125 -0
@@ -1339,6 +1339,7 @@
1339
1339
  "customers.deals.kanban.sort.title": "Sort by",
1340
1340
  "customers.deals.kanban.view.kanban": "Kanban",
1341
1341
  "customers.deals.kanban.view.list": "List",
1342
+ "customers.deals.kanban.view.map": "Mapa",
1342
1343
  "customers.deals.kanban.view.tablistLabel": "Deals views",
1343
1344
  "customers.deals.list.actions.delete": "Eliminar",
1344
1345
  "customers.deals.list.actions.deleting": "Eliminando…",
@@ -1424,6 +1425,25 @@
1424
1425
  "customers.deals.list.unknownOwner": "Responsable desconocido",
1425
1426
  "customers.deals.list.unnamedCompany": "Empresa sin nombre",
1426
1427
  "customers.deals.list.unnamedPerson": "Persona sin nombre",
1428
+ "customers.deals.map.canvas.label": "Mapa de oportunidades",
1429
+ "customers.deals.map.canvas.loading": "Cargando el mapa…",
1430
+ "customers.deals.map.legend.title": "Etapas",
1431
+ "customers.deals.map.loadError": "No se pudieron cargar las oportunidades para el mapa.",
1432
+ "customers.deals.map.panel.count": "{count} con ubicación",
1433
+ "customers.deals.map.panel.empty.description": "Añade latitud y longitud a las direcciones de empresas o personas para mostrar sus oportunidades aquí.",
1434
+ "customers.deals.map.panel.empty.title": "Aún no hay oportunidades en el mapa",
1435
+ "customers.deals.map.panel.hint": "Haz clic en una oportunidad o en un pin del mapa para ver la vista previa.",
1436
+ "customers.deals.map.panel.noAddress": "Sin coordenadas de dirección",
1437
+ "customers.deals.map.panel.sort.label": "Ordenar el panel de oportunidades",
1438
+ "customers.deals.map.panel.sort.listOrder": "Orden de la lista",
1439
+ "customers.deals.map.panel.sort.proximity": "Más cercanas primero",
1440
+ "customers.deals.map.panel.title": "Oportunidades con ubicación",
1441
+ "customers.deals.map.preview.close": "Cerrar vista previa",
1442
+ "customers.deals.map.preview.closeShort": "Cierre {date}",
1443
+ "customers.deals.map.preview.openDeal": "Abrir oportunidad",
1444
+ "customers.deals.map.preview.probabilityShort": "{value}%",
1445
+ "customers.deals.map.retry": "Reintentar",
1446
+ "customers.deals.map.truncated": "Mostrando las primeras {count} de {total} oportunidades ubicadas — ajusta los filtros para acotar el mapa.",
1427
1447
  "customers.deals.pipeline.actions.openDeal": "Abrir oportunidad",
1428
1448
  "customers.deals.pipeline.card.expectedClose": "Cierre previsto",
1429
1449
  "customers.deals.pipeline.card.probability": "Probabilidad",
@@ -1651,6 +1671,7 @@
1651
1671
  "customers.nav.companies": "Empresas",
1652
1672
  "customers.nav.companies.create": "Nueva empresa",
1653
1673
  "customers.nav.deals": "Oportunidades",
1674
+ "customers.nav.deals.map": "Mapa de oportunidades",
1654
1675
  "customers.nav.deals.pipeline": "Pipeline de ventas",
1655
1676
  "customers.nav.group": "Clientes",
1656
1677
  "customers.nav.people": "Personas",
@@ -1766,8 +1787,10 @@
1766
1787
  "customers.people.detail.addresses.fields.country": "País",
1767
1788
  "customers.people.detail.addresses.fields.flatNumber": "Número interior / piso",
1768
1789
  "customers.people.detail.addresses.fields.label": "Etiqueta",
1790
+ "customers.people.detail.addresses.fields.latitude": "Latitud",
1769
1791
  "customers.people.detail.addresses.fields.line1": "Dirección (línea 1)",
1770
1792
  "customers.people.detail.addresses.fields.line2": "Dirección (línea 2)",
1793
+ "customers.people.detail.addresses.fields.longitude": "Longitud",
1771
1794
  "customers.people.detail.addresses.fields.postalCode": "Código postal",
1772
1795
  "customers.people.detail.addresses.fields.primary": "Marcar como principal",
1773
1796
  "customers.people.detail.addresses.fields.region": "Provincia / región",
@@ -1799,6 +1822,7 @@
1799
1822
  "customers.people.detail.addresses.types.saveError": "Error al guardar el tipo de dirección.",
1800
1823
  "customers.people.detail.addresses.update": "Actualizar dirección ⌘⏎ / Ctrl+Enter",
1801
1824
  "customers.people.detail.addresses.updating": "Actualizando…",
1825
+ "customers.people.detail.addresses.validation.coordinateRange": "{{field}} debe estar entre {{min}} y {{max}}",
1802
1826
  "customers.people.detail.addresses.validation.generic": "Revisa {{field}}",
1803
1827
  "customers.people.detail.addresses.validation.invalid": "Valor no válido para {{field}}",
1804
1828
  "customers.people.detail.addresses.validation.required": "Introduce {{field}}",
@@ -1339,6 +1339,7 @@
1339
1339
  "customers.deals.kanban.sort.title": "Sort by",
1340
1340
  "customers.deals.kanban.view.kanban": "Kanban",
1341
1341
  "customers.deals.kanban.view.list": "Lista",
1342
+ "customers.deals.kanban.view.map": "Mapa",
1342
1343
  "customers.deals.kanban.view.tablistLabel": "Deals views",
1343
1344
  "customers.deals.list.actions.delete": "Usuń",
1344
1345
  "customers.deals.list.actions.deleting": "Usuwanie…",
@@ -1424,6 +1425,25 @@
1424
1425
  "customers.deals.list.unknownOwner": "Nieznany właściciel",
1425
1426
  "customers.deals.list.unnamedCompany": "Firma bez nazwy",
1426
1427
  "customers.deals.list.unnamedPerson": "Osoba bez nazwy",
1428
+ "customers.deals.map.canvas.label": "Mapa dealów",
1429
+ "customers.deals.map.canvas.loading": "Ładowanie mapy…",
1430
+ "customers.deals.map.legend.title": "Etapy",
1431
+ "customers.deals.map.loadError": "Nie udało się załadować dealów na mapę.",
1432
+ "customers.deals.map.panel.count": "{count} z lokalizacją",
1433
+ "customers.deals.map.panel.empty.description": "Dodaj szerokość i długość geograficzną do adresów firm lub osób, aby ich deale pojawiły się na mapie.",
1434
+ "customers.deals.map.panel.empty.title": "Brak dealów na mapie",
1435
+ "customers.deals.map.panel.hint": "Kliknij deal lub pinezkę na mapie, aby zobaczyć podgląd.",
1436
+ "customers.deals.map.panel.noAddress": "Brak współrzędnych adresu",
1437
+ "customers.deals.map.panel.sort.label": "Sortowanie panelu dealów",
1438
+ "customers.deals.map.panel.sort.listOrder": "Kolejność listy",
1439
+ "customers.deals.map.panel.sort.proximity": "Najbliższe najpierw",
1440
+ "customers.deals.map.panel.title": "Dealy z lokalizacją",
1441
+ "customers.deals.map.preview.close": "Zamknij podgląd",
1442
+ "customers.deals.map.preview.closeShort": "Zamknięcie {date}",
1443
+ "customers.deals.map.preview.openDeal": "Otwórz deal",
1444
+ "customers.deals.map.preview.probabilityShort": "{value}%",
1445
+ "customers.deals.map.retry": "Spróbuj ponownie",
1446
+ "customers.deals.map.truncated": "Wyświetlono pierwsze {count} z {total} zlokalizowanych dealów — zawęź filtry, aby ograniczyć mapę.",
1427
1447
  "customers.deals.pipeline.actions.openDeal": "Otwórz szansę",
1428
1448
  "customers.deals.pipeline.card.expectedClose": "Planowane zamknięcie",
1429
1449
  "customers.deals.pipeline.card.probability": "Prawdopodobieństwo",
@@ -1651,6 +1671,7 @@
1651
1671
  "customers.nav.companies": "Firmy",
1652
1672
  "customers.nav.companies.create": "Nowa firma",
1653
1673
  "customers.nav.deals": "Szanse sprzedaży",
1674
+ "customers.nav.deals.map": "Mapa szans sprzedaży",
1654
1675
  "customers.nav.deals.pipeline": "Lejek sprzedaży",
1655
1676
  "customers.nav.group": "Klienci",
1656
1677
  "customers.nav.people": "Osoby",
@@ -1766,8 +1787,10 @@
1766
1787
  "customers.people.detail.addresses.fields.country": "Kraj",
1767
1788
  "customers.people.detail.addresses.fields.flatNumber": "Numer lokalu",
1768
1789
  "customers.people.detail.addresses.fields.label": "Etykieta",
1790
+ "customers.people.detail.addresses.fields.latitude": "Szerokość geograficzna",
1769
1791
  "customers.people.detail.addresses.fields.line1": "Adres (linia 1)",
1770
1792
  "customers.people.detail.addresses.fields.line2": "Adres (linia 2)",
1793
+ "customers.people.detail.addresses.fields.longitude": "Długość geograficzna",
1771
1794
  "customers.people.detail.addresses.fields.postalCode": "Kod pocztowy",
1772
1795
  "customers.people.detail.addresses.fields.primary": "Ustaw jako główny",
1773
1796
  "customers.people.detail.addresses.fields.region": "Region / województwo",
@@ -1799,6 +1822,7 @@
1799
1822
  "customers.people.detail.addresses.types.saveError": "Nie udało się zapisać typu adresu.",
1800
1823
  "customers.people.detail.addresses.update": "Zaktualizuj adres ⌘⏎ / Ctrl+Enter",
1801
1824
  "customers.people.detail.addresses.updating": "Aktualizowanie…",
1825
+ "customers.people.detail.addresses.validation.coordinateRange": "Pole {{field}} musi mieścić się w zakresie od {{min}} do {{max}}",
1802
1826
  "customers.people.detail.addresses.validation.generic": "Sprawdź pole {{field}}",
1803
1827
  "customers.people.detail.addresses.validation.invalid": "Nieprawidłowa wartość w polu {{field}}",
1804
1828
  "customers.people.detail.addresses.validation.required": "Wpisz {{field}}",
@@ -0,0 +1,125 @@
1
+ export type DealMapLocationSource = 'company' | 'person'
2
+
3
+ export type DealMapLocation = {
4
+ latitude: number
5
+ longitude: number
6
+ city: string | null
7
+ region: string | null
8
+ country: string | null
9
+ source: DealMapLocationSource
10
+ entityId: string
11
+ addressId: string
12
+ }
13
+
14
+ export type DealMapLink = {
15
+ dealId: string
16
+ entityId: string
17
+ createdAt?: Date | string | null
18
+ }
19
+
20
+ export type DealMapAddress = {
21
+ id: string
22
+ entityId: string
23
+ isPrimary: boolean
24
+ latitude: number | null | undefined
25
+ longitude: number | null | undefined
26
+ city?: string | null
27
+ region?: string | null
28
+ country?: string | null
29
+ createdAt?: Date | string | null
30
+ }
31
+
32
+ type LocatedAddress = DealMapAddress & { latitude: number; longitude: number }
33
+
34
+ function hasFiniteCoordinates(address: DealMapAddress): address is LocatedAddress {
35
+ return (
36
+ typeof address.latitude === 'number' &&
37
+ Number.isFinite(address.latitude) &&
38
+ typeof address.longitude === 'number' &&
39
+ Number.isFinite(address.longitude)
40
+ )
41
+ }
42
+
43
+ function toSortableTime(value: Date | string | null | undefined): number {
44
+ if (value instanceof Date) return value.getTime()
45
+ if (typeof value === 'string') {
46
+ const parsed = new Date(value).getTime()
47
+ return Number.isNaN(parsed) ? Number.MAX_SAFE_INTEGER : parsed
48
+ }
49
+ return Number.MAX_SAFE_INTEGER
50
+ }
51
+
52
+ function compareCandidates(a: LocatedAddress, b: LocatedAddress): number {
53
+ if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1
54
+ const timeDiff = toSortableTime(a.createdAt) - toSortableTime(b.createdAt)
55
+ if (timeDiff !== 0) return timeDiff
56
+ return a.id.localeCompare(b.id)
57
+ }
58
+
59
+ function groupLinkEntityIds(links: DealMapLink[]): Map<string, string[]> {
60
+ const entityIdsByDeal = new Map<string, string[]>()
61
+ for (const link of links) {
62
+ if (!link.dealId || !link.entityId) continue
63
+ const bucket = entityIdsByDeal.get(link.dealId) ?? []
64
+ if (!bucket.includes(link.entityId)) bucket.push(link.entityId)
65
+ entityIdsByDeal.set(link.dealId, bucket)
66
+ }
67
+ return entityIdsByDeal
68
+ }
69
+
70
+ function pickLocation(
71
+ entityIds: string[] | undefined,
72
+ addressesByEntity: Map<string, LocatedAddress[]>,
73
+ source: DealMapLocationSource,
74
+ ): DealMapLocation | null {
75
+ if (!entityIds || entityIds.length === 0) return null
76
+ let best: LocatedAddress | null = null
77
+ for (const entityId of entityIds) {
78
+ const candidate = addressesByEntity.get(entityId)?.[0]
79
+ if (!candidate) continue
80
+ if (!best || compareCandidates(candidate, best) < 0) best = candidate
81
+ }
82
+ if (!best) return null
83
+ return {
84
+ latitude: best.latitude,
85
+ longitude: best.longitude,
86
+ city: best.city ?? null,
87
+ region: best.region ?? null,
88
+ country: best.country ?? null,
89
+ source,
90
+ entityId: best.entityId,
91
+ addressId: best.id,
92
+ }
93
+ }
94
+
95
+ export function resolveDealLocations(
96
+ dealIds: string[],
97
+ companyLinks: DealMapLink[],
98
+ personLinks: DealMapLink[],
99
+ addresses: DealMapAddress[],
100
+ ): Map<string, DealMapLocation | null> {
101
+ const addressesByEntity = new Map<string, LocatedAddress[]>()
102
+ for (const address of addresses) {
103
+ if (!address.id || !address.entityId) continue
104
+ if (!hasFiniteCoordinates(address)) continue
105
+ const bucket = addressesByEntity.get(address.entityId) ?? []
106
+ bucket.push(address)
107
+ addressesByEntity.set(address.entityId, bucket)
108
+ }
109
+ for (const bucket of addressesByEntity.values()) {
110
+ bucket.sort(compareCandidates)
111
+ }
112
+
113
+ const companyEntityIdsByDeal = groupLinkEntityIds(companyLinks)
114
+ const personEntityIdsByDeal = groupLinkEntityIds(personLinks)
115
+
116
+ const locations = new Map<string, DealMapLocation | null>()
117
+ for (const dealId of dealIds) {
118
+ const companyLocation = pickLocation(companyEntityIdsByDeal.get(dealId), addressesByEntity, 'company')
119
+ locations.set(
120
+ dealId,
121
+ companyLocation ?? pickLocation(personEntityIdsByDeal.get(dealId), addressesByEntity, 'person'),
122
+ )
123
+ }
124
+ return locations
125
+ }