Correct ts
This commit is contained in:
Dvurechensky 2025-03-02 22:30:02 +03:00
parent b32b4b9198
commit 1bd84ca8d2
33 changed files with 1064 additions and 497 deletions

@ -4,20 +4,47 @@ using AppWeb.Models;
namespace AppWeb.Controllers
{
/// <summary>
/// Ãëàâíûé êîíòðîëëåð
/// </summary>
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
/// <summary>
/// Êîíñòðóêòîð
/// </summary>
/// <param name="logger">Ëîãèðîâàíèå MVC</param>
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
/// <summary>
/// Ãëàâíàÿ
/// </summary>
[HttpGet]
[Route("/")]
public IActionResult Index()
{
return View();
}
/// <summary>
/// Êîíòàêòû
/// </summary>
[HttpGet]
[Route("/contacts")]
public IActionResult Contacts()
{
return View();
}
/// <summary>
/// Îøèáêà
/// </summary>
[HttpGet]
[Route("/error")]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{

@ -20,3 +20,125 @@ html {
body {
margin-bottom: 60px;
}
/*
**************************
START ALERT
**************************
*/
/* Основные стили для контейнера уведомлений */
#notification-container {
position: fixed;
bottom: 20px;
left: 20px;
z-index: 9999;
}
.error_alert_animate {
border: 1px solid #AC0B00;
}
.ok_alert_animate {
border: 1px solid #007126;
}
.warning_alert_animate {
border: 1px solid #A33E00;
}
/* Стили для уведомлений */
.notification {
background-color: #333;
color: white;
border-radius: 8px;
padding: 10px 20px;
margin-bottom: 10px;
font-size: 16px;
max-width: 300px;
opacity: 1;
animation: slideUpAndFade 5s ease-in-out forwards;
}
/* Анимация для подъема уведомления вверх и его исчезновения */
@keyframes slideUpAndFade {
0% {
transform: translateY(0);
opacity: 1;
}
50% {
transform: translateY(-50%); /* Поднимаем уведомление до середины экрана */
opacity: 0.7;
}
100% {
transform: translateY(-100%); /* Двигаем уведомление еще выше */
opacity: 0;
}
}
/*
**************************
END ALERT
**************************
*/
/*
**************************
START ANIMS
**************************
*/
.puff-in-center {
-webkit-animation: puff-in-center 0.1s cubic-bezier(0.470, 0.000, 0.745, 0.715) both;
animation: puff-in-center 0.1s cubic-bezier(0.470, 0.000, 0.745, 0.715) both;
}
/**
* ----------------------------------------
* animation puff-in-center
* ----------------------------------------
*/
@-webkit-keyframes puff-in-center {
0% {
-webkit-transform: scale(2);
transform: scale(2);
-webkit-filter: blur(4px);
filter: blur(4px);
opacity: 0;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
-webkit-filter: blur(0px);
filter: blur(0px);
opacity: 1;
}
}
@keyframes puff-in-center {
0% {
-webkit-transform: scale(2);
transform: scale(2);
-webkit-filter: blur(4px);
filter: blur(4px);
opacity: 0;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
-webkit-filter: blur(0px);
filter: blur(0px);
opacity: 1;
}
}
/*
**************************
END ANIMS
**************************
*/

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=BaseEvents.js.map

@ -0,0 +1 @@
{"version":3,"file":"BaseEvents.js","sourceRoot":"","sources":["../../../TypeScripts/Base/Components/BaseEvents.ts"],"names":[],"mappings":""}

@ -0,0 +1,33 @@
export class Alerts {
AlertShow(notification) {
const container = document.getElementById('notification-container');
if (!container)
return;
const newNotification = document.createElement('div');
newNotification.classList.add('notification');
switch (notification.type) {
case TypeAlert.Error:
newNotification.classList.add('error_alert_animate');
break;
case TypeAlert.Ok:
newNotification.classList.add('ok_alert_animate');
break;
case TypeAlert.Warning:
newNotification.classList.add('warning_alert_animate');
break;
}
newNotification.textContent = notification.text;
container.appendChild(newNotification);
newNotification.classList.add('puff-in-center');
setTimeout(() => {
newNotification.remove();
}, notification.duration * 1000);
}
}
export var TypeAlert;
(function (TypeAlert) {
TypeAlert["Error"] = "error";
TypeAlert["Warning"] = "warning";
TypeAlert["Ok"] = "ok";
})(TypeAlert || (TypeAlert = {}));
//# sourceMappingURL=Alerts.js.map

@ -0,0 +1 @@
{"version":3,"file":"Alerts.js","sourceRoot":"","sources":["../../../../TypeScripts/Base/Services/Alerts/Alerts.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,MAAM;IAIR,SAAS,CAAC,YAA+B;QAC5C,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,wBAAwB,CAAC,CAAC;QACpE,IAAI,CAAC,SAAS;YAAE,OAAO;QAGvB,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACtD,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAE9C,QAAQ,YAAY,CAAC,IAAI,EAAE,CAAC;YACxB,KAAK,SAAS,CAAC,KAAK;gBAChB,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;gBACrD,MAAM;YACV,KAAK,SAAS,CAAC,EAAE;gBACb,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;gBAClD,MAAM;YACV,KAAK,SAAS,CAAC,OAAO;gBAClB,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;gBACvD,MAAM;QACd,CAAC;QAED,eAAe,CAAC,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC;QAGhD,SAAS,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;QACvC,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAGhD,UAAU,CAAC,GAAG,EAAE;YACZ,eAAe,CAAC,MAAM,EAAE,CAAC;QAC7B,CAAC,EAAE,YAAY,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACrC,CAAC;CACJ;AAiBD,MAAM,CAAN,IAAY,SAIX;AAJD,WAAY,SAAS;IACjB,4BAAe,CAAA;IACf,gCAAmB,CAAA;IACnB,sBAAS,CAAA;AACb,CAAC,EAJW,SAAS,KAAT,SAAS,QAIpB"}

@ -0,0 +1,26 @@
export class Cookies {
getCookie(cookieName) {
const name = cookieName + "=";
const decodedCookie = decodeURIComponent(document.cookie);
const ca = decodedCookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === " ") {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
setCookie(name, value, days) {
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
document.cookie = name + "=" + encodeURIComponent(value) + ";expires=" + date.toUTCString() + ";path=/;secure";
}
removeCookie(name) {
document.cookie = name + "=;Max-Age=-99999999;";
}
}
//# sourceMappingURL=Cookies.js.map

@ -0,0 +1 @@
{"version":3,"file":"Cookies.js","sourceRoot":"","sources":["../../../TypeScripts/Base/Services/Cookies.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,OAAO;IAOhB,SAAS,CAAC,UAAkB;QAExB,MAAM,IAAI,GAAQ,UAAU,GAAG,GAAG,CAAC;QAEnC,MAAM,aAAa,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAE1D,MAAM,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAEjC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;YAEd,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAEzB,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACvB,CAAC;YAED,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAExB,OAAO,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;YAC9C,CAAC;QACL,CAAC;QAED,OAAO,EAAE,CAAC;IACd,CAAC;IAOD,SAAS,CAAC,IAAY,EAAE,KAAa,EAAE,IAAY;QAG/C,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAGxB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAG1D,QAAQ,CAAC,MAAM,GAAG,IAAI,GAAG,GAAG,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,gBAAgB,CAAC;IACnH,CAAC;IAMD,YAAY,CAAC,IAAY;QACrB,QAAQ,CAAC,MAAM,GAAG,IAAI,GAAG,sBAAsB,CAAC;IACpD,CAAC;CACJ"}

@ -0,0 +1,143 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Alerts } from "./Alerts/Alerts";
export class Utilities {
constructor() {
this.Alerts = new Alerts();
}
isEmpty(value) {
if (value == null)
return true;
if (value === "")
return true;
return false;
}
setInputWarning(input) {
input.classList.add("warning_input");
const timeout = setTimeout(() => {
input.classList.remove("warning_input");
clearTimeout(timeout);
}, 3000);
}
setAreaWarning(input) {
input.classList.add("warning_area");
const timeout = setTimeout(() => {
input.classList.remove("warning_area");
clearTimeout(timeout);
}, 3000);
}
isValidEmail(email) {
if (this.isEmpty(email))
return false;
const pattern = /^([a-z0-9_.-])+@[a-z0-9-]+\.([a-z]{2,4}\.)?[a-z]{2,4}$/i;
return pattern.test(email);
}
doDelay(mlsec) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(resolve => {
setTimeout(() => {
resolve(null);
}, mlsec);
});
});
}
get getWidh() {
return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
}
get getHeight() {
return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
}
getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
copyValueToBuffer(value, element) {
try {
const coord = element.getBoundingClientRect();
const scrolled = window.pageYOffset || document.documentElement.scrollTop;
const iOsDevice = navigator.userAgent.match(/ipad|iphone/i);
const textArea = document.createElement("textarea");
textArea.readOnly = true;
textArea.style.top = `${Math.round(scrolled + coord.top)}px`;
textArea.classList.add("text_copy");
textArea.value = value;
document.body.appendChild(textArea);
textArea.focus();
if (iOsDevice) {
const editable = textArea.contentEditable;
const readOnly = textArea.readOnly;
textArea.contentEditable = "true";
textArea.readOnly = false;
const range = document.createRange();
range.selectNodeContents(textArea);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
textArea.setSelectionRange(0, 999999);
textArea.contentEditable = editable;
textArea.readOnly = readOnly;
}
else {
textArea.select();
}
const resultCopy = document.execCommand("copy");
document.body.removeChild(textArea);
return resultCopy;
}
catch (e) {
console.error("Error: ", e);
return false;
}
}
getCurrentDate() {
const date = new Date();
const nowDay = date.getDate();
const nowMonth = date.getMonth() + 1;
let forFullDay = "";
if (nowDay < 10) {
forFullDay = "0";
}
let forFullMonth = "";
if (nowMonth < 10) {
forFullMonth = "0";
}
return `${forFullDay}${nowDay}.${forFullMonth}${nowMonth}.${date.getFullYear()}`;
}
stringToBoolean(str) {
const lowerStr = str.toLowerCase();
if (lowerStr === "true") {
return true;
}
else if (lowerStr === "false") {
return false;
}
else {
return null;
}
}
generateUuid() {
var d = new Date().getTime();
var d2 = ((typeof performance !== "undefined") && performance.now && (performance.now() * 1000)) || 0;
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
var r = Math.random() * 16;
if (d > 0) {
r = (d + r) % 16 | 0;
d = Math.floor(d / 16);
}
else {
r = (d2 + r) % 16 | 0;
d2 = Math.floor(d2 / 16);
}
return (c === "x" ? r : (r & 0x3 | 0x8)).toString(16);
});
}
}
//# sourceMappingURL=Utilities.js.map

