Skip to content

Commit 1e8141b

Browse files
committed
Внедрение коммитов Вани
1 parent cff0b73 commit 1e8141b

62 files changed

Lines changed: 3451 additions & 2013 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
CREATE OR REPLACE FUNCTION check_unique_memory_game_name_pairs_per_user()
2+
RETURNS TRIGGER AS $$
3+
DECLARE
4+
target_user_id character varying(7);
5+
existing_game_count INTEGER;
6+
BEGIN
7+
-- Определяем пользователя для текущей вставляемой/обновляемой записи
8+
SELECT ug.user_id
9+
INTO target_user_id
10+
FROM game_usergame ug
11+
WHERE ug.game_id = NEW.game_id;
12+
13+
-- Выполняем проверку уникальности, только если имя или количество пар менялись (или при INSERT)
14+
IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND (NEW.name IS DISTINCT FROM OLD.name OR NEW.pair_count IS DISTINCT FROM OLD.pair_count)) THEN
15+
16+
-- Ищем существующую игру "Поиск пар" с таким же названием и количеством пар для текущего пользователя,
17+
-- исключая текущую строку (важно для UPDATE)
18+
SELECT COUNT(*)
19+
INTO existing_game_count
20+
FROM game_usermemorygame mg
21+
JOIN game_usergame ug ON mg.game_id = ug.game_id
22+
WHERE mg.name = NEW.name
23+
AND mg.pair_count = NEW.pair_count
24+
AND ug.user_id = target_user_id
25+
AND mg.game_id <> NEW.game_id;
26+
27+
IF existing_game_count > 0 THEN
28+
RAISE EXCEPTION 'Игра "Поиск пар" с названием "%" и количеством пар "%" уже существует.', NEW.name, NEW.pair_count;
29+
END IF;
30+
31+
END IF;
32+
33+
-- Если дубликатов у этого пользователя нет, разрешаем операцию
34+
RETURN NEW;
35+
END;
36+
$$ LANGUAGE plpgsql;
37+
38+
-- Удаляем триггер на случай повторного запуска скрипта
39+
DROP TRIGGER IF EXISTS unique_memory_game_name_pairs_per_user_trigger ON game_usermemorygame;
40+
41+
-- Создаем триггер, который вызывает функцию
42+
CREATE TRIGGER unique_memory_game_name_pairs_per_user_trigger
43+
BEFORE INSERT OR UPDATE ON game_usermemorygame
44+
FOR EACH ROW
45+
EXECUTE FUNCTION check_unique_memory_game_name_pairs_per_user();
46+
47+
COMMENT ON FUNCTION check_unique_memory_game_name_pairs_per_user() IS 'Проверяет уникальность комбинации названия и количества пар для игры "Поиск пар" в рамках одного пользователя перед вставкой или обновлением.';
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
DELETE FROM public.game_genre;
22
INSERT INTO public.game_genre (id, name, code)
33
VALUES
4-
(1, 'Пазл', 'PZL');
4+
(1, 'Пазл', 'PZL');
5+
(2, 'Поиск пар', 'MEM');

videodef/frontend/memory_game/index.js

Lines changed: 734 additions & 0 deletions
Large diffs are not rendered by default.

videodef/frontend/memory_game/memory-game-logic.js

Lines changed: 385 additions & 0 deletions
Large diffs are not rendered by default.

videodef/frontend/my-games.js

