diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index dc752f4..4af5abc 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -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 {
diff --git a/src/components/NewsBlock.astro b/src/components/NewsBlock.astro
index 3831bfc..8bcf3af 100644
--- a/src/components/NewsBlock.astro
+++ b/src/components/NewsBlock.astro
@@ -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 "пойду онлайн"
+ }
+}
---
+
+
+
+
- {user.is_admin || user.is_moderator ? (
-
- Новая статья
-
-
- ) : null}
- {
+
+
Уведомления
+ {user.is_admin || user.is_moderator ? (
+
add
+ ) : null}
+
+ { alerts.length > 0 ?
alerts.map((article, idx) => (
@@ -45,12 +82,72 @@ const formatDate = (dt: Date) => {
Редактировать
- ) : null}
+ ) :
+ Уведомления отсутствуют
+
}
- ))
+ )) :
+
+ Запланированные события отсутствуют
+
}
- { alerts.length > 0 ?
: null }
+
+
+
События
+ {user.is_admin || user.is_moderator ? (
+
+ ) : null}
+
history
+
+ { currentEvents.length > 0 ?
+ currentEvents.map((event, idx) => (
+
+
+
{event.title}
+
{formatDate(event.when)} · {event.attendants.length} посетителей
+
{event.description}
+
+
+ {event.attendants.findIndex(el => el.usersId === user.id) === -1 ? (
+
+
+
+ ) : (
+ Я { attendanceToString(event.attendants.find(el => el.usersId === user.id)!.type) } на данное событие
+
+ )}
+
+
+ {user.is_admin || user.is_moderator ? (
+
+
+ ) : null}
+
+
+
+ )) :
+
+ Запланированные события отсутствуют
+
+ }
+
+
+
Новости
+ {user.is_admin || user.is_moderator ? (
+
add
+ ) : null}
+
{ news.length > 0 ?
news.map((article, idx) => (
@@ -73,63 +170,123 @@ const formatDate = (dt: Date) => {
)) :
-
-
Новостей нет
-
+ Новостей нет
}
{user.is_admin || user.is_moderator ?
: null}
-
+
\ No newline at end of file
diff --git a/src/db.ts b/src/db.ts
index b8ab7fc..ebb8f66 100644
--- a/src/db.ts
+++ b/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;
+ }
+}
diff --git a/src/pages/events/attend.ts b/src/pages/events/attend.ts
new file mode 100644
index 0000000..71c0679
--- /dev/null
+++ b/src/pages/events/attend.ts
@@ -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),
+ };
+}
diff --git a/src/pages/events/create.ts b/src/pages/events/create.ts
new file mode 100644
index 0000000..f59339c
--- /dev/null
+++ b/src/pages/events/create.ts
@@ -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),
+ };
+}
diff --git a/src/pages/events/delete.ts b/src/pages/events/delete.ts
new file mode 100644
index 0000000..9f8b90f
--- /dev/null
+++ b/src/pages/events/delete.ts
@@ -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),
+ };
+}
diff --git a/src/pages/past-events.astro b/src/pages/past-events.astro
new file mode 100644
index 0000000..e09ffd6
--- /dev/null
+++ b/src/pages/past-events.astro
@@ -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 "посетил онлайн";
+ }
+};
+---
+
+
+
+
+
+ { pastEvents.length > 0 ?
+ pastEvents.map((event, idx) => (
+
+
+
{event.title}
+
{formatDate(event.when)} · {event.attendants.length} посетителей
+
{event.description}
+
+
+ {event.attendants.findIndex(el => el.usersId === user.id) >= 0 ? (
+ Я { attendanceToString(event.attendants.find(el => el.usersId === user.id)!.type) } на данное событие
+ ) : (
+ Я не посещал на данное событие
+ )}
+
+
+ {user.is_admin || user.is_moderator ? (
+
+
+ ) : null}
+
+
+
+ )) :
+
+ Прошедшие события отсутствуют
+
+ }
+
+
+
+
+{user.is_admin || user.is_moderator ?
+ : null}
+
+