@ -0,0 +1 @@
{"version":3,"file":"Utilities.js","sourceRoot":"","sources":["../../../TypeScripts/Base/Services/Utilities.ts"],"names":[],"mappings":"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"}

@ -0,0 +1,61 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { TypeAlert } from "../../Base/Services/Alerts/Alerts";
export class BasePage {
constructor(utilities, cookies, alerts) {
this.Utilities = utilities;
this.Cookies = cookies;
this.Alerts = alerts;
}
set PageReadyState(state) {
if (this.readyState !== state) {
this.readyState = state;
const event = new CustomEvent('pageStateChanged', {
detail: {
stateName: 'ReadyState',
stateValue: state
}
});
window.dispatchEvent(event);
}
}
get PageReadyState() {
return this.readyState;
}
WaitForReadyState(conditions, actions) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve) => {
const intervalId = setInterval(() => {
const allConditionsMet = conditions.every(condition => condition === true);
if (allConditionsMet) {
clearInterval(intervalId);
if (allConditionsMet) {
this.PageReadyState = true;
actions.forEach(action => {
action();
});
const alert = {
text: "Страница успешно запущена",
type: TypeAlert.Ok,
duration: 2
};
this.Alerts.AlertShow(alert);
resolve({ states: conditions });
}
else {
console.log("Состояния еще не доступны или не достигнута готовность...");
}
}
}, 100);
});
});
}
}
//# sourceMappingURL=BasePage.js.map

