@open-mercato/core 0.6.5-develop.5382.1.f542de69af → 0.6.6-develop.5412.1.e2a52b14f0
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/.turbo/turbo-build.log +1 -1
- package/dist/helpers/integration/crmFixtures.js +4 -0
- package/dist/helpers/integration/crmFixtures.js.map +2 -2
- package/dist/modules/attachments/api/route.js +2 -0
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/lib/access.js +18 -0
- package/dist/modules/attachments/lib/access.js.map +2 -2
- package/dist/modules/auth/services/rbacService.js +3 -2
- package/dist/modules/auth/services/rbacService.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +0 -3
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +43 -2
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/deals/summary/route.js +402 -0
- package/dist/modules/customers/api/deals/summary/route.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +16 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +22 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +221 -56
- package/dist/modules/customers/backend/customers/deals/page.js.map +3 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/cli.js +15 -9
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/components/DealsKpiStrip.js +282 -0
- package/dist/modules/customers/components/DealsKpiStrip.js.map +7 -0
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +0 -1
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +100 -17
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/kpi/PipelineStageBar.js +63 -0
- package/dist/modules/customers/components/kpi/PipelineStageBar.js.map +7 -0
- package/dist/modules/customers/lib/dealsMetrics.js +82 -0
- package/dist/modules/customers/lib/dealsMetrics.js.map +7 -0
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +2 -1
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +59 -27
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/entities.js +7 -0
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +26 -15
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +12 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/components/useRecordsEntityGuard.js +30 -0
- package/dist/modules/entities/components/useRecordsEntityGuard.js.map +7 -0
- package/dist/modules/query_index/lib/engine.js +4 -2
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/staff/api/team-members.js +9 -2
- package/dist/modules/staff/api/team-members.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +24 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +11 -6
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/commands/team-members.js +1 -1
- package/dist/modules/staff/commands/team-members.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +1 -1
- package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
- package/dist/modules/staff/lib/scheduleSwitch.js +23 -0
- package/dist/modules/staff/lib/scheduleSwitch.js.map +7 -0
- package/dist/modules/workflows/backend/definitions/create/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -4
- package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -5
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
- package/package.json +8 -8
- package/src/helpers/integration/crmFixtures.ts +21 -1
- package/src/modules/attachments/AGENTS.md +79 -0
- package/src/modules/attachments/api/route.ts +2 -0
- package/src/modules/attachments/lib/access.ts +36 -0
- package/src/modules/auth/services/rbacService.ts +11 -2
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +0 -3
- package/src/modules/customers/api/deals/route.ts +51 -2
- package/src/modules/customers/api/deals/summary/route.ts +496 -0
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +28 -6
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +33 -6
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +17 -2
- package/src/modules/customers/backend/customers/deals/page.tsx +254 -66
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -2
- package/src/modules/customers/cli.ts +15 -15
- package/src/modules/customers/components/DealsKpiStrip.tsx +389 -0
- package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +0 -1
- package/src/modules/customers/components/detail/DealForm.tsx +121 -19
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -2
- package/src/modules/customers/components/kpi/PipelineStageBar.tsx +77 -0
- package/src/modules/customers/i18n/de.json +43 -0
- package/src/modules/customers/i18n/en.json +43 -0
- package/src/modules/customers/i18n/es.json +43 -0
- package/src/modules/customers/i18n/pl.json +43 -0
- package/src/modules/customers/lib/dealsMetrics.ts +159 -0
- package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +3 -1
- package/src/modules/directory/utils/organizationScope.ts +85 -30
- package/src/modules/entities/api/entities.ts +11 -0
- package/src/modules/entities/api/records.ts +46 -25
- package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/create/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +23 -0
- package/src/modules/entities/components/useRecordsEntityGuard.ts +41 -0
- package/src/modules/entities/i18n/de.json +1 -0
- package/src/modules/entities/i18n/en.json +1 -0
- package/src/modules/entities/i18n/es.json +1 -0
- package/src/modules/entities/i18n/pl.json +1 -0
- package/src/modules/query_index/lib/engine.ts +11 -5
- package/src/modules/staff/api/team-members.ts +9 -2
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +31 -1
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +18 -8
- package/src/modules/staff/commands/team-members.ts +5 -2
- package/src/modules/staff/components/TeamMemberForm.tsx +4 -1
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/lib/scheduleSwitch.ts +46 -0
- package/src/modules/workflows/backend/definitions/create/page.tsx +1 -2
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -2
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -2
- package/src/modules/workflows/components/NodeEditDialog.tsx +1 -4
- package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +4 -7
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -2
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -3
|
@@ -38,6 +38,9 @@ export type DealFormBaseValues = {
|
|
|
38
38
|
companyIds?: string[]
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
type PipelineOption = { id: string; name: string; isDefault: boolean }
|
|
42
|
+
type PipelineStageOption = { id: string; label: string; order: number }
|
|
43
|
+
|
|
41
44
|
export type DealFormSubmitPayload = {
|
|
42
45
|
base: DealFormBaseValues
|
|
43
46
|
custom: Record<string, unknown>
|
|
@@ -64,6 +67,8 @@ export type DealFormProps = {
|
|
|
64
67
|
showAssociationsGroup?: boolean
|
|
65
68
|
showVersionHistory?: boolean
|
|
66
69
|
showCancelAction?: boolean
|
|
70
|
+
initialPipelineOptions?: PipelineOption[]
|
|
71
|
+
initialPipelineStageOptions?: PipelineStageOption[]
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
type EntityOption = {
|
|
@@ -92,6 +97,84 @@ type EntityMultiSelectProps = {
|
|
|
92
97
|
|
|
93
98
|
const DEAL_ENTITY_IDS = [E.customers.customer_deal]
|
|
94
99
|
const CURRENCY_PRIORITY = ['EUR', 'USD', 'GBP', 'PLN'] as const
|
|
100
|
+
const PIPELINE_OPTIONS_TTL_MS = 60_000
|
|
101
|
+
const PIPELINE_STAGE_OPTIONS_TTL_MS = 30_000
|
|
102
|
+
|
|
103
|
+
type MetadataCacheEntry<T> = {
|
|
104
|
+
expiresAt: number
|
|
105
|
+
promise: Promise<T>
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let pipelineOptionsCache: MetadataCacheEntry<PipelineOption[]> | null = null
|
|
109
|
+
const pipelineStageOptionsCache = new Map<string, MetadataCacheEntry<PipelineStageOption[]>>()
|
|
110
|
+
|
|
111
|
+
function isFreshCacheEntry<T>(entry: MetadataCacheEntry<T> | null | undefined): entry is MetadataCacheEntry<T> {
|
|
112
|
+
return Boolean(entry && entry.expiresAt > Date.now())
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function normalizePipelineOptions(options: PipelineOption[] | undefined): PipelineOption[] {
|
|
116
|
+
const byId = new Map<string, PipelineOption>()
|
|
117
|
+
for (const option of options ?? []) {
|
|
118
|
+
if (!option.id) continue
|
|
119
|
+
byId.set(option.id, {
|
|
120
|
+
id: option.id,
|
|
121
|
+
name: option.name,
|
|
122
|
+
isDefault: option.isDefault === true,
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
return Array.from(byId.values())
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function mergePipelineOptions(seed: PipelineOption[], loaded: PipelineOption[]): PipelineOption[] {
|
|
129
|
+
const byId = new Map<string, PipelineOption>()
|
|
130
|
+
for (const option of seed) byId.set(option.id, option)
|
|
131
|
+
for (const option of loaded) byId.set(option.id, option)
|
|
132
|
+
return Array.from(byId.values())
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function normalizePipelineStageOptions(options: PipelineStageOption[] | undefined): PipelineStageOption[] {
|
|
136
|
+
return [...(options ?? [])]
|
|
137
|
+
.filter((option) => option.id)
|
|
138
|
+
.sort((left, right) => left.order - right.order)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function fetchPipelineOptions(): Promise<PipelineOption[]> {
|
|
142
|
+
if (isFreshCacheEntry(pipelineOptionsCache)) return pipelineOptionsCache.promise
|
|
143
|
+
const entry: MetadataCacheEntry<PipelineOption[]> = {
|
|
144
|
+
expiresAt: Date.now() + PIPELINE_OPTIONS_TTL_MS,
|
|
145
|
+
promise: apiCall<{ items: PipelineOption[] }>('/api/customers/pipelines')
|
|
146
|
+
.then((call) => (call.ok && call.result?.items ? normalizePipelineOptions(call.result.items) : [])),
|
|
147
|
+
}
|
|
148
|
+
pipelineOptionsCache = entry
|
|
149
|
+
try {
|
|
150
|
+
return await entry.promise
|
|
151
|
+
} catch (error) {
|
|
152
|
+
if (pipelineOptionsCache === entry) pipelineOptionsCache = null
|
|
153
|
+
throw error
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function fetchPipelineStageOptions(pipelineId: string): Promise<PipelineStageOption[]> {
|
|
158
|
+
const cached = pipelineStageOptionsCache.get(pipelineId)
|
|
159
|
+
if (isFreshCacheEntry(cached)) return cached.promise
|
|
160
|
+
const entry: MetadataCacheEntry<PipelineStageOption[]> = {
|
|
161
|
+
expiresAt: Date.now() + PIPELINE_STAGE_OPTIONS_TTL_MS,
|
|
162
|
+
promise: apiCall<{ items: PipelineStageOption[] }>(`/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(pipelineId)}`)
|
|
163
|
+
.then((call) => (call.ok && call.result?.items ? normalizePipelineStageOptions(call.result.items) : [])),
|
|
164
|
+
}
|
|
165
|
+
pipelineStageOptionsCache.set(pipelineId, entry)
|
|
166
|
+
try {
|
|
167
|
+
return await entry.promise
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (pipelineStageOptionsCache.get(pipelineId) === entry) pipelineStageOptionsCache.delete(pipelineId)
|
|
170
|
+
throw error
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function resetDealPipelineMetadataCacheForTests() {
|
|
175
|
+
pipelineOptionsCache = null
|
|
176
|
+
pipelineStageOptionsCache.clear()
|
|
177
|
+
}
|
|
95
178
|
|
|
96
179
|
const schema = z.object({
|
|
97
180
|
title: z
|
|
@@ -647,6 +730,8 @@ export function DealForm({
|
|
|
647
730
|
showAssociationsGroup = true,
|
|
648
731
|
showVersionHistory = true,
|
|
649
732
|
showCancelAction = true,
|
|
733
|
+
initialPipelineOptions,
|
|
734
|
+
initialPipelineStageOptions,
|
|
650
735
|
}: DealFormProps) {
|
|
651
736
|
const t = useT()
|
|
652
737
|
const [pending, setPending] = React.useState(false)
|
|
@@ -733,25 +818,36 @@ export function DealForm({
|
|
|
733
818
|
const disabled = pending || isSubmitting
|
|
734
819
|
const canDelete = mode === 'edit' && typeof onDelete === 'function'
|
|
735
820
|
|
|
736
|
-
|
|
737
|
-
|
|
821
|
+
const mountedRef = React.useRef(false)
|
|
822
|
+
const seedPipelineOptions = React.useMemo(
|
|
823
|
+
() => normalizePipelineOptions(initialPipelineOptions),
|
|
824
|
+
[initialPipelineOptions],
|
|
825
|
+
)
|
|
826
|
+
const seedPipelineStageOptions = React.useMemo(
|
|
827
|
+
() => Array.isArray(initialPipelineStageOptions) ? normalizePipelineStageOptions(initialPipelineStageOptions) : null,
|
|
828
|
+
[initialPipelineStageOptions],
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
const [pipelines, setPipelines] = React.useState<PipelineOption[]>(() => seedPipelineOptions)
|
|
832
|
+
const [pipelineStages, setPipelineStages] = React.useState<PipelineStageOption[]>(() => seedPipelineStageOptions ?? [])
|
|
738
833
|
|
|
739
|
-
|
|
740
|
-
|
|
834
|
+
React.useEffect(() => {
|
|
835
|
+
mountedRef.current = true
|
|
836
|
+
return () => {
|
|
837
|
+
mountedRef.current = false
|
|
838
|
+
}
|
|
839
|
+
}, [])
|
|
741
840
|
|
|
742
841
|
const loadStagesForPipeline = React.useCallback(async (pipelineId: string) => {
|
|
743
842
|
if (!pipelineId) {
|
|
744
|
-
setPipelineStages([])
|
|
843
|
+
if (mountedRef.current) setPipelineStages([])
|
|
745
844
|
return
|
|
746
845
|
}
|
|
747
846
|
try {
|
|
748
|
-
const
|
|
749
|
-
if (
|
|
750
|
-
const sorted = [...call.result.items].sort((a, b) => a.order - b.order)
|
|
751
|
-
setPipelineStages(sorted)
|
|
752
|
-
}
|
|
847
|
+
const stages = await fetchPipelineStageOptions(pipelineId)
|
|
848
|
+
if (mountedRef.current) setPipelineStages(stages)
|
|
753
849
|
} catch {
|
|
754
|
-
setPipelineStages([])
|
|
850
|
+
if (mountedRef.current) setPipelineStages([])
|
|
755
851
|
}
|
|
756
852
|
}, [])
|
|
757
853
|
|
|
@@ -759,24 +855,30 @@ export function DealForm({
|
|
|
759
855
|
let cancelled = false
|
|
760
856
|
;(async () => {
|
|
761
857
|
try {
|
|
762
|
-
const
|
|
763
|
-
if (cancelled) return
|
|
764
|
-
|
|
765
|
-
setPipelines(call.result.items)
|
|
766
|
-
}
|
|
858
|
+
const loaded = await fetchPipelineOptions()
|
|
859
|
+
if (cancelled || !mountedRef.current) return
|
|
860
|
+
setPipelines(mergePipelineOptions(seedPipelineOptions, loaded))
|
|
767
861
|
} catch {
|
|
768
|
-
|
|
862
|
+
if (!cancelled && mountedRef.current && seedPipelineOptions.length > 0) {
|
|
863
|
+
setPipelines(seedPipelineOptions)
|
|
864
|
+
}
|
|
769
865
|
}
|
|
770
866
|
})().catch(() => {})
|
|
771
867
|
return () => { cancelled = true }
|
|
772
|
-
}, [])
|
|
868
|
+
}, [seedPipelineOptions])
|
|
773
869
|
|
|
774
870
|
React.useEffect(() => {
|
|
775
871
|
const pid = initialValues?.pipelineId
|
|
776
872
|
if (typeof pid === 'string' && pid.length) {
|
|
873
|
+
if (seedPipelineStageOptions) {
|
|
874
|
+
setPipelineStages(seedPipelineStageOptions)
|
|
875
|
+
return
|
|
876
|
+
}
|
|
777
877
|
loadStagesForPipeline(pid).catch(() => {})
|
|
878
|
+
} else {
|
|
879
|
+
setPipelineStages([])
|
|
778
880
|
}
|
|
779
|
-
}, [initialValues?.pipelineId, loadStagesForPipeline])
|
|
881
|
+
}, [initialValues?.pipelineId, loadStagesForPipeline, seedPipelineStageOptions])
|
|
780
882
|
|
|
781
883
|
const baseFields = React.useMemo<CrudField[]>(() => [
|
|
782
884
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
-
import { Users, Phone, Check, Mail, Calendar,
|
|
4
|
+
import { Users, Phone, Check, Mail, Calendar, X, StickyNote } from 'lucide-react'
|
|
5
5
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
6
6
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
7
7
|
import { validatePhoneNumber } from '@open-mercato/shared/lib/phone'
|
|
@@ -498,7 +498,6 @@ export function ScheduleActivityDialog({
|
|
|
498
498
|
{/* Conflict warning */}
|
|
499
499
|
{state.conflict && (
|
|
500
500
|
<Alert variant="warning" className="rounded-lg">
|
|
501
|
-
<AlertTriangle className="size-5" />
|
|
502
501
|
<AlertTitle>
|
|
503
502
|
{t('customers.schedule.conflict.title', 'Calendar conflict')}
|
|
504
503
|
</AlertTitle>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { cn } from '@open-mercato/shared/lib/utils'
|
|
5
|
+
import type { DictionaryMap } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'
|
|
6
|
+
|
|
7
|
+
export type PipelineStageDatum = {
|
|
8
|
+
stage: string | null
|
|
9
|
+
count: number
|
|
10
|
+
value: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type PipelineStageBarProps = {
|
|
14
|
+
stages: PipelineStageDatum[]
|
|
15
|
+
stageDictionary: DictionaryMap
|
|
16
|
+
unassignedLabel: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveStageColor(stage: string | null, stageDictionary: DictionaryMap): string | null {
|
|
20
|
+
if (!stage) return null
|
|
21
|
+
const entry = stageDictionary[stage]
|
|
22
|
+
return entry?.color ?? null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolveStageLabel(stage: string | null, stageDictionary: DictionaryMap, unassignedLabel: string): string {
|
|
26
|
+
if (!stage) return unassignedLabel
|
|
27
|
+
return stageDictionary[stage]?.label ?? unassignedLabel
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function PipelineStageBar({ stages, stageDictionary, unassignedLabel }: PipelineStageBarProps) {
|
|
31
|
+
const segments = React.useMemo(
|
|
32
|
+
() => stages.filter((entry) => entry.count > 0),
|
|
33
|
+
[stages],
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if (segments.length === 0) return null
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="space-y-2">
|
|
40
|
+
<div className="flex h-2 w-full gap-1 overflow-hidden rounded-full">
|
|
41
|
+
{segments.map((entry, index) => {
|
|
42
|
+
const color = resolveStageColor(entry.stage, stageDictionary)
|
|
43
|
+
return (
|
|
44
|
+
<span
|
|
45
|
+
key={entry.stage ?? `unassigned-${index}`}
|
|
46
|
+
className={cn('h-full rounded-full', color ? null : 'bg-muted')}
|
|
47
|
+
style={{ flexGrow: entry.count, ...(color ? { backgroundColor: color } : {}) }}
|
|
48
|
+
aria-hidden
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
})}
|
|
52
|
+
</div>
|
|
53
|
+
<ul className="flex flex-wrap gap-x-3 gap-y-1">
|
|
54
|
+
{segments.map((entry, index) => {
|
|
55
|
+
const color = resolveStageColor(entry.stage, stageDictionary)
|
|
56
|
+
const label = resolveStageLabel(entry.stage, stageDictionary, unassignedLabel)
|
|
57
|
+
return (
|
|
58
|
+
<li
|
|
59
|
+
key={entry.stage ?? `unassigned-legend-${index}`}
|
|
60
|
+
className="inline-flex items-center gap-1.5 text-xs text-muted-foreground"
|
|
61
|
+
>
|
|
62
|
+
<span
|
|
63
|
+
className={cn('inline-block h-2 w-2 rounded-full border border-border', color ? null : 'bg-muted')}
|
|
64
|
+
style={color ? { backgroundColor: color } : undefined}
|
|
65
|
+
aria-hidden
|
|
66
|
+
/>
|
|
67
|
+
<span>{label}</span>
|
|
68
|
+
<span className="font-medium text-foreground">{entry.count}</span>
|
|
69
|
+
</li>
|
|
70
|
+
)
|
|
71
|
+
})}
|
|
72
|
+
</ul>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default PipelineStageBar
|
|
@@ -1154,6 +1154,9 @@
|
|
|
1154
1154
|
"customers.deals.list.bulkDelete.progressName": "Ausgewählte Deals löschen",
|
|
1155
1155
|
"customers.deals.list.bulkDelete.success": "{count} deals deleted",
|
|
1156
1156
|
"customers.deals.list.bulkDelete.title": "Delete {count} deals?",
|
|
1157
|
+
"customers.deals.list.close.lost": "Verloren",
|
|
1158
|
+
"customers.deals.list.close.overdue": "Überfällig",
|
|
1159
|
+
"customers.deals.list.close.won": "Gewonnen",
|
|
1157
1160
|
"customers.deals.list.columns.companies": "Unternehmen",
|
|
1158
1161
|
"customers.deals.list.columns.expectedClose": "Voraussichtlicher Abschluss",
|
|
1159
1162
|
"customers.deals.list.columns.owner": "Verantwortlich",
|
|
@@ -1171,12 +1174,52 @@
|
|
|
1171
1174
|
"customers.deals.list.error.load": "Geschäftsliste konnte nicht geladen werden",
|
|
1172
1175
|
"customers.deals.list.filters.companies": "Unternehmen",
|
|
1173
1176
|
"customers.deals.list.filters.companiesPlaceholder": "Nach Unternehmen filtern",
|
|
1177
|
+
"customers.deals.list.filters.needsAttention": "Benötigt Aufmerksamkeit",
|
|
1178
|
+
"customers.deals.list.filters.needsAttentionRemove": "Filter \"Benötigt Aufmerksamkeit\" entfernen",
|
|
1174
1179
|
"customers.deals.list.filters.people": "Personen",
|
|
1175
1180
|
"customers.deals.list.filters.peoplePlaceholder": "Nach Personen filtern",
|
|
1181
|
+
"customers.deals.list.kpi.activeAcrossPipelines": "{deals} in {pipelines}",
|
|
1182
|
+
"customers.deals.list.kpi.activeDeals": "Aktive Deals",
|
|
1183
|
+
"customers.deals.list.kpi.avgDeal": "Ø Deal {value}",
|
|
1184
|
+
"customers.deals.list.kpi.currencyApproxMissing": "Näherungswert; Deals in {currencies} ohne Wechselkurs sind ausgeschlossen.",
|
|
1185
|
+
"customers.deals.list.kpi.currencyApproxNoBase": "Näherungswert; keine Basiswährung ist konfiguriert.",
|
|
1186
|
+
"customers.deals.list.kpi.deltaTooltip": "Im Vergleich zum Vorzeitraum",
|
|
1187
|
+
"customers.deals.list.kpi.deltaUnavailable": "Keine vergleichbare Änderung",
|
|
1188
|
+
"customers.deals.list.kpi.error": "Metriken konnten nicht geladen werden",
|
|
1189
|
+
"customers.deals.list.kpi.frag.activeDeals.few": "{count} aktive Deals",
|
|
1190
|
+
"customers.deals.list.kpi.frag.activeDeals.many": "{count} aktive Deals",
|
|
1191
|
+
"customers.deals.list.kpi.frag.activeDeals.one": "{count} aktiver Deal",
|
|
1192
|
+
"customers.deals.list.kpi.frag.activeDeals.other": "{count} aktive Deals",
|
|
1193
|
+
"customers.deals.list.kpi.frag.dealsClosed.few": "{count} Deals in diesem Quartal abgeschlossen",
|
|
1194
|
+
"customers.deals.list.kpi.frag.dealsClosed.many": "{count} Deals in diesem Quartal abgeschlossen",
|
|
1195
|
+
"customers.deals.list.kpi.frag.dealsClosed.one": "{count} Deal in diesem Quartal abgeschlossen",
|
|
1196
|
+
"customers.deals.list.kpi.frag.dealsClosed.other": "{count} Deals in diesem Quartal abgeschlossen",
|
|
1197
|
+
"customers.deals.list.kpi.frag.needAttention.few": "{count} benötigen Aufmerksamkeit",
|
|
1198
|
+
"customers.deals.list.kpi.frag.needAttention.many": "{count} benötigen Aufmerksamkeit",
|
|
1199
|
+
"customers.deals.list.kpi.frag.needAttention.one": "{count} benötigt Aufmerksamkeit",
|
|
1200
|
+
"customers.deals.list.kpi.frag.needAttention.other": "{count} benötigen Aufmerksamkeit",
|
|
1201
|
+
"customers.deals.list.kpi.frag.owners.few": "{count} Inhaber",
|
|
1202
|
+
"customers.deals.list.kpi.frag.owners.many": "{count} Inhaber",
|
|
1203
|
+
"customers.deals.list.kpi.frag.owners.one": "{count} Inhaber",
|
|
1204
|
+
"customers.deals.list.kpi.frag.owners.other": "{count} Inhaber",
|
|
1205
|
+
"customers.deals.list.kpi.frag.pipelines.few": "{count} Pipelines",
|
|
1206
|
+
"customers.deals.list.kpi.frag.pipelines.many": "{count} Pipelines",
|
|
1207
|
+
"customers.deals.list.kpi.frag.pipelines.one": "{count} Pipeline",
|
|
1208
|
+
"customers.deals.list.kpi.frag.pipelines.other": "{count} Pipelines",
|
|
1209
|
+
"customers.deals.list.kpi.fromLastQuarter": "von {value}% im letzten Quartal",
|
|
1210
|
+
"customers.deals.list.kpi.ownersNeedAttention": "{owners} · {attention}",
|
|
1211
|
+
"customers.deals.list.kpi.pipelineValue": "Pipeline-Wert",
|
|
1212
|
+
"customers.deals.list.kpi.retry": "Erneut versuchen",
|
|
1213
|
+
"customers.deals.list.kpi.scopeAllPipelinesThisQuarter": "Alle Pipelines · dieses Quartal",
|
|
1214
|
+
"customers.deals.list.kpi.unassignedStage": "Nicht zugewiesen",
|
|
1215
|
+
"customers.deals.list.kpi.updating": "Metriken werden aktualisiert",
|
|
1216
|
+
"customers.deals.list.kpi.winRate": "Gewinnrate",
|
|
1217
|
+
"customers.deals.list.kpi.wonThisQuarter": "Gewonnen dieses Quartal",
|
|
1176
1218
|
"customers.deals.list.noValue": "Nicht gesetzt",
|
|
1177
1219
|
"customers.deals.list.refresh": "Aktualisieren",
|
|
1178
1220
|
"customers.deals.list.searchPlaceholder": "Geschäfte suchen…",
|
|
1179
1221
|
"customers.deals.list.title": "Geschäfte",
|
|
1222
|
+
"customers.deals.list.unknownOwner": "Unbekannter Inhaber",
|
|
1180
1223
|
"customers.deals.list.unnamedCompany": "Unbenanntes Unternehmen",
|
|
1181
1224
|
"customers.deals.list.unnamedPerson": "Unbenannte Person",
|
|
1182
1225
|
"customers.deals.pipeline.actions.openDeal": "Deal öffnen",
|
|
@@ -1154,6 +1154,9 @@
|
|
|
1154
1154
|
"customers.deals.list.bulkDelete.progressName": "Delete selected deals",
|
|
1155
1155
|
"customers.deals.list.bulkDelete.success": "{count} deals deleted",
|
|
1156
1156
|
"customers.deals.list.bulkDelete.title": "Delete {count} deals?",
|
|
1157
|
+
"customers.deals.list.close.lost": "Lost",
|
|
1158
|
+
"customers.deals.list.close.overdue": "Overdue",
|
|
1159
|
+
"customers.deals.list.close.won": "Won",
|
|
1157
1160
|
"customers.deals.list.columns.companies": "Companies",
|
|
1158
1161
|
"customers.deals.list.columns.expectedClose": "Expected close",
|
|
1159
1162
|
"customers.deals.list.columns.owner": "Owner",
|
|
@@ -1171,12 +1174,52 @@
|
|
|
1171
1174
|
"customers.deals.list.error.load": "Unable to load deals list",
|
|
1172
1175
|
"customers.deals.list.filters.companies": "Companies",
|
|
1173
1176
|
"customers.deals.list.filters.companiesPlaceholder": "Filter by companies",
|
|
1177
|
+
"customers.deals.list.filters.needsAttention": "Needs attention",
|
|
1178
|
+
"customers.deals.list.filters.needsAttentionRemove": "Remove needs attention filter",
|
|
1174
1179
|
"customers.deals.list.filters.people": "People",
|
|
1175
1180
|
"customers.deals.list.filters.peoplePlaceholder": "Filter by people",
|
|
1181
|
+
"customers.deals.list.kpi.activeAcrossPipelines": "{deals} across {pipelines}",
|
|
1182
|
+
"customers.deals.list.kpi.activeDeals": "Active deals",
|
|
1183
|
+
"customers.deals.list.kpi.avgDeal": "avg deal {value}",
|
|
1184
|
+
"customers.deals.list.kpi.currencyApproxMissing": "Approximate; deals in {currencies} without a rate are excluded.",
|
|
1185
|
+
"customers.deals.list.kpi.currencyApproxNoBase": "Approximate; no base currency is configured.",
|
|
1186
|
+
"customers.deals.list.kpi.deltaTooltip": "Compared to previous period",
|
|
1187
|
+
"customers.deals.list.kpi.deltaUnavailable": "No comparable change",
|
|
1188
|
+
"customers.deals.list.kpi.error": "Couldn't load deal metrics",
|
|
1189
|
+
"customers.deals.list.kpi.frag.activeDeals.few": "{count} active deals",
|
|
1190
|
+
"customers.deals.list.kpi.frag.activeDeals.many": "{count} active deals",
|
|
1191
|
+
"customers.deals.list.kpi.frag.activeDeals.one": "{count} active deal",
|
|
1192
|
+
"customers.deals.list.kpi.frag.activeDeals.other": "{count} active deals",
|
|
1193
|
+
"customers.deals.list.kpi.frag.dealsClosed.few": "{count} deals closed this quarter",
|
|
1194
|
+
"customers.deals.list.kpi.frag.dealsClosed.many": "{count} deals closed this quarter",
|
|
1195
|
+
"customers.deals.list.kpi.frag.dealsClosed.one": "{count} deal closed this quarter",
|
|
1196
|
+
"customers.deals.list.kpi.frag.dealsClosed.other": "{count} deals closed this quarter",
|
|
1197
|
+
"customers.deals.list.kpi.frag.needAttention.few": "{count} need attention",
|
|
1198
|
+
"customers.deals.list.kpi.frag.needAttention.many": "{count} need attention",
|
|
1199
|
+
"customers.deals.list.kpi.frag.needAttention.one": "{count} needs attention",
|
|
1200
|
+
"customers.deals.list.kpi.frag.needAttention.other": "{count} need attention",
|
|
1201
|
+
"customers.deals.list.kpi.frag.owners.few": "{count} owners",
|
|
1202
|
+
"customers.deals.list.kpi.frag.owners.many": "{count} owners",
|
|
1203
|
+
"customers.deals.list.kpi.frag.owners.one": "{count} owner",
|
|
1204
|
+
"customers.deals.list.kpi.frag.owners.other": "{count} owners",
|
|
1205
|
+
"customers.deals.list.kpi.frag.pipelines.few": "{count} pipelines",
|
|
1206
|
+
"customers.deals.list.kpi.frag.pipelines.many": "{count} pipelines",
|
|
1207
|
+
"customers.deals.list.kpi.frag.pipelines.one": "{count} pipeline",
|
|
1208
|
+
"customers.deals.list.kpi.frag.pipelines.other": "{count} pipelines",
|
|
1209
|
+
"customers.deals.list.kpi.fromLastQuarter": "from {value}% last quarter",
|
|
1210
|
+
"customers.deals.list.kpi.ownersNeedAttention": "{owners} · {attention}",
|
|
1211
|
+
"customers.deals.list.kpi.pipelineValue": "Pipeline value",
|
|
1212
|
+
"customers.deals.list.kpi.retry": "Retry",
|
|
1213
|
+
"customers.deals.list.kpi.scopeAllPipelinesThisQuarter": "All pipelines · this quarter",
|
|
1214
|
+
"customers.deals.list.kpi.unassignedStage": "Unassigned",
|
|
1215
|
+
"customers.deals.list.kpi.updating": "Updating metrics",
|
|
1216
|
+
"customers.deals.list.kpi.winRate": "Win rate",
|
|
1217
|
+
"customers.deals.list.kpi.wonThisQuarter": "Won this quarter",
|
|
1176
1218
|
"customers.deals.list.noValue": "Not set",
|
|
1177
1219
|
"customers.deals.list.refresh": "Refresh",
|
|
1178
1220
|
"customers.deals.list.searchPlaceholder": "Search by title, description…",
|
|
1179
1221
|
"customers.deals.list.title": "Deals",
|
|
1222
|
+
"customers.deals.list.unknownOwner": "Unknown owner",
|
|
1180
1223
|
"customers.deals.list.unnamedCompany": "Unnamed company",
|
|
1181
1224
|
"customers.deals.list.unnamedPerson": "Unnamed person",
|
|
1182
1225
|
"customers.deals.pipeline.actions.openDeal": "Open deal",
|
|
@@ -1154,6 +1154,9 @@
|
|
|
1154
1154
|
"customers.deals.list.bulkDelete.progressName": "Eliminar negocios seleccionados",
|
|
1155
1155
|
"customers.deals.list.bulkDelete.success": "{count} deals deleted",
|
|
1156
1156
|
"customers.deals.list.bulkDelete.title": "Delete {count} deals?",
|
|
1157
|
+
"customers.deals.list.close.lost": "Perdido",
|
|
1158
|
+
"customers.deals.list.close.overdue": "Vencido",
|
|
1159
|
+
"customers.deals.list.close.won": "Ganado",
|
|
1157
1160
|
"customers.deals.list.columns.companies": "Empresas",
|
|
1158
1161
|
"customers.deals.list.columns.expectedClose": "Cierre esperado",
|
|
1159
1162
|
"customers.deals.list.columns.owner": "Responsable",
|
|
@@ -1171,12 +1174,52 @@
|
|
|
1171
1174
|
"customers.deals.list.error.load": "No se pudo cargar la lista de negocios",
|
|
1172
1175
|
"customers.deals.list.filters.companies": "Empresas",
|
|
1173
1176
|
"customers.deals.list.filters.companiesPlaceholder": "Filtrar por empresas",
|
|
1177
|
+
"customers.deals.list.filters.needsAttention": "Requiere atención",
|
|
1178
|
+
"customers.deals.list.filters.needsAttentionRemove": "Quitar filtro de requiere atención",
|
|
1174
1179
|
"customers.deals.list.filters.people": "Personas",
|
|
1175
1180
|
"customers.deals.list.filters.peoplePlaceholder": "Filtrar por personas",
|
|
1181
|
+
"customers.deals.list.kpi.activeAcrossPipelines": "{deals} en {pipelines}",
|
|
1182
|
+
"customers.deals.list.kpi.activeDeals": "Negocios activos",
|
|
1183
|
+
"customers.deals.list.kpi.avgDeal": "negocio medio {value}",
|
|
1184
|
+
"customers.deals.list.kpi.currencyApproxMissing": "Aproximado; se excluyen los negocios en {currencies} sin tipo de cambio.",
|
|
1185
|
+
"customers.deals.list.kpi.currencyApproxNoBase": "Aproximado; no hay moneda base configurada.",
|
|
1186
|
+
"customers.deals.list.kpi.deltaTooltip": "Comparado con el período anterior",
|
|
1187
|
+
"customers.deals.list.kpi.deltaUnavailable": "Sin cambio comparable",
|
|
1188
|
+
"customers.deals.list.kpi.error": "No se pudieron cargar las métricas",
|
|
1189
|
+
"customers.deals.list.kpi.frag.activeDeals.few": "{count} negocios activos",
|
|
1190
|
+
"customers.deals.list.kpi.frag.activeDeals.many": "{count} negocios activos",
|
|
1191
|
+
"customers.deals.list.kpi.frag.activeDeals.one": "{count} negocio activo",
|
|
1192
|
+
"customers.deals.list.kpi.frag.activeDeals.other": "{count} negocios activos",
|
|
1193
|
+
"customers.deals.list.kpi.frag.dealsClosed.few": "{count} negocios cerrados este trimestre",
|
|
1194
|
+
"customers.deals.list.kpi.frag.dealsClosed.many": "{count} negocios cerrados este trimestre",
|
|
1195
|
+
"customers.deals.list.kpi.frag.dealsClosed.one": "{count} negocio cerrado este trimestre",
|
|
1196
|
+
"customers.deals.list.kpi.frag.dealsClosed.other": "{count} negocios cerrados este trimestre",
|
|
1197
|
+
"customers.deals.list.kpi.frag.needAttention.few": "{count} requieren atención",
|
|
1198
|
+
"customers.deals.list.kpi.frag.needAttention.many": "{count} requieren atención",
|
|
1199
|
+
"customers.deals.list.kpi.frag.needAttention.one": "{count} requiere atención",
|
|
1200
|
+
"customers.deals.list.kpi.frag.needAttention.other": "{count} requieren atención",
|
|
1201
|
+
"customers.deals.list.kpi.frag.owners.few": "{count} responsables",
|
|
1202
|
+
"customers.deals.list.kpi.frag.owners.many": "{count} responsables",
|
|
1203
|
+
"customers.deals.list.kpi.frag.owners.one": "{count} responsable",
|
|
1204
|
+
"customers.deals.list.kpi.frag.owners.other": "{count} responsables",
|
|
1205
|
+
"customers.deals.list.kpi.frag.pipelines.few": "{count} pipelines",
|
|
1206
|
+
"customers.deals.list.kpi.frag.pipelines.many": "{count} pipelines",
|
|
1207
|
+
"customers.deals.list.kpi.frag.pipelines.one": "{count} pipeline",
|
|
1208
|
+
"customers.deals.list.kpi.frag.pipelines.other": "{count} pipelines",
|
|
1209
|
+
"customers.deals.list.kpi.fromLastQuarter": "desde {value}% el trimestre pasado",
|
|
1210
|
+
"customers.deals.list.kpi.ownersNeedAttention": "{owners} · {attention}",
|
|
1211
|
+
"customers.deals.list.kpi.pipelineValue": "Valor del pipeline",
|
|
1212
|
+
"customers.deals.list.kpi.retry": "Reintentar",
|
|
1213
|
+
"customers.deals.list.kpi.scopeAllPipelinesThisQuarter": "Todos los pipelines · este trimestre",
|
|
1214
|
+
"customers.deals.list.kpi.unassignedStage": "Sin asignar",
|
|
1215
|
+
"customers.deals.list.kpi.updating": "Actualizando métricas",
|
|
1216
|
+
"customers.deals.list.kpi.winRate": "Tasa de éxito",
|
|
1217
|
+
"customers.deals.list.kpi.wonThisQuarter": "Ganados este trimestre",
|
|
1176
1218
|
"customers.deals.list.noValue": "Sin definir",
|
|
1177
1219
|
"customers.deals.list.refresh": "Actualizar",
|
|
1178
1220
|
"customers.deals.list.searchPlaceholder": "Buscar negocios…",
|
|
1179
1221
|
"customers.deals.list.title": "Negocios",
|
|
1222
|
+
"customers.deals.list.unknownOwner": "Responsable desconocido",
|
|
1180
1223
|
"customers.deals.list.unnamedCompany": "Empresa sin nombre",
|
|
1181
1224
|
"customers.deals.list.unnamedPerson": "Persona sin nombre",
|
|
1182
1225
|
"customers.deals.pipeline.actions.openDeal": "Abrir oportunidad",
|
|
@@ -1154,6 +1154,9 @@
|
|
|
1154
1154
|
"customers.deals.list.bulkDelete.progressName": "Usuń zaznaczone transakcje",
|
|
1155
1155
|
"customers.deals.list.bulkDelete.success": "{count} deals deleted",
|
|
1156
1156
|
"customers.deals.list.bulkDelete.title": "Delete {count} deals?",
|
|
1157
|
+
"customers.deals.list.close.lost": "Przegrana",
|
|
1158
|
+
"customers.deals.list.close.overdue": "Po terminie",
|
|
1159
|
+
"customers.deals.list.close.won": "Wygrana",
|
|
1157
1160
|
"customers.deals.list.columns.companies": "Firmy",
|
|
1158
1161
|
"customers.deals.list.columns.expectedClose": "Planowana finalizacja",
|
|
1159
1162
|
"customers.deals.list.columns.owner": "Właściciel",
|
|
@@ -1171,12 +1174,52 @@
|
|
|
1171
1174
|
"customers.deals.list.error.load": "Nie udało się wczytać listy szans",
|
|
1172
1175
|
"customers.deals.list.filters.companies": "Firmy",
|
|
1173
1176
|
"customers.deals.list.filters.companiesPlaceholder": "Filtruj po firmach",
|
|
1177
|
+
"customers.deals.list.filters.needsAttention": "Wymaga uwagi",
|
|
1178
|
+
"customers.deals.list.filters.needsAttentionRemove": "Usuń filtr wymaga uwagi",
|
|
1174
1179
|
"customers.deals.list.filters.people": "Osoby",
|
|
1175
1180
|
"customers.deals.list.filters.peoplePlaceholder": "Filtruj po osobach",
|
|
1181
|
+
"customers.deals.list.kpi.activeAcrossPipelines": "{deals} w {pipelines}",
|
|
1182
|
+
"customers.deals.list.kpi.activeDeals": "Aktywne szanse",
|
|
1183
|
+
"customers.deals.list.kpi.avgDeal": "śr. szansa {value}",
|
|
1184
|
+
"customers.deals.list.kpi.currencyApproxMissing": "Wartość przybliżona; szanse w {currencies} bez kursu są pomijane.",
|
|
1185
|
+
"customers.deals.list.kpi.currencyApproxNoBase": "Wartość przybliżona; nie skonfigurowano waluty bazowej.",
|
|
1186
|
+
"customers.deals.list.kpi.deltaTooltip": "W porównaniu z poprzednim okresem",
|
|
1187
|
+
"customers.deals.list.kpi.deltaUnavailable": "Brak porównywalnej zmiany",
|
|
1188
|
+
"customers.deals.list.kpi.error": "Nie udało się załadować metryk",
|
|
1189
|
+
"customers.deals.list.kpi.frag.activeDeals.few": "{count} aktywne szanse",
|
|
1190
|
+
"customers.deals.list.kpi.frag.activeDeals.many": "{count} aktywnych szans",
|
|
1191
|
+
"customers.deals.list.kpi.frag.activeDeals.one": "{count} aktywna szansa",
|
|
1192
|
+
"customers.deals.list.kpi.frag.activeDeals.other": "{count} aktywnych szans",
|
|
1193
|
+
"customers.deals.list.kpi.frag.dealsClosed.few": "{count} szanse zamknięte w tym kwartale",
|
|
1194
|
+
"customers.deals.list.kpi.frag.dealsClosed.many": "{count} szans zamkniętych w tym kwartale",
|
|
1195
|
+
"customers.deals.list.kpi.frag.dealsClosed.one": "{count} szansa zamknięta w tym kwartale",
|
|
1196
|
+
"customers.deals.list.kpi.frag.dealsClosed.other": "{count} szans zamkniętych w tym kwartale",
|
|
1197
|
+
"customers.deals.list.kpi.frag.needAttention.few": "{count} wymagają uwagi",
|
|
1198
|
+
"customers.deals.list.kpi.frag.needAttention.many": "{count} wymaga uwagi",
|
|
1199
|
+
"customers.deals.list.kpi.frag.needAttention.one": "{count} wymaga uwagi",
|
|
1200
|
+
"customers.deals.list.kpi.frag.needAttention.other": "{count} wymaga uwagi",
|
|
1201
|
+
"customers.deals.list.kpi.frag.owners.few": "{count} właścicieli",
|
|
1202
|
+
"customers.deals.list.kpi.frag.owners.many": "{count} właścicieli",
|
|
1203
|
+
"customers.deals.list.kpi.frag.owners.one": "{count} właściciel",
|
|
1204
|
+
"customers.deals.list.kpi.frag.owners.other": "{count} właścicieli",
|
|
1205
|
+
"customers.deals.list.kpi.frag.pipelines.few": "{count} pipeline'y",
|
|
1206
|
+
"customers.deals.list.kpi.frag.pipelines.many": "{count} pipeline'ów",
|
|
1207
|
+
"customers.deals.list.kpi.frag.pipelines.one": "{count} pipeline",
|
|
1208
|
+
"customers.deals.list.kpi.frag.pipelines.other": "{count} pipeline'ów",
|
|
1209
|
+
"customers.deals.list.kpi.fromLastQuarter": "z {value}% w zeszłym kwartale",
|
|
1210
|
+
"customers.deals.list.kpi.ownersNeedAttention": "{owners} · {attention}",
|
|
1211
|
+
"customers.deals.list.kpi.pipelineValue": "Wartość pipeline'u",
|
|
1212
|
+
"customers.deals.list.kpi.retry": "Ponów",
|
|
1213
|
+
"customers.deals.list.kpi.scopeAllPipelinesThisQuarter": "Wszystkie pipeline'y · ten kwartał",
|
|
1214
|
+
"customers.deals.list.kpi.unassignedStage": "Nieprzypisany",
|
|
1215
|
+
"customers.deals.list.kpi.updating": "Aktualizowanie metryk",
|
|
1216
|
+
"customers.deals.list.kpi.winRate": "Skuteczność",
|
|
1217
|
+
"customers.deals.list.kpi.wonThisQuarter": "Wygrane w tym kwartale",
|
|
1176
1218
|
"customers.deals.list.noValue": "Brak danych",
|
|
1177
1219
|
"customers.deals.list.refresh": "Odśwież",
|
|
1178
1220
|
"customers.deals.list.searchPlaceholder": "Szukaj szans…",
|
|
1179
1221
|
"customers.deals.list.title": "Szanse",
|
|
1222
|
+
"customers.deals.list.unknownOwner": "Nieznany właściciel",
|
|
1180
1223
|
"customers.deals.list.unnamedCompany": "Firma bez nazwy",
|
|
1181
1224
|
"customers.deals.list.unnamedPerson": "Osoba bez nazwy",
|
|
1182
1225
|
"customers.deals.pipeline.actions.openDeal": "Otwórz szansę",
|