@open-mercato/core 0.6.7-develop.6674.1.add2064cad → 0.6.7-develop.6677.1.beabb7ca12

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 (39) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/audit_logs/api/audit-logs/access/route.js +5 -1
  3. package/dist/modules/audit_logs/api/audit-logs/access/route.js.map +2 -2
  4. package/dist/modules/audit_logs/api/audit-logs/actions/export/route.js +5 -1
  5. package/dist/modules/audit_logs/api/audit-logs/actions/export/route.js.map +2 -2
  6. package/dist/modules/audit_logs/api/audit-logs/actions/route.js +5 -1
  7. package/dist/modules/audit_logs/api/audit-logs/actions/route.js.map +2 -2
  8. package/dist/modules/audit_logs/api/audit-logs/readScope.js +12 -0
  9. package/dist/modules/audit_logs/api/audit-logs/readScope.js.map +7 -0
  10. package/dist/modules/directory/api/organization-switcher/route.js +84 -20
  11. package/dist/modules/directory/api/organization-switcher/route.js.map +2 -2
  12. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +5 -1
  13. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
  14. package/dist/modules/directory/utils/organizationScope.js +11 -3
  15. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  16. package/package.json +7 -7
  17. package/src/modules/attachments/i18n/de.json +12 -0
  18. package/src/modules/attachments/i18n/en.json +12 -0
  19. package/src/modules/attachments/i18n/es.json +12 -0
  20. package/src/modules/attachments/i18n/pl.json +12 -0
  21. package/src/modules/audit_logs/api/audit-logs/access/route.ts +5 -0
  22. package/src/modules/audit_logs/api/audit-logs/actions/export/route.ts +5 -0
  23. package/src/modules/audit_logs/api/audit-logs/actions/route.ts +5 -0
  24. package/src/modules/audit_logs/api/audit-logs/readScope.ts +28 -0
  25. package/src/modules/auth/i18n/de.json +1 -0
  26. package/src/modules/auth/i18n/en.json +1 -0
  27. package/src/modules/auth/i18n/es.json +1 -0
  28. package/src/modules/auth/i18n/pl.json +1 -0
  29. package/src/modules/catalog/i18n/de.json +18 -0
  30. package/src/modules/catalog/i18n/en.json +18 -0
  31. package/src/modules/catalog/i18n/es.json +18 -0
  32. package/src/modules/catalog/i18n/pl.json +18 -0
  33. package/src/modules/customers/i18n/de.json +44 -0
  34. package/src/modules/customers/i18n/en.json +44 -0
  35. package/src/modules/customers/i18n/es.json +44 -0
  36. package/src/modules/customers/i18n/pl.json +44 -0
  37. package/src/modules/directory/api/organization-switcher/route.ts +97 -26
  38. package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +4 -1
  39. package/src/modules/directory/utils/organizationScope.ts +9 -3
@@ -90,6 +90,7 @@
90
90
  "customers.ai.actions.translate": "Translate",
91
91
  "customers.ai.comingSoon": "Coming soon",
92
92
  "customers.ai.prefix": "IA:",
93
+ "customers.ai_assistant.agents.account.description": "Explora personas, empresas, oportunidades y actividades.",
93
94
  "customers.ai_assistant.agents.account.label": "CRM Assistant",
94
95
  "customers.ai_assistant.context.matchingDeals": "{count} negocios en vista",
95
96
  "customers.ai_assistant.context.matchingPeople": "{count} contacts in view",
@@ -104,31 +105,47 @@
104
105
  "customers.ai_assistant.popover.heading": "AI assistants",
105
106
  "customers.ai_assistant.sheet.composerPlaceholder": "Pregunta sobre personas, empresas, oportunidades...",
106
107
  "customers.ai_assistant.sheet.description": "Asistente de solo lectura. Pregunta sobre personas, empresas, oportunidades y actividades del alcance de esta lista.",
108
+ "customers.ai_assistant.sheet.descriptionWithSelection": "Trabajando con {count} contactos seleccionados. Pregunta por sus datos, oportunidades, empresas y actividades.",
109
+ "customers.ai_assistant.sheet.descriptionWithSelectionCompanies": "Trabajando con {count} empresas seleccionadas. Pregunta por sus oportunidades, contactos y actividades.",
107
110
  "customers.ai_assistant.sheet.descriptionWithSelectionDeals": "Trabajando con {count} negocios seleccionados. Pregunta por su etapa, valor y fechas de cierre.",
108
111
  "customers.ai_assistant.sheet.dock": "Dock to side",
109
112
  "customers.ai_assistant.sheet.selectionPill": "Actuando sobre {count} seleccionados",
110
113
  "customers.ai_assistant.sheet.title": "Asistente AI de clientes",
114
+ "customers.ai_assistant.sheet.welcomeDescriptionAll": "Pregúntame lo que quieras sobre tus clientes, empresas y oportunidades:",
115
+ "customers.ai_assistant.sheet.welcomeDescriptionAllCompanies": "Pregúntame lo que quieras sobre tus empresas, oportunidades y actividades:",
111
116
  "customers.ai_assistant.sheet.welcomeDescriptionAllDeals": "Pregunta por negocios, pipeline o fechas de cierre:",
117
+ "customers.ai_assistant.sheet.welcomeDescriptionSelection": "Listo para explorar tus {count} contactos seleccionados:",
118
+ "customers.ai_assistant.sheet.welcomeDescriptionSelectionCompanies": "Listo para explorar tus {count} empresas seleccionadas:",
112
119
  "customers.ai_assistant.sheet.welcomeDescriptionSelectionDeals": "Listo para explorar tus {count} negocios seleccionados:",
113
120
  "customers.ai_assistant.sheet.welcomeTitle": "CRM Assistant",
114
121
  "customers.ai_assistant.suggestions.activityOverview": "Activity overview",
