Files
Website/resources/js/views/dashboard/EventList.vue

315 lines
9.1 KiB
Vue

<template>
<SMPage permission="admin/events">
<template #container>
<SMHeading heading="Events" />
<SMMessage
v-if="formMessage.message"
:icon="formMessage.icon"
:type="formMessage.type"
:message="formMessage.message" />
<SMToolbar>
<template #left>
<SMButton
type="primary"
label="Create Event"
:small="true"
@click="handleCreate" />
</template>
<template #right>
<SMInput
v-model="search"
label="Search"
:small="true"
style="max-width: 250px" />
</template>
</SMToolbar>
<EasyDataTable
v-model:server-options="serverOptions"
:server-items-length="serverItemsLength"
:loading="formLoading"
:headers="headers"
:items="items"
:search-value="search">
<template #loading>
<SMLoadingIcon />
</template>
<template #item-title="item">
<router-link
:to="{
name: 'dashboard-event-edit',
params: { id: item.id },
}"
>{{ item.title }}</router-link
>
</template>
<template #item-actions="item">
<div class="action-wrapper">
<SMButton
label="Edit"
:dropdown="{
duplicate: 'Duplicate',
delete: 'Delete',
}"
@click="handleClick(item, $event)"></SMButton>
</div>
</template>
</EasyDataTable>
</template>
</SMPage>
</template>
<script setup lang="ts">
import { reactive, ref, watch } from "vue";
import { useRouter } from "vue-router";
import EasyDataTable from "vue3-easy-data-table";
import { openDialog } from "../../components/SMDialog";
import SMDialogConfirm from "../../components/dialogs/SMDialogConfirm.vue";
import SMButton from "../../components/SMButton.vue";
import SMHeading from "../../components/SMHeading.vue";
import SMLoadingIcon from "../../components/SMLoadingIcon.vue";
import SMMessage from "../../components/SMMessage.vue";
import SMToolbar from "../../components/SMToolbar.vue";
import SMInput from "../../components/SMInput.vue";
import { api } from "../../helpers/api";
import { SMDate } from "../../helpers/datetime";
import { debounce } from "../../helpers/debounce";
import { EventCollection, EventResponse } from "../../helpers/api.types";
import { useToastStore } from "../../store/ToastStore";
const router = useRouter();
const search = ref("");
const headers = [
{ text: "Title", value: "title", sortable: true },
{ text: "Starts", value: "start_at_formatted", sortable: true },
{ text: "Created", value: "created_at_formatted", sortable: true },
{ text: "Updated", value: "updated_at_formatted", sortable: true },
{ text: "Actions", value: "actions" },
];
const items = ref([]);
const formMessage = reactive({
icon: "",
type: "",
message: "",
});
const formLoading = ref(false);
const serverItemsLength = ref(0);
const serverOptions = ref({
page: 1,
rowsPerPage: 25,
sortBy: null,
sortType: null,
});
const handleClick = (item, extra: string): void => {
if (extra.length == 0) {
handleEdit(item);
} else if (extra.toLowerCase() == "duplicate") {
handleDuplicate(item);
} else if (extra.toLowerCase() == "delete") {
handleDelete(item);
}
};
const loadFromServer = async () => {
formMessage.icon = "";
formMessage.type = "error";
formMessage.message = "";
formLoading.value = true;
try {
let params = {};
if (serverOptions.value.sortBy) {
params["sort"] = serverOptions.value.sortBy.replace(
"_formatted",
""
);
if (
serverOptions.value.sortType &&
serverOptions.value.sortType === "desc"
) {
params["sort"] = "-" + params["sort"];
}
}
params["page"] = serverOptions.value.page;
params["limit"] = serverOptions.value.rowsPerPage;
if (search.value.length > 0) {
params["title"] = search.value;
}
let result = await api.get({
url: "/events",
params: params,
});
const data = result.data as EventCollection;
if (!data.events) {
throw new Error("The server is currently not available");
}
items.value = data.events;
items.value.forEach((row) => {
if (row.start_at !== "undefined") {
row.start_at_formatted = new SMDate(row.start_at, {
format: "ymd",
utc: true,
}).relative();
}
if (row.created_at !== "undefined") {
row.created_at_formatted = new SMDate(row.created_at, {
format: "ymd",
utc: true,
}).relative();
}
if (row.updated_at !== "undefined") {
row.updated_at_formatted = new SMDate(row.updated_at, {
format: "ymd",
utc: true,
}).relative();
}
});
serverItemsLength.value = data.total;
} catch (err) {
// restParseErrors(formData, [formMessage, "message"], err);
}
formLoading.value = false;
};
loadFromServer();
watch(
serverOptions,
() => {
loadFromServer();
},
{ deep: true }
);
const debouncedFilter = debounce(loadFromServer, 1000);
watch(search, () => {
debouncedFilter();
});
const handleCreate = () => {
router.push({ name: "dashboard-event-create" });
};
const handleEdit = (item) => {
router.push({ name: "dashboard-event-edit", params: { id: item.id } });
};
const handleDuplicate = async (item) => {
const duplicateItem = { ...item };
try {
let tries = 1;
let number = 2;
let originalTitle = duplicateItem.title;
const titleMatch = originalTitle.match(/[- ](\d+)$/);
if (titleMatch !== null) {
number = parseInt(titleMatch[1], 10);
originalTitle = originalTitle.replace(
new RegExp(`[- ]${number}$`),
""
);
}
delete duplicateItem.key;
delete duplicateItem.id;
delete duplicateItem.created_at;
delete duplicateItem.updated_at;
while (tries < 25) {
const title = `${originalTitle} ${number}`;
try {
await api.get({
url: `/events/?title==${title}`,
});
} catch (err) {
if (err.status === 404) {
duplicateItem.title = `${originalTitle} ${number}`;
break;
} else {
useToastStore().addToast({
title: "Server error",
content: "The event could not be duplicated.",
type: "danger",
});
return;
}
}
++tries;
++number;
}
const result = await api.post({
url: "/events",
body: duplicateItem,
});
const data = result.data as EventResponse;
loadFromServer();
useToastStore().addToast({
title: "Event duplicated",
content: "The event was duplicated successfully.",
type: "success",
});
router.push({
name: "dashboard-event-edit",
params: { id: data.event.id },
});
} catch (err) {
useToastStore().addToast({
title: "Server error",
content: "The event could not be duplicated.",
type: "danger",
});
}
};
const handleDelete = async (item) => {
let result = await openDialog(SMDialogConfirm, {
title: "Delete User?",
text: `Are you sure you want to delete the event <strong>${item.title}</strong>?`,
cancel: {
type: "secondary",
label: "Cancel",
},
confirm: {
type: "danger",
label: "Delete Post",
},
});
if (result == true) {
try {
await api.delete(`events${item.id}`);
loadFromServer();
formMessage.message = "Post deleted successfully";
formMessage.type = "success";
} catch (err) {
formMessage.message = err.response?.data?.message;
}
}
};
</script>
<style lang="scss"></style>