Lines changed: 55 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,110 @@
1-
document.addEventListener("DOMContentLoaded", function() {
2-
const modal = document.getElementById("deleteConfirmationModal");
3-
const confirmDeleteBtn = document.getElementById("confirmDeleteBtn");
4-
const cancelDeleteBtn = document.getElementById("cancelDeleteBtn");
5-
const closeBtn = modal.querySelector(".modal-close-btn");
6-
const gameNameElement = document.getElementById("gameNameToDelete");
7-
const noGamesMessage = document.getElementById("no-games-message");
8-
const gamesGrid = document.querySelector(".my-saved-games-grid");
1+
document.addEventListener('DOMContentLoaded', function () {
2+
const modal = document.getElementById('deleteConfirmationModal');
3+
const confirmDeleteBtn = document.getElementById('confirmDeleteBtn');
4+
const cancelDeleteBtn = document.getElementById('cancelDeleteBtn');
5+
const closeBtn = modal.querySelector('.modal-close-btn');
6+
const gameNameElement = document.getElementById('gameNameToDelete');
7+
const noGamesMessage = document.getElementById('no-games-message');
8+
const gamesGrid = document.querySelector('.my-saved-games-grid');
99

1010
let gameIdToDelete = null;
1111
let gameCardElementToDelete = null;
1212

1313
// Функция для получения CSRF-токена
1414
function getCookie(name) {
1515
let cookieValue = null;
16-
if (document.cookie && document.cookie !== "") {
17-
const cookies = document.cookie.split(";");
16+
if (document.cookie && document.cookie !== '') {
17+
const cookies = document.cookie.split(';');
1818
for (let i = 0; i < cookies.length; i++) {
1919
const cookie = cookies[i].trim();
20-
if (cookie.substring(0, name.length + 1) === name + "=") {
20+
if (cookie.substring(0, name.length + 1) === (name + '=')) {
2121
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
2222
break;
2323
}
2424
}
2525
}
2626
return cookieValue;
2727
}
28-
const csrftoken = getCookie("csrftoken");
28+
const csrftoken = getCookie('csrftoken');
2929

3030
// Открытие модального окна
31-
document.querySelectorAll(".delete-game-btn").forEach((button) => {
32-
button.addEventListener("click", function(event) {
31+
document.querySelectorAll('.delete-game-btn').forEach(button => {
32+
button.addEventListener('click', function (event) {
3333
event.preventDefault();
3434
event.stopPropagation();
3535

3636
gameIdToDelete = this.dataset.gameId;
37-
gameCardElementToDelete = this.closest(".saved-game-item-wrapper");
37+
gameCardElementToDelete = this.closest('.saved-game-item-wrapper');
3838

39-
const titleElement = gameCardElementToDelete.querySelector(
40-
".saved-game-card__title"
41-
);
42-
gameNameElement.textContent = titleElement ?
43-
titleElement.textContent.trim() :
44-
"эту игру";
45-
46-
modal.style.display = "flex";
39+
const titleElement = gameCardElementToDelete.querySelector('.saved-game-card__title');
40+
gameNameElement.textContent = titleElement ? titleElement.textContent.trim() : 'эту игру';
41+
42+
modal.style.display = 'flex';
4743
});
4844
});
4945

5046
// Закрытие модального окна
5147
function closeModal() {
52-
modal.style.display = "none";
48+
modal.style.display = 'none';
5349
gameIdToDelete = null;
5450
gameCardElementToDelete = null;
5551
}
5652

57-
if (cancelDeleteBtn) cancelDeleteBtn.addEventListener("click", closeModal);
58-
if (closeBtn) closeBtn.addEventListener("click", closeModal);
59-
window.addEventListener("click", function(event) {
53+
if (cancelDeleteBtn) cancelDeleteBtn.addEventListener('click', closeModal);
54+
if (closeBtn) closeBtn.addEventListener('click', closeModal);
55+
window.addEventListener('click', function (event) {
6056
if (event.target === modal) {
6157
closeModal();
6258
}
6359
});
6460

6561
// Подтверждение удаления
6662
if (confirmDeleteBtn) {
67-
confirmDeleteBtn.addEventListener("click", function() {
63+
confirmDeleteBtn.addEventListener('click', function () {
6864
if (!gameIdToDelete || !gameCardElementToDelete) return;
6965

70-
const deleteUrl = `/games/delete/${gameIdToDelete}/`;
66+
const deleteUrl = `/games/delete/${gameIdToDelete}/`;
7167

7268
fetch(deleteUrl, {
73-
method: "DELETE",
74-
headers: {
75-
"X-CSRFToken": csrftoken,
76-
},
77-
})
78-
.then((response) => {
79-
if (response.ok) {
80-
if (response.status === 204) {
81-
return Promise.resolve({
82-
status: "success",
83-
message: "Игра успешно удалена (204).",
84-
});
85-
}
86-
return response.json();
87-
}
88-
return response.json().then((err) => {
89-
throw new Error(err.message || "Не удалось удалить игру.");
90-
});
91-
})
92-
.then((data) => {
93-
if (data.status === "success") {
94-
gameCardElementToDelete.remove();
95-
checkIfGamesExist();
96-
console.log(data.message || "Игра успешно удалена.");
97-
closeModal();
98-
} else {
99-
throw new Error(
100-
data.message || "Ошибка при удалении игры на сервере."
101-
);
69+
method: 'DELETE',
70+
headers: {
71+
'X-CSRFToken': csrftoken
72+
},
73+
})
74+
.then(response => {
75+
if (response.ok) {
76+
if (response.status === 204) {
77+
return Promise.resolve({ status: 'success', message: 'Игра успешно удалена (204).' });
10278
}
103-
})
104-
.catch((error) => {
105-
console.error("Ошибка при удалении игры:", error);
106-
alert(`Ошибка: ${error.message}`);
79+
return response.json();
80+
}
81+
return response.json().then(err => { throw new Error(err.message || 'Не удалось удалить игру.') });
82+
})
83+
.then(data => {
84+
if (data.status === 'success') {
85+
gameCardElementToDelete.remove();
86+
checkIfGamesExist();
87+
console.log(data.message || 'Игра успешно удалена.');
10788
closeModal();
108-
});
89+
} else {
90+
throw new Error(data.message || 'Ошибка при удалении игры на сервере.');
91+
}
92+
})
93+
.catch(error => {
94+
console.error('Ошибка при удалении игры:', error);
95+
alert(`Ошибка: ${error.message}`);
96+
closeModal();
97+
});
10998
});
11099
}
111100

112101
function checkIfGamesExist() {
113102
if (gamesGrid && noGamesMessage) {
114-
const remainingGames = gamesGrid.querySelectorAll(
115-
".saved-game-item-wrapper"
116-
).length;
103+
const remainingGames = gamesGrid.querySelectorAll('.saved-game-item-wrapper').length;
117104
if (remainingGames === 0) {
118-
noGamesMessage.style.display = "block";
105+
noGamesMessage.style.display = 'block';
119106
} else {
120-
noGamesMessage.style.display = "none";
107+
noGamesMessage.style.display = 'none';
121108
}
122109
}
123110
}

0 commit comments

Comments
 (0)