115
122
  "customers.ai_assistant.suggestions.atRiskSelectedDeals": "Negocios seleccionados en riesgo",
123
+ "customers.ai_assistant.suggestions.companiesActivityOverview": "Resumen de actividad",
116
124
  "customers.ai_assistant.suggestions.companiesForSelectedDeals": "Empresas en negocios seleccionados",
125
+ "customers.ai_assistant.suggestions.companiesWithoutContacts": "Empresas sin contactos",
117
126
  "customers.ai_assistant.suggestions.dealsAtRisk": "Negocios en riesgo de retraso",
118
127
  "customers.ai_assistant.suggestions.dealsClosingThisMonth": "Negocios que cierran este mes",
128
+ "customers.ai_assistant.suggestions.dealsForSelectedCompanies": "Mostrar oportunidades de las empresas seleccionadas",
119
129
  "customers.ai_assistant.suggestions.findCompanies": "Find related companies",
120
130
  "customers.ai_assistant.suggestions.findDeals": "Show deals for selected people",
131
+ "customers.ai_assistant.suggestions.peopleAtSelectedCompanies": "Listar personas de las empresas seleccionadas",
121
132
  "customers.ai_assistant.suggestions.recentDeals": "Show recent deals",
133
+ "customers.ai_assistant.suggestions.searchCompanies": "Buscar una empresa",
122
134
  "customers.ai_assistant.suggestions.searchDeals": "Buscar un negocio",
123
135
  "customers.ai_assistant.suggestions.searchPeople": "Search for a contact",
124
136
  "customers.ai_assistant.suggestions.summarizeSelected": "Summarize selected contacts",
137
+ "customers.ai_assistant.suggestions.summarizeSelectedCompanies": "Resumir las empresas seleccionadas",
125
138
  "customers.ai_assistant.suggestions.summarizeSelectedDeals": "Resumir negocios seleccionados",
126
139
  "customers.ai_assistant.suggestions.topCompanies": "List top companies",
140
+ "customers.ai_assistant.suggestions.topCompaniesByDeals": "Principales empresas por valor de oportunidades",
127
141
  "customers.ai_assistant.suggestions.topDealsByValue": "Mejores negocios por valor",
128
142
  "customers.ai_assistant.suggestions.totalOpenDealValue": "Valor total de negocios abiertos",
129
143
  "customers.ai_assistant.trigger.ariaLabel": "Abrir asistente AI para personas",
144
+ "customers.ai_assistant.trigger.ariaLabelCompanies": "Abrir el asistente de IA para empresas",
130
145
  "customers.ai_assistant.trigger.ariaLabelDeals": "Abrir asistente AI para negocios",
131
146
  "customers.ai_assistant.trigger.label": "AI",
147
+ "customers.ai_assistant.trigger.moreAgentsAriaLabel": "Elegir un asistente de IA",
148
+ "customers.assignableStaff.forbidden": "Permisos insuficientes para cargar el personal asignable.",
132
149
  "customers.assignableStaff.loadError": "No se pudieron cargar los miembros del equipo. Verifica tus permisos y vuelve a intentarlo.",
133
150
  "customers.audit.activities.create": "Crear actividad",
134
151
  "customers.audit.activities.delete": "Eliminar actividad",
@@ -747,6 +764,7 @@
747
764
  "customers.companies.list.bulkDelete.failedGroup": "No se pudieron eliminar {count} empresas: {message}",
748
765
  "customers.companies.list.bulkDelete.operationLabel": "Eliminar {count} empresas",
749
766
  "customers.companies.list.bulkDelete.partial": "{deleted} de {total} empresas eliminadas; {failed} fallaron",
767
+ "customers.companies.list.bulkDelete.progressDescription": "{count} empresas seleccionadas para eliminar",
750
768
  "customers.companies.list.bulkDelete.progressName": "Eliminar empresas seleccionadas",
751
769
  "customers.companies.list.bulkDelete.success": "{count} companies deleted",
752
770
  "customers.companies.list.bulkDelete.title": "Delete {count} companies?",
@@ -885,10 +903,24 @@
885
903
  "customers.countries.it": "Italia",
886
904
  "customers.countries.pl": "Polonia",
887
905
  "customers.countries.us": "Estados Unidos",
906
+ "customers.deal_analyzer.context.dealsInView": "{count} oportunidades en la vista",
907
+ "customers.deal_analyzer.context.selectedDeals": "{count} oportunidades seleccionadas",
888
908
  "customers.deal_analyzer.dock.subtitle": "Negocios",
909
+ "customers.deal_analyzer.sheet.composerPlaceholder": "Analiza oportunidades estancadas, propón cambios de etapa...",
910
+ "customers.deal_analyzer.sheet.description": "Analizador de estado de oportunidades en varios pasos. Detecta oportunidades estancadas y propone transiciones de etapa para su aprobación.",
911
+ "customers.deal_analyzer.sheet.descriptionWithSelection": "Analizando {count} oportunidades seleccionadas para evaluar el pipeline y proponer cambios de etapa.",
889
912
  "customers.deal_analyzer.sheet.dock": "Anclar al lado",
913
+ "customers.deal_analyzer.sheet.selectionPill": "Actuando sobre {count} oportunidades",
890
914
  "customers.deal_analyzer.sheet.title": "Analizador de negocios",
915
+ "customers.deal_analyzer.sheet.welcomeDescriptionAll": "Analiza el estado del pipeline y propón cambios de etapa:",
916
+ "customers.deal_analyzer.sheet.welcomeDescriptionSelection": "Listo para analizar tus {count} oportunidades seleccionadas:",
891
917
  "customers.deal_analyzer.sheet.welcomeTitle": "Analizador de negocios",
