-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
252 lines (227 loc) · 14.9 KB
/
index.html
File metadata and controls
252 lines (227 loc) · 14.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Журнал Дисциплины</title>
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; }
.loader { border-top-color: #10B981; animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
/* Стили для модального окна */
#login-modal-backdrop.hidden { display: none; }
</style>
</head>
<body class="bg-gray-100 text-gray-800">
<!-- МОДАЛЬНОЕ ОКНО ДЛЯ ВХОДА (по умолчанию скрыто) -->
<div id="login-modal-backdrop" class="hidden fixed inset-0 bg-gray-900 bg-opacity-50 flex items-center justify-center z-50">
<div id="login-modal" class="bg-white rounded-xl shadow-lg p-8 w-full max-w-md">
<h2 class="text-2xl font-bold text-center mb-6">Вход в журнал</h2>
<form id="login-form" class="space-y-4">
<div>
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Имя пользователя</label>
<input type="text" id="username" name="username" required autocomplete="username" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-emerald-500 focus:border-emerald-500">
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Пароль</label>
<input type="password" id="password" name="password" required autocomplete="current-password" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-emerald-500 focus:border-emerald-500">
</div>
<button type="submit" id="login-submit-button" class="w-full flex justify-center items-center bg-emerald-600 hover:bg-emerald-700 text-white font-bold py-2.5 px-4 rounded-md transition duration-300">
Войти
</button>
</form>
<div id="auth-message" class="mt-4 text-center text-red-600 h-5"></div>
<button id="close-modal-button" class="mt-4 w-full text-center text-gray-500 hover:text-gray-700">Отмена</button>
</div>
</div>
<!-- ОСНОВНАЯ СТРАНИЦА -->
<div class="container mx-auto p-4 sm:p-6 max-w-4xl">
<header class="flex justify-between items-center my-6">
<div>
<h1 class="text-3xl sm:text-4xl font-bold text-gray-900">Журнал Дисциплины</h1>
<p class="text-gray-600 mt-1">Работает на базе данных SQL</p>
</div>
<!-- Этот блок будет меняться в зависимости от статуса входа -->
<div id="user-status-container">
<!-- Загрузчик статуса -->
<div id="user-status-loader" class="h-8 w-20 bg-gray-200 rounded-md animate-pulse"></div>
<!-- Кнопка "Войти" (для гостей) -->
<button id="show-login-button" class="hidden px-6 py-2 bg-emerald-600 hover:bg-emerald-700 text-white font-bold rounded-md transition">Войти</button>
<!-- Информация о пользователе (для вошедших) -->
<div id="user-info-container" class="hidden flex items-center gap-4">
<span id="user-info" class="text-gray-700"></span>
<button id="logout-button" class="px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-800 rounded-md transition">Выйти</button>
</div>
</div>
</header>
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
<h2 class="text-2xl font-semibold mb-2">Добавить новую запись</h2>
<p id="add-form-login-prompt" class="text-sm text-gray-500 mb-4 hidden">Нужно <a href="#" class="text-emerald-600 font-semibold" id="login-prompt-link">войти в систему</a>, чтобы добавлять записи.</p>
<form id="add-record-form" class="space-y-4">
<input type="text" id="student" name="student" required placeholder="Ученик" class="w-full px-3 py-2 border border-gray-300 rounded-md disabled:bg-gray-100">
<input type="text" id="lesson" name="lesson" required placeholder="Урок" class="w-full px-3 py-2 border border-gray-300 rounded-md disabled:bg-gray-100">
<textarea id="violation" name="violation" rows="3" required placeholder="Действие/Нарушение" class="w-full px-3 py-2 border border-gray-300 rounded-md disabled:bg-gray-100"></textarea>
<button type="submit" id="submit-button" class="w-full flex justify-center items-center bg-emerald-600 hover:bg-emerald-700 text-white font-bold py-2.5 px-4 rounded-md transition disabled:bg-gray-400 disabled:cursor-not-allowed">
<span>Добавить запись</span>
</button>
</form>
<div id="form-message" class="mt-4 text-center h-5"></div>
</div>
<div class="bg-white rounded-xl shadow-lg p-6">
<h2 class="text-2xl font-semibold mb-4">Список записей</h2>
<div class="overflow-x-auto">
<table class="min-w-full bg-white">
<thead>
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Ученик</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Урок</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Нарушение</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Дата</th>
<th id="actions-header" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Действия</th>
</tr>
</thead>
<tbody id="records-table-body" class="divide-y divide-gray-200">
<tr><td colspan="5" class="p-6 text-center"><div class="loader h-8 w-8 mx-auto rounded-full border-4 border-t-4 border-gray-200"></div></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const SUPABASE_URL = 'https://lcjgfxrfhhzncgpjlyzj.supabase.co';
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxjamdmeHJmaGh6bmNncGpseXpqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTc5NDA3NjQsImV4cCI6MjA3MzUxNjc2NH0.IOsgSpZsb6oziniW8ywRYWIrLoptXitgjF88JwF6rBM';
const { createClient } = supabase;
const db = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
// ... DOM элементы ...
const userStatusLoader = document.getElementById('user-status-loader');
const showLoginButton = document.getElementById('show-login-button');
const userInfoContainer = document.getElementById('user-info-container');
const userInfo = document.getElementById('user-info');
const logoutButton = document.getElementById('logout-button');
const addRecordForm = document.getElementById('add-record-form');
const formInputs = addRecordForm.querySelectorAll('input, textarea');
const submitButton = document.getElementById('submit-button');
const addFormLoginPrompt = document.getElementById('add-form-login-prompt');
const loginPromptLink = document.getElementById('login-prompt-link');
const tableBody = document.getElementById('records-table-body');
const actionsHeader = document.getElementById('actions-header');
const loginModalBackdrop = document.getElementById('login-modal-backdrop');
const loginForm = document.getElementById('login-form');
const authMessage = document.getElementById('auth-message');
const closeModalButton = document.getElementById('close-modal-button');
// --- Управление UI ---
async function updateUI(user) {
userStatusLoader.classList.add('hidden');
if (user) {
// Пользователь вошел
const username = await getUsername(user.id);
userInfo.textContent = username || 'Пользователь';
userInfoContainer.classList.remove('hidden');
showLoginButton.classList.add('hidden');
formInputs.forEach(input => input.disabled = false);
submitButton.disabled = false;
addFormLoginPrompt.classList.add('hidden');
actionsHeader.classList.remove('hidden');
} else {
// Гость
userInfoContainer.classList.add('hidden');
showLoginButton.classList.remove('hidden');
formInputs.forEach(input => input.disabled = true);
submitButton.disabled = true;
addFormLoginPrompt.classList.remove('hidden');
actionsHeader.classList.add('hidden');
}
await fetchRecords(user); // Перерисовываем таблицу с учетом прав
}
// --- Получение данных ---
async function fetchRecords(user) {
const { data: records, error } = await db.from('violations').select('*').order('created_at', { ascending: false });
if (error) {
tableBody.innerHTML = `<tr><td colspan="5" class="p-6 text-center text-red-500">Ошибка: ${error.message}</td></tr>`;
return;
}
if (records.length === 0) {
tableBody.innerHTML = '<tr><td colspan="5" class="p-6 text-center text-gray-500">Записей пока нет.</td></tr>';
} else {
tableBody.innerHTML = records.map(record => {
const formattedDate = new Date(record.created_at).toLocaleString('ru-RU');
const deleteButtonHTML = user ? `<button data-id="${record.id}" class="delete-btn text-red-600 hover:text-red-800">Удалить</button>` : '';
return `
<tr>
<td class="px-6 py-4">${record.student}</td>
<td class="px-6 py-4">${record.lesson}</td>
<td class="px-6 py-4">${record.violation}</td>
<td class="px-6 py-4">${formattedDate}</td>
<td class="px-6 py-4">${deleteButtonHTML}</td>
</tr>`;
}).join('');
}
}
async function getUsername(userId) {
if (!userId) return null;
const { data } = await db.from('profiles').select('username').eq('id', userId).single();
return data ? data.username : null;
}
// --- Логика аутентификации ---
db.auth.onAuthStateChange((_event, session) => {
updateUI(session?.user);
});
// --- Модальное окно ---
function showLoginModal() { loginModalBackdrop.classList.remove('hidden'); }
function hideLoginModal() { loginModalBackdrop.classList.add('hidden'); authMessage.textContent = ''; loginForm.reset(); }
showLoginButton.addEventListener('click', showLoginModal);
loginPromptLink.addEventListener('click', showLoginModal);
closeModalButton.addEventListener('click', hideLoginModal);
loginModalBackdrop.addEventListener('click', (e) => {
if (e.target === loginModalBackdrop) hideLoginModal();
});
// ... Остальные обработчики событий (login, logout, add, delete) без изменений ...
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const username = e.target.username.value;
const password = e.target.password.value;
authMessage.textContent = '';
const { data: email, error: rpcError } = await db.rpc('get_email_by_username', { p_username: username });
if (rpcError || !email) {
authMessage.textContent = 'Пользователь не найден.'; return;
}
const { error: signInError } = await db.auth.signInWithPassword({ email, password });
if (signInError) {
authMessage.textContent = 'Неверный пароль.';
} else {
hideLoginModal();
}
});
logoutButton.addEventListener('click', async () => await db.auth.signOut());
addRecordForm.addEventListener('submit', async (e) => {
e.preventDefault();
const recordData = { student: e.target.student.value, lesson: e.target.lesson.value, violation: e.target.violation.value };
const { data: { user } } = await db.auth.getUser();
if (!user) { alert('Ошибка: вы не авторизованы.'); return; }
const { error } = await db.from('violations').insert([recordData]);
if (error) {
alert(`Ошибка: ${error.message}`);
} else {
addRecordForm.reset();
await fetchRecords(user);
}
});
tableBody.addEventListener('click', async (e) => {
if (e.target.classList.contains('delete-btn')) {
const id = e.target.dataset.id;
if (confirm('Удалить запись?')) {
const { data: { user } } = await db.auth.getUser();
const { error } = await db.from('violations').delete().match({ id });
if (error) alert(`Ошибка: ${error.message}`);
else await fetchRecords(user);
}
}
});
});
</script>
</body>
</html>