@budibase/frontend-core 3.39.22 → 3.39.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@budibase/frontend-core",
3
- "version": "3.39.22",
3
+ "version": "3.39.24",
4
4
  "description": "Budibase frontend core libraries used in builder and client",
5
5
  "author": "Budibase",
6
6
  "license": "MPL-2.0",
@@ -23,5 +23,5 @@
23
23
  "devDependencies": {
24
24
  "vitest": "^4.1.0"
25
25
  },
26
- "gitHead": "5e71f6c90d0cb25ca4b7841065824284f1e59554"
26
+ "gitHead": "2427bcf96d0624cdbd4aaf028177ba7b6f6519c2"
27
27
  }
@@ -0,0 +1,28 @@
1
+ import type { FetchAgentRequestsResponse } from "@budibase/types"
2
+ import type { BaseAPIClient } from "./types"
3
+
4
+ export interface AgentRequestEndpoints {
5
+ fetchAgentRequests: (opts?: {
6
+ limit?: number
7
+ page?: number
8
+ }) => Promise<FetchAgentRequestsResponse>
9
+ }
10
+
11
+ export const buildAgentRequestEndpoints = (
12
+ API: BaseAPIClient
13
+ ): AgentRequestEndpoints => ({
14
+ fetchAgentRequests: async (opts = {}) => {
15
+ const params = new URLSearchParams()
16
+ if (opts.limit !== undefined) {
17
+ params.set("limit", String(opts.limit))
18
+ }
19
+ if (opts.page !== undefined) {
20
+ params.set("page", String(opts.page))
21
+ }
22
+
23
+ const query = params.toString()
24
+ return await API.get({
25
+ url: `/api/agent/requests${query ? `?${query}` : ""}`,
26
+ })
27
+ },
28
+ })
@@ -0,0 +1,44 @@
1
+ import {
2
+ type ChatIdentityLink,
3
+ type ChatIdentityLinkProvider,
4
+ } from "@budibase/types"
5
+ import { BaseAPIClient } from "./types"
6
+
7
+ export interface SlackChannel {
8
+ id: string
9
+ name: string
10
+ }
11
+
12
+ export interface MSTeamsChannel {
13
+ id: string
14
+ name: string
15
+ teamId: string
16
+ teamName: string
17
+ }
18
+
19
+ export interface ChatLinksEndpoints {
20
+ fetchChatIdentityLinks: (
21
+ provider?: ChatIdentityLinkProvider
22
+ ) => Promise<ChatIdentityLink[]>
23
+ fetchSlackChannels: (agentId: string) => Promise<SlackChannel[]>
24
+ fetchMSTeamsChannels: (agentId: string) => Promise<MSTeamsChannel[]>
25
+ }
26
+
27
+ export const buildChatLinksEndpoints = (
28
+ API: BaseAPIClient
29
+ ): ChatLinksEndpoints => ({
30
+ fetchChatIdentityLinks: async provider => {
31
+ const query = provider
32
+ ? `?${new URLSearchParams({ provider }).toString()}`
33
+ : ""
34
+ return await API.get({ url: `/api/chat-links${query}` })
35
+ },
36
+ fetchSlackChannels: async agentId => {
37
+ const query = new URLSearchParams({ agentId }).toString()
38
+ return await API.get({ url: `/api/slack-channels?${query}` })
39
+ },
40
+ fetchMSTeamsChannels: async agentId => {
41
+ const query = new URLSearchParams({ agentId }).toString()
42
+ return await API.get({ url: `/api/teams-channels?${query}` })
43
+ },
44
+ })
@@ -0,0 +1,41 @@
1
+ import type {
2
+ EscalationContextDoc,
3
+ EscalationRespondResult,
4
+ EscalationResponse,
5
+ EscalationResult,
6
+ } from "@budibase/types"
7
+ import type { BaseAPIClient } from "./types"
8
+
9
+ export interface EscalationEndpoints {
10
+ fetchEscalationContext: (
11
+ escalationId: string
12
+ ) => Promise<EscalationContextDoc>
13
+ fetchEscalationResult: (escalationId: string) => Promise<EscalationResult>
14
+ resolveEscalation: (
15
+ escalationId: string,
16
+ response: EscalationResponse
17
+ ) => Promise<EscalationRespondResult>
18
+ }
19
+
20
+ export const buildEscalationEndpoints = (
21
+ API: BaseAPIClient
22
+ ): EscalationEndpoints => ({
23
+ fetchEscalationContext: async escalationId => {
24
+ return await API.get({
25
+ url: `/api/escalations/context/${escalationId}`,
26
+ })
27
+ },
28
+
29
+ fetchEscalationResult: async escalationId => {
30
+ return await API.get({
31
+ url: `/api/escalations/${escalationId}/result`,
32
+ })
33
+ },
34
+
35
+ resolveEscalation: async (escalationId, response) => {
36
+ return await API.post({
37
+ url: `/api/escalations/${escalationId}/resolve`,
38
+ body: { response },
39
+ })
40
+ },
41
+ })
package/src/api/index.ts CHANGED
@@ -23,6 +23,7 @@ import { buildFlagEndpoints } from "./flags"
23
23
  import { buildLayoutEndpoints } from "./layouts"