918
+ "customers.deal_analyzer.suggestions.analyzeSelected": "Analizar las oportunidades seleccionadas",
919
+ "customers.deal_analyzer.suggestions.analyzeStalledDeals": "Analizar oportunidades estancadas",
920
+ "customers.deal_analyzer.suggestions.overviewByStage": "Resumen del estado de las oportunidades",
921
+ "customers.deal_analyzer.suggestions.proposeStageMove": "Proponer cambios de etapa para las oportunidades seleccionadas",
922
+ "customers.deal_analyzer.suggestions.showAtRiskPipeline": "Mostrar el pipeline en riesgo",
923
+ "customers.deal_analyzer.trigger.ariaLabel": "Abrir el agente de IA Analizador de Oportunidades",
892
924
  "customers.deals.create.associations.companiesPlaceholder": "Buscar empresas por nombre o dominio…",
893
925
  "customers.deals.create.associations.peoplePlaceholder": "Buscar personas por nombre o correo…",
894
926
  "customers.deals.create.back": "Volver a oportunidades",
@@ -973,7 +1005,10 @@
973
1005
  "customers.deals.detail.highlights": "Aspectos destacados",
974
1006
  "customers.deals.detail.linkedEntities.clearAll": "Clear",
975
1007
  "customers.deals.detail.linkedEntities.clearVisible": "Limpiar visibles",
1008
+ "customers.deals.detail.linkedEntities.confirmButton": "Vincular {{entity}}",
1009
+ "customers.deals.detail.linkedEntities.dialogSubtitle": "Vincula un registro existente de {{entity}} a esta oportunidad",
976
1010
  "customers.deals.detail.linkedEntities.dialogTitle": "Manage linked {{entity}}",
1011
+ "customers.deals.detail.linkedEntities.dialogTitleShort": "Vincular {{entity}}",
977
1012
  "customers.deals.detail.linkedEntities.empty": "No linked records yet.",
978
1013
  "customers.deals.detail.linkedEntities.loadMore": "Load more",
979
1014
  "customers.deals.detail.linkedEntities.loading": "Loading linked records…",
@@ -986,6 +1021,7 @@
986
1021
  "customers.deals.detail.linkedEntities.searchAll": "Search all {{entity}}…",
987
1022
  "customers.deals.detail.linkedEntities.searchResults": "Search results",
988
1023
  "customers.deals.detail.linkedEntities.searching": "Searching…",
1024
+ "customers.deals.detail.linkedEntities.sectionLabel": "{{entity}} COINCIDENTES",
989
1025
  "customers.deals.detail.linkedEntities.selectVisible": "Seleccionar visibles",
990
1026
  "customers.deals.detail.linkedEntities.selectedTitle": "Selected {{entity}}",
991
1027
  "customers.deals.detail.linkedEntities.summary": "{{count}} linked {{entity}}",
@@ -1067,6 +1103,7 @@
1067
1103
  "customers.deals.detail.title": "Detalles del deal",
1068
1104
  "customers.deals.detail.unsavedCancel": "Keep editing",
1069
1105
  "customers.deals.detail.unsavedConfirm": "Discard changes",
1106
+ "customers.deals.detail.unsavedDescription": "Tienes cambios sin guardar en esta oportunidad. Guárdalos primero o continúa para descartarlos.",
1070
1107
  "customers.deals.detail.unsavedTitle": "Discard unsaved changes?",
1071
1108
  "customers.deals.detail.untitled": "Deal sin título",
1072
1109
  "customers.deals.detail.updateSuccess": "Deal updated.",
@@ -1374,6 +1411,7 @@
1374
1411
  "customers.deals.list.bulkDelete.failedGroup": "No se pudieron eliminar {count} deals: {message}",
1375
1412
  "customers.deals.list.bulkDelete.operationLabel": "Eliminar {count} deals",
1376
1413
  "customers.deals.list.bulkDelete.partial": "{deleted} de {total} deals eliminados; {failed} fallaron",
1414
+ "customers.deals.list.bulkDelete.progressDescription": "{count} oportunidades seleccionadas para eliminar",
1377
1415
  "customers.deals.list.bulkDelete.progressName": "Eliminar negocios seleccionados",
1378
1416
  "customers.deals.list.bulkDelete.success": "{count} deals deleted",
1379
1417
  "customers.deals.list.bulkDelete.title": "Delete {count} deals?",
@@ -1646,6 +1684,7 @@
1646
1684
  "customers.linking.deal.addNew": "Añadir nueva oportunidad",
1647
1685
  "customers.linking.deal.confirmButton": "Vincular oportunidad",
1648
1686
  "customers.linking.deal.dialogDescription": "Busca oportunidades existentes para vincularlas a este registro.",
1687
+ "customers.linking.deal.dialogSubtitle": "Vincula una oportunidad existente a este registro",
1649
1688
  "customers.linking.deal.dialogTitle": "Vincular oportunidades",
1650
1689
  "customers.linking.deal.orphanWarning": "Esta oportunidad no tiene otros elementos vinculados. Si la desvinculas más tarde, quedará inaccesible.",
1651
1690
  "customers.linking.deal.orphanWarningTitle": "Oportunidad sin empresa",
@@ -1660,8 +1699,11 @@
1660
1699
  "customers.linking.pagination.previous": "Página anterior",
1661
1700
  "customers.linking.pagination.previousShort": "Anterior",
1662
1701
  "customers.linking.person.addNew": "Añadir nueva persona",
1702
+ "customers.linking.person.addNewSubtitle": "La empresa se rellenará automáticamente",
1663
1703
  "customers.linking.person.confirmButton": "Vincular persona",
1664
1704
  "customers.linking.person.dialogDescription": "Busca personas existentes y vincúlalas a este registro sin salir de la página.",
1705
+ "customers.linking.person.dialogSubtitle": "Vincula un contacto existente a esta empresa",
1706
+ "customers.linking.person.dialogSubtitleFor": "Vincula un contacto existente a {{name}}",
1665
1707
  "customers.linking.person.dialogTitle": "Vincular personas",
