@bagelink/vue 0.0.25 → 0.0.35
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 +34 -9
- package/src/components/Btn.vue +221 -0
- package/src/components/Comments.vue +284 -0
- package/src/components/ContactArray.vue +142 -0
- package/src/components/ContactSubmissions.vue +45 -0
- package/src/components/DataPreview.vue +85 -0
- package/src/components/DropDown.vue +122 -0
- package/src/components/FileUploader.vue +353 -0
- package/src/components/FormKitTable.vue +299 -0
- package/src/components/FormSchema.vue +79 -0
- package/src/components/LangText.vue +32 -0
- package/src/components/ListItem.vue +20 -0
- package/src/components/ListView.vue +54 -0
- package/src/components/MaterialIcon.vue +19 -0
- package/src/components/Modal.vue +62 -0
- package/src/components/ModalForm.vue +109 -0
- package/src/components/NavBar.vue +353 -0
- package/src/components/PageTitle.vue +17 -0
- package/src/components/PersonPreview.vue +203 -0
- package/src/components/PersonPreviewFormkit.vue +205 -0
- package/src/components/RTXEditor.vue +151 -0
- package/src/components/RouterWrapper.vue +17 -0
- package/src/components/TabbedLayout.vue +83 -0
- package/src/components/TableSchema.vue +248 -0
- package/src/components/TopBar.vue +5 -0
- package/src/components/charts/BarChart.vue +316 -0
- package/src/components/dashboard/Lineart.vue +197 -0
- package/src/components/form/ItemRef.vue +45 -0
- package/src/components/form/MaterialIcon.vue +19 -0
- package/src/components/form/PlainInputField.vue +79 -0
- package/src/components/form/inputs/CheckInput.vue +143 -0
- package/src/components/form/inputs/Checkbox.vue +77 -0
- package/src/components/form/inputs/ColorPicker.vue +47 -0
- package/src/components/form/inputs/CurrencyInput.vue +137 -0
- package/src/components/form/inputs/DateInput.vue +55 -0
- package/src/components/form/inputs/DatetimeInput.vue +50 -0
- package/src/components/form/inputs/DurationInput.vue +55 -0
- package/src/components/form/inputs/DynamicLinkField.vue +142 -0
- package/src/components/form/inputs/EmailInput.vue +57 -0
- package/src/components/form/inputs/FloatInput.vue +52 -0
- package/src/components/form/inputs/IntInput.vue +53 -0
- package/src/components/form/inputs/JSONInput.vue +55 -0
- package/src/components/form/inputs/LinkField.vue +300 -0
- package/src/components/form/inputs/Password.vue +91 -0
- package/src/components/form/inputs/PasswordInput.vue +92 -0
- package/src/components/form/inputs/PlainText.vue +63 -0
- package/src/components/form/inputs/ReadOnlyInput.vue +28 -0
- package/src/components/form/inputs/RichTextEditor.vue +56 -0
- package/src/components/form/inputs/SelectField.vue +258 -0
- package/src/components/form/inputs/TableField.vue +319 -0
- package/src/components/form/inputs/TextArea.vue +79 -0
- package/src/components/form/inputs/TextInput.vue +63 -0
- package/src/components/form/inputs/index.ts +16 -0
- package/src/components/formkit/AddressArray.vue +240 -0
- package/src/components/formkit/BankDetailsArray.vue +265 -0
- package/src/components/formkit/ContactArrayFormKit.vue +192 -0
- package/src/components/formkit/FileUploader.vue +391 -0
- package/src/components/formkit/MiscFields.vue +74 -0
- package/src/components/formkit/Toggle.vue +164 -0
- package/src/components/formkit/index.ts +29 -0
- package/src/components/index.ts +20 -0
- package/src/components/whatsapp/form/MsgTemplate.vue +227 -0
- package/src/components/whatsapp/form/TextVariableExamples.vue +79 -0
- package/src/components/whatsapp/interfaces.ts +58 -0
- package/src/index.ts +1 -26
- package/src/plugins/bagel.ts +26 -0
- package/src/styles/modal.css +90 -0
- package/src/types/BagelField.ts +57 -0
- package/src/types/BtnOptions.ts +14 -0
- package/src/types/Person.ts +51 -0
- package/src/types/file.ts +12 -0
- package/src/types/index.ts +4 -0
- package/src/types/materialIcons.d.ts +3005 -0
- package/src/utils/index.ts +62 -0
- package/src/utils/modal.ts +101 -0
- package/src/utils/objects.ts +81 -0
- package/src/utils/strings.ts +36 -0
- package/dist/index.cjs +0 -23
- package/dist/index.d.cts +0 -12
- package/dist/index.d.mts +0 -12
- package/dist/index.d.ts +0 -12
- package/dist/index.mjs +0 -19
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="bagel-input checkbox"
|
|
4
|
+
:class="{ check: context?.attrs.isCheckbox }"
|
|
5
|
+
:title="context?.help"
|
|
6
|
+
>
|
|
7
|
+
<label class="switch">
|
|
8
|
+
<input
|
|
9
|
+
@change="() => props.context?.node.input(inputVal)"
|
|
10
|
+
type="checkbox"
|
|
11
|
+
v-model="inputVal"
|
|
12
|
+
:id="context?.id"
|
|
13
|
+
>
|
|
14
|
+
<span class="slider round" />
|
|
15
|
+
</label>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
import { watch } from 'vue';
|
|
21
|
+
|
|
22
|
+
const props = defineProps({
|
|
23
|
+
context: Object,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
let inputVal = $ref(false);
|
|
27
|
+
|
|
28
|
+
watch(
|
|
29
|
+
() => props.context?.value,
|
|
30
|
+
(newVal) => {
|
|
31
|
+
inputVal = newVal;
|
|
32
|
+
},
|
|
33
|
+
{ immediate: true },
|
|
34
|
+
);
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<style>
|
|
38
|
+
.checkbox-label {
|
|
39
|
+
display: flex;
|
|
40
|
+
gap: 0.5rem;
|
|
41
|
+
flex-direction: row-reverse;
|
|
42
|
+
justify-content: flex-end;
|
|
43
|
+
align-items: center;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
}
|
|
46
|
+
</style>
|
|
47
|
+
|
|
48
|
+
<style scoped>
|
|
49
|
+
.no-edit {
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.radio-wrap p {
|
|
54
|
+
display: inline-block;
|
|
55
|
+
color: var(--input-bg);
|
|
56
|
+
font-size: var(--input-font-size);
|
|
57
|
+
margin-inline-end: 10px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.radio-wrap label {
|
|
61
|
+
padding: 3px 5px;
|
|
62
|
+
display: inline-block;
|
|
63
|
+
border-radius: var(--input-border-radius);
|
|
64
|
+
font-size: var(--input-font-size);
|
|
65
|
+
background: var(--input-bg);
|
|
66
|
+
color: var(--bgl-black);
|
|
67
|
+
text-align: center;
|
|
68
|
+
margin: 8px 5px;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
user-select: none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.bagel-input .radio-wrap label::after {
|
|
74
|
+
background: transparent;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.radio-wrap input {
|
|
78
|
+
display: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.radio-wrap input:checked:checked+label {
|
|
82
|
+
background: var(--bgl-blue);
|
|
83
|
+
color: var(--bgl-white);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.checkbox {
|
|
87
|
+
position: relative;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.switch {
|
|
91
|
+
position: relative;
|
|
92
|
+
display: inline-block;
|
|
93
|
+
height: calc(var(--input-height) / 2);
|
|
94
|
+
width: var(--input-height);
|
|
95
|
+
border-radius: var(--input-height);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.switch input {
|
|
99
|
+
opacity: 0;
|
|
100
|
+
width: 0;
|
|
101
|
+
height: 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.bagel-input.checkbox.check {
|
|
105
|
+
margin: 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.bagel-input.checkbox.check .switch {
|
|
109
|
+
width: calc(var(--input-height) / 2);
|
|
110
|
+
height: calc(var(--input-height) / 2);
|
|
111
|
+
transition: 0.2s;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.bagel-input.checkbox.check .slider:before {
|
|
115
|
+
position: absolute;
|
|
116
|
+
font-family: 'Material Symbols Outlined', serif;
|
|
117
|
+
content: '';
|
|
118
|
+
color: var(--bgl-white);
|
|
119
|
+
background: transparent;
|
|
120
|
+
display: flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.bagel-input.checkbox.check input:checked+.slider:before {
|
|
126
|
+
transform: none;
|
|
127
|
+
content: 'check';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.slider {
|
|
131
|
+
position: absolute;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
top: 0;
|
|
134
|
+
left: 0;
|
|
135
|
+
right: 0;
|
|
136
|
+
bottom: 0;
|
|
137
|
+
background: var(--input-bg);
|
|
138
|
+
-webkit-transition: 0.4s;
|
|
139
|
+
transition: 0.4s;
|
|
140
|
+
border-radius: calc(var(--input-height) / 2);
|
|
141
|
+
box-shadow: inset 0 0 10px #00000020;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.slider:before {
|
|
145
|
+
position: absolute;
|
|
146
|
+
content: '';
|
|
147
|
+
height: calc(var(--input-height) / 2 - 4px);
|
|
148
|
+
width: calc(var(--input-height) / 2 - 4px);
|
|
149
|
+
left: 2px;
|
|
150
|
+
bottom: 2px;
|
|
151
|
+
border-radius: 50%;
|
|
152
|
+
background: var(--bgl-white);
|
|
153
|
+
-webkit-transition: 0.4s;
|
|
154
|
+
transition: 0.4s;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
input:checked+.slider {
|
|
158
|
+
background: var(--bgl-blue);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
input:checked+.slider:before {
|
|
162
|
+
transform: translateX(calc(var(--input-height) / 2));
|
|
163
|
+
}
|
|
164
|
+
</style>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createInput } from '@formkit/vue';
|
|
2
|
+
import ContactArrayFormKit from '@/components/formkit/ContactArrayFormKit.vue';
|
|
3
|
+
import AddressArray from './AddressArray.vue';
|
|
4
|
+
import BankDetailsArray from './BankDetailsArray.vue';
|
|
5
|
+
import MiscFieldsBtns from './MiscFields.vue';
|
|
6
|
+
import Toggle from './Toggle.vue';
|
|
7
|
+
import FileUploader from './FileUploader.vue';
|
|
8
|
+
import TextVariableExamples from '@/components/whatsapp/form/TextVariableExamples.vue';
|
|
9
|
+
import PPV from '../PersonPreviewFormkit.vue';
|
|
10
|
+
|
|
11
|
+
const ContactArray = createInput(ContactArrayFormKit);
|
|
12
|
+
const Address = createInput(AddressArray);
|
|
13
|
+
const BankDetails = createInput(BankDetailsArray);
|
|
14
|
+
const MiscFields = createInput(MiscFieldsBtns);
|
|
15
|
+
const ToggleSwitch = createInput(Toggle);
|
|
16
|
+
const FileUpload = createInput(FileUploader);
|
|
17
|
+
const TextVariables = createInput(TextVariableExamples);
|
|
18
|
+
const PersonPreview = createInput(PPV);
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
TextVariables,
|
|
22
|
+
ContactArray,
|
|
23
|
+
PersonPreview,
|
|
24
|
+
Address,
|
|
25
|
+
BankDetails,
|
|
26
|
+
MiscFields,
|
|
27
|
+
ToggleSwitch,
|
|
28
|
+
FileUpload,
|
|
29
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export * as RTXEditor from './RTXEditor.vue';
|
|
2
|
+
export * as MaterialIcon from './MaterialIcon.vue';
|
|
3
|
+
export * as BagelForm from './BagelForm.vue';
|
|
4
|
+
export * as NavBar from './NavBar.vue';
|
|
5
|
+
export * as Btn from './Btn.vue';
|
|
6
|
+
export * as Modal from './Modal.vue';
|
|
7
|
+
export * as BarChart from './charts/BarChart.vue';
|
|
8
|
+
export * as DropDown from './DropDown.vue';
|
|
9
|
+
export * as ListView from './ListView.vue';
|
|
10
|
+
export * as ListItem from './ListItem.vue';
|
|
11
|
+
export * as TabbedLayout from './TabbedLayout.vue';
|
|
12
|
+
export * as Comments from './Comments.vue';
|
|
13
|
+
export * as PageTitle from './PageTitle.vue';
|
|
14
|
+
export * as ModalForm from './ModalForm.vue';
|
|
15
|
+
export * as TextInput from './form/inputs/TextInput.vue';
|
|
16
|
+
export * as DataPreview from './DataPreview.vue';
|
|
17
|
+
export * as SubscriptionCharges from './SubscriptionCharges.vue';
|
|
18
|
+
export * as FormSchema from './FormSchema.vue';
|
|
19
|
+
export * as TableSchema from './TableSchema.vue';
|
|
20
|
+
export * as TopBar from './TopBar.vue';
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="main-content">
|
|
3
|
+
<PageTitle>Whatsapp Template</PageTitle>
|
|
4
|
+
<div class="view-wrapper card thin">
|
|
5
|
+
<div class="whatsapp-wrap">
|
|
6
|
+
<div class="create-template-form">
|
|
7
|
+
<FormSchema
|
|
8
|
+
:schema="whatsappTemplateSchema()"
|
|
9
|
+
v-model="localWhatsappData"
|
|
10
|
+
@submit="upsertTemplate"
|
|
11
|
+
/>
|
|
12
|
+
</div>
|
|
13
|
+
<div
|
|
14
|
+
class="whatsapp-preview"
|
|
15
|
+
:class="{ whatsappHebrew: localWhatsappData?.language === 'he' }"
|
|
16
|
+
>
|
|
17
|
+
Preview
|
|
18
|
+
<div class="whatsapp-msg">
|
|
19
|
+
<b>{{
|
|
20
|
+
localWhatsappData?.headerText?.replace(regex, replaceHeader)
|
|
21
|
+
}}</b>
|
|
22
|
+
<p class="whatsapp-body">
|
|
23
|
+
{{ localWhatsappData?.bodyText?.replace(regex, replaceBody) }}
|
|
24
|
+
</p>
|
|
25
|
+
<p class="whatsapp-footer">
|
|
26
|
+
{{ localWhatsappData?.footerText }}
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
import { onMounted } from 'vue';
|
|
37
|
+
|
|
38
|
+
import { useBagel } from 'src/plugins/bagel';
|
|
39
|
+
import type { FormKitSchemaDefinition } from '@formkit/core';
|
|
40
|
+
import type { RouteLocationNormalizedLoaded, Router } from 'vue-router';
|
|
41
|
+
import {
|
|
42
|
+
BodyComponent,
|
|
43
|
+
HeaderComponent,
|
|
44
|
+
LocalTemplateData,
|
|
45
|
+
ServerTemplateData,
|
|
46
|
+
FooterComponent,
|
|
47
|
+
} from '../interfaces';
|
|
48
|
+
import { PageTitle, FormSchema } from '@/components';
|
|
49
|
+
|
|
50
|
+
const props = defineProps<{
|
|
51
|
+
whatsappTemplateSchema: () => FormKitSchemaDefinition;
|
|
52
|
+
router: Router;
|
|
53
|
+
route: RouteLocationNormalizedLoaded;
|
|
54
|
+
}>();
|
|
55
|
+
|
|
56
|
+
const bagel = useBagel();
|
|
57
|
+
|
|
58
|
+
const regex = /\{\{(\d+)}}/g;
|
|
59
|
+
let localWhatsappData = $ref<LocalTemplateData>();
|
|
60
|
+
|
|
61
|
+
const serverToLocal = (data: ServerTemplateData): LocalTemplateData => ({
|
|
62
|
+
id: data.id,
|
|
63
|
+
status: data.status,
|
|
64
|
+
name: data.name,
|
|
65
|
+
language: data.language,
|
|
66
|
+
headerText: data.components.find(
|
|
67
|
+
(c): c is HeaderComponent => c.type === 'HEADER',
|
|
68
|
+
)?.text,
|
|
69
|
+
bodyText: data.components.find((c): c is BodyComponent => c.type === 'BODY')
|
|
70
|
+
?.text,
|
|
71
|
+
bodyExamples: data.components.find(
|
|
72
|
+
(c): c is BodyComponent => c.type === 'BODY',
|
|
73
|
+
)?.example?.body_text[0],
|
|
74
|
+
footerText: data.components.find(
|
|
75
|
+
(c): c is FooterComponent => c.type === 'FOOTER',
|
|
76
|
+
)?.text,
|
|
77
|
+
category: data.category,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const localToServer = (data: LocalTemplateData) => {
|
|
81
|
+
const obj: any = {};
|
|
82
|
+
obj.language = data.language;
|
|
83
|
+
if (data.id) obj.id = data.id;
|
|
84
|
+
obj.name = data.name;
|
|
85
|
+
obj.components = [];
|
|
86
|
+
obj.category = data.category;
|
|
87
|
+
if (data.headerText) {
|
|
88
|
+
obj.components.push({
|
|
89
|
+
type: 'HEADER',
|
|
90
|
+
format: 'TEXT',
|
|
91
|
+
text: data.headerText,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (data.bodyText) {
|
|
95
|
+
const bodyComponent: BodyComponent = { type: 'BODY', text: data.bodyText };
|
|
96
|
+
if (data.bodyExamples) { bodyComponent.example = { body_text: [data.bodyExamples] }; }
|
|
97
|
+
obj.components.push(bodyComponent);
|
|
98
|
+
}
|
|
99
|
+
if (data.footerText) { obj.components.push({ type: 'FOOTER', text: data.footerText }); }
|
|
100
|
+
return obj;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const loadTemplateData = async () => {
|
|
104
|
+
const template_name = props.route.params.id;
|
|
105
|
+
if (template_name) {
|
|
106
|
+
const res = await bagel.get(
|
|
107
|
+
`communication/whatsapp/get_template_by_name/${template_name}`,
|
|
108
|
+
);
|
|
109
|
+
localWhatsappData = serverToLocal(res.data[0]);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const replaceHeader = (match: any) => {
|
|
114
|
+
const index = +match[2];
|
|
115
|
+
const example = localWhatsappData?.headerExamples?.[index - 1];
|
|
116
|
+
return example || match;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const replaceBody = (match: any) => {
|
|
120
|
+
const index = +match[2];
|
|
121
|
+
const example = localWhatsappData?.bodyExamples?.[index - 1];
|
|
122
|
+
return example || match;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const upsertTemplate = async () => {
|
|
126
|
+
if (!localWhatsappData) return;
|
|
127
|
+
const payload = localToServer(localWhatsappData);
|
|
128
|
+
console.log(payload);
|
|
129
|
+
if (payload.id) {
|
|
130
|
+
await bagel.put(
|
|
131
|
+
`communication/whatsapp/update_template/${payload.id}`,
|
|
132
|
+
payload,
|
|
133
|
+
);
|
|
134
|
+
} else {
|
|
135
|
+
await bagel.post('communication/whatsapp/create_template', payload);
|
|
136
|
+
void props.router.push(`/whatsapp/template/${payload.name}`);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
onMounted(loadTemplateData);
|
|
141
|
+
</script>
|
|
142
|
+
|
|
143
|
+
<style scoped>
|
|
144
|
+
.btn-end {
|
|
145
|
+
text-align: end;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.whatsapp-preview {
|
|
149
|
+
direction: ltr;
|
|
150
|
+
width: 400px;
|
|
151
|
+
padding: 1rem;
|
|
152
|
+
height: auto;
|
|
153
|
+
margin-top: 1rem;
|
|
154
|
+
/* border-left: 1px solid var(--border-color); */
|
|
155
|
+
margin-left: 20px;
|
|
156
|
+
position: sticky;
|
|
157
|
+
top: 15px;
|
|
158
|
+
flex: 1 1 40%;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.whatsapp-wrap {
|
|
162
|
+
display: flex;
|
|
163
|
+
gap: 1rem;
|
|
164
|
+
max-width: 1200px;
|
|
165
|
+
overflow: auto;
|
|
166
|
+
height: 100%;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.create-template-form {
|
|
170
|
+
flex: 1 0 50%;
|
|
171
|
+
min-width: 300px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.whatsapp-msg {
|
|
175
|
+
background: var(--whatsapp-green);
|
|
176
|
+
border-radius: 0.5rem;
|
|
177
|
+
border-start-start-radius: 0px;
|
|
178
|
+
padding: 10px;
|
|
179
|
+
line-height: 1.5;
|
|
180
|
+
margin-top: 1rem;
|
|
181
|
+
filter: drop-shadow(0 0px 2px var(--bgl-shadow));
|
|
182
|
+
position: relative;
|
|
183
|
+
min-height: 30px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.whatsapp-msg::after {
|
|
187
|
+
content: '';
|
|
188
|
+
inset-inline-start: -5px;
|
|
189
|
+
border-radius: 0.1rem;
|
|
190
|
+
transform: skew(45deg);
|
|
191
|
+
top: 0;
|
|
192
|
+
height: 8px;
|
|
193
|
+
width: 8px;
|
|
194
|
+
background: var(--whatsapp-green);
|
|
195
|
+
position: absolute;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.whatsapp-body {
|
|
199
|
+
margin: 2px 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.whatsapp-footer {
|
|
203
|
+
margin: 0;
|
|
204
|
+
opacity: 0.6;
|
|
205
|
+
font-size: 0.8rem;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.whatsappHebrew {
|
|
209
|
+
direction: rtl;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.whatsappHebrew .whatsapp-msg::after {
|
|
213
|
+
transform: skew(-45deg);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@media screen and (max-width: 1200px) {
|
|
217
|
+
.whatsapp-preview {
|
|
218
|
+
width: 300px;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@media screen and (max-width: 910px) {
|
|
223
|
+
.whatsapp-preview {
|
|
224
|
+
display: none;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
</style>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="exampleEl">
|
|
3
|
+
<TextInput
|
|
4
|
+
v-for="v in variables"
|
|
5
|
+
:key="v"
|
|
6
|
+
:placeholder="`Variable ${v}`"
|
|
7
|
+
:modelValue="val?.[v - 1]"
|
|
8
|
+
@update:modelValue="($event: string) => handleUpdate($event, v)"
|
|
9
|
+
/>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { onMounted, watch } from 'vue';
|
|
15
|
+
import { FormKitNode } from '@formkit/core';
|
|
16
|
+
import { TextInput } from '@/components';
|
|
17
|
+
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
context: Object,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const exampleEl = $ref<HTMLElement>();
|
|
23
|
+
let boundValue = $ref<any>();
|
|
24
|
+
let boundNode = $ref<FormKitNode>();
|
|
25
|
+
let val = $ref<any[]>([]);
|
|
26
|
+
|
|
27
|
+
const handleUpdate = (value: string, index: number) => {
|
|
28
|
+
if (!boundValue) return;
|
|
29
|
+
if (index > val?.length) val = [...val, ...Array(index - val.length).fill(''), value];
|
|
30
|
+
else val?.splice(index - 1, 1, value);
|
|
31
|
+
props.context?.node.input(val);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const extractVariables = (text: string): number[] => {
|
|
35
|
+
const matches = text?.match(/{{(\d+)}}/g) || [];
|
|
36
|
+
return matches.map((match: any) => +match.match(/\d+/)[0]);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const txtType = $computed(() => (props.context?.id === 'bodyExamples' ? 'bodyText' : 'headerText'));
|
|
40
|
+
|
|
41
|
+
const variables = $computed(() => {
|
|
42
|
+
let vars = extractVariables(boundValue);
|
|
43
|
+
if (txtType === 'headerText') vars.slice(0, 1);
|
|
44
|
+
const nonSequentialDigits = vars?.reduce((acc: number[], currDigit: number, i: number) => {
|
|
45
|
+
const prevDigit = i > 0 ? vars[i - 1] : 0;
|
|
46
|
+
if (currDigit - prevDigit !== 1) {
|
|
47
|
+
acc.push(currDigit);
|
|
48
|
+
}
|
|
49
|
+
return acc;
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
if (nonSequentialDigits?.length) {
|
|
53
|
+
const regex = new RegExp(nonSequentialDigits.map((d: number) => `\\{\\{${d}\\}\\}`).join('|'), 'g');
|
|
54
|
+
const newVal = `${boundValue}`.replace(regex, '');
|
|
55
|
+
void boundNode?.input(newVal); // TODO: Nati - should be awaited?
|
|
56
|
+
vars = extractVariables(boundValue);
|
|
57
|
+
}
|
|
58
|
+
const parentEl = exampleEl?.closest('.formkit-outer');
|
|
59
|
+
if (!vars.length && parentEl) {
|
|
60
|
+
parentEl.classList.add('hide');
|
|
61
|
+
} else if (parentEl) {
|
|
62
|
+
parentEl.classList.remove('hide');
|
|
63
|
+
}
|
|
64
|
+
return vars;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const setupListners = (node?: FormKitNode) => {
|
|
68
|
+
node?.on('input', (v: any) => {
|
|
69
|
+
boundValue = v.payload;
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
onMounted(() => {
|
|
74
|
+
boundNode = props.context?.node.parent.children.find((c: any) => c.name === txtType);
|
|
75
|
+
setupListners(boundNode);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
watch(() => props.context?.value, (v: any) => val = v, { immediate: true });
|
|
79
|
+
</script>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface HeaderComponent {
|
|
2
|
+
type: 'HEADER';
|
|
3
|
+
text: string;
|
|
4
|
+
example_header?: string[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Example {
|
|
8
|
+
body_text: string[][];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BodyComponent {
|
|
12
|
+
type: 'BODY';
|
|
13
|
+
text: string;
|
|
14
|
+
example?: Example
|
|
15
|
+
add_security_recommendation?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface FooterComponent {
|
|
19
|
+
type: 'FOOTER';
|
|
20
|
+
text: string;
|
|
21
|
+
code_expiration_minutes?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface Button {
|
|
25
|
+
type: 'OTP' | 'CATALOG' | 'MPM' | 'QUICK_REPLY' | 'PHONE_NUMBER' | 'URL';
|
|
26
|
+
text: string;
|
|
27
|
+
// Additional properties specific to each button type
|
|
28
|
+
// Add them here based on the possible button types
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ButtonsComponent {
|
|
32
|
+
type: 'BUTTONS';
|
|
33
|
+
buttons: Button[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type Component = BodyComponent | FooterComponent | HeaderComponent | ButtonsComponent
|
|
37
|
+
|
|
38
|
+
export interface ServerTemplateData {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
status: string;
|
|
42
|
+
language: string;
|
|
43
|
+
category: string;
|
|
44
|
+
components: readonly Component[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface LocalTemplateData {
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
language: string;
|
|
51
|
+
status: string;
|
|
52
|
+
category: string;
|
|
53
|
+
headerText?: string;
|
|
54
|
+
headerExamples?: string[];
|
|
55
|
+
bodyText?: string;
|
|
56
|
+
bodyExamples?: string[];
|
|
57
|
+
footerText?: string;
|
|
58
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,26 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { InjectionKey, Plugin } from 'vue';
|
|
3
|
-
import { Bagel } from '@bagelink/sdk';
|
|
4
|
-
|
|
5
|
-
export const bagelInjectionKey = Symbol('bagel') as InjectionKey<Bagel>;
|
|
6
|
-
|
|
7
|
-
export function useBagel() {
|
|
8
|
-
const bagel = inject(bagelInjectionKey);
|
|
9
|
-
if (!bagel)
|
|
10
|
-
throw new Error('No bagel provided');
|
|
11
|
-
|
|
12
|
-
return bagel;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface BagelOptions {
|
|
16
|
-
host: string
|
|
17
|
-
onError?: (err: Error) => void
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const BagelVue: Plugin = {
|
|
21
|
-
install: (app, options: BagelOptions) => {
|
|
22
|
-
const bagel = new Bagel({ host: options.host, onError: options.onError });
|
|
23
|
-
app.config.globalProperties.$bagel = bagel;
|
|
24
|
-
app.provide(bagelInjectionKey, bagel);
|
|
25
|
-
},
|
|
26
|
-
};
|
|
1
|
+
export { BagelVue } from './plugins/bagel';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { inject } from 'vue';
|
|
2
|
+
import type { InjectionKey, Plugin } from 'vue';
|
|
3
|
+
import { Bagel } from '@bagelink/sdk';
|
|
4
|
+
|
|
5
|
+
export const bagelInjectionKey = Symbol('bagel') as InjectionKey<Bagel>;
|
|
6
|
+
|
|
7
|
+
export function useBagel() {
|
|
8
|
+
const bagel = inject(bagelInjectionKey);
|
|
9
|
+
if (!bagel) throw new Error('No bagel provided');
|
|
10
|
+
|
|
11
|
+
return bagel;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface BagelOptions {
|
|
15
|
+
host: string
|
|
16
|
+
// eslint-disable-next-line no-unused-vars
|
|
17
|
+
onError?: (err: Error) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const BagelVue: Plugin = {
|
|
21
|
+
install: (app, options: BagelOptions) => {
|
|
22
|
+
const bagel = new Bagel({ host: options.host, onError: options.onError });
|
|
23
|
+
app.config.globalProperties.$bagel = bagel;
|
|
24
|
+
app.provide(bagelInjectionKey, bagel);
|
|
25
|
+
},
|
|
26
|
+
};
|