24
24
  import { buildOtherEndpoints } from "./other"
25
25
  import { buildPermissionsEndpoints } from "./permissions"
26
+ import { buildProjectEndpoints } from "./projects"
26
27
  import { buildQueryEndpoints } from "./queries"
27
28
  import { buildRelationshipEndpoints } from "./relationships"
28
29
  import { buildRoleEndpoints } from "./roles"
@@ -49,7 +50,10 @@ import { buildOAuth2Endpoints } from "./oauth2"
49
50
  import { buildAgentEndpoints } from "./agents"
50
51
  import { buildAgentTestEndpoints } from "./agentTests"
51
52
  import { buildAgentLogEndpoints } from "./agentLogs"
53
+ import { buildAgentRequestEndpoints } from "./agentRequests"
52
54
  import { buildChatAppEndpoints } from "./chatApps"
55
+ import { buildEscalationEndpoints } from "./escalations"
56
+ import { buildChatLinksEndpoints } from "./chatLinks"
53
57
  import { buildFeatureFlagEndpoints } from "./features"
54
58
  import { buildNavigationEndpoints } from "./navigation"
55
59
  import { buildWorkspaceAppEndpoints } from "./workspaceApps"
@@ -320,13 +324,17 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
320
324
  ...buildAgentEndpoints(API),
321
325
  ...buildAgentTestEndpoints(API),
322
326
  ...buildAgentLogEndpoints(API),
327
+ ...buildAgentRequestEndpoints(API),
323
328
  ...buildChatAppEndpoints(API),
329
+ ...buildEscalationEndpoints(API),
330
+ ...buildChatLinksEndpoints(API),
324
331
  ...buildFeatureFlagEndpoints(API),
325
332
  deployment: buildDeploymentEndpoints(API),
326
333
  viewV2: buildViewV2Endpoints(API),
327
334
  rowActions: buildRowActionEndpoints(API),
328
335
  oauth2: buildOAuth2Endpoints(API),
329
336
  navigation: buildNavigationEndpoints(API),
337
+ projects: buildProjectEndpoints(API),
330
338
  workspaceApp: buildWorkspaceAppEndpoints(API),
331
339
  workspace: buildWorkspaceFavouriteEndpoints(API),
332
340
  workspaceHome: buildWorkspaceHomeEndpoints(API),