1666
1708
  "customers.linking.person.searchEmpty": "No se encontraron personas.",
1667
1709
  "customers.linking.person.searchPlaceholder": "Buscar todas las personas…",
@@ -2290,6 +2332,7 @@
2290
2332
  "customers.people.list.bulkDelete.failedGroup": "No se pudieron eliminar {count} personas: {message}",
2291
2333
  "customers.people.list.bulkDelete.operationLabel": "Eliminar {count} personas",
2292
2334
  "customers.people.list.bulkDelete.partial": "{deleted} de {total} personas eliminadas; {failed} fallaron",
2335
+ "customers.people.list.bulkDelete.progressDescription": "{count} personas seleccionadas para eliminar",
2293
2336
  "customers.people.list.bulkDelete.progressName": "Eliminar personas seleccionadas",
2294
2337
  "customers.people.list.bulkDelete.success": "{count} people deleted",
2295
2338
  "customers.people.list.bulkDelete.title": "Delete {count} people?",
@@ -2465,6 +2508,7 @@
2465
2508
  "customers.roles.dialog.team.unassigned": "No team",
2466
2509
  "customers.roles.dialog.teamLabel": "Select a team member",
2467
2510
  "customers.roles.dialog.title": "Assign role",
2511
+ "customers.roles.dialog.visibleCount": "Mostrando {{shown}} de {{total}} miembros del equipo",
2468
2512
  "customers.roles.email": "Send email",
2469
2513
  "customers.roles.emailUnavailable": "Email unavailable",
2470
2514
  "customers.roles.emptySlot": "Not assigned",
@@ -90,6 +90,7 @@
90
90
  "customers.ai.actions.translate": "Przetłumacz",
91
91
  "customers.ai.comingSoon": "Wkrótce",
92
92
  "customers.ai.prefix": "AI:",
93
+ "customers.ai_assistant.agents.account.description": "Przeglądaj osoby, firmy, szanse i aktywności.",
93
94
  "customers.ai_assistant.agents.account.label": "Asystent CRM",
94
95
  "customers.ai_assistant.context.matchingDeals": "{count} transakcji w widoku",
95
96
  "customers.ai_assistant.context.matchingPeople": "Kontaktów w widoku: {count}",
@@ -104,31 +105,47 @@
104
105
  "customers.ai_assistant.popover.heading": "Asystenci AI",
105
106
  "customers.ai_assistant.sheet.composerPlaceholder": "Zapytaj o osoby, firmy, transakcje...",
106
107
  "customers.ai_assistant.sheet.description": "Asystent tylko do odczytu. Zadawaj pytania o osoby, firmy, transakcje i aktywności w zasięgu tej listy.",
108
+ "customers.ai_assistant.sheet.descriptionWithSelection": "Praca na {count} wybranych kontaktach. Pytaj o ich dane, szanse, firmy i aktywności.",
109
+ "customers.ai_assistant.sheet.descriptionWithSelectionCompanies": "Praca na {count} wybranych firmach. Pytaj o ich szanse, kontakty i aktywności.",
107
110
  "customers.ai_assistant.sheet.descriptionWithSelectionDeals": "Pracujesz na {count} zaznaczonych transakcjach. Zadawaj pytania o etap, wartość i daty zamknięcia.",
108
111
  "customers.ai_assistant.sheet.dock": "Przypnij z boku",
109
112
  "customers.ai_assistant.sheet.selectionPill": "Działam na {count} zaznaczonych",
110
113
  "customers.ai_assistant.sheet.title": "Asystent AI dla klientów",
114
+ "customers.ai_assistant.sheet.welcomeDescriptionAll": "Zapytaj mnie o cokolwiek dotyczącego Twoich klientów, firm i szans:",
115
+ "customers.ai_assistant.sheet.welcomeDescriptionAllCompanies": "Zapytaj mnie o cokolwiek dotyczącego Twoich firm, szans i aktywności:",
111
116
  "customers.ai_assistant.sheet.welcomeDescriptionAllDeals": "Zadaj pytanie o transakcje, pipeline lub daty zamknięcia:",
117
+ "customers.ai_assistant.sheet.welcomeDescriptionSelection": "Gotowe do przeglądu Twoich {count} wybranych kontaktów:",
118
+ "customers.ai_assistant.sheet.welcomeDescriptionSelectionCompanies": "Gotowe do przeglądu Twoich {count} wybranych firm:",
112
119
  "customers.ai_assistant.sheet.welcomeDescriptionSelectionDeals": "Możemy przejrzeć Twoje {count} zaznaczonych transakcji:",
113
120
  "customers.ai_assistant.sheet.welcomeTitle": "Asystent CRM",
114
121
  "customers.ai_assistant.suggestions.activityOverview": "Przegląd aktywności",
115
122
  "customers.ai_assistant.suggestions.atRiskSelectedDeals": "Zaznaczone transakcje zagrożone",
123
+ "customers.ai_assistant.suggestions.companiesActivityOverview": "Przegląd aktywności",
116
124
  "customers.ai_assistant.suggestions.companiesForSelectedDeals": "Firmy w zaznaczonych transakcjach",
125
+ "customers.ai_assistant.suggestions.companiesWithoutContacts": "Firmy bez kontaktów",
117
126
  "customers.ai_assistant.suggestions.dealsAtRisk": "Transakcje zagrożone poślizgiem",
118
127
  "customers.ai_assistant.suggestions.dealsClosingThisMonth": "Transakcje zamykające się w tym miesiącu",
128
+ "customers.ai_assistant.suggestions.dealsForSelectedCompanies": "Pokaż szanse dla wybranych firm",
119
129
  "customers.ai_assistant.suggestions.findCompanies": "Znajdź powiązane firmy",
120
130
  "customers.ai_assistant.suggestions.findDeals": "Pokaż szanse dla zaznaczonych osób",
