Система событий
This commit is contained in:
parent
cde119f769
commit
1175ccff74
7 changed files with 587 additions and 58 deletions
|
|
@ -15,6 +15,34 @@ model news {
|
|||
created_at DateTime @default(now())
|
||||
}
|
||||
|
||||
model events {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
description String
|
||||
when DateTime
|
||||
created_at DateTime @default(now())
|
||||
|
||||
attendants event_attendance[]
|
||||
}
|
||||
|
||||
enum EventAttendanceType {
|
||||
NO
|
||||
YES
|
||||
YES_ONLINE
|
||||
}
|
||||
|
||||
model event_attendance {
|
||||
user users @relation(fields: [usersId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
event events @relation(fields: [eventsId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
type EventAttendanceType
|
||||
created_at DateTime @default(now())
|
||||
|
||||
usersId Int
|
||||
eventsId Int
|
||||
|
||||
@@id([eventsId, usersId])
|
||||
}
|
||||
|
||||
model study_item {
|
||||
id Int @id(map: "pk_study_item") @default(autoincrement())
|
||||
title String
|
||||
|
|
@ -54,6 +82,7 @@ model users {
|
|||
user_session user_session[]
|
||||
chat_message chat_message[]
|
||||
user_in_chat user_in_chat[]
|
||||
attended event_attendance[]
|
||||
}
|
||||
|
||||
model user_session {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
import { getNewsAndAlerts, getSessionUser } from "../db";
|
||||
import { getNewsAndAlertsAlsoEvents, getSessionUser, EventAttendanceTypeV } from "../db";
|
||||
|
||||
const { news, alerts } = await getNewsAndAlerts();
|
||||
const { news, alerts, currentEvents } = await getNewsAndAlertsAlsoEvents();
|
||||
|
||||
const sessId = Astro.cookies.get("session").value!;
|
||||
const user = (await getSessionUser(sessId))!;
|
||||
|
|
@ -14,21 +14,58 @@ const formatDate = (dt: Date) => {
|
|||
"." +
|
||||
dt.getFullYear() +
|
||||
" в " +
|
||||
dt.getHours() +
|
||||
("0" + dt.getHours()).substr(-2) +
|
||||
":" +
|
||||
("0" + dt.getMinutes()).substr(-2)
|
||||
);
|
||||
};
|
||||
|
||||
const attendanceToString = (attt: EventAttendanceTypeV) => {
|
||||
switch (attt) {
|
||||
case "NO":
|
||||
return "не пойду"
|
||||
case "YES":
|
||||
return "пойду"
|
||||
case "YES_ONLINE":
|
||||
return "пойду онлайн"
|
||||
}
|
||||
}
|
||||
---
|
||||
|
||||
<div class="container">
|
||||
{user.is_admin || user.is_moderator ? (
|
||||
<a class="btn btn-sm btn-primary d-block mt-2" href="/articleEditor">
|
||||
Новая статья
|
||||
</a>
|
||||
<link rel="stylesheet" href="/assets/css/hystmodal.min.css" />
|
||||
<script is:inline src="/assets/js/hystmodal.min.js"></script>
|
||||
<div class="hystmodal" id="settingsModal" aria-hidden="true">
|
||||
<div class="hystmodal__wrap">
|
||||
<div class="hystmodal__window card p-2" role="dialog" aria-modal="true">
|
||||
<form onsubmit="createNewEvent(new FormData(this)); return false">
|
||||
<button data-hystclose class="hystmodal__close" type="reset">Закрыть</button>
|
||||
<h3>Новое событие</h3>
|
||||
|
||||
<label for="title" class="form-label mt-4">Название</label>
|
||||
<input type="text" id="title" name="title" class="form-control mb-2" required/>
|
||||
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<input type="text" id="description" name="description" class="form-control mb-2" required/>
|
||||
|
||||
<label for="event-time" class="form-label">Описание</label>
|
||||
<input type="datetime-local" id="event-time" name="event-time" class="form-control" required>
|
||||
|
||||
<hr/>
|
||||
|
||||
<button type="submit" class="btn btn-sm btn-success w-100">Создать</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="d-flex flex-row align-items-center mt-2">
|
||||
<h4>Уведомления</h4>
|
||||
{user.is_admin || user.is_moderator ? (
|
||||
<a id="openUsersModalBtn" class="btn btn-sm btn-primary ms-2 material-icons-outlined" style="height: 1.5rem; font-size: 1rem" href="/articleEditor">add</a>
|
||||
) : null}
|
||||
{
|
||||
</div>
|
||||
{ alerts.length > 0 ?
|
||||
alerts.map((article, idx) => (
|
||||
<div class:list={[idx === 0 ? "mt-2" : ""]}>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
|
|
@ -45,12 +82,72 @@ const formatDate = (dt: Date) => {
|
|||
Редактировать
|
||||
</a>
|
||||
</div>
|
||||
) : <div class="mt-2">
|
||||
Уведомления отсутствуют
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
)) :
|
||||
<div class="mt-2">
|
||||
Запланированные события отсутствуют
|
||||
</div>
|
||||
}
|
||||
<hr />
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
<h4>События</h4>
|
||||
{user.is_admin || user.is_moderator ? (
|
||||
<button id="openUsersModalBtn" class="btn btn-sm btn-primary ms-2 material-icons-outlined" style="height: 1.5rem; font-size: 1rem" data-hystmodal="#settingsModal">add</button>
|
||||
) : null}
|
||||
<a class="btn btn-sm btn-primary ms-2 material-icons-outlined" style="height: 1.5rem; font-size: 1rem" href="/past-events">history</a>
|
||||
</div>
|
||||
{ currentEvents.length > 0 ?
|
||||
currentEvents.map((event, idx) => (
|
||||
<div class:list={[idx === 0 ? "mt-2" : ""]}>
|
||||
<div class="alert alert-event" role="alert">
|
||||
<h4 class="alert-heading">{event.title}</h4>
|
||||
<h6>{formatDate(event.when)} · {event.attendants.length} посетителей</h6>
|
||||
<p>{event.description}</p>
|
||||
<hr />
|
||||
<div class="d-flex gap-3 ">
|
||||
{event.attendants.findIndex(el => el.usersId === user.id) === -1 ? (
|
||||
<button class="btn btn-sm btn-warning flex-grow-1" onclick={`attendEvent(${event.id}, "NO")`}>
|
||||
Не пойду
|
||||
</button>
|
||||
<button class="btn btn-sm btn-success flex-grow-1" onclick={`attendEvent(${event.id}, "YES")`}>
|
||||
Пойду
|
||||
</button>
|
||||
<button class="btn btn-sm btn-primary flex-grow-1" onclick={`attendEvent(${event.id}, "YES_ONLINE")`}>
|
||||
Пойду онлайн
|
||||
</button>
|
||||
) : (
|
||||
<i>Я <b>{ attendanceToString(event.attendants.find(el => el.usersId === user.id)!.type) }</b> на данное событие</i>
|
||||
<button class="btn btn-sm btn-primary" onclick={`attendEvent(${event.id}, "UNDO")`}>
|
||||
Отменить
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div class="d-flex gap-3 mt-2">
|
||||
{user.is_admin || user.is_moderator ? (
|
||||
<span class="flex-grow-1"></span>
|
||||
<button class="btn btn-sm btn-danger" onclick={`deleteEvent(${event.id})`}>
|
||||
Удалить
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
</div>
|
||||
)) :
|
||||
<div class="mt-2">
|
||||
Запланированные события отсутствуют
|
||||
</div>
|
||||
}
|
||||
{ alerts.length > 0 ? <hr /> : null }
|
||||
<hr />
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
<h4>Новости</h4>
|
||||
{user.is_admin || user.is_moderator ? (
|
||||
<a id="openUsersModalBtn" class="btn btn-sm btn-primary ms-2 material-icons-outlined" style="height: 1.5rem; font-size: 1rem" href="/articleEditor">add</a>
|
||||
) : null}
|
||||
</div>
|
||||
{ news.length > 0 ?
|
||||
news.map((article, idx) => (
|
||||
<div class:list={[idx === 0 ? "mt-2" : ""]}>
|
||||
|
|
@ -73,9 +170,7 @@ const formatDate = (dt: Date) => {
|
|||
</div>
|
||||
)) :
|
||||
<div class="mt-2">
|
||||
<div class="alert alert-success" role="alert">
|
||||
<h4 class="alert-heading m-0">Новостей нет</h4>
|
||||
</div>
|
||||
Новостей нет
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -96,9 +191,58 @@ async function deleteArticle(articleId) {
|
|||
alert(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function createNewEvent(formData) {
|
||||
try {
|
||||
const resp = await fetch(`/events/create`, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (json.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
throw new Error(json.reason)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteEvent(eventId) {
|
||||
try {
|
||||
const resp = await fetch(`/events/delete?id=${eventId}`, {
|
||||
method: "POST"
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (json.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
throw new Error(json.reason)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
}
|
||||
</script> : null}
|
||||
<script src ="https://cdn.jsdelivr.net/npm/editorjs-html@latest/build/edjsHTML.js"></script>
|
||||
<script is:inline>
|
||||
async function attendEvent(eventId, attt) {
|
||||
try {
|
||||
const resp = await fetch(`/events/attend?id=${eventId}&attt=${encodeURIComponent(attt)}`, {
|
||||
method: "POST"
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (json.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
throw new Error(json.reason)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function checklist(data) {
|
||||
const items = data.data.items.reduce(
|
||||
(acc, item) => acc + `<div class="form-check"><input class="form-check-input" type="checkbox"${item.checked ? " checked" : ""} disabled/><label class="form-check-label">${item.text}</label></div>`,
|
||||
|
|
@ -122,6 +266,12 @@ async function deleteArticle(articleId) {
|
|||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const modalsController = new HystModal({
|
||||
linkAttributeName: "data-hystmodal",
|
||||
closeOnOverlay: false,
|
||||
closeOnEsc: false,
|
||||
});
|
||||
|
||||
const edjsParser = edjsHTML({ checklist, quote, image });
|
||||
document.querySelectorAll("[data-article]").forEach(e => {
|
||||
e.innerHTML = edjsParser.parse(JSON.parse(e.dataset["article"])).reduce((a, b) => a + b, "");
|
||||
|
|
@ -132,4 +282,11 @@ async function deleteArticle(articleId) {
|
|||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.alert-event {
|
||||
--bs-alert-color: hsl(250 61% 21% / 1);
|
||||
--bs-alert-bg: hsl(250 70% 91% / 1);
|
||||
--bs-alert-border-color: hsl(250 71% 81% / 1);
|
||||
--bs-alert-link-color: hsl(250 61% 21% / 1);
|
||||
}
|
||||
</style>
|
||||
91
src/db.ts
91
src/db.ts
|
|
@ -1,7 +1,9 @@
|
|||
import { PrismaClient, chat, chat_message, user_in_chat, users } from "@prisma/client";
|
||||
import { PrismaClient, chat, chat_message, user_in_chat, users, EventAttendanceType } from "@prisma/client";
|
||||
import { blake3 } from "@noble/hashes/blake3";
|
||||
import { bytesToHex as toHex } from "@noble/hashes/utils";
|
||||
|
||||
export type EventAttendanceTypeV = EventAttendanceType;
|
||||
|
||||
const client = new PrismaClient();
|
||||
|
||||
export async function getSessionUser(sessionId: string) {
|
||||
|
|
@ -143,7 +145,7 @@ export async function createSession(user: users) {
|
|||
return session;
|
||||
}
|
||||
|
||||
export async function getNewsAndAlerts() {
|
||||
export async function getNewsAndAlertsAlsoEvents() {
|
||||
const allNews = await client.news.findMany({
|
||||
orderBy: {
|
||||
created_at: "desc",
|
||||
|
|
@ -158,9 +160,33 @@ export async function getNewsAndAlerts() {
|
|||
news.push(n);
|
||||
}
|
||||
}
|
||||
const allEvents = await client.events.findMany({
|
||||
orderBy: {
|
||||
created_at: "desc",
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
when: true,
|
||||
attendants: true,
|
||||
},
|
||||
});
|
||||
const currentEvents = [];
|
||||
const pastEvents = [];
|
||||
const now = new Date();
|
||||
for (let event of allEvents) {
|
||||
if (event.when >= now) {
|
||||
currentEvents.push(event);
|
||||
} else {
|
||||
pastEvents.push(event);
|
||||
}
|
||||
}
|
||||
return {
|
||||
news,
|
||||
alerts,
|
||||
currentEvents,
|
||||
pastEvents,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -533,3 +559,64 @@ export async function deleteChat(chatId: string) {
|
|||
|
||||
return chat !== null;
|
||||
}
|
||||
|
||||
export async function deleteEvent(eventId: number) {
|
||||
const result = await client.events.delete({
|
||||
where: {
|
||||
id: eventId,
|
||||
},
|
||||
});
|
||||
|
||||
return result !== null;
|
||||
}
|
||||
|
||||
export async function createEvent(title: string, description: string, when: Date) {
|
||||
const article = await client.events.create({
|
||||
data: {
|
||||
title: title,
|
||||
description: description,
|
||||
when: when,
|
||||
},
|
||||
});
|
||||
|
||||
return article !== null;
|
||||
}
|
||||
export async function attendEvent(eventId: number, userId: number, attendanceType: string) {
|
||||
if (attendanceType === "UNDO") {
|
||||
const article = await client.event_attendance.delete({
|
||||
where: {
|
||||
eventsId_usersId: {
|
||||
usersId: userId,
|
||||
eventsId: eventId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return article !== null;
|
||||
} else {
|
||||
let attt: EventAttendanceType;
|
||||
switch (attendanceType) {
|
||||
case "NO":
|
||||
attt = "NO";
|
||||
break;
|
||||
case "YES":
|
||||
attt = "YES";
|
||||
break;
|
||||
case "YES_ONLINE":
|
||||
attt = "YES_ONLINE";
|
||||
break;
|
||||
default:
|
||||
throw new Error("Неправильный тип");
|
||||
}
|
||||
|
||||
const article = await client.event_attendance.create({
|
||||
data: {
|
||||
type: attt,
|
||||
usersId: userId,
|
||||
eventsId: eventId,
|
||||
},
|
||||
});
|
||||
|
||||
return article !== null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
src/pages/events/attend.ts
Normal file
34
src/pages/events/attend.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import type { APIContext } from "astro";
|
||||
import { attendEvent, getSessionUser } from "../../db";
|
||||
|
||||
export async function post({ request, url, cookies }: APIContext) {
|
||||
const response: { ok: boolean; reason?: string } = {
|
||||
ok: true,
|
||||
};
|
||||
try {
|
||||
const sessId = cookies.get("session").value!;
|
||||
const user = (await getSessionUser(sessId))!;
|
||||
|
||||
const eventId = parseInt(url.searchParams.get("id") ?? "");
|
||||
const attendanceType = url.searchParams.get("attt");
|
||||
if (!eventId || attendanceType === null) {
|
||||
throw new Error("Неправильный формат запроса");
|
||||
}
|
||||
|
||||
const attended = await attendEvent(eventId, user.id, attendanceType);
|
||||
if (!attended) {
|
||||
throw new Error("Не удалось изменить состояние");
|
||||
}
|
||||
} catch (e: any) {
|
||||
response.ok = false;
|
||||
if (e instanceof Error) {
|
||||
response.reason = e.message;
|
||||
} else {
|
||||
response.reason = e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
body: JSON.stringify(response),
|
||||
};
|
||||
}
|
||||
39
src/pages/events/create.ts
Normal file
39
src/pages/events/create.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import type { APIContext } from "astro";
|
||||
import { createEvent, getSessionUser } from "../../db";
|
||||
|
||||
export async function post({ request, cookies }: APIContext) {
|
||||
const response: { ok: boolean; reason?: string } = {
|
||||
ok: true,
|
||||
};
|
||||
try {
|
||||
const sessId = cookies.get("session").value!;
|
||||
const user = (await getSessionUser(sessId))!;
|
||||
if (!user.is_admin && !user.is_moderator) {
|
||||
throw new Error("Доступно только администраторам или модераторам");
|
||||
}
|
||||
|
||||
const fd = await request.formData();
|
||||
const title = fd.get("title");
|
||||
const description = fd.get("description");
|
||||
const eventTime = fd.get("event-time");
|
||||
if (title === null || description === null || eventTime === null) {
|
||||
throw new Error("Неправильный формат запроса");
|
||||
}
|
||||
|
||||
const created = await createEvent(title.toString(), description.toString(), new Date(eventTime.toString()));
|
||||
if (!created) {
|
||||
throw new Error("Не удалось создать");
|
||||
}
|
||||
} catch (e: any) {
|
||||
response.ok = false;
|
||||
if (e instanceof Error) {
|
||||
response.reason = e.message;
|
||||
} else {
|
||||
response.reason = e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
body: JSON.stringify(response),
|
||||
};
|
||||
}
|
||||
36
src/pages/events/delete.ts
Normal file
36
src/pages/events/delete.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import type { APIContext } from "astro";
|
||||
import { deleteEvent, getSessionUser } from "../../db";
|
||||
|
||||
export async function post({ request, url, cookies }: APIContext) {
|
||||
const response: { ok: boolean; reason?: string } = {
|
||||
ok: true,
|
||||
};
|
||||
try {
|
||||
const sessId = cookies.get("session").value!;
|
||||
const user = (await getSessionUser(sessId))!;
|
||||
if (!user.is_admin && !user.is_moderator) {
|
||||
throw new Error("Доступно только администраторам или модераторам");
|
||||
}
|
||||
|
||||
const eventId = parseInt(url.searchParams.get("id") ?? "");
|
||||
if (!eventId) {
|
||||
throw new Error("Неправильный формат запроса");
|
||||
}
|
||||
|
||||
const deleted = await deleteEvent(eventId);
|
||||
if (!deleted) {
|
||||
throw new Error("Не удалось удалить");
|
||||
}
|
||||
} catch (e: any) {
|
||||
response.ok = false;
|
||||
if (e instanceof Error) {
|
||||
response.reason = e.message;
|
||||
} else {
|
||||
response.reason = e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
body: JSON.stringify(response),
|
||||
};
|
||||
}
|
||||
147
src/pages/past-events.astro
Normal file
147
src/pages/past-events.astro
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
|
||||
import { getNewsAndAlertsAlsoEvents, getSessionUser, EventAttendanceTypeV, getUserSession } from "../db";
|
||||
import NewsBlock from "../components/NewsBlock.astro";
|
||||
import Navbar from "../components/Navbar.astro";
|
||||
|
||||
if (Astro.cookies.has("session")) {
|
||||
const sessId = Astro.cookies.get("session").value!;
|
||||
const dbSess = await getUserSession(sessId);
|
||||
if (dbSess === null) {
|
||||
Astro.cookies.delete("session");
|
||||
return Astro.redirect("/login");
|
||||
}
|
||||
} else {
|
||||
return Astro.redirect("/login");
|
||||
}
|
||||
|
||||
const { pastEvents } = await getNewsAndAlertsAlsoEvents();
|
||||
|
||||
const sessId = Astro.cookies.get("session").value!;
|
||||
const user = (await getSessionUser(sessId))!;
|
||||
|
||||
const formatDate = (dt: Date) => {
|
||||
return (
|
||||
("0" + dt.getDay()).substr(-2) +
|
||||
"." +
|
||||
("0" + (0 + dt.getMonth())).substr(-2) +
|
||||
"." +
|
||||
dt.getFullYear() +
|
||||
" в " +
|
||||
("0" + dt.getHours()).substr(-2) +
|
||||
":" +
|
||||
("0" + dt.getMinutes()).substr(-2)
|
||||
);
|
||||
};
|
||||
|
||||
const attendanceToString = (attt: EventAttendanceTypeV) => {
|
||||
switch (attt) {
|
||||
case "NO":
|
||||
return "отказался посетить";
|
||||
case "YES":
|
||||
return "посетил";
|
||||
case "YES_ONLINE":
|
||||
return "посетил онлайн";
|
||||
}
|
||||
};
|
||||
---
|
||||
|
||||
<Layout title="Прошедшие события">
|
||||
<main>
|
||||
<Navbar is_user_admin={user.is_admin} user={user} />
|
||||
<div class="container">
|
||||
{ pastEvents.length > 0 ?
|
||||
pastEvents.map((event, idx) => (
|
||||
<div class:list={[idx === 0 ? "mt-4" : ""]}>
|
||||
<div class="alert alert-event" role="alert">
|
||||
<h4 class="alert-heading">{event.title}</h4>
|
||||
<h6>{formatDate(event.when)} · {event.attendants.length} посетителей</h6>
|
||||
<p>{event.description}</p>
|
||||
<hr />
|
||||
<div class="d-flex gap-3 ">
|
||||
{event.attendants.findIndex(el => el.usersId === user.id) >= 0 ? (
|
||||
<i>Я <b>{ attendanceToString(event.attendants.find(el => el.usersId === user.id)!.type) }</b> на данное событие</i>
|
||||
) : (
|
||||
<i>Я <b>не посещал</b> на данное событие</i>
|
||||
)}
|
||||
</div>
|
||||
<div class="d-flex gap-3 mt-2">
|
||||
{user.is_admin || user.is_moderator ? (
|
||||
<span class="flex-grow-1"></span>
|
||||
<button class="btn btn-sm btn-danger" onclick={`deleteEvent(${event.id})`}>
|
||||
Удалить
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)) :
|
||||
<div class="mt-2">
|
||||
Прошедшие события отсутствуют
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
{user.is_admin || user.is_moderator ?
|
||||
<script is:inline>
|
||||
async function deleteArticle(articleId) {
|
||||
try {
|
||||
const resp = await fetch(`/articles/delete?id=${articleId}`, {
|
||||
method: "POST"
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (json.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
throw new Error(json.reason)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function createNewEvent(formData) {
|
||||
try {
|
||||
const resp = await fetch(`/events/create`, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (json.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
throw new Error(json.reason)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteEvent(eventId) {
|
||||
try {
|
||||
const resp = await fetch(`/events/delete?id=${eventId}`, {
|
||||
method: "POST"
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (json.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
throw new Error(json.reason)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
}
|
||||
</script> : null}
|
||||
|
||||
<style>
|
||||
.alert-event {
|
||||
--bs-alert-color: hsl(250 61% 21% / 1);
|
||||
--bs-alert-bg: hsl(250 70% 91% / 1);
|
||||
--bs-alert-border-color: hsl(250 71% 81% / 1);
|
||||
--bs-alert-link-color: hsl(250 61% 21% / 1);
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue