Initial commit

This commit is contained in:
Viet An
2026-03-02 09:45:33 +07:00
commit d17a9e2588
415 changed files with 92113 additions and 0 deletions

View 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>