131
+ "customers.ai_assistant.suggestions.peopleAtSelectedCompanies": "Wypisz osoby z wybranych firm",
121
132
  "customers.ai_assistant.suggestions.recentDeals": "Pokaż ostatnie szanse",
133
+ "customers.ai_assistant.suggestions.searchCompanies": "Wyszukaj firmę",
122
134
  "customers.ai_assistant.suggestions.searchDeals": "Wyszukaj transakcję",
123
135
  "customers.ai_assistant.suggestions.searchPeople": "Wyszukaj kontakt",
124
136
  "customers.ai_assistant.suggestions.summarizeSelected": "Podsumuj zaznaczone kontakty",
137
+ "customers.ai_assistant.suggestions.summarizeSelectedCompanies": "Podsumuj wybrane firmy",
125
138
  "customers.ai_assistant.suggestions.summarizeSelectedDeals": "Podsumuj zaznaczone transakcje",
126
139
  "customers.ai_assistant.suggestions.topCompanies": "Wypisz najważniejsze firmy",
140
+ "customers.ai_assistant.suggestions.topCompaniesByDeals": "Najlepsze firmy wg wartości szans",
127
141
  "customers.ai_assistant.suggestions.topDealsByValue": "Najlepsze transakcje wg wartości",
128
142
  "customers.ai_assistant.suggestions.totalOpenDealValue": "Łączna wartość otwartych transakcji",
129
143
  "customers.ai_assistant.trigger.ariaLabel": "Otwórz asystenta AI dla osób",
144
+ "customers.ai_assistant.trigger.ariaLabelCompanies": "Otwórz asystenta AI dla firm",
130
145
  "customers.ai_assistant.trigger.ariaLabelDeals": "Otwórz asystenta AI dla transakcji",
131
146
  "customers.ai_assistant.trigger.label": "AI",
147
+ "customers.ai_assistant.trigger.moreAgentsAriaLabel": "Wybierz asystenta AI",
148
+ "customers.assignableStaff.forbidden": "Brak wystarczających uprawnień do wczytania pracowników możliwych do przypisania.",
132
149
  "customers.assignableStaff.loadError": "Nie udało się załadować pracowników. Sprawdź uprawnienia i spróbuj ponownie.",
133
150
  "customers.audit.activities.create": "Utwórz aktywność",
134
151
  "customers.audit.activities.delete": "Usuń aktywność",
@@ -747,6 +764,7 @@
747
764
  "customers.companies.list.bulkDelete.failedGroup": "Nie udało się usunąć {count} firm: {message}",
748
765
  "customers.companies.list.bulkDelete.operationLabel": "Usuń {count} firm",
749
766
  "customers.companies.list.bulkDelete.partial": "Usunięto {deleted} z {total} firm; {failed} nie powiodło się",
767
+ "customers.companies.list.bulkDelete.progressDescription": "Wybrano {count} firm do usunięcia",
750
768
  "customers.companies.list.bulkDelete.progressName": "Usuń zaznaczone firmy",
751
769
  "customers.companies.list.bulkDelete.success": "Usunięto {count} firm",
752
770
  "customers.companies.list.bulkDelete.title": "Usunąć {count} firm?",
@@ -885,10 +903,24 @@
885
903
  "customers.countries.it": "Włochy",
886
904
  "customers.countries.pl": "Polska",
887
905
  "customers.countries.us": "Stany Zjednoczone",
906
+ "customers.deal_analyzer.context.dealsInView": "{count} szans w widoku",
907
+ "customers.deal_analyzer.context.selectedDeals": "Wybrano {count} szans",
888
908
  "customers.deal_analyzer.dock.subtitle": "Transakcje",
909
+ "customers.deal_analyzer.sheet.composerPlaceholder": "Przeanalizuj zastane szanse, zaproponuj zmiany etapu...",
910
+ "customers.deal_analyzer.sheet.description": "Wieloetapowy analizator kondycji szans. Wskazuje zastane szanse i proponuje zmiany etapu do zatwierdzenia.",
911
+ "customers.deal_analyzer.sheet.descriptionWithSelection": "Analiza {count} wybranych szans pod kątem kondycji lejka i propozycji zmian etapu.",
889
912
  "customers.deal_analyzer.sheet.dock": "Zadokuj z boku",
913
+ "customers.deal_analyzer.sheet.selectionPill": "Działanie na {count} szansach",
890
914
  "customers.deal_analyzer.sheet.title": "Analizator transakcji",
915
+ "customers.deal_analyzer.sheet.welcomeDescriptionAll": "Przeanalizuj kondycję lejka sprzedaży i zaproponuj zmiany etapu:",
916
+ "customers.deal_analyzer.sheet.welcomeDescriptionSelection": "Gotowe do analizy Twoich {count} wybranych szans:",
891
917
  "customers.deal_analyzer.sheet.welcomeTitle": "Analizator transakcji",
918
+ "customers.deal_analyzer.suggestions.analyzeSelected": "Przeanalizuj wybrane szanse",
919
+ "customers.deal_analyzer.suggestions.analyzeStalledDeals": "Przeanalizuj zastane szanse",
920
+ "customers.deal_analyzer.suggestions.overviewByStage": "Przegląd kondycji szans",
921
+ "customers.deal_analyzer.suggestions.proposeStageMove": "Zaproponuj zmiany etapu dla wybranych szans",
922
+ "customers.deal_analyzer.suggestions.showAtRiskPipeline": "Pokaż zagrożony lejek",
923
+ "customers.deal_analyzer.trigger.ariaLabel": "Otwórz agenta AI do analizy szans",
892
924
  "customers.deals.create.associations.companiesPlaceholder": "Szukaj firm po nazwie lub domenie…",
893
925
  "customers.deals.create.associations.peoplePlaceholder": "Szukaj osób po nazwie lub e-mailu…",