@@ -0,0 +1,78 @@
1
+ import {
2
+ CreateProjectRequest,
3
+ CreateProjectResponse,
4
+ ExportProjectRequest,
5
+ FetchProjectsResponse,
6
+ ImportProjectRequest,
7
+ ImportProjectResponse,
8
+ UpdateProjectRequest,
9
+ UpdateProjectResponse,
10
+ } from "@budibase/types"
11
+ import { BaseAPIClient } from "./types"
12
+
13
+ export interface ProjectEndpoints {
14
+ fetch: () => Promise<FetchProjectsResponse>
15
+ create: (project: CreateProjectRequest) => Promise<CreateProjectResponse>
16
+ exportBundle: (id: string, body?: ExportProjectRequest) => Promise<Response>
17
+ importBundle: (
18
+ file: File,
19
+ body?: ImportProjectRequest
20
+ ) => Promise<ImportProjectResponse>
21
+ update: (project: UpdateProjectRequest) => Promise<UpdateProjectResponse>
22
+ delete: (id: string, rev: string) => Promise<void>
23
+ }
24
+
25
+ export const buildProjectEndpoints = (
26
+ API: BaseAPIClient
27
+ ): ProjectEndpoints => ({
28
+ fetch: async () => {
29
+ return await API.get({
30
+ url: "/api/projects",
31
+ })
32
+ },
33
+ create: async project => {
34
+ return await API.post({
35
+ url: "/api/projects",
36
+ body: project,
37
+ })
38
+ },
39
+ exportBundle: async (id, body) => {
40
+ return await API.post<ExportProjectRequest | undefined, Response>({
41
+ url: `/api/projects/${id}/export`,
42
+ body,
43
+ parseResponse: response => response,
44
+ })
45
+ },
46
+ importBundle: async (file, body) => {
47
+ const formData = new FormData()
48
+ formData.append("file", file)
49
+ for (const [key, value] of Object.entries(body || {})) {
50
+ if (value !== undefined) {
51
+ formData.append(key, value)
52
+ }
53
+ }
54
+ return await API.post<FormData, ImportProjectResponse>({
55
+ url: "/api/projects/import",
56
+ body: formData,
57
+ json: false,
58
+ })
59
+ },
60
+ update: async project => {
61
+ const { _id, _rev, name, description, color } = project
62
+ return await API.put({
63
+ url: `/api/projects/${_id}`,
64
+ body: {
65
+ _id,
66
+ _rev,
67
+ name,
68
+ description,
69
+ color,
70
+ },
71
+ })
72
+ },
73
+ delete: async (id, rev) => {
74
+ return await API.delete({
75
+ url: `/api/projects/${id}/${rev}`,
76
+ })
77
+ },
78
+ })
package/src/api/types.ts CHANGED
@@ -20,6 +20,7 @@ import { MigrationEndpoints } from "./migrations"
20
20
  import { OAuth2Endpoints } from "./oauth2"
21
21
  import { OtherEndpoints } from "./other"
22
22
  import { PermissionEndpoints } from "./permissions"
23
+ import { ProjectEndpoints } from "./projects"
23
24
  import { PluginEndpoins } from "./plugins"
24
25
  import { QueryEndpoints } from "./queries"
25
26
  import { RelationshipEndpoints } from "./relationships"
@@ -37,7 +38,10 @@ import { ViewV2Endpoints } from "./viewsV2"
37
38
  import { AgentEndpoints } from "./agents"
38
39
  import { AgentTestEndpoints } from "./agentTests"
39
40
  import { AgentLogEndpoints } from "./agentLogs"
41
+ import { AgentRequestEndpoints } from "./agentRequests"
40
42
  import { ChatAppEndpoints } from "./chatApps"
43
+ import { ChatLinksEndpoints } from "./chatLinks"
44
+ import { EscalationEndpoints } from "./escalations"
41
45
  import { NavigationEndpoints } from "./navigation"
42
46
  import { WorkspaceAppEndpoints } from "./workspaceApps"
43
47
  import { ResourceEndpoints } from "./resource"
@@ -122,7 +126,10 @@ export type APIClient = BaseAPIClient &
122
126
  AgentEndpoints &
123
127
  AgentTestEndpoints &
124
128
  AgentLogEndpoints &
129
+ AgentRequestEndpoints &
125
130
  ChatAppEndpoints &
