Compare commits
1 Commits
main
...
fc9c3d025e
| Author | SHA1 | Date | |
|---|---|---|---|
| fc9c3d025e |
4
.gitignore
vendored
@@ -501,7 +501,3 @@ src/Otchinslator/appsettings.Development.json
|
||||
src/.idea/.idea.Otchinslator/.idea/
|
||||
|
||||
src/.vscode/
|
||||
|
||||
src/Otchinslator/wwwroot/img/memes/
|
||||
|
||||
src/Otchinslator/wwwroot/img/notifications/
|
||||
|
||||
@@ -2,13 +2,6 @@
|
||||
|
||||
Веб приложение отчислятора 3000
|
||||
|
||||
# TODO
|
||||
|
||||
- [ ] Сделать обновление валидации от radiobutton
|
||||
- [ ] Добавить направления других институтов
|
||||
- [ ] Найти шаблоны других институтов и написать выбор шаблонов
|
||||
- [x] Рефактор кода
|
||||
|
||||
# Лицензия
|
||||
|
||||
```
|
||||
|
||||
@@ -6,9 +6,6 @@ services:
|
||||
- "8025:8080"
|
||||
volumes:
|
||||
- /srv/otchislator/pdfs:/app/PDFCache
|
||||
- /srv/otchislator/data:/app/data
|
||||
- /srv/otchislator/memes:/app/wwwroot/img/memes
|
||||
- /srv/otchislator/notifications:/app/wwwroot/img/notifications
|
||||
environment:
|
||||
- BOT_TOKEN=123:ABC
|
||||
- ChatId=
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<base href="/"/>
|
||||
<meta property="og:image" content="/favicon.png" />
|
||||
@* TODO: скачать шрифты *@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/css/styles.css" asp-append-version="true" />
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
@page "/Achievements"
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<PageTitle>Зал славы</PageTitle>
|
||||
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: #111;
|
||||
overflow: hidden;
|
||||
overflow-y: hidden;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.carousel-container {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
transform: rotate(20deg);
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.carousel {
|
||||
display: flex;
|
||||
width: max-content;
|
||||
flex-wrap: nowrap;
|
||||
animation: scroll 15s linear infinite;
|
||||
}
|
||||
|
||||
.carousel.reverse {
|
||||
animation: scroll-reverse 15s linear infinite;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 213px;
|
||||
height: 300px;
|
||||
background: #222;
|
||||
margin: 0 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.card img {
|
||||
width: 210px;
|
||||
height: 297px;
|
||||
object-fit: cover;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
@@keyframes scroll {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-10%);
|
||||
}
|
||||
}
|
||||
|
||||
@@keyframes scroll-reverse {
|
||||
0% {
|
||||
transform: translateX(-10%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.hall-of-fame {
|
||||
z-index: 9999;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
font-size: 3vw;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
background-color: #444;
|
||||
border-radius: 9999px;
|
||||
padding: 2px 15px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
z-index: 9999;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 20px;
|
||||
font-size: 2vw;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background-color: #444;
|
||||
border-radius: 9999px;
|
||||
padding: 4px 20px;
|
||||
}
|
||||
|
||||
/* Адаптивность для маленьких экранов */
|
||||
@@media (max-width: 600px) {
|
||||
.hall-of-fame {
|
||||
font-size: 5vw;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
font-size: 4vw;
|
||||
padding: 6px 25px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Адаптивность для средних экранов */
|
||||
@@media (min-width: 601px) and (max-width: 1024px) {
|
||||
.hall-of-fame {
|
||||
font-size: 4vw;
|
||||
padding: 4px 18px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
font-size: 3vw;
|
||||
padding: 5px 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.back-button:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="hall-of-fame">Зал славы</div>
|
||||
<a class="back-button" href="/">Назад</a>
|
||||
|
||||
<div class="carousel-container">
|
||||
<div class="carousel">
|
||||
<div class="card"><img src="/img/notifications/1.png" alt="Image 1"></div>
|
||||
<div class="card"><img src="/img/notifications/2.png" alt="Image 2"></div>
|
||||
<div class="card"><img src="/img/notifications/3.png" alt="Image 3"></div>
|
||||
<div class="card"><img src="/img/notifications/4.png" alt="Image 4"></div>
|
||||
<div class="card"><img src="/img/notifications/5.png" alt="Image 5"></div>
|
||||
<div class="card"><img src="/img/notifications/6.png" alt="Image 6"></div>
|
||||
<div class="card"><img src="/img/notifications/7.png" alt="Image 7"></div>
|
||||
<div class="card"><img src="/img/notifications/8.png" alt="Image 8"></div>
|
||||
<div class="card"><img src="/img/notifications/8.png" alt="Image 9"></div>
|
||||
<div class="card"><img src="/img/notifications/9.png" alt="Image 10"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-container">
|
||||
<div class="carousel reverse">
|
||||
<div class="card"><img src="/img/notifications/10.png" alt="Image 1"></div>
|
||||
<div class="card"><img src="/img/notifications/11.png" alt="Image 2"></div>
|
||||
<div class="card"><img src="/img/notifications/12.png" alt="Image 3"></div>
|
||||
<div class="card"><img src="/img/notifications/13.png" alt="Image 4"></div>
|
||||
<div class="card"><img src="/img/notifications/14.png" alt="Image 5"></div>
|
||||
<div class="card"><img src="/img/notifications/15.png" alt="Image 6"></div>
|
||||
<div class="card"><img src="/img/notifications/16.png" alt="Image 7"></div>
|
||||
<div class="card"><img src="/img/notifications/17.png" alt="Image 8"></div>
|
||||
<div class="card"><img src="/img/notifications/18.png" alt="Image 9"></div>
|
||||
<div class="card"><img src="/img/notifications/19.png" alt="Image 10"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-container">
|
||||
<div class="carousel">
|
||||
<div class="card"><img src="/img/notifications/20.png" alt="Image 1"></div>
|
||||
<div class="card"><img src="/img/notifications/21.png" alt="Image 2"></div>
|
||||
<div class="card"><img src="/img/notifications/22.png" alt="Image 3"></div>
|
||||
<div class="card"><img src="/img/notifications/23.png" alt="Image 4"></div>
|
||||
<div class="card"><img src="/img/notifications/24.png" alt="Image 5"></div>
|
||||
<div class="card"><img src="/img/notifications/25.png" alt="Image 6"></div>
|
||||
<div class="card"><img src="/img/notifications/26.png" alt="Image 7"></div>
|
||||
<div class="card"><img src="/img/notifications/27.png" alt="Image 8"></div>
|
||||
<div class="card"><img src="/img/notifications/28.png" alt="Image 9"></div>
|
||||
<div class="card"><img src="/img/notifications/29.png" alt="Image 10"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +0,0 @@
|
||||
export class Achievements {
|
||||
|
||||
}
|
||||
|
||||
window.Achievements = Achievements;
|
||||
@@ -8,18 +8,6 @@
|
||||
<PageTitle>Институт | Отчислятор 3000</PageTitle>
|
||||
<PageScript Src="./Components/Pages/ChooseInstitut.razor.js"/>
|
||||
|
||||
<dialog id="notready_modal" class="modal modal-bottom sm:modal-middle">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold">Сервис отчислений</h3>
|
||||
<p class="py-4">Для этого института ещё не был добавлен шаблон заявления, если Вы знаете где его найти и кому направлять заявления, свяжитесь с разработчиком<br><br>ТГ: <a target="_blank" href="https://t.me/serega404">@@serega404</a></p>
|
||||
<div class="modal-action">
|
||||
<form method="dialog">
|
||||
<button class="btn">Закрыть</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<div class="relative w-96 sm:w-[32rem]">
|
||||
<div
|
||||
class="text-center font-bold text-4xl md:text-5xl w-max absolute left-1/2 -top-1/4 transform -translate-x-1/2 italic">
|
||||
@@ -29,19 +17,19 @@
|
||||
<div class="card rounded-badge bg-base-200 p-4">
|
||||
<div class="grid grid-cols-2 gap-4 p-4">
|
||||
<button id="1" class="institut-button btn h-auto card bg-white p-4 select-none">
|
||||
<img src="img/iktib.png" alt="ИКТИБ" class="w-full h-32 object-contain rounded-md" draggable="false">
|
||||
<img src="img/iktib.jpg" alt="ИКТИБ" class="w-full h-32 object-contain rounded-md" draggable="false">
|
||||
<p class="text-center mt-2">ИКТИБ</p>
|
||||
</button>
|
||||
<button id="2" onclick="notready_modal.showModal()" class="dis-institut-button btn h-auto card bg-white p-4 select-none">
|
||||
<img src="img/irtsu.png" alt="ИРТСУ" class="w-full h-32 object-contain rounded-md" draggable="false">
|
||||
<button id="2" class="institut-button btn h-auto card bg-white p-4 select-none">
|
||||
<img src="img/irtsu.jpg" alt="ИРТСУ" class="w-full h-32 object-contain rounded-md" draggable="false">
|
||||
<p class="text-center mt-2">ИРТСУ</p>
|
||||
</button>
|
||||
<button id="3" onclick="notready_modal.showModal()" class="dis-institut-button btn h-auto card bg-white p-4 select-none">
|
||||
<img src="img/inep.png" alt="ИНЭП" class="w-full h-32 object-contain rounded-md" draggable="false">
|
||||
<button id="3" class="institut-button btn h-auto card bg-white p-4 select-none">
|
||||
<img src="img/inep.jpg" alt="ИНЭП" class="w-full h-32 object-contain rounded-md" draggable="false">
|
||||
<p class="text-center mt-2">ИНЭП</p>
|
||||
</button>
|
||||
<button id="4" onclick="notready_modal.showModal()" class="dis-institut-button btn h-auto card bg-white p-4 select-none">
|
||||
<img src="img/iues.png" alt="ИУЭС" class="w-full h-32 object-contain rounded-md" draggable="false">
|
||||
<button id="4" class="institut-button btn h-auto card bg-white p-4 select-none">
|
||||
<img src="img/iues.jpg" alt="ИУЭС" class="w-full h-32 object-contain rounded-md" draggable="false">
|
||||
<p class="text-center mt-2">ИУЭС</p>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
@page "/fstaprl"
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using NPetrovich
|
||||
@using Otchinslator.Services
|
||||
@attribute [Authorize]
|
||||
@inject IStatementGenerator StatementGenerator
|
||||
|
||||
<PageTitle>Заяление отправлено | 1 апреля</PageTitle>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100dvh;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
var userEmail = authState.User.Identity.Name;
|
||||
var userFIO = authState.User.Claims.FirstOrDefault(x => x.Type == "name")?.Value;
|
||||
|
||||
var petrovich = new Petrovich()
|
||||
{
|
||||
FirstName = userFIO.Split(' ')[0],
|
||||
LastName = userFIO.Split(' ')[1],
|
||||
MiddleName = userFIO.Split(' ').Length > 2 ? userFIO.Split(' ')[2] : "",
|
||||
Gender = Gender.Male
|
||||
};
|
||||
|
||||
userFIO = petrovich.InflectFirstNameTo(Case.Genitive) + " " + petrovich.InflectLastNameTo(Case.Genitive) + " " + petrovich.InflectMiddleNameTo(Case.Genitive);
|
||||
|
||||
var userData = new UserData
|
||||
{
|
||||
reason = "Прошу отчислить меня по собственному желанию в связи с не желанием продолжать обучение в данном учебном заведении, и планами перевестись в ДГТУ.",
|
||||
email = userEmail,
|
||||
phone = "",
|
||||
fio = userFIO,
|
||||
kurs = 1,
|
||||
isFreeEducation = true,
|
||||
isOchno = true,
|
||||
speciality = SpecialityType.Bakalavriat,
|
||||
direction = ""
|
||||
};
|
||||
var statement = await StatementGenerator.GenerateStatementAsync(userData, "frstaprl.docx");
|
||||
var pdf = await StatementGenerator.ConvertToPDFAsync(statement);
|
||||
|
||||
await using var fileStream = new FileStream("./PDFCache/" + userEmail.Split('@')[0] + ".pdf", FileMode.Create, FileAccess.Write);
|
||||
await pdf.CopyToAsync(fileStream);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
<dialog id="denyModal" class="modal modal-bottom sm:modal-middle">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold text-center">С 1 апреля!</h3>
|
||||
<img src="https://risovach.ru/upload/2013/04/mem/moe-lico_15228723_orig_.jpeg" class="h-80 my-2 mx-auto"
|
||||
alt=""/>
|
||||
<div class="modal-action justify-center">
|
||||
<form method="dialog">
|
||||
<button class="btn btn-primary">Понял, принял</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<div class="relative flex flex-col items-center justify-center">
|
||||
<div class="text-center font-bold text-3xl md:text-5xl w-max italic mb-3">
|
||||
<br>Заявление на отчисление<p class="text-2xl font-normal">отправлено директору</p>
|
||||
</div>
|
||||
<div id="pdfrenderer" class="flex justify-center">
|
||||
</div>
|
||||
<div class="join w-96 sm:w-[27rem] mt-4 flex gap-2 justify-center">
|
||||
<button onclick="denyModal.showModal()" class="btn btn-error rounded-full flex-grow w-30 flex items-center justify-center">
|
||||
Отозвать
|
||||
</button>
|
||||
<a id="downloadPDF" target="_blank" href="/getStatement"
|
||||
class="btn btn-primary bg-base-200 border-base-200 rounded-full flex-grow-0 w-[3rem] flex items-center justify-center relative">
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<img class="p-2" src="img/pdf.svg" alt=""/>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"></script>
|
||||
<script>
|
||||
const pdfrenderer = document.getElementById('pdfrenderer');
|
||||
const url = '/getStatement';
|
||||
pdfrenderer.innerHTML = '<canvas id="pdf-canvas" class="w-96 sm:w-[27rem] rounded-2xl justify-center"></canvas>';
|
||||
pdfjsLib.getDocument(url).promise.then(function (pdf) {
|
||||
pdf.getPage(1).then(function (page) {
|
||||
const viewport = page.getViewport({scale: 1});
|
||||
|
||||
const canvas = document.getElementById('pdf-canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
canvas.width = viewport.width;
|
||||
canvas.height = viewport.height;
|
||||
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport
|
||||
};
|
||||
page.render(renderContext);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -1,21 +1,18 @@
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@page "/"
|
||||
@attribute [DiscoverCollocatedJS]
|
||||
|
||||
<PageTitle>Отчислятор 3000</PageTitle>
|
||||
|
||||
<JS For="this"
|
||||
Args="[AuthenticationStateProvider.GetAuthenticationStateAsync().Result.User.Identity?.IsAuthenticated]"/>
|
||||
<JS For="this" Args="[AuthenticationStateProvider.GetAuthenticationStateAsync().Result.User.Identity?.IsAuthenticated]" />
|
||||
|
||||
<div class="relative">
|
||||
@* TODO: Дописать модальное окно *@
|
||||
<dialog id="info_modal" class="modal modal-bottom sm:modal-middle">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold">Сервис отчислений</h3>
|
||||
<p class="py-4">Привет, это НЕОФИЦИАЛЬНЫЙ сервис отчислений!<br><br>Авторизуясь Вы соглашаетесь передать
|
||||
личные данные, такие как email + фио (из авторизации ЮФУ), и все остальные запрошенные далее. На нашем
|
||||
сервере хранятся только Ваши сгенерированные pdf заявления, другая информация сохраняется только в
|
||||
браузере.<br><br>Автор: <a target="_blank" href="https://t.me/serega404">@@serega404</a></p>
|
||||
<p class="py-4">Привет, это сервис отчислений!<br>Автор: <a target="_blank" href="https://t.me/serega404">@@serega404</a></p>
|
||||
<div class="modal-action">
|
||||
<form method="dialog">
|
||||
<button class="btn">Закрыть</button>
|
||||
@@ -23,31 +20,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
<dialog id="meme_modal" class="modal modal-middle sm:modal-middle">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold text-center">Мемная поддержка</h3>
|
||||
<img id="memeImg" src="/img/memes/1.jpg" alt="meme" class="h-96 mt-2 mx-auto rounded-lg">
|
||||
<div class="flex items-center mt-4">
|
||||
<span class="text-gray-400 text-sm">Может содержаться ненормативная лексика!</span>
|
||||
<div class="modal-action mt-0 ml-auto mr-0">
|
||||
<form method="dialog">
|
||||
<button class="btn btn-error">Закрыть</button>
|
||||
</form>
|
||||
<button id="nextmeme" class="btn btn-success">Дальше</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
<div
|
||||
class="text-center font-bold text-4xl md:text-5xl w-max absolute left-1/2 -top-1/2 transform -translate-x-1/2 italic">
|
||||
<br>Отчислятор 3000<br>
|
||||
</div>
|
||||
@* TODO: Исправить расположение названия *@
|
||||
<div class="text-center font-bold text-4xl md:text-5xl w-max absolute left-1/2 -top-1/3 transform -translate-x-1/2 italic"><br>Отчислятор 3000<br><p class="text-2xl text-red-500">alpha</p></div>
|
||||
<div class="flex flex-col space-y-4 w-96">
|
||||
<div class="card rounded-badge bg-base-200 p-4">
|
||||
@* <h2 class="card-title text-center text-3xl justify-center my-4">Мне нужно...</h2> *@
|
||||
<h2 class="card-title text-center text-3xl justify-center my-4">Мне нужно...</h2>
|
||||
<div class="flex flex-col space-y-4 mt-1">
|
||||
<button id="otchislenie"
|
||||
class="action-button btn h-16 btn-primary bg-green-300 hover:bg-green-400 border-none rounded-full text-2xl relative">
|
||||
<button id="otchislenie" class="action-button btn h-16 btn-primary rounded-full text-2xl relative">
|
||||
Отчислиться
|
||||
<div class="absolute bg-base-200 rounded-full right-1 w-14 h-14">
|
||||
@{
|
||||
@@ -62,45 +41,40 @@
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
<div class="divider" style="margin-top: 0.25rem;"></div>
|
||||
<a href="/Achievements" class="btn h-12 btn-primary rounded-full text-xl relative"
|
||||
style="margin-top: 0.25rem;">
|
||||
Наши достижения
|
||||
<div class="absolute bg-base-200 rounded-full right-1 w-10 h-10">
|
||||
<button id="downgrade" class="btn h-16 btn-primary rounded-full text-2xl relative">
|
||||
Понизить курс
|
||||
<div class="absolute bg-base-200 rounded-full right-1 w-14 h-14">
|
||||
<img class="p-2" src="img/down.svg" alt=""/>
|
||||
</div>
|
||||
</a>
|
||||
<button onclick="meme_modal.showModal()" class="btn h-12 btn-primary rounded-full text-xl relative">
|
||||
Мемная помощь
|
||||
<div class="absolute bg-base-200 rounded-full right-1 w-10 h-10">
|
||||
<i class="absolute text-sm bottom-0 text-base-200 font-medium">временно недоступно</i>
|
||||
</button>
|
||||
<button id="akadem" class="btn h-16 btn-primary p-0 rounded-full text-2xl relative">
|
||||
Уйти в академ
|
||||
<div class="absolute bg-base-200 rounded-full right-1 w-14 h-14">
|
||||
<img class="p-2" src="img/akadem.svg" alt=""/>
|
||||
</div>
|
||||
<i class="absolute text-sm bottom-0 text-base-200 font-medium">временно недоступно</i>
|
||||
</button>
|
||||
<div class="divider" style="margin-top: 0.25rem;"></div>
|
||||
<div class="divider"></div>
|
||||
<CascadingAuthenticationState>
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<a href="MicrosoftIdentity/Account/SignOut"
|
||||
class="btn btn-md btn-primary rounded-full text-2xl relative"
|
||||
style="margin-top: 0.25rem;">
|
||||
<a href="MicrosoftIdentity/Account/SignOut" class="btn btn-lg btn-primary rounded-full text-2xl relative">
|
||||
@context.User.Identity?.Name!.Split("@")[0]
|
||||
<div class="absolute bg-base-200 rounded-full right-1 w-10 h-10">
|
||||
<div class="absolute bg-base-200 rounded-full right-1 w-14 h-14">
|
||||
<img class="p-3" src="img/exit.svg" alt=""/>
|
||||
</div>
|
||||
</a>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<a class="btn btn-md btn-primary rounded-full text-base"
|
||||
href="MicrosoftIdentity/Account/SignIn">Авторизоваться</a>
|
||||
<a class="btn btn-lg bg-green-300 rounded-full text-2xl" href="MicrosoftIdentity/Account/SignIn">Начать</a>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
</CascadingAuthenticationState>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button onclick="info_modal.showModal()" class="btn btn-sm btn-primary bg-base-200 rounded-full text-1xl">
|
||||
?
|
||||
</button>
|
||||
<button onclick="info_modal.showModal()" class="btn btn-sm btn-primary bg-base-200 rounded-full text-1xl">?</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,6 +1,7 @@
|
||||
export default class extends BlazorJSComponents.Component {
|
||||
setParameters(IsAuthenticated) {
|
||||
const button = document.querySelector('.action-button');
|
||||
const buttons = document.querySelectorAll('.action-button');
|
||||
buttons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
localStorage.setItem('action', button.id);
|
||||
if (IsAuthenticated) {
|
||||
@@ -10,15 +11,7 @@ export default class extends BlazorJSComponents.Component {
|
||||
}
|
||||
}
|
||||
)
|
||||
const nextMemeButton = document.getElementById('nextmeme');
|
||||
const nextMemeImg = document.getElementById('memeImg');
|
||||
var random = Math.floor(Math.random() * 100) + 1;
|
||||
nextMemeImg.src = "/img/memes/" + random + ".jpg";
|
||||
|
||||
nextMemeButton.addEventListener('click', () => {
|
||||
random = Math.floor(Math.random() * 100) + 1;
|
||||
nextMemeImg.src = "/img/memes/" + random + ".jpg";
|
||||
})
|
||||
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
@@ -6,39 +6,34 @@
|
||||
|
||||
<PageTitle>Уже почти | Отчислятор 3000</PageTitle>
|
||||
|
||||
<dialog id="printModal" class="modal modal-bottom sm:modal-middle">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold">Есть проблема...</h3>
|
||||
<p class="py-4 font-bold text-center">К сожалению отправить или распечатать заявление нельзя.</p>
|
||||
<img src="/img/write.png" class="h-80 my-4 mx-auto" alt=""/>
|
||||
<p class="text-center">Вам нужно написать его от руки, сфотографировать и отправить замдиректора по учебной работе на почту.</p>
|
||||
<div class="modal-action justify-center">
|
||||
<form method="dialog">
|
||||
<button class="btn btn-primary">Ок</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
@* <dialog id="SendToDirectorModal" class="modal modal-bottom sm:modal-middle"> *@
|
||||
@* <div class="modal-box"> *@
|
||||
@* <h3 class="text-lg font-bold">Отправка письма</h3> *@
|
||||
@* <p class="py-4">Вы уверены что хотите отправить ваше заявление директору?<br>Отменить отправку невозможно!</p> *@
|
||||
@* <div class="modal-action"> *@
|
||||
@* <form method="dialog"> *@
|
||||
@* <button class="btn btn-primary">Нет</button> *@
|
||||
@* <a href="/otchislenie/congratulation" class="btn btn-error">Да</a> *@
|
||||
@* </form> *@
|
||||
@* </div> *@
|
||||
@* </div> *@
|
||||
@* </dialog> *@
|
||||
|
||||
<div class="relative">
|
||||
<div
|
||||
class="text-center font-bold text-4xl md:text-5xl w-max absolute left-1/2 -top-1/4 transform -translate-x-1/2 italic">
|
||||
<br>Заявление готово
|
||||
</div>
|
||||
<div id="pdfrenderer">
|
||||
<object data="/getStatement" type="application/pdf" class="w-96 sm:w-[32rem] h-[30rem] rounded-2xl text-center">
|
||||
<p>Не удалось отобразить заявление</p>
|
||||
<button id="alterRender" class="btn btn-primary my-2">использовать альтернативную отрисовку</button>
|
||||
<p>или откойте в браузере на основе Firefox</p>
|
||||
<object data="/getStatement" type="application/pdf" class="w-96 sm:w-[32rem] h-[30rem] rounded-2xl">
|
||||
<p class="text-center">Не удалось отобразить заявление, попробуйте скачать <a href="/getStatement">(тык) или открыть в браузере на основе Firefox</a></p>
|
||||
</object>
|
||||
</div>
|
||||
<div class="join w-full mt-4 flex gap-2">
|
||||
<a href="/" class="relative btn btn-primary rounded-full flex-grow-0 w-[3rem] h-[3rem]">
|
||||
<img class="absolute p-3" src="img/exit.svg" alt=""/>
|
||||
</a>
|
||||
<button onclick="printModal.showModal()" class="btn btn-primary rounded-full flex-grow w-30">
|
||||
<a href="/otchislenie/congratulation" class="btn btn-primary rounded-full flex-grow w-30">
|
||||
Печать
|
||||
</button>
|
||||
</a>
|
||||
<a id="downloadPDF" target="_blank" href="/getStatement"
|
||||
class="btn btn-primary bg-base-200 border-base-200 rounded-full flex-grow-0 w-[3rem]">
|
||||
@* Скачать PDF *@
|
||||
@@ -48,31 +43,3 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"></script>
|
||||
<script>
|
||||
const alterRenderButton = document.getElementById('alterRender');
|
||||
alterRenderButton.addEventListener('click', alterRender);
|
||||
|
||||
function alterRender() {
|
||||
const pdfrenderer = document.getElementById('pdfrenderer');
|
||||
const url = '/getStatement';
|
||||
pdfrenderer.innerHTML = '<canvas id="pdf-canvas" class="w-96 rounded-2xl"></canvas>';
|
||||
pdfjsLib.getDocument(url).promise.then(function (pdf) {
|
||||
pdf.getPage(1).then(function (page) {
|
||||
const viewport = page.getViewport({scale: 1});
|
||||
|
||||
const canvas = document.getElementById('pdf-canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
canvas.width = viewport.width;
|
||||
canvas.height = viewport.height;
|
||||
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport
|
||||
};
|
||||
page.render(renderContext);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -9,10 +9,6 @@ public class DatabaseContext : DbContext
|
||||
public DbSet<Specialty> Specialties { get; set; }
|
||||
public string DbPath { get; }
|
||||
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Institut>().HasKey(i => i.Id);
|
||||
@@ -38,13 +34,20 @@ public class DatabaseContext : DbContext
|
||||
new Specialty() { Id = 7, Name = "Применение и эксплуатация автоматизированных систем специального назначения", Code = "09.05.01", InstitutId = 1 },
|
||||
new Specialty() { Id = 8, Name = "Информационная безопасность телекоммуникационных систем", Code = "10.05.02", InstitutId = 1 },
|
||||
new Specialty() { Id = 9, Name = "Информационная безопасность автоматизированных систем", Code = "10.05.03", InstitutId = 1 },
|
||||
new Specialty() { Id = 10, Name = "Безопасность информационных технологий в правоохранительной сфере", Code = "10.05.05", InstitutId = 1 },
|
||||
// ИРТСУ
|
||||
new Specialty() { Id = 11, Name = "НЕ ЗАПОЛНЕНО", Code = "0.0.0", InstitutId = 2 },
|
||||
// ИУЭС
|
||||
new Specialty() { Id = 12, Name = "НЕ ЗАПОЛНЕНО", Code = "0.0.0", InstitutId = 3 },
|
||||
// ИНЭП
|
||||
new Specialty() { Id = 13, Name = "НЕ ЗАПОЛНЕНО", Code = "0.0.0", InstitutId = 4 }
|
||||
new Specialty() { Id = 10, Name = "Безопасность информационных технологий в правоохранительной сфере", Code = "10.05.05", InstitutId = 1 }
|
||||
);
|
||||
}
|
||||
|
||||
public DatabaseContext()
|
||||
{
|
||||
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
var folder = Path.Join(baseDirectory, "Data");
|
||||
if (!Directory.Exists(folder))
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
DbPath = Path.Join(folder, "database.db");
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
=> options.UseSqlite($"Data Source={DbPath}");
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0.2-alpine3.21 AS base
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0.1-alpine3.21 AS base
|
||||
USER root
|
||||
RUN apk update && apk add --no-cache curl icu tzdata musl-locales musl-locales-lang
|
||||
USER $APP_UID
|
||||
|
||||
@@ -28,15 +28,17 @@
|
||||
<PackageReference Include="BlazorJSComponents" Version="1.0.0" />
|
||||
<PackageReference Include="BlazorPageScript"
|
||||
Version="1.0.0" />
|
||||
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
|
||||
<PackageReference Include="Gotenberg.Sharp.API.Client" Version="2.5.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2">
|
||||
<PackageReference Include="DocumentFormat.OpenXml"
|
||||
Version="3.2.0" />
|
||||
<PackageReference Include="Gotenberg.Sharp.API.Client"
|
||||
Version="2.4.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="3.8.0" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.UI" Version="3.8.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="3.6.0" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.UI" Version="3.6.0" />
|
||||
<PackageReference Include="NPetrovich"
|
||||
Version="2.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -24,22 +24,19 @@ builder.Services.AddControllersWithViews(options =>
|
||||
}).AddMicrosoftIdentityUI().AddDataAnnotationsLocalization();
|
||||
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
{
|
||||
options.KnownNetworks.Clear();
|
||||
options.KnownProxies.Clear();
|
||||
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||
});
|
||||
|
||||
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
var folder = Path.Join(baseDirectory, "data");
|
||||
if (!Directory.Exists(folder))
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
folder = Path.Join(folder, "database.db");
|
||||
builder.Services.AddDbContext<DatabaseContext>(options => options.UseSqlite($"Data Source={folder}"));
|
||||
builder.Services.AddDbContext<DatabaseContext>();
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents();
|
||||
builder.Services.AddJSComponents();
|
||||
|
||||
builder.Services.AddRazorPages(); //////////////
|
||||
builder.Services.AddServerSideBlazor(); //////////////
|
||||
|
||||
builder.Services.AddOptions<GotenbergSharpClientOptions>()
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Otchinslator.Services;
|
||||
|
||||
public interface IStatementGenerator
|
||||
{
|
||||
public Task<MemoryStream> GenerateStatementAsync(UserData userData, string TemplateName = "ictis.docx");
|
||||
public Task<MemoryStream> GenerateStatementAsync(UserData userData);
|
||||
public Task<Stream> ConvertToPDFAsync(MemoryStream stream);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,9 @@ public class StatementGenerator(GotenbergSharpClient gotenbergSharpClient) : ISt
|
||||
private const string FreeEducationText = "за счет ассигнований федерального бюджета";
|
||||
private const string PaidEducationText = "на договорной (платной) основе";
|
||||
|
||||
// TODO: Выбор темплейтов не реализован
|
||||
public async Task<MemoryStream> GenerateStatementAsync(UserData userData, string TemplateName = "ictis.docx")
|
||||
public async Task<MemoryStream> GenerateStatementAsync(UserData userData)
|
||||
{
|
||||
byte[] textByteArray = File.ReadAllBytes("Templates/" + TemplateName);
|
||||
byte[] textByteArray = File.ReadAllBytes("Templates/ictis.docx");
|
||||
MemoryStream stream = new MemoryStream();
|
||||
stream.Write(textByteArray, 0, textByteArray.Length);
|
||||
using (WordprocessingDocument doc = WordprocessingDocument.Open(stream, true))
|
||||
|
||||
BIN
src/Otchinslator/wwwroot/img/iktib.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 61 KiB |
BIN
src/Otchinslator/wwwroot/img/inep.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 348 KiB |
BIN
src/Otchinslator/wwwroot/img/irtsu.jpg
Normal file
|
After Width: | Height: | Size: 341 KiB |
|
Before Width: | Height: | Size: 778 KiB |
BIN
src/Otchinslator/wwwroot/img/iues.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 152 KiB |
6
src/docker-compose.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
services:
|
||||
otchinslator:
|
||||
image: otchinslator
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Otchinslator/Dockerfile
|
||||