894
926
  "customers.deals.create.back": "Powrót do szans sprzedaży",
@@ -973,7 +1005,10 @@
973
1005
  "customers.deals.detail.highlights": "Kluczowe informacje",
974
1006
  "customers.deals.detail.linkedEntities.clearAll": "Wyczyść",
975
1007
  "customers.deals.detail.linkedEntities.clearVisible": "Wyczyść widoczne",
1008
+ "customers.deals.detail.linkedEntities.confirmButton": "Powiąż {{entity}}",
1009
+ "customers.deals.detail.linkedEntities.dialogSubtitle": "Powiąż istniejący rekord {{entity}} z tą szansą",
976
1010
  "customers.deals.detail.linkedEntities.dialogTitle": "Zarządzaj powiązanymi — {{entity}}",
1011
+ "customers.deals.detail.linkedEntities.dialogTitleShort": "Powiąż {{entity}}",
977
1012
  "customers.deals.detail.linkedEntities.empty": "Brak powiązanych rekordów.",
978
1013
  "customers.deals.detail.linkedEntities.loadMore": "Wczytaj więcej",
979
1014
  "customers.deals.detail.linkedEntities.loading": "Ładowanie powiązanych rekordów…",
@@ -986,6 +1021,7 @@
986
1021
  "customers.deals.detail.linkedEntities.searchAll": "Szukaj we wszystkich — {{entity}}…",
987
1022
  "customers.deals.detail.linkedEntities.searchResults": "Wyniki wyszukiwania",
988
1023
  "customers.deals.detail.linkedEntities.searching": "Wyszukiwanie…",
1024
+ "customers.deals.detail.linkedEntities.sectionLabel": "PASUJĄCE {{entity}}",
989
1025
  "customers.deals.detail.linkedEntities.selectVisible": "Zaznacz widoczne",
990
1026
  "customers.deals.detail.linkedEntities.selectedTitle": "Wybrane — {{entity}}",
991
1027
  "customers.deals.detail.linkedEntities.summary": "Powiązane: {{count}} {{entity}}",
@@ -1067,6 +1103,7 @@
1067
1103
  "customers.deals.detail.title": "Szczegóły szansy",
1068
1104
  "customers.deals.detail.unsavedCancel": "Kontynuuj edycję",
1069
1105
  "customers.deals.detail.unsavedConfirm": "Odrzuć zmiany",
1106
+ "customers.deals.detail.unsavedDescription": "Masz niezapisane zmiany w tej szansie. Zapisz je najpierw albo kontynuuj, aby je odrzucić.",
1070
1107
  "customers.deals.detail.unsavedTitle": "Odrzucić niezapisane zmiany?",
1071
1108
  "customers.deals.detail.untitled": "Szansa bez nazwy",
1072
1109
  "customers.deals.detail.updateSuccess": "Zaktualizowano szansę.",
@@ -1374,6 +1411,7 @@
1374
1411
  "customers.deals.list.bulkDelete.failedGroup": "Nie udało się usunąć {count} transakcji: {message}",
1375
1412
  "customers.deals.list.bulkDelete.operationLabel": "Usuń {count} transakcji",
1376
1413
  "customers.deals.list.bulkDelete.partial": "Usunięto {deleted} z {total} transakcji; {failed} nie powiodło się",
1414
+ "customers.deals.list.bulkDelete.progressDescription": "Wybrano {count} szans do usunięcia",
1377
1415
  "customers.deals.list.bulkDelete.progressName": "Usuń zaznaczone transakcje",
1378
1416
  "customers.deals.list.bulkDelete.success": "Usunięto {count} szans",
1379
1417
  "customers.deals.list.bulkDelete.title": "Usunąć {count} szans?",
@@ -1646,6 +1684,7 @@
1646
1684
  "customers.linking.deal.addNew": "Dodaj nową szansę",
1647
1685
  "customers.linking.deal.confirmButton": "Powiąż szansę",
1648
1686
  "customers.linking.deal.dialogDescription": "Wyszukaj istniejące szanse, aby powiązać je z tym rekordem.",
1687
+ "customers.linking.deal.dialogSubtitle": "Powiąż istniejącą szansę z tym rekordem",
1649
1688
  "customers.linking.deal.dialogTitle": "Powiąż szanse",
1650
1689
  "customers.linking.deal.orphanWarning": "Ta szansa nie ma innych powiązanych obiektów. Jeśli ją odepniesz, stanie się niedostępna.",
1651
1690
  "customers.linking.deal.orphanWarningTitle": "Szansa bez firmy",
@@ -1660,8 +1699,11 @@
1660
1699
  "customers.linking.pagination.previous": "Poprzednia strona",
1661
1700
  "customers.linking.pagination.previousShort": "Wstecz",
1662
1701
  "customers.linking.person.addNew": "Dodaj nową osobę",
1702
+ "customers.linking.person.addNewSubtitle": "Firma zostanie uzupełniona automatycznie",
1663
1703
  "customers.linking.person.confirmButton": "Powiąż osobę",
1664
1704
  "customers.linking.person.dialogDescription": "Wyszukaj istniejące osoby i przypisz je do tego rekordu bez opuszczania strony.",
1705
+ "customers.linking.person.dialogSubtitle": "Powiąż istniejący kontakt z tą firmą",
1706
+ "customers.linking.person.dialogSubtitleFor": "Powiąż istniejący kontakt z {{name}}",
1665
1707
  "customers.linking.person.dialogTitle": "Powiąż osoby",
1666
1708
  "customers.linking.person.searchEmpty": "Nie znaleziono pasujących osób.",
1667
1709
  "customers.linking.person.searchPlaceholder": "Szukaj wszystkich osób…",
@@ -2290,6 +2332,7 @@
2290
2332
  "customers.people.list.bulkDelete.failedGroup": "Nie udało się usunąć {count} osób: {message}",
