327 lines
8.4 KiB
Vue
327 lines
8.4 KiB
Vue
<template>
|
|
<div class="box">
|
|
<div class="level">
|
|
<div class="level-left">
|
|
<h5 class="title is-5">Automation Jobs</h5>
|
|
</div>
|
|
<div class="level-right">
|
|
<button
|
|
class="button is-primary"
|
|
@click="openNewJobForm"
|
|
>
|
|
<SvgIcon v-bind="{ name: 'add4.svg', type: 'white', size: 18 }" />
|
|
<span class="ml-2">Add New Job</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-if="isLoading"
|
|
class="has-text-centered"
|
|
>
|
|
<p>Loading jobs...</p>
|
|
</div>
|
|
|
|
<div
|
|
v-else-if="jobs.length === 0"
|
|
class="has-text-centered"
|
|
>
|
|
<p>No automation jobs found for this template.</p>
|
|
</div>
|
|
|
|
<div v-else>
|
|
<div
|
|
v-for="job in jobs"
|
|
:key="job.id"
|
|
class="mb-4 p-3 border"
|
|
>
|
|
<div class="level">
|
|
<div class="level-left">
|
|
<div>
|
|
<p class="has-text-weight-bold">{{ job.name }}</p>
|
|
<p class="is-size-7">Model: {{ job.model_name }}</p>
|
|
<p class="is-size-7">
|
|
Triggers:
|
|
<span
|
|
v-if="job.trigger_on_create"
|
|
class="tag is-info is-light mr-1"
|
|
>On Create</span
|
|
>
|
|
<span
|
|
v-if="job.trigger_on_update"
|
|
class="tag is-warning is-light"
|
|
>On Update</span
|
|
>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="level-right">
|
|
<div class="field has-addons">
|
|
<div class="control">
|
|
<button
|
|
class="button is-small"
|
|
:class="{ 'is-success': job.active, 'is-light': !job.active }"
|
|
@click="toggleJobStatus(job)"
|
|
>
|
|
{{ job.active ? "Active" : "Inactive" }}
|
|
</button>
|
|
</div>
|
|
<div class="control">
|
|
<button
|
|
class="button is-small"
|
|
@click="openEditJobForm(job)"
|
|
>
|
|
<SvgIcon v-bind="{ name: 'pen1.svg', type: 'primary', size: 18 }" />
|
|
</button>
|
|
</div>
|
|
<div class="control">
|
|
<button
|
|
class="button is-danger is-small"
|
|
@click="confirmDelete(job)"
|
|
>
|
|
<SvgIcon v-bind="{ name: 'trash.svg', type: 'white', size: 18 }" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job Form Modal -->
|
|
<div
|
|
v-if="showForm"
|
|
class="modal is-active"
|
|
>
|
|
<div
|
|
class="modal-background"
|
|
@click="closeForm"
|
|
></div>
|
|
<div class="modal-card">
|
|
<header class="modal-card-head">
|
|
<p class="modal-card-title">
|
|
{{ isEditing ? "Edit Job" : "Create New Job" }}
|
|
</p>
|
|
<button
|
|
class="delete"
|
|
@click="closeForm"
|
|
></button>
|
|
</header>
|
|
<section class="modal-card-body">
|
|
<div class="field">
|
|
<label class="label">Job Name</label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
v-model="jobForm.name"
|
|
placeholder="e.g., Notify on Transaction Update"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">Model Name</label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
v-model="jobForm.model_name"
|
|
placeholder="e.g., app.Transaction_Detail"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">Triggers</label>
|
|
<div class="control">
|
|
<label class="checkbox mr-4">
|
|
<input
|
|
type="checkbox"
|
|
v-model="jobForm.trigger_on_create"
|
|
/>
|
|
On Create
|
|
</label>
|
|
<label class="checkbox">
|
|
<input
|
|
type="checkbox"
|
|
v-model="jobForm.trigger_on_update"
|
|
/>
|
|
On Update
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">Status</label>
|
|
<div class="control">
|
|
<label class="checkbox">
|
|
<input
|
|
type="checkbox"
|
|
v-model="jobForm.active"
|
|
/>
|
|
Active
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<footer class="modal-card-foot">
|
|
<button
|
|
class="button is-success"
|
|
@click="saveJob"
|
|
:disabled="isSaving"
|
|
>
|
|
{{ isSaving ? "Saving..." : "Save" }}
|
|
</button>
|
|
<button
|
|
class="button"
|
|
@click="closeForm"
|
|
>
|
|
Cancel
|
|
</button>
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, watch, onMounted } from "vue";
|
|
import axios from "axios";
|
|
import { useNuxtApp } from "nuxt/app";
|
|
import { apiUrl, putApiUrl } from "@/components/marketing/email/Email.utils";
|
|
|
|
const props = defineProps({
|
|
templateId: {
|
|
type: [Number, null],
|
|
default: null,
|
|
},
|
|
});
|
|
|
|
const nuxtApp = useNuxtApp();
|
|
const $snackbar = nuxtApp.$snackbar as (message: string) => void;
|
|
|
|
const jobs = ref<any[]>([]);
|
|
const isLoading = ref(false);
|
|
const isSaving = ref(false);
|
|
const showForm = ref(false);
|
|
const isEditing = ref(false);
|
|
|
|
const defaultJobForm = () => ({
|
|
id: null,
|
|
name: "",
|
|
model_name: "app.Transaction_Detail",
|
|
template: props.templateId,
|
|
trigger_on_create: false,
|
|
trigger_on_update: false,
|
|
active: true,
|
|
});
|
|
|
|
const jobForm = ref(defaultJobForm());
|
|
|
|
const fetchJobs = async () => {
|
|
if (!props.templateId) {
|
|
jobs.value = [];
|
|
return;
|
|
}
|
|
isLoading.value = true;
|
|
try {
|
|
const response = await axios.get(`${apiUrl}/Email_Job/`, {
|
|
params: { template_id: props.templateId },
|
|
});
|
|
jobs.value = response.data.rows || [];
|
|
} catch (error) {
|
|
console.error("Error fetching email jobs:", error);
|
|
$snackbar(`Error fetching jobs`);
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
};
|
|
|
|
watch(() => props.templateId, fetchJobs, { immediate: true });
|
|
|
|
const openNewJobForm = () => {
|
|
if (!props.templateId) {
|
|
$snackbar(`Please save the template first.`);
|
|
return;
|
|
}
|
|
isEditing.value = false;
|
|
jobForm.value = defaultJobForm();
|
|
showForm.value = true;
|
|
};
|
|
|
|
const openEditJobForm = (job: any) => {
|
|
isEditing.value = true;
|
|
jobForm.value = { ...job };
|
|
showForm.value = true;
|
|
};
|
|
|
|
const closeForm = () => {
|
|
showForm.value = false;
|
|
};
|
|
|
|
const saveJob = async () => {
|
|
isSaving.value = true;
|
|
try {
|
|
let response;
|
|
const data = { ...jobForm.value };
|
|
if (isEditing.value) {
|
|
response = await axios.put(`${putApiUrl}/Email_Job/${data.id}`, data);
|
|
} else {
|
|
response = await axios.post(`${apiUrl}/Email_Job/`, data);
|
|
}
|
|
if (response.status === 200 || response.status === 201) {
|
|
$snackbar(`Job saved successfully`);
|
|
await fetchJobs();
|
|
closeForm();
|
|
}
|
|
} catch (error) {
|
|
console.error("Error saving job:", error);
|
|
$snackbar(`Error saving job`);
|
|
} finally {
|
|
isSaving.value = false;
|
|
}
|
|
};
|
|
|
|
const toggleJobStatus = async (job: any) => {
|
|
const updatedJob = { ...job, active: !job.active };
|
|
try {
|
|
await axios.put(`${putApiUrl}/Email_Job/${job.id}`, updatedJob);
|
|
$snackbar(`Job status updated`);
|
|
await fetchJobs();
|
|
} catch (error) {
|
|
console.error("Error updating job status:", error);
|
|
$snackbar(`Error updating job status`);
|
|
}
|
|
};
|
|
|
|
const confirmDelete = (job: any) => {
|
|
if (confirm(`Are you sure you want to delete the job "${job.name}"?`)) {
|
|
deleteJob(job.id);
|
|
}
|
|
};
|
|
|
|
const deleteJob = async (jobId: number) => {
|
|
try {
|
|
await axios.delete(`${putApiUrl}/Email_Job/${jobId}`);
|
|
$snackbar(`Job deleted successfully`);
|
|
await fetchJobs();
|
|
} catch (error) {
|
|
console.error("Error deleting job:", error);
|
|
$snackbar(`Error deleting job`);
|
|
}
|
|
};
|
|
|
|
onMounted(fetchJobs);
|
|
</script>
|
|
|
|
<style scoped>
|
|
.box {
|
|
padding: 1.5rem;
|
|
}
|
|
.border {
|
|
border: 1px solid #dbdbdb;
|
|
border-radius: 4px;
|
|
}
|
|
.modal-card-foot {
|
|
justify-content: flex-end;
|
|
}
|
|
</style>
|