|
1 |
| -<script> |
| 1 | +<script lang="ts"> |
2 | 2 | import { onMount, tick } from "svelte"
|
3 | 3 | import {
|
4 | 4 | Button,
|
|
14 | 14 | import Editor from "@/components/integration/QueryEditor.svelte"
|
15 | 15 | import TemplateBindings from "./_components/TemplateBindings.svelte"
|
16 | 16 | import { Breadcrumbs, Breadcrumb } from "@/components/portal/page"
|
| 17 | + import type { Template, GlobalTemplateBinding } from "@budibase/types" |
| 18 | +
|
| 19 | + // QueryEditor component interface based on exposed methods |
| 20 | + interface QueryEditor { |
| 21 | + // eslint-disable-next-line no-unused-vars |
| 22 | + set: (newValue: string, opts?: string) => Promise<void> |
| 23 | + update: (_: string) => void |
| 24 | + resize: () => void |
| 25 | + focus: () => void |
| 26 | + insertAtCursor: (_: string) => void |
| 27 | + } |
| 28 | +
|
| 29 | + // Tab select event interface |
| 30 | + interface TabSelectEvent { |
| 31 | + detail: string |
| 32 | + } |
| 33 | +
|
| 34 | + // Binding tab select event handler |
| 35 | + function handleBindingTabSelect(event: TabSelectEvent): void { |
| 36 | + selectedBindingTab = event.detail |
| 37 | + } |
| 38 | +
|
| 39 | + // Editor change event interface |
| 40 | + interface EditorChangeEvent { |
| 41 | + detail: { |
| 42 | + value: string |
| 43 | + } |
| 44 | + } |
17 | 45 |
|
18 | 46 | // this is the email purpose
|
19 |
| - export let template |
| 47 | + export let template: string |
20 | 48 |
|
21 |
| - let htmlEditor |
22 |
| - let mounted = false |
| 49 | + let htmlEditor: QueryEditor | null = null |
| 50 | + let mounted: boolean = false |
| 51 | + let selectedBindingTab: string = "" |
23 | 52 |
|
24 | 53 | $: selectedTemplate = $email.templates?.find(
|
25 |
| - ({ purpose }) => purpose === template |
26 |
| - ) |
27 |
| - $: name = $email.definitions?.info[template]?.name |
28 |
| - $: description = $email.definitions?.info[template]?.description |
29 |
| - $: baseTemplate = $email.templates?.find(({ purpose }) => purpose === "base") |
30 |
| - $: templateBindings = |
31 |
| - $email.definitions?.bindings?.[selectedTemplate?.purpose] || [] |
| 54 | + ({ purpose }: Template) => purpose === template |
| 55 | + ) as Template | undefined |
| 56 | + $: name = $email.definitions?.info[template]?.name as string | undefined |
| 57 | + $: description = $email.definitions?.info[template]?.description as |
| 58 | + | string |
| 59 | + | undefined |
| 60 | + $: baseTemplate = $email.templates?.find( |
| 61 | + ({ purpose }: Template) => purpose === "base" |
| 62 | + ) as Template | undefined |
| 63 | + $: templateBindings = selectedTemplate?.purpose |
| 64 | + ? (($email.definitions?.bindings?.[selectedTemplate.purpose] || |
| 65 | + []) as GlobalTemplateBinding[]) |
| 66 | + : [] |
32 | 67 | $: previewContent = makePreviewContent(baseTemplate, selectedTemplate)
|
33 | 68 |
|
34 |
| - async function saveTemplate() { |
| 69 | + async function saveTemplate(): Promise<void> { |
35 | 70 | try {
|
| 71 | + if (!selectedTemplate) { |
| 72 | + notifications.error("No template selected to save") |
| 73 | + return |
| 74 | + } |
36 | 75 | // Save your template config
|
37 | 76 | await email.saveTemplate(selectedTemplate)
|
38 | 77 | notifications.success("Template saved")
|
|
41 | 80 | }
|
42 | 81 | }
|
43 | 82 |
|
44 |
| - function setTemplateBinding(binding) { |
45 |
| - htmlEditor.update((selectedTemplate.contents += `{{ ${binding.name} }}`)) |
| 83 | + function setTemplateBinding(binding: GlobalTemplateBinding): void { |
| 84 | + if (!selectedTemplate) { |
| 85 | + console.warn("No template selected") |
| 86 | + return |
| 87 | + } |
| 88 | + if (!htmlEditor) { |
| 89 | + console.warn("Editor not available") |
| 90 | + return |
| 91 | + } |
| 92 | +
|
| 93 | + // Insert the binding at the current cursor position |
| 94 | + const bindingText = `{{ ${binding.name} }}` |
| 95 | + htmlEditor.insertAtCursor(bindingText) |
46 | 96 | }
|
47 | 97 |
|
48 |
| - const makePreviewContent = (baseTemplate, selectedTemplate) => { |
| 98 | + function makePreviewContent( |
| 99 | + baseTemplate: Template | undefined, |
| 100 | + selectedTemplate: Template | undefined |
| 101 | + ): string { |
49 | 102 | if (!selectedTemplate) {
|
50 | 103 | return ""
|
51 | 104 | }
|
52 | 105 | if (selectedTemplate.purpose === "base") {
|
53 | 106 | return selectedTemplate.contents
|
54 | 107 | }
|
55 |
| - const base = baseTemplate?.contents ?? "" |
| 108 | + const base: string = baseTemplate?.contents ?? "" |
56 | 109 | return base.replace("{{ body }}", selectedTemplate?.contents ?? "")
|
57 | 110 | }
|
58 | 111 |
|
| 112 | + // Set initial binding tab based on available bindings, only if not already set |
| 113 | + $: { |
| 114 | + if (!selectedBindingTab && templateBindings) { |
| 115 | + selectedBindingTab = templateBindings.length ? "Template" : "Common" |
| 116 | + } |
| 117 | + } |
| 118 | +
|
59 | 119 | onMount(() => {
|
60 | 120 | mounted = true
|
61 | 121 | })
|
62 | 122 |
|
63 |
| - async function fixMountBug({ detail }) { |
| 123 | + async function fixMountBug(event: TabSelectEvent): Promise<void> { |
| 124 | + const { detail } = event |
64 | 125 | if (detail === "Edit") {
|
65 | 126 | await tick()
|
66 | 127 | mounted = true
|
67 | 128 | } else {
|
68 | 129 | mounted = false
|
69 | 130 | }
|
70 | 131 | }
|
| 132 | +
|
| 133 | + function handleEditorChange(event: EditorChangeEvent): void { |
| 134 | + if (selectedTemplate) { |
| 135 | + selectedTemplate.contents = event.detail.value |
| 136 | + } |
| 137 | + } |
71 | 138 | </script>
|
72 | 139 |
|
73 | 140 | <Layout gap="L" noPadding>
|
|
95 | 162 | editorHeight={640}
|
96 | 163 | bind:this={htmlEditor}
|
97 | 164 | mode="handlebars"
|
98 |
| - on:change={e => { |
99 |
| - selectedTemplate.contents = e.detail.value |
100 |
| - }} |
| 165 | + on:change={handleEditorChange} |
101 | 166 | value={selectedTemplate?.contents}
|
102 | 167 | />
|
103 | 168 | </div>
|
104 | 169 | <div class="bindings-editor">
|
105 | 170 | <Heading size="XS">Bindings</Heading>
|
106 | 171 | {#if mounted}
|
107 |
| - <Tabs noHorizPadding selected="Template"> |
| 172 | + <Tabs |
| 173 | + noHorizPadding |
| 174 | + selected={selectedBindingTab} |
| 175 | + on:select={handleBindingTabSelect} |
| 176 | + > |
108 | 177 | <Tab title="Template">
|
109 | 178 | <TemplateBindings
|
110 |
| - title="Template Bindings" |
111 | 179 | bindings={templateBindings}
|
112 | 180 | onBindingClick={setTemplateBinding}
|
113 | 181 | />
|
114 | 182 | </Tab>
|
115 | 183 | <Tab title="Common">
|
116 | 184 | <TemplateBindings
|
117 |
| - title="Common Bindings" |
118 |
| - bindings={$email?.definitions?.bindings?.common} |
| 185 | + bindings={$email?.definitions?.bindings?.common || []} |
119 | 186 | onBindingClick={setTemplateBinding}
|
120 | 187 | />
|
121 | 188 | </Tab>
|
|
0 commit comments