2291
2333
  "customers.people.list.bulkDelete.operationLabel": "Usuń {count} osób",
2292
2334
  "customers.people.list.bulkDelete.partial": "Usunięto {deleted} z {total} osób; {failed} nie powiodło się",
2335
+ "customers.people.list.bulkDelete.progressDescription": "Wybrano {count} osób do usunięcia",
2293
2336
  "customers.people.list.bulkDelete.progressName": "Usuń zaznaczone osoby",
2294
2337
  "customers.people.list.bulkDelete.success": "Usunięto {count} osób",
2295
2338
  "customers.people.list.bulkDelete.title": "Usunąć {count} osób?",
@@ -2465,6 +2508,7 @@
2465
2508
  "customers.roles.dialog.team.unassigned": "Brak zespołu",
2466
2509
  "customers.roles.dialog.teamLabel": "Wybierz członka zespołu",
2467
2510
  "customers.roles.dialog.title": "Przypisz rolę",
2511
+ "customers.roles.dialog.visibleCount": "Wyświetlane {{shown}} z {{total}} członków zespołu",
2468
2512
  "customers.roles.email": "Wyślij e-mail",
2469
2513
  "customers.roles.emailUnavailable": "E-mail niedostępny",
2470
2514
  "customers.roles.emptySlot": "Nieprzypisane",
@@ -6,12 +6,19 @@ import { Organization } from '@open-mercato/core/modules/directory/data/entities
6
6
  import { computeHierarchyForOrganizations, type ComputedHierarchy } from '@open-mercato/core/modules/directory/lib/hierarchy'
