Initial commit
This commit is contained in:
225
app/components/marketing/email/dataTemplate/SaveListTemplate.vue
Normal file
225
app/components/marketing/email/dataTemplate/SaveListTemplate.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div class="field"
|
||||
style="min-width: 360px"
|
||||
>
|
||||
<label class="label is-flex is-align-items-center fsb-18 mb-2" for="name">
|
||||
Template name <span class="has-text-danger ml-1">*</span>
|
||||
</label>
|
||||
<div class="control">
|
||||
<input
|
||||
ref="listNameRef"
|
||||
id="name"
|
||||
name="name"
|
||||
class="input"
|
||||
placeholder="Enter template name (e.g., My Template)"
|
||||
v-model="templateName"
|
||||
:disabled="loading"
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-flex is-justify-content-flex-end pt-2">
|
||||
<div class="buttons">
|
||||
<button
|
||||
class="button is-success"
|
||||
@click="handleSave"
|
||||
:disabled="loading || !templateName.trim()"
|
||||
>
|
||||
<span class="icon" v-if="loading">
|
||||
<SvgIcon v-bind="{ name: 'loading.svg', type: 'white', size: 16 }"></SvgIcon>
|
||||
</span>
|
||||
<span class="icon" v-else-if="isEditMode">
|
||||
<SvgIcon v-bind="{ name: 'save.svg', type: 'white', size: 16 }"></SvgIcon>
|
||||
</span>
|
||||
<span class="icon" v-else>
|
||||
<SvgIcon v-bind="{ name: 'add4.svg', type: 'white', size: 16 }"></SvgIcon>
|
||||
</span>
|
||||
<span>{{
|
||||
loading
|
||||
? isEditMode
|
||||
? "Updating..."
|
||||
: "Creating..."
|
||||
: isEditMode
|
||||
? "Update Template"
|
||||
: "Create Template"
|
||||
}}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="isEditMode"
|
||||
class="button is-success"
|
||||
:disabled="loadingCreate"
|
||||
@click="handleCreateNew"
|
||||
>
|
||||
<span class="icon" v-if="loadingCreate">
|
||||
<SvgIcon v-bind="{ name: 'loading.svg', type: 'white', size: 16 }" />
|
||||
</span>
|
||||
<span class="icon" v-else>
|
||||
<SvgIcon v-bind="{ name: 'add4.svg', type: 'white', size: 16 }"></SvgIcon>
|
||||
</span>
|
||||
<span>{{ loadingCreate ? "Creating..." : "Create" }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import { useNuxtApp } from "nuxt/app";
|
||||
|
||||
interface TemplateContent {
|
||||
receiver: string;
|
||||
subject: string;
|
||||
content: string;
|
||||
imageUrl: string | null;
|
||||
linkUrl: string[];
|
||||
textLinkUrl: string[];
|
||||
keyword: string[];
|
||||
html: string;
|
||||
}
|
||||
|
||||
interface DataTemplate {
|
||||
id?: number;
|
||||
name: string;
|
||||
content: TemplateContent;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data: DataTemplate;
|
||||
editMode?: boolean;
|
||||
onClose?: () => void;
|
||||
onSuccess?: () => void;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
data: () => ({
|
||||
name: "",
|
||||
content: {
|
||||
receiver: "",
|
||||
subject: "",
|
||||
content: "",
|
||||
imageUrl: null,
|
||||
linkUrl: [""],
|
||||
textLinkUrl: [""],
|
||||
keyword: [""],
|
||||
html: "",
|
||||
},
|
||||
}),
|
||||
editMode: false,
|
||||
onSuccess: () => {},
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: [];
|
||||
success: [];
|
||||
}>();
|
||||
|
||||
const nuxtApp = useNuxtApp();
|
||||
const { $snackbar, $insertapi, $patchapi } = nuxtApp;
|
||||
|
||||
const templateName = ref("");
|
||||
|
||||
const loading = ref(false);
|
||||
const loadingCreate = ref(false);
|
||||
const isEditMode = ref(props.editMode || false);
|
||||
const listNameRef = ref<HTMLInputElement | null>(null);
|
||||
const dataTemplate = ref<DataTemplate[]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
templateName.value = props.data.name || "";
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.data.name,
|
||||
(newName) => {
|
||||
templateName.value = newName || "";
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.editMode,
|
||||
(newEditMode) => {
|
||||
isEditMode.value = newEditMode || false;
|
||||
}
|
||||
);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!templateName.value.trim()) {
|
||||
$snackbar("Validation Error: Please enter a template name");
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
let response;
|
||||
if (isEditMode.value) {
|
||||
const patchData = {
|
||||
...props.data,
|
||||
name: templateName.value,
|
||||
};
|
||||
response = await $patchapi("Email_Template", patchData);
|
||||
} else {
|
||||
const insertData = {
|
||||
...props.data,
|
||||
name: templateName.value,
|
||||
};
|
||||
response = await $insertapi("Email_Template", insertData);
|
||||
}
|
||||
|
||||
if (response && response !== "error") {
|
||||
$snackbar(`Template ${isEditMode.value ? "updated" : "created"} successfully`);
|
||||
if (props.onClose) props.onClose();
|
||||
if (props.onSuccess) props.onSuccess();
|
||||
emit("close");
|
||||
emit("success");
|
||||
} else {
|
||||
throw new Error("Failed to save template");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error saving template:", error);
|
||||
const errorMessage = error.message || "Failed to save template.";
|
||||
$snackbar(errorMessage);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateNew = async () => {
|
||||
const postTemplateUrl = `${apiUrl}/Email_Template/`;
|
||||
try {
|
||||
loadingCreate.value = true;
|
||||
const newTemplateData = {
|
||||
...props.data,
|
||||
name: templateName.value,
|
||||
};
|
||||
const response = await axios.post(postTemplateUrl, newTemplateData);
|
||||
if (response.status === 201 || response.status === 200) {
|
||||
$snackbar("Template created successfully");
|
||||
if (props.onClose) props.onClose();
|
||||
emit("close");
|
||||
emit("success");
|
||||
templateName.value = "";
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
$snackbar("Error: Failed to create new template");
|
||||
} finally {
|
||||
loadingCreate.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.rotating {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user