@ -0,0 +1 @@
{"version":3,"file":"BasePage.js","sourceRoot":"","sources":["../../../TypeScripts/Pages/Base/BasePage.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAU,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAKtE,MAAM,OAAO,QAAQ;IA0BjB,YAAY,SAAoB,EAAE,OAAgB,EAAE,MAAc;QAE9D,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAG3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QAGvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAKD,IAAW,cAAc,CAAC,KAAK;QAC3B,IAAI,IAAI,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;YAC5B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;YAExB,MAAM,KAAK,GAAG,IAAI,WAAW,CAA6B,kBAAkB,EAAE;gBAC1E,MAAM,EAAE;oBACJ,SAAS,EAAE,YAAY;oBACvB,UAAU,EAAE,KAAK;iBACpB;aACJ,CAAC,CAAC;YAEH,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACL,CAAC;IAKD,IAAW,cAAc;QACrB,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAQY,iBAAiB,CAAC,UAAqB,EAAE,OAAuB;;YACzE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC3B,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;oBAEhC,MAAM,gBAAgB,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC;oBAE3E,IAAI,gBAAgB,EAAE,CAAC;wBACnB,aAAa,CAAC,UAAU,CAAC,CAAC;wBAE1B,IAAI,gBAAgB,EAAE,CAAC;4BACnB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;4BAK3B,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;gCACrB,MAAM,EAAE,CAAC;4BACb,CAAC,CAAC,CAAC;4BAGH,MAAM,KAAK,GAAsB;gCAC7B,IAAI,EAAE,2BAA2B;gCACjC,IAAI,EAAE,SAAS,CAAC,EAAE;gCAClB,QAAQ,EAAE,CAAC;6BACd,CAAC;4BAEF,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;4BAE5B,OAAO,CAAC,EAAE,MAAM,EAAE,UAAU,EAAC,CAAC,CAAC;wBACnC,CAAC;6BAAM,CAAC;4BACJ,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;wBAC7E,CAAC;oBACL,CAAC;gBACL,CAAC,EAAE,GAAG,CAAC,CAAC;YACZ,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;CACJ"}

@ -0,0 +1,18 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { BasePage } from "./Base/BasePage";
export class ContactPage extends BasePage {
StartPage() {
return __awaiter(this, void 0, void 0, function* () {
yield this.WaitForReadyState([true], []);
});
}
}
//# sourceMappingURL=Contacts.js.map

@ -0,0 +1 @@
{"version":3,"file":"Contacts.js","sourceRoot":"","sources":["../../TypeScripts/Pages/Contacts.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,OAAO,WAAY,SAAQ,QAAQ;IAIxB,SAAS;;YAClB,MAAM,IAAI,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,CAAC;KAAA;CACJ"}

@ -0,0 +1,18 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { BasePage } from "./Base/BasePage";
export class IndexPage extends BasePage {
StartPage() {
return __awaiter(this, void 0, void 0, function* () {
yield this.WaitForReadyState([true], []);
});
}
}
//# sourceMappingURL=IndexPage.js.map

@ -0,0 +1 @@
{"version":3,"file":"IndexPage.js","sourceRoot":"","sources":["../../TypeScripts/Pages/IndexPage.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,OAAO,SAAU,SAAQ,QAAQ;IAItB,SAAS;;YAClB,MAAM,IAAI,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,CAAC;KAAA;CACJ"}

@ -7,23 +7,56 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Cookies } from "./cookies";
import { Utilities } from "./utilities";
import { Alerts } from "./Base/Services/Alerts/Alerts";
import { Cookies } from "./Base/Services/Cookies";
import { Utilities } from "./Base/Services/Utilities";
import { ContactPage } from "./Pages/Contacts";
import { IndexPage } from "./Pages/IndexPage";
(() => {
window.addEventListener("load", () => __awaiter(void 0, void 0, void 0, function* () {
const utilities = new Utilities();
const cookies = new Cookies();
const alerts = new Alerts();
const currentUrl = new URL(document.location.href);
const pathname = currentUrl.pathname.toLowerCase();
const partsPath = pathname.split("/");
let indexPageState = {
ReadyState: undefined
};
window.addEventListener('pageStateChanged', function (event) {
const stateName = event.detail.stateName;
const stateValue = event.detail.stateValue;
if (stateName === 'ReadyState') {
indexPageState.ReadyState = stateValue;
}
CheckStatesAndProceed();
});
switch (partsPath[1]) {
case "":
{
const page = new IndexPage(utilities, cookies, alerts);
yield page.StartPage();
}
break;
case "":
{
const page = new ContactPage(utilities, cookies, alerts);
yield page.StartPage();
}
break;
default:
break;
}
function CheckStatesAndProceed() {
if (indexPageState.ReadyState !== undefined) {
if (indexPageState.ReadyState) {
console.log("Success Start Page");
}
else {
console.log("Wait Load Page");
}
}
}
}));
})();
//# sourceMappingURL=main_api.js.map

@ -1 +1 @@
{"version":3,"file":"main_api.js","sourceRoot":"","sources":["../TypeScripts/main_api.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,CAAC,GAAG,EAAE;IAEF,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAS,EAAE;QAEvC,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;QAGlC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;QAG9B,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAGnD,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAGnD,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAGtC,QAAQ,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;YACnB,KAAK,EAAE;gBACH,CAAC;gBAKD,CAAC;gBACD,MAAM;YACV;gBACI,MAAM;QACd,CAAC;IACL,CAAC,CAAA,CAAC,CAAC;AACP,CAAC,CAAC,EAAE,CAAC"}
{"version":3,"file":"main_api.js","sourceRoot":"","sources":["../TypeScripts/main_api.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,CAAC,GAAG,EAAE;IAEF,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAS,EAAE;QAEvC,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;QAGlC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;QAG9B,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAG5B,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAGnD,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAGnD,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAGtC,IAAI,cAAc,GAAG;YACjB,UAAU,EAAE,SAAS;SACxB,CAAC;QAEF,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,UAAU,KACX;YACvC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC;YACzC,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;YAE3C,IAAI,SAAS,KAAK,YAAY,EAAE,CAAC;gBAC7B,cAAc,CAAC,UAAU,GAAG,UAAU,CAAC;YAC3C,CAAC;YAED,qBAAqB,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAGH,QAAQ,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;YACnB,KAAK,EAAE;gBACH,CAAC;oBAEG,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;oBAEvD,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC3B,CAAC;gBACD,MAAM;YAEV,KAAK,EAAE;gBACH,CAAC;oBAEG,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;oBAEzD,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC3B,CAAC;gBACD,MAAM;YACV;gBACI,MAAM;QACd,CAAC;QAED,SAAS,qBAAqB;YAC1B,IAAI,cAAc,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBAE1C,IAAI,cAAc,CAAC,UAAU,EAAE,CAAC;oBAC5B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;gBACtC,CAAC;qBAAM,CAAC;oBACJ,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;gBAClC,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC,CAAA,CAAC,CAAC;AACP,CAAC,CAAC,EAAE,CAAC"}

@ -0,0 +1,4 @@
export interface PageReadyStateChangedEvent {
stateName: string;
stateValue: any;
}

@ -1,10 +1,8 @@
export class Alerts {
/**
* Функция для отображения уведомления
*
* TODO: Сделать синхронизацию уведомления для всех - если она касается размещения задач на сервере
*/
public AlertShow(notification: Notification): void {
public AlertShow(notification: NotificationAlert): void {
const container = document.getElementById('notification-container');
if (!container) return;
@ -37,15 +35,23 @@
}
}
// Тип для уведомлений
interface Notification {
text: string;
duration: number; // продолжительность отображения в секундах
type: TypeAlert;
declare global {
/**
* Уведомление
*/
interface NotificationAlert {
text: string; // текст сообщения уведомления
duration: number; // продолжительность отображения в секундах
type: TypeAlert; // тип уведомления (расцветка)
}
}
/**
* Тип оповещения
*/
export enum TypeAlert {
Error = 'error',
Warning = 'warning',
Ok = 'ok',
Ok = 'ok'
}

@ -1,4 +1,4 @@
import { Alerts } from "./alerts";
import { Alerts } from "./Alerts/Alerts";
/**
* Класс вспомогательных методов

@ -0,0 +1,110 @@
import { Alerts, TypeAlert } from "../../Base/Services/Alerts/Alerts";
import { PageReadyStateChangedEvent } from "../../Base/Components/BaseEvents";
import { Utilities } from "../../Base/Services/Utilities";
import { Cookies } from "../../Base/Services/Cookies";
export class BasePage {
/**
* Экземпляр класса утилит
*/
public readonly Utilities: Utilities;
/**
* Экземпляр класса кук
*/
public readonly Cookies: Cookies;
/**
* Экземпляр класса системы уведомлений
*/
public readonly Alerts: Alerts;
/**
* Состояние готовности страницы
*/
private readyState: boolean;
/**
* Конструктор
* @param utilities - экземпляр класса утилит
* @param cookies - экземпляр класса кук
*/
constructor(utilities: Utilities, cookies: Cookies, alerts: Alerts) {
//присваиваем экземпляр класса утилит
this.Utilities = utilities;
//присваиваем экземпляр класса кук
this.Cookies = cookies;
//присваиваем экземпляр класса кук
this.Alerts = alerts;
}
/**
* Метод изменяет состояние готовности страницы
*/
public set PageReadyState(state) {
if (this.readyState !== state) {
this.readyState = state;
// Создаем и отправляем CustomEvent
const event = new CustomEvent<PageReadyStateChangedEvent>('pageStateChanged', {
detail: {
stateName: 'ReadyState',
stateValue: state
}
});
window.dispatchEvent(event);
}
}
/**
* Метод получает состояние готовности страницы
*/
public get PageReadyState() {
return this.readyState;
}
/**
* Метод ждёт состояния готовности страницы к показу пользователю
* P.s на ней могут работать разные сервисы или запускаться перед началом работы со страницей
* @param conditions - массив булевых условий, которые должны быть истинными
* @param actions - массив отложенных функций, которые будут выполнены по очереди после выполнения всех условий
*/
public async WaitForReadyState(conditions: boolean[], actions: (() => void)[]) {
return new Promise((resolve) => {
const intervalId = setInterval(() => {
// Проверяем все условия готовности
const allConditionsMet = conditions.every(condition => condition === true);
if (allConditionsMet) {
clearInterval(intervalId); // Останавливаем интервал
if (allConditionsMet) {
this.PageReadyState = true;
/*
После того как все условия выполнены, выполняем отложенные функции по очереди
*/
actions.forEach(action => {
action(); // Выполняем каждую отложенную функцию
});
// Пример использования NotificationAlert и TypeAlert
const alert: NotificationAlert = {
text: "Страница успешно запущена",
type: TypeAlert.Ok,
duration: 2
};
this.Alerts.AlertShow(alert)
resolve({ states: conditions});
} else {
console.log("Состояния еще не доступны или не достигнута готовность...");
}
}
}, 100); // Проверяем каждые 100 мс
});
}
}

@ -0,0 +1,14 @@
import { BasePage } from "./Base/BasePage";
export class ContactPage extends BasePage {
/**
* Метод запускает страницу
*/
public async StartPage(): Promise<void> {
await this.WaitForReadyState([true], [this.PostEventLoad]);
}
private PostEventLoad(): void {
console.log("Contact post event...")
}
}

@ -0,0 +1,14 @@
import { BasePage } from "./Base/BasePage";
export class IndexPage extends BasePage {
/**
* Метод запускает страницу
*/
public async StartPage(): Promise<void> {
await this.WaitForReadyState([true], [ this.PostEventLoad ]);
}
private PostEventLoad(): void {
console.log("Index post event...")
}
}

@ -1,5 +1,9 @@
import { Cookies } from "./cookies";
import { Utilities } from "./utilities";
import { PageReadyStateChangedEvent } from "./Base/Components/BaseEvents";
import { Alerts } from "./Base/Services/Alerts/Alerts";
import { Cookies } from "./Base/Services/Cookies";
import { Utilities } from "./Base/Services/Utilities";
import { ContactPage } from "./Pages/Contacts";
import { IndexPage } from "./Pages/IndexPage";
(() => {
//по загрузке окна
@ -10,6 +14,9 @@ import { Utilities } from "./utilities";
//создаем экземпляр класса кук
const cookies = new Cookies();
//создаем экземпляр класса уведомлений
const alerts = new Alerts();
//получаем текущий URL
const currentUrl = new URL(document.location.href);
@ -19,18 +26,54 @@ import { Utilities } from "./utilities";
//разбиваем пути URL на части
const partsPath = pathname.split("/");
// Состояние IndexPage, изначально неопределенное
let indexPageState = {
ReadyState: undefined
};
window.addEventListener('pageStateChanged', function (event:
CustomEvent<PageReadyStateChangedEvent>) {
const stateName = event.detail.stateName;
const stateValue = event.detail.stateValue;
if (stateName === 'ReadyState') {
indexPageState.ReadyState = stateValue;
}
CheckStatesAndProceed();
});
//смотрим путь
switch (partsPath[1]) {
case "": //страница авторизации
case "":
{
//создаем экземпляр класса авторизации
//const auth = new IndexPage(utilities, cookies);
////запускаем авторизацию
//await auth.startPage();
// создаем экземпляр страницы
const page = new IndexPage(utilities, cookies, alerts);
// запускаем
await page.StartPage();
}
break;
case "contacts":
{
// создаем экземпляр страницы
const page = new ContactPage(utilities, cookies, alerts);
// запускаем
await page.StartPage();
}
break;
default:
break;
}
function CheckStatesAndProceed() {
if (indexPageState.ReadyState !== undefined) {
// Все необходимые состояния получены
if (indexPageState.ReadyState) {
console.log("Success Start Page");
} else {
console.log("Wait Load Page");
}
}
}
});
})();

@ -0,0 +1,4 @@
@{
ViewData["Title"] = "Контакты";
}

@ -1,8 +1,4 @@
@{
ViewData["Title"] = "Home Page";
ViewData["Title"] = "Главная";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

@ -3,20 +3,29 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - AppWeb</title>
<title>@ViewData["Title"]</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/app.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/AppWeb.styles.css" asp-append-version="true" />
<script src="~/js/app.min.js"></script>
</head>
<body>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
@RenderBody()
<div id="notification-container"></div> <!-- Контейнер для уведомлений -->
<!--Start Example Svg Icons-->
@* <div class="hidden">
<svg xmlns="http://www.w3.org/2000/svg">
<symbol viewBox="0 0 512 512" xml:space="preserve" id="menu" xmlns="http://www.w3.org/2000/svg">
<path d="M256 0C114.617 0 0 114.618 0 256c0 141.383 114.617 256 256 256 141.382 0 256-114.617 256-256C512 114.618 397.382 0 256 0zm117.649 366.297H138.351V322.18h235.298v44.117zm0-88.242H138.351v-44.102h235.298v44.102zm0-88.227H138.351v-44.117h235.298v44.117z" />
</symbol>
</svg>
</div> *@
<!--End svg icons-->
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/app.min.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

@ -1 +1,147 @@
html{font-size:14px}@media (min-width:768px){html{font-size:16px}}.btn-link.nav-link:focus,.btn:active:focus,.btn:focus,.form-check-input:focus,.form-control:focus{box-shadow:0 0 0 .1rem #fff,0 0 0 .25rem #258cfb}html{min-height:100%;position:relative}body{margin-bottom:60px}
/*!*************************************************************************************!*\
!*** css ./node_modules/css-loader/dist/cjs.js!./ScriptsAndCss/CssFiles/styles.css ***!
\*************************************************************************************/
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
html {
position: relative;
min-height: 100%;
}
body {
margin-bottom: 60px;
}
/*
**************************
START ALERT
**************************
*/
/* Основные стили для контейнера уведомлений */
#notification-container {
position: fixed;
bottom: 20px;
left: 20px;
z-index: 9999;
}
.error_alert_animate {
border: 1px solid #AC0B00;
}
.ok_alert_animate {
border: 1px solid #007126;
}
.warning_alert_animate {
border: 1px solid #A33E00;
}
/* Стили для уведомлений */
.notification {
background-color: #333;
color: white;
border-radius: 8px;
padding: 10px 20px;
margin-bottom: 10px;
font-size: 16px;
max-width: 300px;
opacity: 1;
animation: slideUpAndFade 5s ease-in-out forwards;
}
/* Анимация для подъема уведомления вверх и его исчезновения */
@keyframes slideUpAndFade {
0% {
transform: translateY(0);
opacity: 1;
}
50% {
transform: translateY(-50%); /* Поднимаем уведомление до середины экрана */
opacity: 0.7;
}
100% {
transform: translateY(-100%); /* Двигаем уведомление еще выше */
opacity: 0;
}
}
/*
**************************
END ALERT
**************************
*/
/*
**************************
START ANIMS
**************************
*/
.puff-in-center {
-webkit-animation: puff-in-center 0.1s cubic-bezier(0.470, 0.000, 0.745, 0.715) both;
animation: puff-in-center 0.1s cubic-bezier(0.470, 0.000, 0.745, 0.715) both;
}
/**
* ----------------------------------------
* animation puff-in-center
* ----------------------------------------
*/
@-webkit-keyframes puff-in-center {
0% {
-webkit-transform: scale(2);
transform: scale(2);
-webkit-filter: blur(4px);
filter: blur(4px);
opacity: 0;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
-webkit-filter: blur(0px);
filter: blur(0px);
opacity: 1;
}
}
@keyframes puff-in-center {
0% {
-webkit-transform: scale(2);
transform: scale(2);
-webkit-filter: blur(4px);
filter: blur(4px);
opacity: 0;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
-webkit-filter: blur(0px);
filter: blur(0px);
opacity: 1;
}
}
/*
**************************
END ANIMS
**************************
*/

File diff suppressed because one or more lines are too long

@ -0,0 +1,49 @@
/*
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./ScriptsAndCss/CssFiles/styles.css":
/*!*******************************************!*\
!*** ./ScriptsAndCss/CssFiles/styles.css ***!
\*******************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n// extracted by mini-css-extract-plugin\n\n\n//# sourceURL=webpack://asp.net/./ScriptsAndCss/CssFiles/styles.css?");
/***/ })
/******/ });
/************************************************************************/
/******/ // The require scope
/******/ var __webpack_require__ = {};
/******/
/************************************************************************/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module can't be inlined because the eval devtool is used.
/******/ var __webpack_exports__ = {};
/******/ __webpack_modules__["./ScriptsAndCss/CssFiles/styles.css"](0, __webpack_exports__, __webpack_require__);
/******/
/******/ })()
;