7
7
  import { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'
8
8
  import {
9
+ buildOrgScopeTenantCacheTag,
9
10
  getSelectedOrganizationFromRequest,
10
11
  getSelectedTenantFromRequest,
11
12
  resolveOrganizationScope,
12
13
  } from '@open-mercato/core/modules/directory/utils/organizationScope'
14
+ import { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'
13
15
  import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
14
- import { directoryTag, directoryErrorSchema, organizationSwitcherResponseSchema } from '../openapi'
16
+ import {
17
+ directoryTag,
18
+ directoryErrorSchema,
19
+ directoryIdSchema,
20
+ organizationSwitcherResponseSchema,
21
+ } from '../openapi'
15
22
  import { Tenant } from '@open-mercato/core/modules/directory/data/entities'
16
23
  import type { EntityManager } from '@mikro-orm/postgresql'
17
24
  import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
@@ -19,6 +26,21 @@ import type { FilterQuery } from '@mikro-orm/core'
19
26
  import { createLogger } from '@open-mercato/shared/lib/logger'
20
27
 
21
28
  const logger = createLogger('directory').child({ component: 'organization-switcher' })
29
+ const ORGANIZATION_SWITCHER_CACHE_TTL_MS = 60_000
30
+ const organizationSwitcherCachePayloadSchema = organizationSwitcherResponseSchema.or(
31
+ organizationSwitcherResponseSchema.pick({
32
+ items: true,
33
+ selectedId: true,
34
+ canManage: true,
35
+ }),
36
+ )
37
+
38
+ function buildSelectionCacheKeyPart(rawSelected: string | null): string | null {
39
+ if (isAllOrganizationsSelection(rawSelected)) return 'mode:all'
40
+ if (rawSelected === null) return 'mode:none'
41
+ const parsedOrganizationId = directoryIdSchema.safeParse(rawSelected)
42
+ return parsedOrganizationId.success ? `org:${parsedOrganizationId.data.toLowerCase()}` : null
43
+ }
22
44
 
23
45
  type OrganizationMenuNode = {
24
46
  id: string
@@ -141,6 +163,49 @@ export async function GET(req: NextRequest) {
141
163
  })
142
164
  }
143
165
 
166
+ const rawSelected = getSelectedOrganizationFromRequest(req)
167
+ const requestedAll = isAllOrganizationsSelection(rawSelected)
168
+ const selectedKeyPart = buildSelectionCacheKeyPart(rawSelected)
169
+ const cacheKey = selectedKeyPart
170
+ ? `org-switcher:v1:${auth.sub}:${tenantId}:${selectedKeyPart}`
171
+ : null
172
+ let cache: CacheStrategy | null = null
173
+ if (!actorIsSuperAdmin && cacheKey) {
174
+ try {
175
+ cache = container.resolve<CacheStrategy>('cache')
176
+ } catch {
177
+ cache = null
178
+ }
179
+ }
180
+
181
+ const logAccess = async (payload: { items: unknown[]; selectedId: string | null }) => {
182
+ await logCrudAccess({
183
+ container,
184
+ auth,
185
+ request: req,
186
+ items: payload.items,
187
+ idField: 'id',
188
+ resourceKind: 'directory.organization_switcher',
189
+ organizationId: payload.selectedId,
190
+ tenantId,
191
+ query: Object.fromEntries(url.searchParams.entries()),
192
+ })
193
+ }
194
+
195
+ if (cache && cacheKey) {
196
+ const activeCache = cache
197
+ try {
198
+ const cached = await runWithCacheTenant(tenantId, () => activeCache.get(cacheKey))
199
+ const parsedCached = organizationSwitcherCachePayloadSchema.safeParse(cached)
200
+ if (parsedCached.success) {
201
+ await logAccess(parsedCached.data)
202
+ return NextResponse.json(parsedCached.data)
203
+ }
204
+ } catch (err) {
205
+ logger.warn('Failed to read organization switcher cache', { err })
206
+ }
207
+ }
208
+
144
209
  const scopedOrgId = actorTenantId && actorTenantId === tenantId ? auth.orgId ?? null : null
145
210
  const acl = await rbac.loadAcl(auth.sub, { tenantId, organizationId: scopedOrgId })
146
211
  const aclIsSuperAdmin = acl?.isSuperAdmin === true
@@ -163,9 +228,7 @@ export async function GET(req: NextRequest) {
163
228
  { orderBy: { name: 'ASC' } },
164
229
  )
165
230
  const hierarchy = computeHierarchyForOrganizations(orgEntities, tenantId)
166
- const rawSelected = getSelectedOrganizationFromRequest(req)
167
231
  let hasSelectionCookie = rawSelected !== null
168
- const requestedAll = isAllOrganizationsSelection(rawSelected)
169
232
  const scope = await resolveOrganizationScope({
170
233
  em,
171
234
  rbac,
@@ -194,32 +257,40 @@ export async function GET(req: NextRequest) {
194
257
  }
195
258
 
196
259
  const showMenu = menuData.nodes.length > 0 || hasManageFeature || effectiveIsSuperAdmin
197
- if (!showMenu) {
198
- return NextResponse.json({ items: [], selectedId: null, canManage: false })
199
- }
260
+ const response = showMenu
261
+ ? {
262
+ items: menuData.nodes,
263
+ selectedId,
264
+ canManage: !!hasManageFeature,
265
+ canViewAllOrganizations: accessible === null,
266
+ tenantId,
267
+ tenants: tenantRecords,
268
+ isSuperAdmin: effectiveIsSuperAdmin,
269
+ }
270
+ : {
271
+ items: [],
272
+ selectedId: null,
273
+ canManage: false,
274
+ }
200
275
 
201
- const canViewAllOrganizations = accessible === null
202
- const response = {
203
- items: menuData.nodes,
204
- selectedId,
205
- canManage: !!hasManageFeature,
206
- canViewAllOrganizations,
207
- tenantId,
208
- tenants: tenantRecords,
209
- isSuperAdmin: effectiveIsSuperAdmin,
276
+ if (cache && cacheKey) {
277
+ const activeCache = cache
278
+ try {
279
+ await runWithCacheTenant(tenantId, () =>
280
+ activeCache.set(cacheKey, response, {
281
+ ttl: ORGANIZATION_SWITCHER_CACHE_TTL_MS,
282
+ tags: [
283
+ buildOrgScopeTenantCacheTag(tenantId),
284
+ `rbac:user:${auth.sub}`,
285
+ ],
286
+ }),
287
+ )
288
+ } catch (err) {
289
+ logger.warn('Failed to write organization switcher cache', { err })
290
+ }
210
291
  }
211
292
 
212
- await logCrudAccess({
213
- container,
214
- auth,
215
- request: req,
216
- items: response.items,
217
- idField: 'id',
218
- resourceKind: 'directory.organization_switcher',
219
- organizationId: response.selectedId,
220
- tenantId,
221
- query: Object.fromEntries(url.searchParams.entries()),
222
- })
293
+ await logAccess(response)
223
294
 
224
295
  return NextResponse.json(response)
225
296
  } catch (err) {
@@ -8,6 +8,7 @@
8
8
  // for races where the event fires after a request reads the cache.
9
9
 
10
10
  import { buildOrgScopeTenantCacheTag } from '@open-mercato/core/modules/directory/utils/organizationScope'
11
+ import { runWithCacheTenant } from '@open-mercato/cache'
11
12
 
12
13
  type CacheService = {
13
14
  deleteByTags(tags: string[]): Promise<number>
@@ -34,7 +35,9 @@ export default async function handle(
34
35
  }
35
36
  if (!cache) return
36
37
  try {
37
- await cache.deleteByTags([buildOrgScopeTenantCacheTag(tenantId)])
38
+ await runWithCacheTenant(tenantId, () =>
39
+ cache.deleteByTags([buildOrgScopeTenantCacheTag(tenantId)]),
40
+ )
38
41
  } catch {
39
42
  // best-effort; TTL is the backstop.
40
43
  }
@@ -5,7 +5,7 @@ import { Organization } from '@open-mercato/core/modules/directory/data/entities
5
5
  import { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'
6
6
  import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
7
7
  import type { AuthContext } from '@open-mercato/shared/lib/auth/server'
8
- import type { CacheStrategy } from '@open-mercato/cache'
8
+ import { getCurrentCacheTenant, runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'
9
9
  import { parseSelectedOrganizationCookie, parseSelectedTenantCookie } from './scopeCookies'
10
10
  import { createLogger } from '@open-mercato/shared/lib/logger'
11
11
 
@@ -104,11 +104,15 @@ function resolveCacheFromContainer(container: AwilixContainer | null | undefined
104
104
  export async function invalidateOrganizationScopeCacheForUser(
105
105
  container: AwilixContainer,
106
106
  userId: string,
107
+ tenantId?: string | null,
107
108
  ): Promise<void> {
108
109
  const cache = resolveCacheFromContainer(container)
109
110
  if (!cache?.deleteByTags) return
110
111
  try {
111
- await cache.deleteByTags([buildOrgScopeUserCacheTag(userId)])
112
+ const cacheTenantId = tenantId === undefined ? getCurrentCacheTenant() : tenantId
113
+ await runWithCacheTenant(cacheTenantId, () =>
114
+ cache.deleteByTags([buildOrgScopeUserCacheTag(userId)]),
115
+ )
112
116
  } catch (err) {
113
117
  logger.warn('Cache invalidate user failed', { err })
114
118
  }
@@ -121,7 +125,9 @@ export async function invalidateOrganizationScopeCacheForTenant(
121
125
  const cache = resolveCacheFromContainer(container)
122
126
  if (!cache?.deleteByTags) return
123
127
  try {
124
- await cache.deleteByTags([buildOrgScopeTenantCacheTag(tenantId)])
128
+ await runWithCacheTenant(tenantId, () =>
129
+ cache.deleteByTags([buildOrgScopeTenantCacheTag(tenantId)]),
130
+ )
125
131
  } catch (err) {
126
132
  logger.warn('Cache invalidate tenant failed', { err })
127
133
  }