131
+ ChatLinksEndpoints &
132
+ EscalationEndpoints &
126
133
  AnalyticsEndpoints &
127
134
  AppEndpoints &
128
135
  AttachmentEndpoints &
@@ -160,6 +167,7 @@ export type APIClient = BaseAPIClient &
160
167
  viewV2: ViewV2Endpoints
161
168
  oauth2: OAuth2Endpoints
162
169
  navigation: NavigationEndpoints
170
+ projects: ProjectEndpoints
163
171
  workspaceApp: WorkspaceAppEndpoints
164
172
  workspace: WorkspaceFavouriteEndpoints
165
173
  workspaceHome: WorkspaceHomeEndpoints
@@ -0,0 +1,113 @@
1
+ <script lang="ts">
2
+ import { Body, Button, Icon } from "@budibase/bbui"
3
+ import type { EscalationContextDoc } from "@budibase/types"
4
+
5
+ interface Props {
6
+ title?: string
7
+ summary?: string
8
+ // Live resolution from the poll (not the frozen tool output).
9
+ resolution: EscalationContextDoc["resolution"]
10
+ // Message relayed from resolve ("Response recorded." etc.) - set as soon as
11
+ // the action is taken here, before the poll flips resolution.
12
+ statusMessage?: string
13
+ // Dev-only: whether to show the inline Approve/Reject buttons.
14
+ showApproval?: boolean
15
+ resolving?: boolean
16
+ onApprove?: () => void
17
+ onReject?: () => void
18
+ }
19
+
20
+ let {
21
+ title,
22
+ summary,
23
+ resolution,
24
+ statusMessage,
25
+ showApproval = false,
26
+ resolving = false,
27
+ onApprove,
28
+ onReject,
29
+ }: Props = $props()
30
+
31
+ // Resolved either by this card's action (statusMessage) or elsewhere (poll).
32
+ let isResolved = $derived(!!statusMessage || resolution !== "pending")
33
+ </script>
34
+
35
+ <div class="escalation-card" aria-live="polite">
36
+ <div class="escalation-card-header">
37
+ <Icon name={isResolved ? "check-circle" : "clock"} size="M" />
38
+ <span class="escalation-card-title">{title || "Approval required"}</span>
39
+ {#if showApproval && !isResolved}
40
+ <span class="escalation-card-badge">Test mode</span>
41
+ {/if}
42
+ </div>
43
+
44
+ {#if summary}
45
+ <Body size="S" color="var(--spectrum-global-color-gray-700)">
46
+ {summary}
47
+ </Body>
48
+ {/if}
49
+
50
+ {#if isResolved}
51
+ <Body size="XS" color="var(--spectrum-global-color-gray-600)">
52
+ {statusMessage || "Response recorded."}
53
+ </Body>
54
+ {:else if showApproval}
55
+ <Body size="XS" color="var(--spectrum-global-color-gray-600)">
56
+ Approve or reject here to simulate a reviewer response.
57
+ </Body>
58
+ <div class="escalation-card-actions">
59
+ <Button cta disabled={resolving} on:click={() => onApprove?.()}>
60
+ Approve
61
+ </Button>
62
+ <Button secondary disabled={resolving} on:click={() => onReject?.()}>
63
+ Reject
64
+ </Button>
65
+ </div>
66
+ {:else}
67
+ <Body size="XS" color="var(--spectrum-global-color-gray-600)">
68
+ Awaiting a human response.
69
+ </Body>
70
+ {/if}
71
+ </div>
72
+
73
+ <style>
74
+ .escalation-card {
75
+ align-self: flex-start;
76
+ width: 100%;
77
+ max-width: 100%;
78
+ box-sizing: border-box;
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: var(--spacing-s);
82
+ padding: var(--spacing-m);
83
+ border: 1px solid var(--spectrum-global-color-gray-300);
84
+ border-radius: 12px;
85
+ background: var(--spectrum-global-color-gray-75);
86
+ }
87
+ .escalation-card-header {
88
+ display: flex;
89
+ align-items: center;
90
+ gap: var(--spacing-s);
91
+ }
92
+ .escalation-card-title {
93
+ font-weight: 600;
94
+ font-size: 14px;
95
+ color: var(--spectrum-global-color-gray-900);
96
+ }
97
+ .escalation-card-badge {
98
+ margin-left: auto;
99
+ border-radius: 999px;
100
+ border: 1px solid var(--spectrum-global-color-blue-400);
101
+ color: var(--spectrum-global-color-blue-700);
102
+ background: var(--spectrum-global-color-blue-100);
103
+ font-size: 11px;
104
+ font-weight: 600;
105
+ line-height: 1;
106
+ padding: 4px 8px;
107
+ }
108
+ .escalation-card-actions {
109
+ display: flex;
110
+ gap: var(--spacing-s);
111
+ margin-top: var(--spacing-xs);
112
+ }
113
+ </style>
@@ -11,6 +11,8 @@
11
11
  ChatConversation,
12
12
  DraftChatConversation,
13
13
  AgentMessageMetadata,
14
+ EscalationContextDoc,
15
+ EscalationRespondResult,
14
16
  } from "@budibase/types"
15
17
  import { Header } from "@budibase/shared-core"
16
18
  import { tick, untrack } from "svelte"
@@ -19,6 +21,7 @@
19
21
  import { formatToolName } from "../../utils/aiTools"
20
22
  import ReasoningStatus from "./ReasoningStatus.svelte"
21
23
  import ContextUsage from "./ContextUsage.svelte"
24
+ import EscalationCard from "./EscalationCard.svelte"
22
25
  import {
23
26
  DefaultChatTransport,
24
27
  isTextUIPart,
@@ -38,6 +41,20 @@
38
41
  onchatsaved?: (_event: {
39
42
  detail: { chatId?: string; chat: ChatConversationLike }
40
43
  }) => void
44
+ // Fired when an escalation parks; the consumer polls the outcome and
45
+ // injects it via appendAssistantMessage.
46
+ onEscalationPending?: (_detail: { escalationId: string }) => void
47
+ // Live resolution per escalationId (from the poll) - drives the card state.
48
+ escalationState?: Record<
49
+ string,
50
+ { resolution: EscalationContextDoc["resolution"] }
51
+ >
52
+ // Dev-only: show the inline Approve/Reject buttons on the escalation card.
53
+ showInlineApproval?: boolean
54
+ onResolve?: (
55
+ _escalationId: string,
56
+ _accepted: boolean
57
+ ) => Promise<EscalationRespondResult | undefined>
41
58
  isAgentPreviewChat?: boolean
42
59
  readOnly?: boolean
43
60
  readOnlyReason?: "disabled" | "deleted" | "offline"
@@ -50,11 +67,47 @@
50
67
  conversationStarters = [],
51
68
  initialPrompt = "",
52
69
  onchatsaved,
70
+ onEscalationPending,
71
+ escalationState,
72
+ showInlineApproval = false,
73
+ onResolve,
53
74
  isAgentPreviewChat = false,
54
75
  readOnly = false,
55
76
  readOnlyReason,
56
77
  }: Props = $props()
57
78
 
79
+ // Per-escalation in-flight flag + the message relayed from resolve, so the
80
+ // card shows the real outcome on click rather than a static label.
81
+ let resolvingEscalations = $state<Record<string, boolean>>({})
82
+ let resolveMessages = $state<Record<string, string>>({})
83
+ const handleResolve = async (escalationId: string, accepted: boolean) => {
84
+ resolvingEscalations = { ...resolvingEscalations, [escalationId]: true }
85
+ try {
86
+ const result = await onResolve?.(escalationId, accepted)
87
+ if (result?.message) {
88
+ resolveMessages = { ...resolveMessages, [escalationId]: result.message }
89
+ }
90
+ } finally {
91
+ resolvingEscalations = { ...resolvingEscalations, [escalationId]: false }
92
+ }
93
+ }
94
+
95
+ // The escalate part's input/output are loosely typed by the AI SDK, so the
96
+ // casts live here rather than cluttering the template.
97
+ const escalationCardProps = (part: { input?: unknown; output?: unknown }) => {
98
+ const output = part.output as { escalationId?: string } | undefined
99
+ const input = part.input as { title?: string; summary?: string } | undefined
100
+ const escalationId = output?.escalationId
101
+ return {
102
+ escalationId,
103
+ title: input?.title,
104
+ summary: input?.summary,
105
+ resolution:
106
+ (escalationId && escalationState?.[escalationId]?.resolution) ||
107
+ "pending",
108
+ }
109
+ }
110
+
58
111
  let API = $state(
59
112
  createAPIClient({
60
113
  attachHeaders: headers => {
@@ -322,6 +375,48 @@
322
375
  let messages = $derived(chatInstance.messages)
323
376
  let lastMessage = $derived(messages[messages.length - 1])
324
377
 
378
+ // Notify the consumer of every unseen parked escalation. The escalate part's
379
+ // output stays frozen at pending_approval, so notifying only the first would
380
+ // shadow later escalations.
381
+ const notifiedEscalations = new Set<string>()
382
+ let pendingEscalationIds = $derived.by(() => {
383
+ const ids: string[] = []
384
+ for (const message of messages) {
385
+ for (const part of message.parts ?? []) {
386
+ if (
387
+ !isToolUIPart(part) ||
388
+ getToolName(part) !== "escalate" ||
389
+ part.state !== "output-available"
390
+ ) {
391
+ continue
392
+ }
393
+ const output = part.output as
394
+ | { status?: string; escalationId?: string }
395
+ | undefined
396
+ if (output?.status === "pending_approval" && output.escalationId) {
397
+ ids.push(output.escalationId)
398
+ }
399
+ }
400
+ }
401
+ return ids
402
+ })
403
+ $effect(() => {
404
+ for (const escalationId of pendingEscalationIds) {
405
+ if (!notifiedEscalations.has(escalationId)) {
406
+ notifiedEscalations.add(escalationId)
407
+ onEscalationPending?.({ escalationId })
408
+ }
409
+ }
410
+ })
411
+
412
+ // Injects the polled resume outcome as a plain assistant turn - the agent's
413
+ // own reply self-frames the approval.
414
+ export function appendAssistantMessage(
415
+ message: UIMessage<AgentMessageMetadata>
416
+ ) {
417
+ chatInstance.messages = [...chatInstance.messages, message]
418
+ }
419
+
325
420
  let lastAssistantUsage = $derived(
326
421
  messages.findLast(m => m.role === "assistant" && m.metadata?.usage)
327
422
  ?.metadata?.usage
@@ -678,6 +773,24 @@
678
773
  {#each message.parts ?? [] as part, partIndex}
679
774
  {#if isTextUIPart(part)}
680
775
  <MarkdownViewer value={part.text} />
776
+ {:else if isToolUIPart(part) && getToolName(part) === "escalate"}
777
+ {@const card = escalationCardProps(part)}
778
+ <EscalationCard
779
+ title={card.title}
780
+ summary={card.summary}
781
+ resolution={card.resolution}
782
+ statusMessage={card.escalationId
783
+ ? resolveMessages[card.escalationId]
784
+ : undefined}
785
+ showApproval={showInlineApproval}
786
+ resolving={!!card.escalationId &&
787
+ !!resolvingEscalations[card.escalationId]}
788
+ onApprove={() =>
789
+ card.escalationId && handleResolve(card.escalationId, true)}
790
+ onReject={() =>
791
+ card.escalationId &&
792
+ handleResolve(card.escalationId, false)}
793
+ />
681
794
  {:else if isToolUIPart(part)}
682
795
  {@const rawToolName = getToolName(part)}
683
796
  {@const displayToolName = formatToolName(
@@ -16,7 +16,11 @@
16
16
  import { QueryUtils, Constants } from "@budibase/frontend-core"
17
17
  import { getContext, createEventDispatcher } from "svelte"
18
18
  import FilterField from "./FilterField.svelte"
19
- import { utils } from "@budibase/shared-core"
19
+ import {
20
+ resolveTranslationGroup,
21
+ resolveWorkspaceTranslations,
22
+ utils,
23
+ } from "@budibase/shared-core"
20
24
 
21
25
  const dispatch = createEventDispatcher()
22
26
  const {
@@ -46,7 +50,13 @@
46
50
  export let toReadable
47
51
  export let toRuntime
48
52
  export let evaluationContext = {}
53
+ const sdk = getContext("sdk") || {}
54
+ const { appStore } = sdk
49
55
 
56
+ $: translationOverrides = resolveWorkspaceTranslations(
57
+ appStore ? $appStore.application?.translationOverrides : undefined
58
+ )
59
+ $: calendarLabels = resolveTranslationGroup("calendar", translationOverrides)
50
60
  $: editableFilters = migrateFilters(filters)
51
61
  $: {
52
62
  if (
@@ -423,6 +433,7 @@
423
433
  ...filter,
424
434
  }}
425
435
  {schemaFields}
436
+ {calendarLabels}
426
437
  {bindings}
427
438
  {panel}
428
439
  {toReadable}
@@ -14,6 +14,7 @@
14
14
  import { FieldType, ArrayOperator } from "@budibase/types"
15
15
  import * as Constants from "../constants"
16
16
  import { isJSBinding, findHBSBlocks } from "@budibase/string-templates"
17
+ import { resolveTranslationGroup } from "@budibase/shared-core"
17
18
  import { createEventDispatcher } from "svelte"
18
19
 
19
20
  export let filter
@@ -28,6 +29,7 @@
28
29
  export let evaluationContext = {}
29
30
  export let bindingValueType = Constants.FilterValueType.BINDING
30
31
  export let useConditionValueControls = false
32
+ export let calendarLabels = resolveTranslationGroup("calendar")
31
33
 
32
34
  const dispatch = createEventDispatcher()
33
35
  const { OperatorOptions, FilterValueType } = Constants
@@ -238,6 +240,7 @@
238
240
  disabled={filter.noValue}
239
241
  enableTime={!getSchema(filter)?.dateOnly}
240
242
  timeOnly={getSchema(filter)?.timeOnly}
243
+ {calendarLabels}
241
244
  value={readableValue}
242
245
  on:change={onChange}
243
246
  />
@@ -1,8 +1,12 @@
1
1
  <script>
2
2
  import { CoreDatePickerPopoverContents, Icon, Helpers } from "@budibase/bbui"
3
- import { onMount } from "svelte"
3
+ import { getContext, onMount } from "svelte"
4
4
  import dayjs from "dayjs"
5
5
  import GridPopover from "../overlays/GridPopover.svelte"
6
+ import {
7
+ resolveTranslationGroup,
8
+ resolveWorkspaceTranslations,
9
+ } from "@budibase/shared-core"
6
10
 
7
11
  export let value
8
12
  export let schema
@@ -14,7 +18,13 @@
14
18
 
15
19
  let isOpen
16
20
  let anchor
21
+ const sdk = getContext("sdk") || {}
22
+ const { appStore } = sdk
17
23
 
24
+ $: translationOverrides = resolveWorkspaceTranslations(
25
+ appStore ? $appStore.application?.translationOverrides : undefined
26
+ )
27
+ $: calendarLabels = resolveTranslationGroup("calendar", translationOverrides)
18
28
  $: timeOnly = schema?.timeOnly
19
29
  $: enableTime = !schema?.dateOnly
20
30
  $: ignoreTimezones = schema?.ignoreTimezones
@@ -120,6 +130,7 @@
120
130
  {timeOnly}
121
131
  {ignoreTimezones}
122
132
  {startDayOfWeek}
133
+ {calendarLabels}
123
134
  />
124
135
  </GridPopover>
125
136
  {/if}