From 1175ccff74c3e91810296167709c6b89d987524c Mon Sep 17 00:00:00 2001 From: Artem VV Date: Tue, 20 Jun 2023 23:36:01 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D0=B0=20?= =?UTF-8?q?=D1=81=D0=BE=D0=B1=D1=8B=D1=82=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prisma/schema.prisma | 29 ++++ src/components/NewsBlock.astro | 269 ++++++++++++++++++++++++++------- src/db.ts | 91 ++++++++++- src/pages/events/attend.ts | 34 +++++ src/pages/events/create.ts | 39 +++++ src/pages/events/delete.ts | 36 +++++ src/pages/past-events.astro | 147 ++++++++++++++++++ 7 files changed, 587 insertions(+), 58 deletions(-) create mode 100644 src/pages/events/attend.ts create mode 100644 src/pages/events/create.ts create mode 100644 src/pages/events/delete.ts create mode 100644 src/pages/past-events.astro 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) => (
- ) : null} + ) :
+ Уведомления отсутствуют +
}
- )) + )) : +
+ Запланированные события отсутствуют +
} - { alerts.length > 0 ?
: null } +
+
+

События

+ {user.is_admin || user.is_moderator ? ( + + ) : null} + history +
+ { currentEvents.length > 0 ? + currentEvents.map((event, idx) => ( +
+ +
+ )) : +
+ Запланированные события отсутствуют +
+ } +
+
+

Новости

+ {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) => ( +
+ +
+ )) : +
+ Прошедшие события отсутствуют +
+ } +
+
+
+ +{user.is_admin || user.is_moderator ? + : null} + +