починил список и форму
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"no-console": "off"
|
||||
"no-console": "off",
|
||||
"class-methods-use-this": "off"
|
||||
}
|
||||
}
|
||||
|
||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -4,7 +4,7 @@
|
||||
"type": "chrome",
|
||||
"name": "Debug",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:8080"
|
||||
"url": "http://localhost:5173"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
|
||||
38
components/api/client.js
Normal file
38
components/api/client.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const URL = "http://localhost:3000/";
|
||||
|
||||
const makeRequest = async (path, params, vars, method = "GET", data = null) => {
|
||||
try {
|
||||
const requestParams = params ? `?${params}` : "";
|
||||
const pathVariables = vars ? `/${vars}` : "";
|
||||
const options = { method };
|
||||
const hasBody = (method === "POST" || method === "PUT") && data;
|
||||
if (hasBody) {
|
||||
options.headers = { "Content-Type": "application/json;charset=utf-8" };
|
||||
options.body = JSON.stringify(data);
|
||||
}
|
||||
const response = await fetch(`${URL}${path}${pathVariables}${requestParams}`, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Response status: ${response?.status}`);
|
||||
}
|
||||
|
||||
const json = await response.json();
|
||||
console.debug(path, json);
|
||||
return json;
|
||||
} catch (error) {
|
||||
if (error instanceof SyntaxError) {
|
||||
throw new Error("There was a SyntaxError", error);
|
||||
} else {
|
||||
throw new Error("There was an error", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getAllItems = (path, params) => makeRequest(path, params);
|
||||
|
||||
export const getItem = (path, id) => makeRequest(path, null, id);
|
||||
|
||||
export const createItem = (path, data) => makeRequest(path, null, null, "POST", data);
|
||||
|
||||
export const updateItem = (path, id, data) => makeRequest(path, null, id, "PUT", data);
|
||||
|
||||
export const deleteItem = (path, id) => makeRequest(path, null, id, "DELETE");
|
||||
22
components/button-helper.js
Normal file
22
components/button-helper.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const icon = (name) => {
|
||||
const inconElement = document.createElement("i");
|
||||
inconElement.classList.add("bi", `bi-${name}`);
|
||||
return inconElement;
|
||||
};
|
||||
|
||||
const button = (text, iconName, color = "primary") => {
|
||||
const buttonElement = document.createElement("button");
|
||||
buttonElement.classList.add("btn", `btn-${color}`);
|
||||
buttonElement.setAttribute("type", "button");
|
||||
if (text) {
|
||||
buttonElement.innerText = text;
|
||||
}
|
||||
if (icon) {
|
||||
buttonElement.appendChild(icon(iconName));
|
||||
}
|
||||
return buttonElement;
|
||||
};
|
||||
|
||||
export const buttonText = (text, color) => button(text, null, color);
|
||||
|
||||
export const buttonIcon = (iconName, color) => button(null, iconName, color);
|
||||
93
components/modal-helper.js
Normal file
93
components/modal-helper.js
Normal file
@@ -0,0 +1,93 @@
|
||||
import { Modal } from "bootstrap";
|
||||
|
||||
const wrapper = () => {
|
||||
const wrapperElement = document.createElement("div");
|
||||
wrapperElement.classList.add("modal");
|
||||
wrapperElement.setAttribute("tabindex", -1);
|
||||
return wrapperElement;
|
||||
};
|
||||
|
||||
const container = (type) => {
|
||||
const containerElement = document.createElement("div");
|
||||
containerElement.classList.add(type);
|
||||
return containerElement;
|
||||
};
|
||||
|
||||
const closeButton = (closeCallback) => {
|
||||
const closeElement = document.createElement("button");
|
||||
closeElement.setAttribute("type", "button");
|
||||
closeElement.addEventListener("click", () => closeCallback());
|
||||
return closeElement;
|
||||
};
|
||||
|
||||
const header = (title, closeCallback) => {
|
||||
const containerElement = container("modal-header");
|
||||
const titleElement = document.createElement("h5");
|
||||
titleElement.innerText = title;
|
||||
containerElement.appendChild(titleElement);
|
||||
|
||||
const closeElement = closeButton(closeCallback);
|
||||
closeElement.classList.add("btn-close");
|
||||
containerElement.appendChild(closeElement);
|
||||
|
||||
return containerElement;
|
||||
};
|
||||
|
||||
const footer = (okTitle, okCallback, closeCallback) => {
|
||||
const footerElement = container("modal-footer");
|
||||
|
||||
const okElement = document.createElement("button");
|
||||
okElement.classList.add("btn", "btn-primary");
|
||||
okElement.setAttribute("type", "button");
|
||||
okElement.innerText = okTitle;
|
||||
okElement.addEventListener("click", () => okCallback());
|
||||
footerElement.appendChild(okElement);
|
||||
|
||||
const closeElement = closeButton(closeCallback);
|
||||
closeElement.classList.add("btn", "btn-secondary");
|
||||
closeElement.innerText = "Отмена";
|
||||
footerElement.appendChild(closeElement);
|
||||
|
||||
return footerElement;
|
||||
};
|
||||
|
||||
const modal = (title, okTitle, body, okCallback, closeCallback) => {
|
||||
const wrapperElement = wrapper();
|
||||
const dialogElement = container("modal-dialog");
|
||||
const contentElement = container("modal-content");
|
||||
|
||||
const headerElement = header(title, closeCallback);
|
||||
contentElement.appendChild(headerElement);
|
||||
|
||||
const bodyElement = container("modal-body");
|
||||
bodyElement.appendChild(body);
|
||||
contentElement.appendChild(bodyElement);
|
||||
|
||||
const footerElement = footer(okTitle, okCallback, closeCallback);
|
||||
contentElement.appendChild(footerElement);
|
||||
|
||||
dialogElement.appendChild(contentElement);
|
||||
wrapperElement.appendChild(dialogElement);
|
||||
return wrapperElement;
|
||||
};
|
||||
|
||||
const showQuestion = (title, okTitle, message) => {
|
||||
const messageElement = document.createElement("p");
|
||||
messageElement.innerText = message;
|
||||
return new Promise((resolve) => {
|
||||
let modalDialog;
|
||||
const okCallback = () => {
|
||||
modalDialog.hide();
|
||||
resolve(true);
|
||||
};
|
||||
const cancelCallback = () => {
|
||||
modalDialog.hide();
|
||||
resolve(false);
|
||||
};
|
||||
const questionElement = modal(title, okTitle, messageElement, okCallback, cancelCallback);
|
||||
modalDialog = new Modal(questionElement);
|
||||
modalDialog.show();
|
||||
});
|
||||
};
|
||||
|
||||
export default showQuestion;
|
||||
10
components/select-helper.js
Normal file
10
components/select-helper.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const populateSelect = (select, data) => {
|
||||
data.forEach((playlist) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = playlist.id;
|
||||
option.innerText = playlist.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
};
|
||||
|
||||
export default populateSelect;
|
||||
5
components/storage.js
Normal file
5
components/storage.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export const lsSave = (key, value) => {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
};
|
||||
|
||||
export const lsReadArray = (key) => JSON.parse(localStorage.getItem(key)) || [];
|
||||
87
components/table-helper.js
Normal file
87
components/table-helper.js
Normal file
@@ -0,0 +1,87 @@
|
||||
import { buttonIcon } from "./button-helper";
|
||||
|
||||
const deepGet = (obj, keys) => keys.reduce((xs, x) => xs?.[x] ?? null, obj);
|
||||
|
||||
const deepGetByPath = (obj, path) =>
|
||||
deepGet(
|
||||
obj,
|
||||
path.split(".").filter((t) => t !== "")
|
||||
);
|
||||
|
||||
const headCell = (text) => {
|
||||
const column = document.createElement("th");
|
||||
column.setAttribute("scope", "col");
|
||||
column.innerText = text;
|
||||
return column;
|
||||
};
|
||||
|
||||
const head = (cols) => {
|
||||
if (!cols || !Array.isArray(cols) || cols.length === 0) {
|
||||
throw new Error("Columns are not defined");
|
||||
}
|
||||
const headElement = document.createElement("thead");
|
||||
const rowElement = document.createElement("tr");
|
||||
cols.forEach((col) => rowElement.appendChild(headCell(col)));
|
||||
headElement.appendChild(rowElement);
|
||||
return headElement;
|
||||
};
|
||||
|
||||
const bodyCell = (value) => {
|
||||
const column = document.createElement("td");
|
||||
column.innerText = value;
|
||||
return column;
|
||||
};
|
||||
|
||||
const bodyButtonCell = (item, icon, color, callback) => {
|
||||
const button = buttonIcon(icon, color);
|
||||
button.addEventListener("click", () => callback(item));
|
||||
const column = document.createElement("td");
|
||||
column.appendChild(button);
|
||||
return column;
|
||||
};
|
||||
|
||||
const bodyRow = (item, keys, callbacks) => {
|
||||
const rowElement = document.createElement("tr");
|
||||
if (callbacks?.star) {
|
||||
const starIcon = item.star ? "star-fill" : "star";
|
||||
rowElement.appendChild(bodyButtonCell(item, starIcon, "null", callbacks.star));
|
||||
}
|
||||
if (!keys || !Array.isArray(keys)) {
|
||||
Object.values(item).forEach((value) => rowElement.appendChild(bodyCell(value)));
|
||||
} else {
|
||||
keys.forEach((key) => rowElement.appendChild(bodyCell(deepGetByPath(item, key))));
|
||||
}
|
||||
if (callbacks) {
|
||||
if (callbacks.edit) {
|
||||
rowElement.appendChild(bodyButtonCell(item, "pencil-fill", "warning", callbacks.edit));
|
||||
}
|
||||
if (callbacks.delete) {
|
||||
rowElement.appendChild(bodyButtonCell(item, "trash-fill", "danger", callbacks.delete));
|
||||
}
|
||||
}
|
||||
return rowElement;
|
||||
};
|
||||
|
||||
const body = (data, keys, callbacks) => {
|
||||
if (!data || !Array.isArray(data)) {
|
||||
throw new Error("Data is not defined");
|
||||
}
|
||||
const bodyElement = document.createElement("tbody");
|
||||
data.forEach((item) => bodyElement.appendChild(bodyRow(item, keys, callbacks)));
|
||||
return bodyElement;
|
||||
};
|
||||
|
||||
export const table = (cols) => {
|
||||
const tableElement = document.createElement("table");
|
||||
tableElement.classList.add("table", "table-hover", "table-sm");
|
||||
tableElement.appendChild(head(cols));
|
||||
return tableElement;
|
||||
};
|
||||
|
||||
export const populateTable = (tableElement, data, keys, callbacks) => {
|
||||
const tableBody = tableElement.querySelector("tbody");
|
||||
if (tableBody) {
|
||||
tableElement.removeChild(tableBody);
|
||||
}
|
||||
tableElement.appendChild(body(data, keys, callbacks));
|
||||
};
|
||||
70
components/toast-helper.js
Normal file
70
components/toast-helper.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Toast } from "bootstrap";
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import moment from "moment";
|
||||
|
||||
export const toastsInit = () => {
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.classList.add("toast-container", "position-fixed", "top-0", "end-0", "p-3", "mt-5");
|
||||
const body = document.querySelector("body");
|
||||
body.appendChild(wrapper);
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
const toast = () => {
|
||||
const toastElement = document.createElement("div");
|
||||
toastElement.classList.add("toast", "bg-dark", "text-light");
|
||||
toastElement.setAttribute("role", "alert");
|
||||
return toastElement;
|
||||
};
|
||||
|
||||
const caption = (title) => {
|
||||
const captionElement = document.createElement("strong");
|
||||
captionElement.classList.add("me-auto");
|
||||
captionElement.innerText = title;
|
||||
return captionElement;
|
||||
};
|
||||
|
||||
const dateTime = () => {
|
||||
const dateTimeElement = document.createElement("small");
|
||||
dateTimeElement.classList.add("text-light");
|
||||
dateTimeElement.innerText = moment().format("DD.MM HH:mm");
|
||||
return dateTimeElement;
|
||||
};
|
||||
|
||||
const closeButton = () => {
|
||||
const closeElement = document.createElement("button");
|
||||
closeElement.setAttribute("type", "button");
|
||||
closeElement.setAttribute("data-bs-dismiss", "toast");
|
||||
closeElement.classList.add("btn-close", "btn-close-white");
|
||||
return closeElement;
|
||||
};
|
||||
|
||||
const header = (title) => {
|
||||
const headerElement = document.createElement("div");
|
||||
headerElement.classList.add("toast-header", "bg-dark", "text-light");
|
||||
headerElement.appendChild(caption(title));
|
||||
headerElement.appendChild(dateTime());
|
||||
headerElement.appendChild(closeButton());
|
||||
return headerElement;
|
||||
};
|
||||
|
||||
const body = (message) => {
|
||||
const bodyElement = document.createElement("div");
|
||||
bodyElement.classList.add("toast-body");
|
||||
bodyElement.innerText = message;
|
||||
return bodyElement;
|
||||
};
|
||||
|
||||
export const showToast = (container, title, message) => {
|
||||
const toastElement = toast();
|
||||
toastElement.appendChild(header(title));
|
||||
toastElement.appendChild(body(message));
|
||||
|
||||
toastElement.addEventListener("hidden.bs.toast", () => {
|
||||
container.removeChild(toastElement);
|
||||
});
|
||||
|
||||
container.appendChild(toastElement);
|
||||
|
||||
new Toast(toastElement).show();
|
||||
};
|
||||
58
components/videos/form/controller.js
Normal file
58
components/videos/form/controller.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import FormModel from "./model";
|
||||
import FormView from "./view";
|
||||
|
||||
class FormElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.currentId = null;
|
||||
|
||||
const saveCallback = this.save.bind(this);
|
||||
const backCallback = this.back.bind(this);
|
||||
this.view = new FormView(this, saveCallback, backCallback);
|
||||
this.model = new FormModel();
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
this.currentId = params.get("id") || null;
|
||||
console.log("FormElement connected");
|
||||
|
||||
try {
|
||||
this.view.render(this.model);
|
||||
await this.model.getPlaylists();
|
||||
await this.model.getCategories();
|
||||
console.log("Playlists:", this.model.playlists);
|
||||
console.log("Categories:", this.model.categories);
|
||||
this.view.update(this.model);
|
||||
if (this.currentId) {
|
||||
await this.get();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading data:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async get() {
|
||||
if (this.currentId) {
|
||||
await this.model.get(this.currentId);
|
||||
}
|
||||
this.view.update(this.model);
|
||||
}
|
||||
|
||||
async save() {
|
||||
if (!this.currentId) {
|
||||
await this.model.create();
|
||||
} else {
|
||||
await this.model.update();
|
||||
}
|
||||
this.view.successToast();
|
||||
this.view.update(this.model);
|
||||
}
|
||||
|
||||
back() {
|
||||
window.location.href = "/pageAccount"; // ?
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("videos-form", FormElement);
|
||||
50
components/videos/form/model.js
Normal file
50
components/videos/form/model.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import { createItem, getAllItems, getItem, updateItem } from "../../api/client";
|
||||
|
||||
const PATH = "videos";
|
||||
|
||||
export default class FormModel {
|
||||
constructor() {
|
||||
this.element = {};
|
||||
this.playlists = [];
|
||||
this.categories = [];
|
||||
}
|
||||
|
||||
async getPlaylists() {
|
||||
this.playlists = [];
|
||||
this.playlists = await getAllItems("playlists");
|
||||
}
|
||||
|
||||
async getCategories() {
|
||||
this.categories = [];
|
||||
this.categories = await getAllItems("categories");
|
||||
}
|
||||
|
||||
async get(id) {
|
||||
if (!id) {
|
||||
throw new Error("Element id is not defined!");
|
||||
}
|
||||
this.element = await getItem(PATH, id);
|
||||
}
|
||||
|
||||
async create() {
|
||||
if (!this.element || Object.keys(this.element).length === 0) {
|
||||
throw new Error("Item is null or empty!");
|
||||
}
|
||||
this.element = await createItem(PATH, this.element);
|
||||
}
|
||||
|
||||
async update() {
|
||||
if (!this.element || Object.keys(this.element).length === 0) {
|
||||
throw new Error("Item is null or empty!");
|
||||
}
|
||||
this.element = await updateItem(PATH, this.element.id, this.element);
|
||||
}
|
||||
|
||||
getValue(attribute) {
|
||||
return this.element[attribute] || "";
|
||||
}
|
||||
|
||||
setValue(attribute, value) {
|
||||
this.element[attribute] = value;
|
||||
}
|
||||
}
|
||||
70
components/videos/form/view.js
Normal file
70
components/videos/form/view.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import validation from "../../../js/validation";
|
||||
import populateSelect from "../../select-helper";
|
||||
import { showToast, toastsInit } from "../../toast-helper";
|
||||
|
||||
const getKey = (input) => input.getAttribute("id").replace("-", "_");
|
||||
|
||||
export default class FormView {
|
||||
constructor(root, saveCallback, backCallback) {
|
||||
this.root = root;
|
||||
this.saveCallback = saveCallback;
|
||||
this.backCallback = backCallback;
|
||||
}
|
||||
|
||||
render(model) {
|
||||
const template = document.getElementById("videos-form-template").content.cloneNode(true);
|
||||
if (!template) {
|
||||
console.error("Template not found!");
|
||||
return;
|
||||
}
|
||||
console.debug("GOOD");
|
||||
const form = template.querySelector("form");
|
||||
form.addEventListener("submit", async () => {
|
||||
if (!form.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
this.saveCallback();
|
||||
});
|
||||
|
||||
this.inputs = template.querySelectorAll("input");
|
||||
this.inputs.forEach((input) => {
|
||||
input.addEventListener("change", (event) => {
|
||||
model.setValue(getKey(event.target), event.target.value);
|
||||
});
|
||||
});
|
||||
|
||||
this.playlistsSelector = template.getElementById("playlist");
|
||||
this.playlistsSelector.addEventListener("change", (event) => {
|
||||
model.setValue("playlistId", event.target.value);
|
||||
});
|
||||
populateSelect(this.playlistsSelector, model.playlists);
|
||||
|
||||
this.categoriesSelector = template.getElementById("category");
|
||||
this.categoriesSelector.addEventListener("change", (event) => {
|
||||
model.setValue("categoryId", event.target.value);
|
||||
});
|
||||
populateSelect(this.categoriesSelector, model.categories);
|
||||
|
||||
const backButton = template.getElementById("btn-back");
|
||||
backButton.addEventListener("click", this.backCallback);
|
||||
|
||||
this.root.appendChild(template);
|
||||
|
||||
this.toasts = toastsInit();
|
||||
|
||||
validation();
|
||||
}
|
||||
|
||||
update(model) {
|
||||
this.inputs.forEach((input) => {
|
||||
const control = input;
|
||||
control.value = model.getValue(getKey(input));
|
||||
});
|
||||
this.playlistsSelector.value = model.element.playlistId || "";
|
||||
this.categoriesSelector.value = model.element.categoryId || "";
|
||||
}
|
||||
|
||||
successToast() {
|
||||
showToast(this.toasts, "Сохранение", "Сохранение успешно завершено");
|
||||
}
|
||||
}
|
||||
42
components/videos/table/controller.js
Normal file
42
components/videos/table/controller.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import TableModel from "./model";
|
||||
import TableView from "./view";
|
||||
|
||||
class TableElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
const toggleStarCallback = this.toggleStar.bind(this);
|
||||
const editCallback = this.editVideo.bind(this);
|
||||
const deleteCallback = this.deleteVideo.bind(this);
|
||||
this.view = new TableView(this, toggleStarCallback, editCallback, deleteCallback);
|
||||
this.model = new TableModel();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.view.render();
|
||||
this.getAllVideos();
|
||||
}
|
||||
|
||||
async getAllVideos() {
|
||||
await this.model.getAll();
|
||||
this.view.update(this.model);
|
||||
}
|
||||
|
||||
editVideo(item) {
|
||||
window.location.href = `/pageForm?id=${item.id}`; // ?
|
||||
}
|
||||
|
||||
toggleStar(item) {
|
||||
this.model.toggleStar(item);
|
||||
this.view.update(this.model);
|
||||
}
|
||||
|
||||
async deleteVideo(item) {
|
||||
if (await this.view.deleteQuestion(item)) {
|
||||
await this.model.delete(item);
|
||||
this.view.successToast();
|
||||
this.getAllVideos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("videos-table", TableElement);
|
||||
37
components/videos/table/model.js
Normal file
37
components/videos/table/model.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { deleteItem, getAllItems } from "../../api/client";
|
||||
import { lsReadArray, lsSave } from "../../storage";
|
||||
|
||||
const PATH = "video";
|
||||
const STARS_KEY = "video-stars";
|
||||
|
||||
export default class TableModel {
|
||||
constructor() {
|
||||
this.data = [];
|
||||
this.stars = lsReadArray(STARS_KEY);
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
const elements = await getAllItems(PATH, "_embed=playlist&_embed=category");
|
||||
this.data = elements.map((item) => ({
|
||||
...item,
|
||||
star: this.stars.includes(item.id),
|
||||
}));
|
||||
}
|
||||
|
||||
async delete(item) {
|
||||
await deleteItem(PATH, item.id);
|
||||
this.stars = this.stars.filter((element) => element !== item.id);
|
||||
lsSave(STARS_KEY, this.stars);
|
||||
}
|
||||
|
||||
toggleStar(item) {
|
||||
const current = item;
|
||||
current.star = !current.star;
|
||||
if (current.star) {
|
||||
this.stars.push(current.id);
|
||||
} else {
|
||||
this.stars = this.stars.filter((element) => element !== current.id);
|
||||
}
|
||||
lsSave(STARS_KEY, this.stars);
|
||||
}
|
||||
}
|
||||
42
components/videos/table/view.js
Normal file
42
components/videos/table/view.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import showQuestion from "../../modal-helper";
|
||||
import { populateTable, table } from "../../table-helper";
|
||||
import { showToast, toastsInit } from "../../toast-helper";
|
||||
|
||||
export default class TableView {
|
||||
constructor(root, toggleStarCallback, editCallback, deleteCallback) {
|
||||
this.root = root;
|
||||
this.toggleStarCallback = toggleStarCallback;
|
||||
this.editCallback = editCallback;
|
||||
this.deleteCallback = deleteCallback;
|
||||
}
|
||||
|
||||
render() {
|
||||
const columns = ["", "№", "Название", "Превью", "Описание", "Плейлист", "Категория", "Группа", "", ""];
|
||||
this.keys = ["id", "name", "image", "description", "playlist.name", "category.name"];
|
||||
this.callbacks = {
|
||||
star: this.toggleStarCallback,
|
||||
edit: this.editCallback,
|
||||
delete: this.deleteCallback,
|
||||
};
|
||||
this.tableElement = table(columns);
|
||||
|
||||
const tableWrapper = document.createElement("div");
|
||||
tableWrapper.classList.add("table-responsive");
|
||||
tableWrapper.appendChild(this.tableElement);
|
||||
this.root.appendChild(tableWrapper);
|
||||
|
||||
this.toasts = toastsInit();
|
||||
}
|
||||
|
||||
update(model) {
|
||||
populateTable(this.tableElement, model.data, this.keys, this.callbacks);
|
||||
}
|
||||
|
||||
deleteQuestion(item = null) {
|
||||
return showQuestion("Удаление", "Удалить", `Удалить элемент '${item.id}'?`);
|
||||
}
|
||||
|
||||
successToast() {
|
||||
showToast(this.toasts, "Удаление", "Удаление успешно завершено");
|
||||
}
|
||||
}
|
||||
64
database/data.json
Normal file
64
database/data.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"video": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "new_vid",
|
||||
"image": "adfefwefw",
|
||||
"description": "frefeerfe",
|
||||
"playlistId": "1",
|
||||
"сategoryId": "1"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "new_vid2",
|
||||
"image": "fdsdvrvr",
|
||||
"description": "vvgtgtg",
|
||||
"playlistId": "0",
|
||||
"сategoryId": "1"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "new_vid3",
|
||||
"image": "gergreg",
|
||||
"description": "wsfrf3",
|
||||
"playlistId": "3",
|
||||
"сategoryId": "2"
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"name": "new_vid4",
|
||||
"image": "btbtb4b",
|
||||
"description": "xccrfr",
|
||||
"playlistId": "2",
|
||||
"сategoryId": "3"
|
||||
}
|
||||
],
|
||||
"playlist": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Very good videos"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "baskemtbal"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "How to make a nuclear bomb at home! Guide"
|
||||
}
|
||||
],
|
||||
"сategory": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "jst vibing"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "letsplay"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "cooking"
|
||||
}
|
||||
]
|
||||
}
|
||||
1
dist/assets/page3-B-T1pNgL.js
vendored
Normal file
1
dist/assets/page3-B-T1pNgL.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import{g as y,d as f,M as w,t as A,s as v}from"./toast-helper-Cn8ctBTk.js";/* empty css */const p=(e,t)=>{localStorage.setItem(e,JSON.stringify(t))},T=e=>JSON.parse(localStorage.getItem(e))||[],u="video",c="video-stars";class S{constructor(){this.data=[],this.stars=T(c)}async getAll(){const t=await y(u,"_embed=playlist&_embed=category");this.data=t.map(n=>({...n,star:this.stars.includes(n.id)}))}async delete(t){await f(u,t.id),this.stars=this.stars.filter(n=>n!==t.id),p(c,this.stars)}toggleStar(t){const n=t;n.star=!n.star,n.star?this.stars.push(n.id):this.stars=this.stars.filter(s=>s!==n.id),p(c,this.stars)}}const L=()=>{const e=document.createElement("div");return e.classList.add("modal"),e.setAttribute("tabindex",-1),e},d=e=>{const t=document.createElement("div");return t.classList.add(e),t},C=e=>{const t=document.createElement("button");return t.setAttribute("type","button"),t.addEventListener("click",()=>e()),t},k=(e,t)=>{const n=d("modal-header"),s=document.createElement("h5");s.innerText=e,n.appendChild(s);const o=C(t);return o.classList.add("btn-close"),n.appendChild(o),n},V=(e,t,n)=>{const s=d("modal-footer"),o=document.createElement("button");o.classList.add("btn","btn-primary"),o.setAttribute("type","button"),o.innerText=e,o.addEventListener("click",()=>t()),s.appendChild(o);const a=C(n);return a.classList.add("btn","btn-secondary"),a.innerText="Отмена",s.appendChild(a),s},I=(e,t,n,s,o)=>{const a=L(),i=d("modal-dialog"),l=d("modal-content"),r=k(e,o);l.appendChild(r);const m=d("modal-body");m.appendChild(n),l.appendChild(m);const g=V(t,s,o);return l.appendChild(g),i.appendChild(l),a.appendChild(i),a},x=(e,t,n)=>{const s=document.createElement("p");return s.innerText=n,new Promise(o=>{let a;const r=I(e,t,s,()=>{a.hide(),o(!0)},()=>{a.hide(),o(!1)});a=new w(r),a.show()})},b=e=>{const t=document.createElement("i");return t.classList.add("bi",`bi-${e}`),t},B=(e,t,n="primary")=>{const s=document.createElement("button");return s.classList.add("btn",`btn-${n}`),s.setAttribute("type","button"),b&&s.appendChild(b(t)),s},M=(e,t)=>B(null,e,t),$=(e,t)=>t.reduce((n,s)=>(n==null?void 0:n[s])??null,e),O=(e,t)=>$(e,t.split(".").filter(n=>n!=="")),P=e=>{const t=document.createElement("th");return t.setAttribute("scope","col"),t.innerText=e,t},Q=e=>{if(!e||!Array.isArray(e)||e.length===0)throw new Error("Columns are not defined");const t=document.createElement("thead"),n=document.createElement("tr");return e.forEach(s=>n.appendChild(P(s))),t.appendChild(n),t},E=e=>{const t=document.createElement("td");return t.innerText=e,t},h=(e,t,n,s)=>{const o=M(t,n);o.addEventListener("click",()=>s(e));const a=document.createElement("td");return a.appendChild(o),a},R=(e,t,n)=>{const s=document.createElement("tr");if(n!=null&&n.star){const o=e.star?"star-fill":"star";s.appendChild(h(e,o,"null",n.star))}return!t||!Array.isArray(t)?Object.values(e).forEach(o=>s.appendChild(E(o))):t.forEach(o=>s.appendChild(E(O(e,o)))),n&&(n.edit&&s.appendChild(h(e,"pencil-fill","warning",n.edit)),n.delete&&s.appendChild(h(e,"trash-fill","danger",n.delete))),s},_=(e,t,n)=>{if(!e||!Array.isArray(e))throw new Error("Data is not defined");const s=document.createElement("tbody");return e.forEach(o=>s.appendChild(R(o,t,n))),s},q=e=>{const t=document.createElement("table");return t.classList.add("table","table-hover","table-sm"),t.appendChild(Q(e)),t},D=(e,t,n,s)=>{const o=e.querySelector("tbody");o&&e.removeChild(o),e.appendChild(_(t,n,s))};class G{constructor(t,n,s,o){this.root=t,this.toggleStarCallback=n,this.editCallback=s,this.deleteCallback=o}render(){const t=["","№","Название","Превью","Описание","Плейлист","Категория","Группа","",""];this.keys=["id","name","image","description","playlist.name","category.name"],this.callbacks={star:this.toggleStarCallback,edit:this.editCallback,delete:this.deleteCallback},this.tableElement=q(t);const n=document.createElement("div");n.classList.add("table-responsive"),n.appendChild(this.tableElement),this.root.appendChild(n),this.toasts=A()}update(t){D(this.tableElement,t.data,this.keys,this.callbacks)}deleteQuestion(t=null){return x("Удаление","Удалить",`Удалить элемент '${t.id}'?`)}successToast(){v(this.toasts,"Удаление","Удаление успешно завершено")}}class H extends HTMLElement{constructor(){super();const t=this.toggleStar.bind(this),n=this.editVideo.bind(this),s=this.deleteVideo.bind(this);this.view=new G(this,t,n,s),this.model=new S}connectedCallback(){this.view.render(),this.getAllVideos()}async getAllVideos(){await this.model.getAll(),this.view.update(this.model)}editVideo(t){window.location.href=`/pageForm?id=${t.id}`}toggleStar(t){this.model.toggleStar(t),this.view.update(this.model)}async deleteVideo(t){await this.view.deleteQuestion(t)&&(await this.model.delete(t),this.view.successToast(),this.getAllVideos())}}customElements.define("videos-table",H);
|
||||
1
dist/assets/page6-CVUz5E0R.js
vendored
Normal file
1
dist/assets/page6-CVUz5E0R.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import{g as n,a as h,c as d,u as m,t as u,s as g}from"./toast-helper-Cn8ctBTk.js";/* empty css */const l="videos";class p{constructor(){this.element={},this.playlists=[],this.categories=[]}async getPlaylists(){this.playlists=[],this.playlists=await n("playlists")}async getCategories(){this.categories=[],this.categories=await n("categories")}async get(e){if(!e)throw new Error("Element id is not defined!");this.element=await h(l,e)}async create(){if(!this.element||Object.keys(this.element).length===0)throw new Error("Item is null or empty!");this.element=await d(l,this.element)}async update(){if(!this.element||Object.keys(this.element).length===0)throw new Error("Item is null or empty!");this.element=await m(l,this.element.id,this.element)}getValue(e){return this.element[e]||""}setValue(e,t){this.element[e]=t}}const y=()=>{document.querySelectorAll("form.needs-validation").forEach(e=>{e.setAttribute("novalidate",""),e.addEventListener("submit",t=>{t.preventDefault(),t.stopPropagation(),e.checkValidity()?e.classList.remove("was-validated"):e.classList.add("was-validated")})})},c=(a,e)=>{e.forEach(t=>{const s=document.createElement("option");s.value=t.id,s.innerText=t.name,a.appendChild(s)})},r=a=>a.getAttribute("id").replace("-","_");class v{constructor(e,t,s){this.root=e,this.saveCallback=t,this.backCallback=s}render(e){const t=document.getElementById("videos-form-template").content.cloneNode(!0);if(!t){console.error("Template not found!");return}console.debug("GOOD");const s=t.querySelector("form");s.addEventListener("submit",async()=>{s.checkValidity()&&this.saveCallback()}),this.inputs=t.querySelectorAll("input"),this.inputs.forEach(i=>{i.addEventListener("change",o=>{e.setValue(r(o.target),o.target.value)})}),this.playlistsSelector=t.getElementById("playlist"),this.playlistsSelector.addEventListener("change",i=>{e.setValue("playlistId",i.target.value)}),c(this.playlistsSelector,e.playlists),this.categoriesSelector=t.getElementById("category"),this.categoriesSelector.addEventListener("change",i=>{e.setValue("categoryId",i.target.value)}),c(this.categoriesSelector,e.categories),t.getElementById("btn-back").addEventListener("click",this.backCallback),this.root.appendChild(t),this.toasts=u(),y()}update(e){this.inputs.forEach(t=>{const s=t;s.value=e.getValue(r(t))}),this.playlistsSelector.value=e.element.playlistId||"",this.categoriesSelector.value=e.element.categoryId||""}successToast(){g(this.toasts,"Сохранение","Сохранение успешно завершено")}}class w extends HTMLElement{constructor(){super(),this.currentId=null;const e=this.save.bind(this),t=this.back.bind(this);this.view=new v(this,e,t),this.model=new p}async connectedCallback(){const e=new URLSearchParams(document.location.search);this.currentId=e.get("id")||null,console.log("FormElement connected");try{this.view.render(this.model),await this.model.getPlaylists(),await this.model.getCategories(),console.log("Playlists:",this.model.playlists),console.log("Categories:",this.model.categories),this.view.update(this.model),this.currentId&&await this.get()}catch(t){console.error("Error loading data:",t)}}async get(){this.currentId&&await this.model.get(this.currentId),this.view.update(this.model)}async save(){this.currentId?await this.model.update():await this.model.create(),this.view.successToast(),this.view.update(this.model)}back(){window.location.href="/pageAccount"}}customElements.define("videos-form",w);
|
||||
14
dist/assets/toast-helper-Cn8ctBTk.js
vendored
Normal file
14
dist/assets/toast-helper-Cn8ctBTk.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
dist/pageAccount.html
vendored
10
dist/pageAccount.html
vendored
@@ -6,6 +6,8 @@
|
||||
<title>Мой аккаунт</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
|
||||
|
||||
<script type="module" crossorigin src="/assets/page3-B-T1pNgL.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/toast-helper-Cn8ctBTk.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/styleStreamingService-CUHvxLFI.css">
|
||||
</head>
|
||||
@@ -34,6 +36,12 @@
|
||||
<div class="buttons d-flex align-items-center gap-3">
|
||||
<div class="button">Выйти из аккаунта</div>
|
||||
<div class="button">Сменить пароль</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<a class="btn btn-primary" href="/pageForm"
|
||||
><i class="bi bi-plus-circle-fill me-2"></i>Создать новую запись</a
|
||||
>
|
||||
</div>
|
||||
<videos-table></videos-table>
|
||||
</div>
|
||||
<div class="footer overflow-hidden flex-shrink-1 h-75 p-0 px-4">
|
||||
@@ -47,6 +55,6 @@
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEnklEQVR4nO2aS2wbRRjHfae8xOuOqnbXalMkJLiAhDggBOVQaBFSpCKOoCJVnAoSQoKm0AMSJw6gIh4ioliigXbGjqzmUSVxaF4YE+okjpO6TrBD0zpOZuxde/dDM7YTO3bWM/auzcF/6bvsrmf3N99jvp21y9VRRx21TF39m4+6vfSEgkmPgqhHwWRYxXSSGyJDxWM97Jr9OP2I6/+kw1dSD7oxeVdB9LqKiKliCkKGiKlgOq4gcurIpbsPtBVAReQzFZO08MPvDbWhYnKu5UAKoicVRJNNA+wyBdOEG9NuxwEO9sG9KiK9dgNUASHyg3sQ9jkCcejXzcdURKechlB3bJIVD/shMJlrIQQUjMzZBsPCSUF0uvUQdNsztoQZi9c2QgDPGUx+ahKCnmw3hFq0hqsZWyecKLG17IWhDJwJavDM1YyFV2iioXWmsNg59/BdPgrvzWgQuG2ACQWdCWr1QqxH2huF1dZ+gBeHM/D1Yg7WtdLj7+jYSNb694impLzCeic7H/6Ij8LpaQ3GymZ/t3QDuJfqjaVg8o4wCG8AbQB4yWL2NV2vgAqlDLFxEQ0IQbAFSKqLlZz9XD4PscQaBOejoOm57eMXYzlBEGIe+C39cF0Q9Qp9vRGAl4uzf1ffK3gA1jfSEFpYgj/no7BFMxXnPgrpwvdyY3K8flhh0iM64BNls28lmsnC/M04zIQj8FdkCWhWq7rmxGidRMflRj4WyQ9PvYGOXivMfspi9pnyeQPiyX/hj3CEQ8wuLkNW06uvM4FPiioIomByUQCEXKv148NeCh+GdJ6UIiqFEQNgdiMaAz23kxPlCqcFEx1v58mgiEdqNohfzFXPZC2RsjAq2dzyLZ7ke+mXeF4ShE41DMLaCH8iD1s56zAqB2C2EFsBw7D24tlZ3QkQUjO0SnbIS+H4aBY+D+s8yTWjOoxKFo3/A6ZpnUdM3YGsI6HlkRn02FC6CoDZrcRa/TgEAMMEeLJfrtQrQskuUX5Ldjm0XAGxsnYbRBXZNOW8gQXLL9s8kx34g7HkNkRfcBkGltaFQS6vSCY6pnAQkdccaVGe86dhugjy3cQSXBoYgx9nYkIg529IJjomhlCLUggvOi47S55gjINcCCxA38Aot68CEd7VWunNcelEHxOC4CCInJIFOT1SKL3fjMxyCM9AAF7xJXlFqtX9MrGjT/kzsh55WxiEvbzIvlg93b8FU+FF6B2e4BBHfcmKNWhhs9o1N4lkoiOaetx/536XjNherKxXvpyMw8+7IErGSuzVROXq7l2VS3QFkbMuWe3HcJ+K6KrMjZ71bcDz3juWi+mF6E5r8Onf4omuNLr5wMS2YGS9ImJv/Z6F94Mab0QlQN5wNSMFk++dgJHLDdLbFAT3yiDsK351aguEguj1rn64x2WH+CLZjk1sRMK2f6JT/BsPsR2MFoJM2P5ZoTzMWrKpjci3toWTJRCm3awc2g9AV5uuTrJiNZ23/HZsrSKaUhD5RHrVtlPs5mwbs5A/xBAHIAZrAFnv1FaAWmLtNXtXYLPL3uLYK2nZHwYG2TF+zkteZcWj5iAdddSRywn9B9E5uZlJrzFcAAAAAElFTkSuQmCC" alt="tg"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
94
dist/pageForm.html
vendored
Normal file
94
dist/pageForm.html
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Новое видео</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
|
||||
|
||||
<script type="module" crossorigin src="/assets/page6-CVUz5E0R.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/toast-helper-Cn8ctBTk.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/styleStreamingService-CUHvxLFI.css">
|
||||
</head>
|
||||
<body class="min-vh-100 d-flex flex-column m-0 p-0">
|
||||
<header class="d-flex align-items-center justify-content-between position-sticky h-60 p-3">
|
||||
<div class="header-logo d-flex align-items-center g-3">
|
||||
<a href="pageMain.html"><img src="/assets/%D0%9A%D0%90%D0%99%D0%A4-Dbw7IVze.jpg" alt="Логотип"/></a>
|
||||
<h1>НАЗВАНИЕ СЕРВИСА</h1>
|
||||
</div>
|
||||
<nav class="navbar d-flex align-items-center justify-content-center me-3 g-3 column-gap-2">
|
||||
<a href="pageCategories.html">Категории</a>
|
||||
<nav class="dropdown position-relative">
|
||||
<span><a>Мой аккаунт ▾</a></span>
|
||||
<nav class="features-menu">
|
||||
<nav class="features-item"><a href="pageAccount.html">Настройки</a></nav>
|
||||
<nav class="features-item"><a href="pageSubscriptions.html">Подписки</a></nav>
|
||||
<nav class="features-item"><a href="pageSavedStreams.html">Сохраненные трансляции</a></nav>
|
||||
</nav>
|
||||
</nav>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="content flex-grow-1 p-3 px-4">
|
||||
<videos-form></videos-form>
|
||||
<template id="videos-form-template">
|
||||
<div class="row justify-content-center">
|
||||
<form class="col col-md-6 needs-validation">
|
||||
<div class="mb-2">
|
||||
<label for="id" class="form-label">Номер</label>
|
||||
<input type="text" class="form-control" id="id" readonly disabled />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="name" class="form-label">Название</label>
|
||||
<input type="text" class="form-control" id="name" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="image" class="form-label">Превью</label>
|
||||
<input type="text" class="form-control" id="image" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<input type="text" class="form-control" id="description" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="playlist" class="form-label">Плейлист</label>
|
||||
<select class="form-select" id="playlist">
|
||||
<option value="" selected>Выберите плейлист</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="category" class="form-label">Категория</label>
|
||||
<select class="form-select" id="category" required>
|
||||
<option value="" selected>Выберите категорию</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row row-cols-1 justify-content-center">
|
||||
<div class="col col-md-6 col-lg-4 col-xl-3 m-lg-0 mb-2">
|
||||
<button id="btn-save" type="submit" class="btn btn-primary d-block m-auto w-100">
|
||||
Сохранить
|
||||
</button>
|
||||
</div>
|
||||
<div class="col col-md-6 col-lg-4 col-xl-3 m-lg-0 mb-2">
|
||||
<button id="btn-back" type="button" class="btn btn-primary d-block m-auto w-100">
|
||||
Назад
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="footer overflow-hidden flex-shrink-1 h-75 p-0 px-4">
|
||||
<div class="footer-content d-flex justify-content-between align-items-center">
|
||||
<div class="company-name flex-grow-1">RBCS CORP. <i class="bi bi-c-circle"></i></div>
|
||||
<div class="footer-images d-flex gap-3 p-3">
|
||||
<a href="https://vk.com/sheym_not_shame" target="_blank">
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEcUlEQVR4nO2aX2wURRzH71GNSigSd5BAYmIwMTFGjfHVvvhm5E80QV980KqRRExnripwtgLyYDCaaJACUSFYQVBfjOFBia31wEM6v7tem0Na6l1pKdw1pbQ0ba9jfnu0ureztzO7e3s+3Df5JZd2Ojefnd+/mW0kUldddYUm8lbiHqO5ZyOhsJNQOE4YP00oJEzDzwyO4e8MltywqiW9IvJ/UsOWzN1GtOc1g/FuQnmRMBBKRnnRoPw3g0ETzlEzgOXRxLLS04Vx5cU7mEGhQBhvDR3oXgbPEcov+wWwATEYNhjfVHUAEkvcYVB+KGgACVD76q3dt1cFYk0LLEefrjYEWYohOIPJI1AIzDCEQio0CLZkydVbUw2BuRNhEK8BhCi5Ge8OxM3CiAlXGAr7/WenGkOQf3dmk/dCx2C41gBkyfgI1i5tELPNqPniwWqUv6cFgeRBVOzA3YtCQav6Y+8km+jFQ4Ni548jFlv7TlL6pZsPWse+8fXfjgtkJ3KWsRv3XawQK9CkDoINoGSS9s6rolyP7uqTfuGRM3nLuLODU9JxT3zQLxYWrHO+ftQZ2mC8S6P4ybvYPT+N2kAa92Z8gRzoumYZNz1bFOtivZXipKhU8THNOU1CT+RsIC8cHPQM8tTejJidt27HV/G8e6yw5AZf2QoXXS7cJS8gD2xPib6Rm5YxkzNF8cj7aYXA560qIMedJngw1iuKZf58Kj2hDYKuEx+4YXsobx7LqmUwyr9RcC34tdIk/aPWpzgztyD1aRnIfVEQL315SWQLszaIT34eU4MogfziDkLhz0qTfPG7NThR0ZM5V5DrN4viyvU5IRNmQ2UIZqbgc75Bnv74gm0hoxOz4v53UxVBZMKY2NLhnGqJLxAX10I7n522LQrTqC5IYWpeNH+bM11OC4YquJZ5ZeMyERYsmbb9MKwFsqjOC5Ni3Y5ejR3hHYE0i6uiYAavTEfPFsRDrWkbSOLSlOj4oyDyN+Rxgqn4sd3yLsFT+jUv2BQme3JPv5iYnpcuCotc+YL33wroNW8nRcvJYenfZq7MqO1MM6z31aKUGzZ32FKoaBHkvw9i4OqMbdz3PeNu8VFUPsfr3JY889lfYmxyThsE7fHdfeKaxNU2O7Q9ZnxQ6FSCKIHAqzpZ5OG2tPju/Liti3UDQXv58JBtbGJoqsKOwCt6x1zzGlMvLTZ+lDGbPkyrqiBoPZJ03ijpqg3G8yto310RHWFm0AVZNKwLCIWHqQ9PjYpPT4+JpiNDjuPRlQ7H8xZ7vn1A5lY7IrpCcoNBzitM0GZQyK6Mpe6MeFGls0noIEzhDFIRhvIDtYYglO+L+BVeVzqd4cPZCd61NjZ4m2+QEkyqAS+Uw98JgMAusS2vFRjvChEkHvhrBaubQXsIEJ8H5k5u2awaqdmgkPWdnbzUGbyL9dIBSAI6j8Vupdc6EYRu3do3mfGj/XoaOrF30m47qi0zuzXDekJ5G17Z4JF06R8GzM/mz9oIhWcDz0Z11VVXpJL+Aemdmrez0du5AAAAAElFTkSuQmCC" alt="vk"/>
|
||||
</a>
|
||||
<a href="https://t.me/sheymuh" target="_blank">
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEnklEQVR4nO2aS2wbRRjHfae8xOuOqnbXalMkJLiAhDggBOVQaBFSpCKOoCJVnAoSQoKm0AMSJw6gIh4ioliigXbGjqzmUSVxaF4YE+okjpO6TrBD0zpOZuxde/dDM7YTO3bWM/auzcF/6bvsrmf3N99jvp21y9VRRx21TF39m4+6vfSEgkmPgqhHwWRYxXSSGyJDxWM97Jr9OP2I6/+kw1dSD7oxeVdB9LqKiKliCkKGiKlgOq4gcurIpbsPtBVAReQzFZO08MPvDbWhYnKu5UAKoicVRJNNA+wyBdOEG9NuxwEO9sG9KiK9dgNUASHyg3sQ9jkCcejXzcdURKechlB3bJIVD/shMJlrIQQUjMzZBsPCSUF0uvUQdNsztoQZi9c2QgDPGUx+ahKCnmw3hFq0hqsZWyecKLG17IWhDJwJavDM1YyFV2iioXWmsNg59/BdPgrvzWgQuG2ACQWdCWr1QqxH2huF1dZ+gBeHM/D1Yg7WtdLj7+jYSNb694impLzCeic7H/6Ij8LpaQ3GymZ/t3QDuJfqjaVg8o4wCG8AbQB4yWL2NV2vgAqlDLFxEQ0IQbAFSKqLlZz9XD4PscQaBOejoOm57eMXYzlBEGIe+C39cF0Q9Qp9vRGAl4uzf1ffK3gA1jfSEFpYgj/no7BFMxXnPgrpwvdyY3K8flhh0iM64BNls28lmsnC/M04zIQj8FdkCWhWq7rmxGidRMflRj4WyQ9PvYGOXivMfspi9pnyeQPiyX/hj3CEQ8wuLkNW06uvM4FPiioIomByUQCEXKv148NeCh+GdJ6UIiqFEQNgdiMaAz23kxPlCqcFEx1v58mgiEdqNohfzFXPZC2RsjAq2dzyLZ7ke+mXeF4ShE41DMLaCH8iD1s56zAqB2C2EFsBw7D24tlZ3QkQUjO0SnbIS+H4aBY+D+s8yTWjOoxKFo3/A6ZpnUdM3YGsI6HlkRn02FC6CoDZrcRa/TgEAMMEeLJfrtQrQskuUX5Ldjm0XAGxsnYbRBXZNOW8gQXLL9s8kx34g7HkNkRfcBkGltaFQS6vSCY6pnAQkdccaVGe86dhugjy3cQSXBoYgx9nYkIg529IJjomhlCLUggvOi47S55gjINcCCxA38Aot68CEd7VWunNcelEHxOC4CCInJIFOT1SKL3fjMxyCM9AAF7xJXlFqtX9MrGjT/kzsh55WxiEvbzIvlg93b8FU+FF6B2e4BBHfcmKNWhhs9o1N4lkoiOaetx/536XjNherKxXvpyMw8+7IErGSuzVROXq7l2VS3QFkbMuWe3HcJ+K6KrMjZ71bcDz3juWi+mF6E5r8Onf4omuNLr5wMS2YGS9ImJv/Z6F94Mab0QlQN5wNSMFk++dgJHLDdLbFAT3yiDsK351aguEguj1rn64x2WH+CLZjk1sRMK2f6JT/BsPsR2MFoJM2P5ZoTzMWrKpjci3toWTJRCm3awc2g9AV5uuTrJiNZ23/HZsrSKaUhD5RHrVtlPs5mwbs5A/xBAHIAZrAFnv1FaAWmLtNXtXYLPL3uLYK2nZHwYG2TF+zkteZcWj5iAdddSRywn9B9E5uZlJrzFcAAAAAElFTkSuQmCC" alt="tg"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
8
dist/pageMain.html
vendored
8
dist/pageMain.html
vendored
@@ -53,12 +53,12 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
name="images"
|
||||
value="https://i.ytimg.com/vi/enyylQ9sFRc/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGGAgZShUMA8=&rs=AOn4CLAnYQfwEgcw2b2sKoCxOJE8xs0Hbg"
|
||||
value="https://avatars.mds.yandex.net/i?id=7839e8d6f40309bdce67fac62990d108_sr-10636981-images-thumbs&n=13"
|
||||
id="derzko"
|
||||
/>
|
||||
<label for="derzko"
|
||||
><img
|
||||
src="https://i.ytimg.com/vi/enyylQ9sFRc/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGGAgZShUMA8=&rs=AOn4CLAnYQfwEgcw2b2sKoCxOJE8xs0Hbg"
|
||||
src="https://avatars.mds.yandex.net/i?id=7839e8d6f40309bdce67fac62990d108_sr-10636981-images-thumbs&n=13"
|
||||
alt="derzko"
|
||||
/></label>
|
||||
</div>
|
||||
@@ -79,12 +79,12 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
name="images"
|
||||
value="https://i.ytimg.com/vi/WCrXqI_0FhU/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGEwgWChlMA8=&rs=AOn4CLDIJ5NCb473i-I_fpiD4-G8TEPjLw"
|
||||
value="https://avatars.mds.yandex.net/i?id=c111e02e6999cca7c4a7aa47f00a2ab3c384b6dd-5884537-images-thumbs&n=13"
|
||||
id="teddy"
|
||||
/>
|
||||
<label for="teddy"
|
||||
><img
|
||||
src="https://i.ytimg.com/vi/WCrXqI_0FhU/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGEwgWChlMA8=&rs=AOn4CLDIJ5NCb473i-I_fpiD4-G8TEPjLw"
|
||||
src="https://avatars.mds.yandex.net/i?id=c111e02e6999cca7c4a7aa47f00a2ab3c384b6dd-5884537-images-thumbs&n=13"
|
||||
alt="teddy"
|
||||
/></label>
|
||||
</div>
|
||||
|
||||
156
dist/pageSavedStreams.html
vendored
156
dist/pageSavedStreams.html
vendored
@@ -1,79 +1,79 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Сохранённые трансляции</title>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" />
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Сохранённые трансляции</title>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" />
|
||||
|
||||
</head>
|
||||
<body class="min-vh-100 d-flex flex-column m-0 p-0">
|
||||
<header class="d-flex align-items-center justify-content-between position-sticky h-60 p-3">
|
||||
<div class="header-logo d-flex align-items-center g-3">
|
||||
<a href="pageMain.html"><img src="/assets/%D0%9A%D0%90%D0%99%D0%A4-Dbw7IVze.jpg" alt="Логотип" /></a>
|
||||
<h1>НАЗВАНИЕ СЕРВИСА</h1>
|
||||
</div>
|
||||
<nav class="navbar d-flex align-items-center justify-content-center me-3 g-3 column-gap-2">
|
||||
<a href="pageCategories.html">Категории</a>
|
||||
<nav class="dropdown position-relative">
|
||||
<span><a>Мой аккаунт ▾</a></span>
|
||||
<nav class="features-menu">
|
||||
<nav class="features-item"><a href="pageAccount.html">Настройки</a></nav>
|
||||
<nav class="features-item"><a href="pageSubscriptions.html">Подписки</a></nav>
|
||||
<nav class="features-item"><a href="pageSavedStreams.html">Сохраненные трансляции</a></nav>
|
||||
</nav>
|
||||
</nav>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="content flex-grow-1 p-3 px-4">
|
||||
<h2>Смотреть позже <i class="bi bi-clock-fill"></i></h2>
|
||||
<div class="photo-grid-container d-flex justify-content-center">
|
||||
<div class="photo-grid d-flex align-items-center flex-wrap w-100" id="savedImagesGrid">
|
||||
<div class="photo-grid-item"><img src="/assets/2016-BUapLfv6.jpeg" alt="стрим ксго" /></div>
|
||||
<div class="photo-grid-item"><img src="/assets/%D0%B0%D1%81%D0%BC%D1%80%20%D1%87%D0%B5%D0%BB%D0%BE%D0%B2%D0%B5%D0%BA%20%D0%BF%D0%B0%D1%83%D0%BA-RgZcxljT.webp" alt="асмр" /></div>
|
||||
<div class="photo-grid-item"><img src="/assets/%D1%80%D0%B5%D0%B7%D0%BD%D1%8F-Du5Ks9ja.jpg" alt="резня" /></div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Запланированные трансляции <i class="bi bi-calendar-event"></i></h2>
|
||||
<div class="photo-grid-container d-flex justify-content-center">
|
||||
<div class="photo-grid d-flex align-items-center flex-wrap w-100">
|
||||
<div class="photo-grid-item"><img src="/assets/%D1%81%D1%82%D1%80%D0%B8%D0%BC%20%D0%BA%D1%81%D0%B3%D0%BE-d7c_w3u0.webp" alt="стрим ксго" /></div>
|
||||
<div class="photo-grid-item"><img src="/assets/goats-D56R5-1i.png" alt="goats" /></div>
|
||||
<div class="photo-grid-item"><img src="/assets/%D0%BF%D0%B0%D0%BF%D0%B0%D0%BD%D1%8F-BZOoCf1B.jpg" alt="папаня" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer overflow-hidden flex-shrink-1 h-75 p-0 px-4">
|
||||
<div class="footer-content d-flex justify-content-between align-items-center">
|
||||
<div class="company-name flex-grow-1">RBCS CORP. <i class="bi bi-c-circle"></i></div>
|
||||
<div class="footer-images d-flex gap-3 p-3">
|
||||
<a href="https://vk.com/sheym_not_shame" target="_blank">
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEcUlEQVR4nO2aX2wURRzH71GNSigSd5BAYmIwMTFGjfHVvvhm5E80QV980KqRRExnripwtgLyYDCaaJACUSFYQVBfjOFBia31wEM6v7tem0Na6l1pKdw1pbQ0ba9jfnu0ureztzO7e3s+3Df5JZd2Ojefnd+/mW0kUldddYUm8lbiHqO5ZyOhsJNQOE4YP00oJEzDzwyO4e8MltywqiW9IvJ/UsOWzN1GtOc1g/FuQnmRMBBKRnnRoPw3g0ETzlEzgOXRxLLS04Vx5cU7mEGhQBhvDR3oXgbPEcov+wWwATEYNhjfVHUAEkvcYVB+KGgACVD76q3dt1cFYk0LLEefrjYEWYohOIPJI1AIzDCEQio0CLZkydVbUw2BuRNhEK8BhCi5Ge8OxM3CiAlXGAr7/WenGkOQf3dmk/dCx2C41gBkyfgI1i5tELPNqPniwWqUv6cFgeRBVOzA3YtCQav6Y+8km+jFQ4Ni548jFlv7TlL6pZsPWse+8fXfjgtkJ3KWsRv3XawQK9CkDoINoGSS9s6rolyP7uqTfuGRM3nLuLODU9JxT3zQLxYWrHO+ftQZ2mC8S6P4ybvYPT+N2kAa92Z8gRzoumYZNz1bFOtivZXipKhU8THNOU1CT+RsIC8cHPQM8tTejJidt27HV/G8e6yw5AZf2QoXXS7cJS8gD2xPib6Rm5YxkzNF8cj7aYXA560qIMedJngw1iuKZf58Kj2hDYKuEx+4YXsobx7LqmUwyr9RcC34tdIk/aPWpzgztyD1aRnIfVEQL315SWQLszaIT34eU4MogfziDkLhz0qTfPG7NThR0ZM5V5DrN4viyvU5IRNmQ2UIZqbgc75Bnv74gm0hoxOz4v53UxVBZMKY2NLhnGqJLxAX10I7n522LQrTqC5IYWpeNH+bM11OC4YquJZ5ZeMyERYsmbb9MKwFsqjOC5Ni3Y5ejR3hHYE0i6uiYAavTEfPFsRDrWkbSOLSlOj4oyDyN+Rxgqn4sd3yLsFT+jUv2BQme3JPv5iYnpcuCotc+YL33wroNW8nRcvJYenfZq7MqO1MM6z31aKUGzZ32FKoaBHkvw9i4OqMbdz3PeNu8VFUPsfr3JY889lfYmxyThsE7fHdfeKaxNU2O7Q9ZnxQ6FSCKIHAqzpZ5OG2tPju/Liti3UDQXv58JBtbGJoqsKOwCt6x1zzGlMvLTZ+lDGbPkyrqiBoPZJ03ijpqg3G8yto310RHWFm0AVZNKwLCIWHqQ9PjYpPT4+JpiNDjuPRlQ7H8xZ7vn1A5lY7IrpCcoNBzitM0GZQyK6Mpe6MeFGls0noIEzhDFIRhvIDtYYglO+L+BVeVzqd4cPZCd61NjZ4m2+QEkyqAS+Uw98JgMAusS2vFRjvChEkHvhrBaubQXsIEJ8H5k5u2awaqdmgkPWdnbzUGbyL9dIBSAI6j8Vupdc6EYRu3do3mfGj/XoaOrF30m47qi0zuzXDekJ5G17Z4JF06R8GzM/mz9oIhWcDz0Z11VVXpJL+Aemdmrez0du5AAAAAElFTkSuQmCC" alt="vk" />
|
||||
</a>
|
||||
<a href="https://t.me/sheymuh" target="_blank">
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEnklEQVR4nO2aS2wbRRjHfae8xOuOqnbXalMkJLiAhDggBOVQaBFSpCKOoCJVnAoSQoKm0AMSJw6gIh4ioliigXbGjqzmUSVxaF4YE+okjpO6TrBD0zpOZuxde/dDM7YTO3bWM/auzcF/6bvsrmf3N99jvp21y9VRRx21TF39m4+6vfSEgkmPgqhHwWRYxXSSGyJDxWM97Jr9OP2I6/+kw1dSD7oxeVdB9LqKiKliCkKGiKlgOq4gcurIpbsPtBVAReQzFZO08MPvDbWhYnKu5UAKoicVRJNNA+wyBdOEG9NuxwEO9sG9KiK9dgNUASHyg3sQ9jkCcejXzcdURKechlB3bJIVD/shMJlrIQQUjMzZBsPCSUF0uvUQdNsztoQZi9c2QgDPGUx+ahKCnmw3hFq0hqsZWyecKLG17IWhDJwJavDM1YyFV2iioXWmsNg59/BdPgrvzWgQuG2ACQWdCWr1QqxH2huF1dZ+gBeHM/D1Yg7WtdLj7+jYSNb694impLzCeic7H/6Ij8LpaQ3GymZ/t3QDuJfqjaVg8o4wCG8AbQB4yWL2NV2vgAqlDLFxEQ0IQbAFSKqLlZz9XD4PscQaBOejoOm57eMXYzlBEGIe+C39cF0Q9Qp9vRGAl4uzf1ffK3gA1jfSEFpYgj/no7BFMxXnPgrpwvdyY3K8flhh0iM64BNls28lmsnC/M04zIQj8FdkCWhWq7rmxGidRMflRj4WyQ9PvYGOXivMfspi9pnyeQPiyX/hj3CEQ8wuLkNW06uvM4FPiioIomByUQCEXKv148NeCh+GdJ6UIiqFEQNgdiMaAz23kxPlCqcFEx1v58mgiEdqNohfzFXPZC2RsjAq2dzyLZ7ke+mXeF4ShE41DMLaCH8iD1s56zAqB2C2EFsBw7D24tlZ3QkQUjO0SnbIS+H4aBY+D+s8yTWjOoxKFo3/A6ZpnUdM3YGsI6HlkRn02FC6CoDZrcRa/TgEAMMEeLJfrtQrQskuUX5Ldjm0XAGxsnYbRBXZNOW8gQXLL9s8kx34g7HkNkRfcBkGltaFQS6vSCY6pnAQkdccaVGe86dhugjy3cQSXBoYgx9nYkIg529IJjomhlCLUggvOi47S55gjINcCCxA38Aot68CEd7VWunNcelEHxOC4CCInJIFOT1SKL3fjMxyCM9AAF7xJXlFqtX9MrGjT/kzsh55WxiEvbzIvlg93b8FU+FF6B2e4BBHfcmKNWhhs9o1N4lkoiOaetx/536XjNherKxXvpyMw8+7IErGSuzVROXq7l2VS3QFkbMuWe3HcJ+K6KrMjZ71bcDz3juWi+mF6E5r8Onf4omuNLr5wMS2YGS9ImJv/Z6F94Mab0QlQN5wNSMFk++dgJHLDdLbFAT3yiDsK351aguEguj1rn64x2WH+CLZjk1sRMK2f6JT/BsPsR2MFoJM2P5ZoTzMWrKpjci3toWTJRCm3awc2g9AV5uuTrJiNZ23/HZsrSKaUhD5RHrVtlPs5mwbs5A/xBAHIAZrAFnv1FaAWmLtNXtXYLPL3uLYK2nZHwYG2TF+zkteZcWj5iAdddSRywn9B9E5uZlJrzFcAAAAAElFTkSuQmCC" alt="tg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <script>
|
||||
const savedImages = JSON.parse(localStorage.getItem("savedImages")) || [];
|
||||
|
||||
const savedImagesGrid = document.getElementById("savedImagesGrid");
|
||||
|
||||
savedImages.forEach((image) => {
|
||||
console.log(image);
|
||||
const item = document.createElement("div");
|
||||
item.className = "photo-grid-item";
|
||||
item.innerHTML = `<img src="${image}" alt="сохраненное изображение"/>`;
|
||||
savedImagesGrid.appendChild(item);
|
||||
});
|
||||
</script> -->
|
||||
</body>
|
||||
</html>
|
||||
<link rel="stylesheet" crossorigin href="/assets/styleStreamingService-CUHvxLFI.css">
|
||||
</head>
|
||||
<body class="min-vh-100 d-flex flex-column m-0 p-0">
|
||||
<header class="d-flex align-items-center justify-content-between position-sticky h-60 p-3">
|
||||
<div class="header-logo d-flex align-items-center g-3">
|
||||
<a href="pageMain.html"><img src="/assets/%D0%9A%D0%90%D0%99%D0%A4-Dbw7IVze.jpg" alt="Логотип" /></a>
|
||||
<h1>НАЗВАНИЕ СЕРВИСА</h1>
|
||||
</div>
|
||||
<nav class="navbar d-flex align-items-center justify-content-center me-3 g-3 column-gap-2">
|
||||
<a href="pageCategories.html">Категории</a>
|
||||
<nav class="dropdown position-relative">
|
||||
<span><a>Мой аккаунт ▾</a></span>
|
||||
<nav class="features-menu">
|
||||
<nav class="features-item"><a href="pageAccount.html">Настройки</a></nav>
|
||||
<nav class="features-item"><a href="pageSubscriptions.html">Подписки</a></nav>
|
||||
<nav class="features-item"><a href="pageSavedStreams.html">Сохраненные трансляции</a></nav>
|
||||
</nav>
|
||||
</nav>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="content flex-grow-1 p-3 px-4">
|
||||
<h2>Смотреть позже <i class="bi bi-clock-fill"></i></h2>
|
||||
<div class="photo-grid-container d-flex justify-content-center">
|
||||
<div class="photo-grid d-flex align-items-center flex-wrap w-100" id="savedImagesGrid">
|
||||
<div class="photo-grid-item"><img src="/assets/2016-BUapLfv6.jpeg" alt="стрим ксго" /></div>
|
||||
<div class="photo-grid-item"><img src="/assets/%D0%B0%D1%81%D0%BC%D1%80%20%D1%87%D0%B5%D0%BB%D0%BE%D0%B2%D0%B5%D0%BA%20%D0%BF%D0%B0%D1%83%D0%BA-RgZcxljT.webp" alt="асмр" /></div>
|
||||
<div class="photo-grid-item"><img src="/assets/%D1%80%D0%B5%D0%B7%D0%BD%D1%8F-Du5Ks9ja.jpg" alt="резня" /></div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Запланированные трансляции <i class="bi bi-calendar-event"></i></h2>
|
||||
<div class="photo-grid-container d-flex justify-content-center">
|
||||
<div class="photo-grid d-flex align-items-center flex-wrap w-100">
|
||||
<div class="photo-grid-item"><img src="/assets/%D1%81%D1%82%D1%80%D0%B8%D0%BC%20%D0%BA%D1%81%D0%B3%D0%BE-d7c_w3u0.webp" alt="стрим ксго" /></div>
|
||||
<div class="photo-grid-item"><img src="/assets/goats-D56R5-1i.png" alt="goats" /></div>
|
||||
<div class="photo-grid-item"><img src="/assets/%D0%BF%D0%B0%D0%BF%D0%B0%D0%BD%D1%8F-BZOoCf1B.jpg" alt="папаня" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer overflow-hidden flex-shrink-1 h-75 p-0 px-4">
|
||||
<div class="footer-content d-flex justify-content-between align-items-center">
|
||||
<div class="company-name flex-grow-1">RBCS CORP. <i class="bi bi-c-circle"></i></div>
|
||||
<div class="footer-images d-flex gap-3 p-3">
|
||||
<a href="https://vk.com/sheym_not_shame" target="_blank">
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEcUlEQVR4nO2aX2wURRzH71GNSigSd5BAYmIwMTFGjfHVvvhm5E80QV980KqRRExnripwtgLyYDCaaJACUSFYQVBfjOFBia31wEM6v7tem0Na6l1pKdw1pbQ0ba9jfnu0ureztzO7e3s+3Df5JZd2Ojefnd+/mW0kUldddYUm8lbiHqO5ZyOhsJNQOE4YP00oJEzDzwyO4e8MltywqiW9IvJ/UsOWzN1GtOc1g/FuQnmRMBBKRnnRoPw3g0ETzlEzgOXRxLLS04Vx5cU7mEGhQBhvDR3oXgbPEcov+wWwATEYNhjfVHUAEkvcYVB+KGgACVD76q3dt1cFYk0LLEefrjYEWYohOIPJI1AIzDCEQio0CLZkydVbUw2BuRNhEK8BhCi5Ge8OxM3CiAlXGAr7/WenGkOQf3dmk/dCx2C41gBkyfgI1i5tELPNqPniwWqUv6cFgeRBVOzA3YtCQav6Y+8km+jFQ4Ni548jFlv7TlL6pZsPWse+8fXfjgtkJ3KWsRv3XawQK9CkDoINoGSS9s6rolyP7uqTfuGRM3nLuLODU9JxT3zQLxYWrHO+ftQZ2mC8S6P4ybvYPT+N2kAa92Z8gRzoumYZNz1bFOtivZXipKhU8THNOU1CT+RsIC8cHPQM8tTejJidt27HV/G8e6yw5AZf2QoXXS7cJS8gD2xPib6Rm5YxkzNF8cj7aYXA560qIMedJngw1iuKZf58Kj2hDYKuEx+4YXsobx7LqmUwyr9RcC34tdIk/aPWpzgztyD1aRnIfVEQL315SWQLszaIT34eU4MogfziDkLhz0qTfPG7NThR0ZM5V5DrN4viyvU5IRNmQ2UIZqbgc75Bnv74gm0hoxOz4v53UxVBZMKY2NLhnGqJLxAX10I7n522LQrTqC5IYWpeNH+bM11OC4YquJZ5ZeMyERYsmbb9MKwFsqjOC5Ni3Y5ejR3hHYE0i6uiYAavTEfPFsRDrWkbSOLSlOj4oyDyN+Rxgqn4sd3yLsFT+jUv2BQme3JPv5iYnpcuCotc+YL33wroNW8nRcvJYenfZq7MqO1MM6z31aKUGzZ32FKoaBHkvw9i4OqMbdz3PeNu8VFUPsfr3JY889lfYmxyThsE7fHdfeKaxNU2O7Q9ZnxQ6FSCKIHAqzpZ5OG2tPju/Liti3UDQXv58JBtbGJoqsKOwCt6x1zzGlMvLTZ+lDGbPkyrqiBoPZJ03ijpqg3G8yto310RHWFm0AVZNKwLCIWHqQ9PjYpPT4+JpiNDjuPRlQ7H8xZ7vn1A5lY7IrpCcoNBzitM0GZQyK6Mpe6MeFGls0noIEzhDFIRhvIDtYYglO+L+BVeVzqd4cPZCd61NjZ4m2+QEkyqAS+Uw98JgMAusS2vFRjvChEkHvhrBaubQXsIEJ8H5k5u2awaqdmgkPWdnbzUGbyL9dIBSAI6j8Vupdc6EYRu3do3mfGj/XoaOrF30m47qi0zuzXDekJ5G17Z4JF06R8GzM/mz9oIhWcDz0Z11VVXpJL+Aemdmrez0du5AAAAAElFTkSuQmCC" alt="vk" />
|
||||
</a>
|
||||
<a href="https://t.me/sheymuh" target="_blank">
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEnklEQVR4nO2aS2wbRRjHfae8xOuOqnbXalMkJLiAhDggBOVQaBFSpCKOoCJVnAoSQoKm0AMSJw6gIh4ioliigXbGjqzmUSVxaF4YE+okjpO6TrBD0zpOZuxde/dDM7YTO3bWM/auzcF/6bvsrmf3N99jvp21y9VRRx21TF39m4+6vfSEgkmPgqhHwWRYxXSSGyJDxWM97Jr9OP2I6/+kw1dSD7oxeVdB9LqKiKliCkKGiKlgOq4gcurIpbsPtBVAReQzFZO08MPvDbWhYnKu5UAKoicVRJNNA+wyBdOEG9NuxwEO9sG9KiK9dgNUASHyg3sQ9jkCcejXzcdURKechlB3bJIVD/shMJlrIQQUjMzZBsPCSUF0uvUQdNsztoQZi9c2QgDPGUx+ahKCnmw3hFq0hqsZWyecKLG17IWhDJwJavDM1YyFV2iioXWmsNg59/BdPgrvzWgQuG2ACQWdCWr1QqxH2huF1dZ+gBeHM/D1Yg7WtdLj7+jYSNb694impLzCeic7H/6Ij8LpaQ3GymZ/t3QDuJfqjaVg8o4wCG8AbQB4yWL2NV2vgAqlDLFxEQ0IQbAFSKqLlZz9XD4PscQaBOejoOm57eMXYzlBEGIe+C39cF0Q9Qp9vRGAl4uzf1ffK3gA1jfSEFpYgj/no7BFMxXnPgrpwvdyY3K8flhh0iM64BNls28lmsnC/M04zIQj8FdkCWhWq7rmxGidRMflRj4WyQ9PvYGOXivMfspi9pnyeQPiyX/hj3CEQ8wuLkNW06uvM4FPiioIomByUQCEXKv148NeCh+GdJ6UIiqFEQNgdiMaAz23kxPlCqcFEx1v58mgiEdqNohfzFXPZC2RsjAq2dzyLZ7ke+mXeF4ShE41DMLaCH8iD1s56zAqB2C2EFsBw7D24tlZ3QkQUjO0SnbIS+H4aBY+D+s8yTWjOoxKFo3/A6ZpnUdM3YGsI6HlkRn02FC6CoDZrcRa/TgEAMMEeLJfrtQrQskuUX5Ldjm0XAGxsnYbRBXZNOW8gQXLL9s8kx34g7HkNkRfcBkGltaFQS6vSCY6pnAQkdccaVGe86dhugjy3cQSXBoYgx9nYkIg529IJjomhlCLUggvOi47S55gjINcCCxA38Aot68CEd7VWunNcelEHxOC4CCInJIFOT1SKL3fjMxyCM9AAF7xJXlFqtX9MrGjT/kzsh55WxiEvbzIvlg93b8FU+FF6B2e4BBHfcmKNWhhs9o1N4lkoiOaetx/536XjNherKxXvpyMw8+7IErGSuzVROXq7l2VS3QFkbMuWe3HcJ+K6KrMjZ71bcDz3juWi+mF6E5r8Onf4omuNLr5wMS2YGS9ImJv/Z6F94Mab0QlQN5wNSMFk++dgJHLDdLbFAT3yiDsK351aguEguj1rn64x2WH+CLZjk1sRMK2f6JT/BsPsR2MFoJM2P5ZoTzMWrKpjci3toWTJRCm3awc2g9AV5uuTrJiNZ23/HZsrSKaUhD5RHrVtlPs5mwbs5A/xBAHIAZrAFnv1FaAWmLtNXtXYLPL3uLYK2nZHwYG2TF+zkteZcWj5iAdddSRywn9B9E5uZlJrzFcAAAAAElFTkSuQmCC" alt="tg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <script>
|
||||
const savedImages = JSON.parse(localStorage.getItem("savedImages")) || [];
|
||||
|
||||
const savedImagesGrid = document.getElementById("savedImagesGrid");
|
||||
|
||||
savedImages.forEach((image) => {
|
||||
console.log(image);
|
||||
const item = document.createElement("div");
|
||||
item.className = "photo-grid-item";
|
||||
item.innerHTML = `<img src="${image}" alt="сохраненное изображение"/>`;
|
||||
savedImagesGrid.appendChild(item);
|
||||
});
|
||||
</script> -->
|
||||
</body>
|
||||
|
||||
25
js/validation.js
Normal file
25
js/validation.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// модуль используется для валидации форма на странице
|
||||
|
||||
const validation = () => {
|
||||
// поиск всех форм с классом .needs-validation
|
||||
const forms = document.querySelectorAll("form.needs-validation");
|
||||
|
||||
forms.forEach((form) => {
|
||||
form.setAttribute("novalidate", "");
|
||||
form.addEventListener("submit", (event) => {
|
||||
// выключить стандартное действие
|
||||
event.preventDefault();
|
||||
// предотвращает распространение preventDefault
|
||||
// на другие объекты
|
||||
event.stopPropagation();
|
||||
if (!form.checkValidity()) {
|
||||
// добавляет к форме класс was-validated
|
||||
form.classList.add("was-validated");
|
||||
} else {
|
||||
form.classList.remove("was-validated");
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default validation;
|
||||
1029
package-lock.json
generated
1029
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -3,15 +3,18 @@
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"start": "npm-run-all --parallel serve vite",
|
||||
"vite": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "http-server -p 4000 ./dist/",
|
||||
"server": "json-server --watch ./database/data.json --port 3000",
|
||||
"prod": "npm-run-all build serve",
|
||||
"lint": "eslint . --ext js --report-unused-disable-directives --max-warnings 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.3",
|
||||
"bootstrap-icons": "1.11.3"
|
||||
"bootstrap-icons": "1.11.3",
|
||||
"moment": "^2.30.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "8.56.0",
|
||||
@@ -21,7 +24,8 @@
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-prettier": "5.2.3",
|
||||
"http-server": "14.1.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"json-server": "^1.0.0-beta.3",
|
||||
"npm-run-all": "4.1.5",
|
||||
"vite": "6.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
<div class="button">Выйти из аккаунта</div>
|
||||
<div class="button">Сменить пароль</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<a class="btn btn-primary" href="/pageForm"
|
||||
><i class="bi bi-plus-circle-fill me-2"></i>Создать новую запись</a
|
||||
>
|
||||
</div>
|
||||
<videos-table></videos-table>
|
||||
</div>
|
||||
<div class="footer overflow-hidden flex-shrink-1 h-75 p-0 px-4">
|
||||
<div class="footer-content d-flex justify-content-between align-items-center">
|
||||
@@ -48,5 +54,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/components/videos/table/controller.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
93
pageForm.html
Normal file
93
pageForm.html
Normal file
@@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Новое видео</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="styleStreamingService.css"/>
|
||||
</head>
|
||||
<body class="min-vh-100 d-flex flex-column m-0 p-0">
|
||||
<header class="d-flex align-items-center justify-content-between position-sticky h-60 p-3">
|
||||
<div class="header-logo d-flex align-items-center g-3">
|
||||
<a href="pageMain.html"><img src="Resources\КАЙФ.jpg" alt="Логотип"/></a>
|
||||
<h1>НАЗВАНИЕ СЕРВИСА</h1>
|
||||
</div>
|
||||
<nav class="navbar d-flex align-items-center justify-content-center me-3 g-3 column-gap-2">
|
||||
<a href="pageCategories.html">Категории</a>
|
||||
<nav class="dropdown position-relative">
|
||||
<span><a>Мой аккаунт ▾</a></span>
|
||||
<nav class="features-menu">
|
||||
<nav class="features-item"><a href="pageAccount.html">Настройки</a></nav>
|
||||
<nav class="features-item"><a href="pageSubscriptions.html">Подписки</a></nav>
|
||||
<nav class="features-item"><a href="pageSavedStreams.html">Сохраненные трансляции</a></nav>
|
||||
</nav>
|
||||
</nav>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="content flex-grow-1 p-3 px-4">
|
||||
<videos-form></videos-form>
|
||||
<template id="videos-form-template">
|
||||
<div class="row justify-content-center">
|
||||
<form class="col col-md-6 needs-validation">
|
||||
<div class="mb-2">
|
||||
<label for="id" class="form-label">Номер</label>
|
||||
<input type="text" class="form-control" id="id" readonly disabled />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="name" class="form-label">Название</label>
|
||||
<input type="text" class="form-control" id="name" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="image" class="form-label">Превью</label>
|
||||
<input type="text" class="form-control" id="image" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<input type="text" class="form-control" id="description" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="playlist" class="form-label">Плейлист</label>
|
||||
<select class="form-select" id="playlist">
|
||||
<option value="" selected>Выберите плейлист</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="category" class="form-label">Категория</label>
|
||||
<select class="form-select" id="category" required>
|
||||
<option value="" selected>Выберите категорию</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row row-cols-1 justify-content-center">
|
||||
<div class="col col-md-6 col-lg-4 col-xl-3 m-lg-0 mb-2">
|
||||
<button id="btn-save" type="submit" class="btn btn-primary d-block m-auto w-100">
|
||||
Сохранить
|
||||
</button>
|
||||
</div>
|
||||
<div class="col col-md-6 col-lg-4 col-xl-3 m-lg-0 mb-2">
|
||||
<button id="btn-back" type="button" class="btn btn-primary d-block m-auto w-100">
|
||||
Назад
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="footer overflow-hidden flex-shrink-1 h-75 p-0 px-4">
|
||||
<div class="footer-content d-flex justify-content-between align-items-center">
|
||||
<div class="company-name flex-grow-1">RBCS CORP. <i class="bi bi-c-circle"></i></div>
|
||||
<div class="footer-images d-flex gap-3 p-3">
|
||||
<a href="https://vk.com/sheym_not_shame" target="_blank">
|
||||
<img src="Resources\vk_icon.png" alt="vk"/>
|
||||
</a>
|
||||
<a href="https://t.me/sheymuh" target="_blank">
|
||||
<img src="Resources\tg_icon.png" alt="tg"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/components/videos/form/controller.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -53,12 +53,12 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
name="images"
|
||||
value="https://i.ytimg.com/vi/enyylQ9sFRc/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGGAgZShUMA8=&rs=AOn4CLAnYQfwEgcw2b2sKoCxOJE8xs0Hbg"
|
||||
value="https://avatars.mds.yandex.net/i?id=7839e8d6f40309bdce67fac62990d108_sr-10636981-images-thumbs&n=13"
|
||||
id="derzko"
|
||||
/>
|
||||
<label for="derzko"
|
||||
><img
|
||||
src="https://i.ytimg.com/vi/enyylQ9sFRc/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGGAgZShUMA8=&rs=AOn4CLAnYQfwEgcw2b2sKoCxOJE8xs0Hbg"
|
||||
src="https://avatars.mds.yandex.net/i?id=7839e8d6f40309bdce67fac62990d108_sr-10636981-images-thumbs&n=13"
|
||||
alt="derzko"
|
||||
/></label>
|
||||
</div>
|
||||
@@ -79,12 +79,12 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
name="images"
|
||||
value="https://i.ytimg.com/vi/WCrXqI_0FhU/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGEwgWChlMA8=&rs=AOn4CLDIJ5NCb473i-I_fpiD4-G8TEPjLw"
|
||||
value="https://avatars.mds.yandex.net/i?id=c111e02e6999cca7c4a7aa47f00a2ab3c384b6dd-5884537-images-thumbs&n=13"
|
||||
id="teddy"
|
||||
/>
|
||||
<label for="teddy"
|
||||
><img
|
||||
src="https://i.ytimg.com/vi/WCrXqI_0FhU/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGEwgWChlMA8=&rs=AOn4CLDIJ5NCb473i-I_fpiD4-G8TEPjLw"
|
||||
src="https://avatars.mds.yandex.net/i?id=c111e02e6999cca7c4a7aa47f00a2ab3c384b6dd-5884537-images-thumbs&n=13"
|
||||
alt="teddy"
|
||||
/></label>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ export default defineConfig({
|
||||
page3: resolve(__dirname, "pageAccount.html"),
|
||||
page4: resolve(__dirname, "pageSubscriptions.html"),
|
||||
page5: resolve(__dirname, "pageSavedStreams.html"),
|
||||
page6: resolve(__dirname, "pageForm.html"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user