463
README.md

@ -1,463 +0,0 @@
# 🌁 Шаблон проекта ASP NET CORE MVC, Frontend - на TypeScript
## 🎇 Этапы наполнения конфигурациями 🎇
---
1. ⛅ Импорт `tsconfig.json`
---
<details>
<summary>Раскрыть код tsconfig.json</summary>
```json
{
"compilerOptions": {
"target": "es2016", // Устанавливает уровень JavaScript, в который компилируется TypeScript (в данном случае ES2016).
"module": "es6", // Определяет, какой модульный формат используется в процессе компиляции (ES6 модули).
"moduleResolution": "node", // Способ разрешения модулей. В данном случае используется разрешение как в Node.js.
"jsx": "preserve", // Как компилировать JSX. В данном случае оставляем JSX без изменений.
"declaration": false, // Указывает, генерировать ли файлы типов (.d.ts). Здесь это отключено.
"removeComments": true, // Указывает, что комментарии должны быть удалены из скомпилированного кода.
"noImplicitAny": false, // Отключает предупреждения о неявных типах `any` в коде.
"noEmitOnError": true, // Указывает, что компиляция должна быть остановлена, если есть ошибки.
"sourceMap": true, // Генерирует карты исходных кодов для упрощения отладки.
"esModuleInterop": true, // Включает совместимость с модулями ES при импорте CommonJS модулей.
"experimentalDecorators": true, // Включает поддержку экспериментальных декораторов в TypeScript.
"emitDecoratorMetadata": true, // Включает генерацию метаданных для декораторов, которые могут использоваться в таких библиотеках как TypeORM или Angular.
"outDir": "ScriptsAndCss/JsScripts", // Указывает директорию для сохранения скомпилированных файлов.
"lib": [ "es2016", "dom" ] // Указывает библиотеки, которые будут включены при компиляции (ES2016 и DOM).
},
"exclude": [
"node_modules" // Исключает папку node_modules из процесса компиляции.
]
}
```
</details>
---
2. ⛅ Импорт `webpack.config.js`
---
<details>
<summary>Раскрыть код webpack.config.js</summary>
```js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin'); // Импортируем TerserPlugin
module.exports = {
entry: {
app: './ScriptsAndCss/TypeScripts/main_api.ts', // Точка входа для JavaScript
styles: './ScriptsAndCss/CssFiles/styles.css' // Точка входа для CSS (может быть любой CSS-файл)
},
output: {
path: path.resolve(__dirname, 'wwwroot/js'),
filename: '[name].min.js' // Используем [name] для динамического имени файла
},
resolve: {
extensions: ['.ts', '.js', '.css'] // Добавили .css
},
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // Извлекает CSS в отдельные файлы
'css-loader' // Обрабатывает @import и url()
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '../css/app.min.css' // Куда Webpack должен поместить CSS-файл
})
],
mode: 'development',
watch: true,
};
```
</details>
<br>
> В случае публикации в Production релиза нужно сменить режим `mode: 'development'` на `mode: 'production'` в файле конфигурации
<details>
<summary>Раскрыть код изменений webpack.config.js</summary>
```js
mode: 'production', // Изменен режим на production для минимизации
optimization: {
minimize: true, // Включаем минимизацию
minimizer: [
new TerserPlugin(), // Плагин для минимизации JS
new CssMinimizerPlugin() // Плагин для минимизации CSS
]
},
```
</details>
---
3. ⛅ Импорт `package.json` - ряд плагинов опционален под ваш проект ⛅
---
<details>
<summary>Раскрыть код package.json</summary>
```json
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"css-minimizer-webpack-plugin": "7.0.0",
"terser-webpack-plugin": "5.3.12",
"cytoscape": "^3.31.0",
"cytoscape-cose-bilkent": "4.1.0",
"grunt": "1.4.1",
"style-loader": "^4.0.0",
"css-loader": "^7.1.2",
"mini-css-extract-plugin": "^2.9.2",
"grunt-webpack": "^7.0.0",
"webpack-cli": "^6.0.1",
"ts-loader": "^9.5.2",
"browser-sync": "^3.0.3",
"grunt-browser-sync": "^2.2.0",
"grunt-contrib-clean": "2.0.0",
"grunt-contrib-concat": "2.0.0",
"grunt-contrib-cssmin": "5.0.0",
"grunt-contrib-uglify": "5.0.1",
"grunt-contrib-watch": "1.1.0",
"grunt-ts": "6.0.0-beta.22"
},
"dependencies": {
"cytoscape": "^3.31.0"
},
"comments": {
"version": "Указывает текущую версию вашего проекта.",
"name": "Имя вашего проекта.",
"private": "Указывает, что проект является приватным и не должен быть опубликован в npm.",
"devDependencies": {
"css-minimizer-webpack-plugin": "Плагин для минимизации CSS файлов в процессе сборки Webpack.",
"terser-webpack-plugin": "Плагин для минимизации JavaScript с использованием Terser.",
"cytoscape": "Библиотека для работы с графами и сетями.",
"cytoscape-cose-bilkent": "Плагин для Cytoscape, который добавляет алгоритм планирования расположения узлов.",
"grunt": "Система автоматизации задач для Node.js, например, для сборки, минификации и тестирования.",
"style-loader": "Лоадер для инжекции CSS в DOM через теги `<style>` в процессе сборки.",
"css-loader": "Лоадер для обработки CSS файлов и поддержки импорта других CSS или стилей.",
"mini-css-extract-plugin": "Плагин для извлечения CSS в отдельные файлы, что улучшает производительность.",
"grunt-webpack": "Плагин для интеграции Webpack с системой Grunt.",
"webpack-cli": "CLI для работы с Webpack, позволяет запускать сборку через командную строку.",
"ts-loader": "Лоадер для загрузки TypeScript файлов в Webpack.",
"browser-sync": "Инструмент для синхронизации браузеров и автоперезагрузки в процессе разработки.",
"grunt-browser-sync": "Плагин для интеграции BrowserSync с Grunt.",
"grunt-contrib-clean": "Плагин для удаления файлов или папок перед выполнением задач.",
"grunt-contrib-concat": "Плагин для конкатенации (объединения) файлов в один.",
"grunt-contrib-cssmin": "Плагин для сжатия CSS файлов.",
"grunt-contrib-uglify": "Плагин для сжатия JavaScript файлов (использует UglifyJS).",
"grunt-contrib-watch": "Плагин для отслеживания изменений файлов и автоматического выполнения задач.",
"grunt-ts": "Плагин для компиляции TypeScript файлов в Grunt."
}
}
}
```
</details>
---
4. ⛅ Импорт `Gruntfile.js`
---
<details>
<summary>Раскрыть код Gruntfile.js</summary>
```js
const webpackConfig = require('./webpack.config.js');
module.exports = function (grunt) {
grunt.initConfig({
webpack: {
options: webpackConfig,
build: {
}
},
browserSync: {
dev: {
bsFiles: {
src: [
'wwwroot/css/*.css',
'wwwroot/js/app.min.js', // Webpack создает app.min.js
'Views/**/*.cshtml'
]
},
options: {
watchTask: true,
proxy: "localhost:5000" // Замените на свой локальный адрес
}
}
},
watch: {
ts: {
files: ['ScriptsAndCss/TypeScripts/**/*.ts'],
tasks: ['webpack:build'], // Webpack компилирует и собирает
options: {
spawn: false,
},
},
bsReload: {
files: ['wwwroot/css/*.css', 'wwwroot/js/app.min.js', 'Views/**/*.cshtml'],
options: {
reload: true
}
}
},
clean: ["wwwroot/css/*", "wwwroot/js/*", "ScriptsAndCss/Combined/*"],
cssmin: { //сжатие CSS
css: {
src: ["ScriptsAndCss/CssFiles/*"], //какой файл сжимать
dest: "wwwroot/css/app.min.css" //сжатый выходной файл
}
},
});
grunt.loadNpmTasks('grunt-webpack');
grunt.loadNpmTasks('grunt-browser-sync');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.registerTask("build", ["clean", "webpack:build", "cssmin"]);
grunt.registerTask("default", ["build", "browserSync:dev", "watch"]);
};
```
</details>
---
4. ⛅ Импорт `app_configuration.json`
---
<details>
<summary>Раскрыть код app_configuration.json</summary>
```json
{
"appSettings": {
"appHost": "localhost:8833"
}
}
```
</details>
---
## 🎇 Этапы сборочного процесса 🎇
---
- 🌋 Восстановить NPM пакеты
---
- 🌋 Настроить директории для генерации Frontend
- в корне проекта где мы будем его внедрять создаём такуб структуру папок
- 📁ScriptsAndCss
- 📁〰📁CssFiles
- 📁〰📁JsScripts
- 📁〰📁TypeScripts
---
- 🌋 Установить Nuget пакеты
- **`TypeScript.MSBuild`**
- **`AspNetCore.Mvc.Razor.RuntimeCompilation`**
- **`Swashbuckle.AspNetCore.Swagger`**
- **`Swashbuckle.AspNetCore.SwaggerGen`**
- **`Swashbuckle.AspNetCore.SwaggerUI`**
---
- 🌋 В файле `Views/Shared/_Layout.cshtml` изменить адреса до `css` и `js` файлов так как теперь они называются **`app.min.css`** и **`app.min.js`**
---
- 🌋 Добавить простейшую точку входа `[js]` приложения в папку 📁ScriptsAndCss〰>📁TypeScripts - **`main_api.ts`**
<details>
<summary>Раскрыть код main_api.ts</summary>
```ts
(() => {
//по загрузке окна
window.addEventListener("load", async () => {
//получаем текущий URL
const currentUrl = new URL(document.location.href);
//получаем путь из URL
const pathname = currentUrl.pathname.toLowerCase();
//разбиваем пути URL на части
const partsPath = pathname.split("/");
//смотрим путь
switch (partsPath[1]) {
case "": //страница авторизации
{
}
break;
default:
break;
}
}
})();
```
</details>
<br>
---
- 🌋 Добавить простейшую точку входа `[css]` приложения в папку 📁ScriptsAndCss〰>📁CssFiles - **`style.css`**
<details>
<summary>Раскрыть код style.css</summary>
```ts
// ваши стили - скопируйте из wwwroot то что там было
```
</details>
<br>
---
- 🌋 Добавить примерно такое содержимое в **`Program.cs`**
> Поясняю - в данном примере вырезано внесение `CSRF` токена в логику работы сервера а также `Middleware` отвечающий за обработку служб защиты от подделки запросов. Тут мы вносим лишь Swagger для генерации документации по API и конфигурационный файл декларирующий **`host`** и **`port`** вашего сервера принудительно.
<details>
<summary>Раскрыть код Program.cs</summary>
```cs
using System.Reflection;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile($"{LoggingExtensions.AppDir}/app_configuration.json");
builder.WebHost.UseUrls($"https://{builder.Configuration["appSettings:appHost"]}");
builder.Services.AddControllersWithViews();
#region В этом регионе объявляем сервисы
// Например
// builder.Services.AddSingleton<ICashService, CashService>();
#endregion
builder.Services.Configure<JsonOptions>(options =>
{
options.JsonSerializerOptions.WriteIndented = true; //для красивого форматирования
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "App API",
Version = "1.0.0",
Description = "Информация об API",
Contact = new OpenApiContact
{
Name = "Dvurechensky"
}
});
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
else
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Документация");
c.RoutePrefix = "docs";
});
app.UseDeveloperExceptionPage(); //используем страницу исключений
}
// app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
```
</details>
<br>
---
- 🌋 В диспетчере выполнения задач выполнить задачу `default` для генерации файлов
---
- 🌋 В настройках проекта в разделе `Выходные данные` поставить галочку на `Файл документации` - `Создание файла, содержащего документацию по API` для успешной работы **`Swagger`**
---
## 🎇 Итог 🎇
#### 📷 База 📷
При успешном старте в данном примере мы получим сервер:
- по адресу **`https://localhost:8833`**
- документацию Swagger **`https://localhost:8833/docs/`**
---
#### 📷 Плюшки 📷
---
- в свою очередь при вёрстке `Typescript` и активированной задаче `default` в `Диспетчере выполнения задач` - в реальном времени будет перезагружаться страница и мы увидим новые изменения на `Frontned` - лицевой стороне Web-сервера после каждого сохранения файла в проекте
- при установке параметров указанных для `production` версии в файле `webpack.config.js` мы можем собрать `Release` версию и опубликовать её в сеть в которой все файлы `css` и `js` будут сжаты и минимизированы для более быстрой и качественной работы сервера в сети
- мы можем прикрутить любую существующую библиотеку NPM и использовать её в скриптах `Typescript` для создания качественного `Frontend` - состояние которого мы можем отслеживать в реальном времени при разработке. Typescript поддерживает последние возможности ECMAScript (например, async/await, декораторы, модули и т. д.), что делает код более современным и удобным.
- используя ASP.NET Core MVC для серверной части (backend), позволяет создавать масштабируемые, производительные и безопасные веб-приложения
- Typescript для фронтенда (frontend) обеспечивает типизацию и улучшение читаемости кода, предотвращая множество ошибок, которые могут возникнуть при использовании обычного JavaScript.
---
#### 📷 Примерный процесс разработки: 📷
---
🎃 Frontend (Typescript + Webpack):
🎈 Вы пишете код на Typescript, который потом компилируется с помощью Webpack.
Webpack минифицирует и обрабатывает ваши файлы, генерирует отдельные чанки для кода, CSS и изображений.
Grunt помогает автоматизировать задачи, такие как запуск Webpack, минификация CSS/JS, запуск локального сервера с BrowserSync.
Backend (ASP.NET Core MVC):
🎈 Серверная часть обрабатывает бизнес-логику, запросы от клиента и взаимодействует с базой данных.
Вся верстка и фронтенд код (собранный через Webpack) размещаются в папке wwwroot или другой папке для статических файлов.
🎃 Автоматизация сборки и деплоя:
🎈 Grunt автоматически обрабатывает сборку фронтенда, выполняет задачи, такие как минификация, копирование файлов и т. д.
Webpack поддерживает горячую замену модулей, что ускоряет процесс разработки.
Разработка с возможностью тестирования и отладки:
🎈 В процессе разработки с помощью BrowserSync можно синхронизировать несколько браузеров и автоматически перезагружать страницы.
Webpack и Grunt делают разработку более продуктивной за счет автоматизации рутинных задач.