Skip to content
Closed

Hw 8 #1328

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
6385182
Реализовано добавление комментариев
NessieflakeS Jun 4, 2025
91a8ce6
выполнение 2-го ДЗ
NessieflakeS Jun 7, 2025
920eb8d
hw-2
NessieflakeS Jun 7, 2025
907b97b
some fix
NessieflakeS Jun 8, 2025
e5a56dd
Merge pull request #1 from NessieflakeS/HW-2
NessieflakeS Jul 3, 2025
b326933
выполнение 3-го дз
NessieflakeS Jul 3, 2025
5e28152
some fix
NessieflakeS Jul 3, 2025
b108fc1
Merge branch 'main' into HW-3
NessieflakeS Jul 3, 2025
24ffc18
Merge pull request #2 from NessieflakeS/HW-3
NessieflakeS Jul 16, 2025
0f9bc50
Добавление HW-6
NessieflakeS Aug 12, 2025
50c73ad
некоторые изменения
NessieflakeS Aug 12, 2025
2918e24
Исправление ошибок
NessieflakeS Aug 12, 2025
0824230
мелкие исправления
NessieflakeS Aug 12, 2025
12edf87
мелкие исправления
NessieflakeS Aug 12, 2025
39c1ea4
Исправление стилей и лайков
NessieflakeS Aug 12, 2025
f079ccd
изменение загрузки
NessieflakeS Aug 12, 2025
51d0ae4
изменение стилей
NessieflakeS Aug 12, 2025
2d64d3a
фикс
NessieflakeS Aug 12, 2025
f6ddff9
фикс стилей
NessieflakeS Aug 12, 2025
1ffb65a
фикс стилей
NessieflakeS Aug 12, 2025
8209098
фикс css
NessieflakeS Aug 12, 2025
f74533e
Переделал АПИ
NessieflakeS Aug 26, 2025
13a62d6
восстановление работоспособности
NessieflakeS Aug 26, 2025
594ee7f
фикс
NessieflakeS Aug 26, 2025
0981894
изменение АПИ
NessieflakeS Aug 26, 2025
0efd1ae
фикс
NessieflakeS Aug 26, 2025
a9f16c2
фикс лайков
NessieflakeS Aug 26, 2025
45a588c
добавление ошибок 500, 400
NessieflakeS Sep 1, 2025
0e1dd1a
фикс
NessieflakeS Sep 1, 2025
accf8f6
500 ошибка
NessieflakeS Sep 1, 2025
e9129c5
исправление ошибок
NessieflakeS Sep 1, 2025
27bc334
изменил коды ошибок
NessieflakeS Sep 4, 2025
08b9ae3
Авторизация
NessieflakeS Sep 6, 2025
edffafb
.
NessieflakeS Sep 6, 2025
7836014
.
NessieflakeS Sep 6, 2025
3437d9c
.
NessieflakeS Sep 6, 2025
b5f5042
.
NessieflakeS Sep 6, 2025
71081ee
.
NessieflakeS Sep 6, 2025
ed462c1
.
NessieflakeS Sep 6, 2025
b622e19
.
NessieflakeS Sep 6, 2025
551bb0b
.
NessieflakeS Sep 6, 2025
d055692
.
NessieflakeS Sep 6, 2025
9fbcee6
.
NessieflakeS Sep 6, 2025
0ceb389
.
NessieflakeS Sep 6, 2025
d82f789
.
NessieflakeS Sep 6, 2025
55b48ee
.
NessieflakeS Sep 7, 2025
ca6b379
.
NessieflakeS Sep 7, 2025
ac22b69
.
NessieflakeS Sep 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": ["eslint:recommended", "prettier"],
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
"no-console": "warn",
"no-unused-vars": "warn",
"no-var": "error",
"prefer-const": "error"
}
}
8 changes: 4 additions & 4 deletions .github/workflows/greetings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
if: github.repository == 'GlebkaF/webdev-dom-homework'
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3
with:
# Optional. Post a issue comment just before closing a pull request.
comment: "Привет! Сделай пожалуйста Pull Request в свой репозиторий, сейчас ты сделал PR в репозиторий Глеба. Посмотри в домашке, там на скриншоте нарисовано где выбрать свой репозиторий"
- uses: superbrothers/close-pull-request@v3
with:
# Optional. Post a issue comment just before closing a pull request.
comment: 'Привет! Сделай пожалуйста Pull Request в свой репозиторий, сейчас ты сделал PR в репозиторий Глеба. Посмотри в домашке, там на скриншоте нарисовано где выбрать свой репозиторий'
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules/

.env

*.log

.vscode/
.idea/

dist/
coverage/

.DS_Store
Thumbs.db
5 changes: 5 additions & 0 deletions .hintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": [
"development"
]
}
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"tabWidth": 2,
"printWidth": 100
}
185 changes: 185 additions & 0 deletions api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
const PERSONAL_KEY = 'nikandrov-danil';
const BASE_URL = `https://wedev-api.sky.pro/api/v2/${PERSONAL_KEY}`;
const AUTH_URL = `https://wedev-api.sky.pro/api/user`;

export let token = null;
export let user = null;

export const setToken = (newToken) => {
token = newToken;
if (newToken) {
localStorage.setItem('token', newToken);
} else {
localStorage.removeItem('token');
}
};

export const setUser = (newUser) => {
user = newUser;
if (newUser) {
localStorage.setItem('user', JSON.stringify(newUser));
} else {
localStorage.removeItem('user');
}
};

export const getToken = () => {
return token || localStorage.getItem('token');
};

export const getUser = () => {
try {
if (user) return user;

const userData = localStorage.getItem('user');

if (!userData) return null;

return JSON.parse(userData);
} catch (error) {
console.error('Ошибка при получении пользователя из localStorage:', error);
localStorage.removeItem('user');
return null;
}
};

export const removeAuthData = () => {
token = null;
user = null;
localStorage.removeItem('token');
localStorage.removeItem('user');
};

export const getComments = async () => {
try {
const response = await fetch(`${BASE_URL}/comments`);

if (!response.ok) {
if (response.status === 500) {
throw new Error('Сервер сломался, попробуй позже');
} else {
throw new Error('Ошибка сервера');
}
}

const data = await response.json();

return data.comments.map(comment => ({
id: comment.id,
name: comment.author.name,
text: comment.text,
likes: comment.likes || 0,
isLiked: comment.isLiked || false,
date: new Date(comment.date).getTime()
}));
} catch (error) {
if (error.message === 'Failed to fetch') {
throw new Error('Кажется, у вас сломался интернет, попробуйте позже');
}
throw error;
}
};

export const postComment = async (text) => {
const currentToken = getToken();

if (!currentToken) {
throw new Error('Ошибка авторизации');
}

try {
const response = await fetch(`${BASE_URL}/comments`, {
method: 'POST',
headers: {
Authorization: `Bearer ${currentToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
text,
}),
});

if (!response.ok) {
if (response.status === 400) {
const errorData = await response.json();
throw new Error(errorData.error || 'Комментарий должен быть не короче 3 символов');
} else if (response.status === 500) {
throw new Error('Сервер сломался, попробуй позже');
} else if (response.status === 401) {
throw new Error('Ошибка авторизации');
} else {
throw new Error('Ошибка сервера');
}
}

return await getComments();
} catch (error) {
if (error.message === 'Failed to fetch') {
throw new Error('Кажется, у вас сломался интернет, попробуйте позже');
}
throw error;
}
};

export const login = async ({ login, password }) => {
try {
const response = await fetch(`${AUTH_URL}/login`, {
method: 'POST',
body: JSON.stringify({
login,
password,
}),
});

const data = await response.json();

if (!response.ok) {
if (response.status === 400) {
throw new Error(data.error || 'Неверный логин или пароль');
} else {
throw new Error(data.error || 'Ошибка сервера');
}
}

setToken(data.user.token);
setUser(data.user);
return data;
} catch (error) {
if (error.message === 'Failed to fetch') {
throw new Error('Кажется, у вас сломался интернет, попробуйте позже');
}
throw error;
}
};

export const register = async ({ name, login, password }) => {
try {
const response = await fetch(AUTH_URL, {
method: 'POST',
body: JSON.stringify({
name,
login,
password,
}),
});

const data = await response.json();

if (!response.ok) {
if (response.status === 400) {
throw new Error(data.error || 'Пользователь с таким логином уже существует');
} else {
throw new Error(data.error || 'Ошибка сервера');
}
}

setToken(data.user.token);
setUser(data.user);
return data;
} catch (error) {
if (error.message === 'Failed to fetch') {
throw new Error('Кажется, у вас сломался интернет, попробуйте позже');
}
throw error;
}
};
40 changes: 40 additions & 0 deletions db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"comments": [
{
"id": "1",
"name": "Тестовый пользователь",
"text": "Это первый комментарий!",
"date": "28.07.2025, 11:00:00",
"likes": 1,
"isLiked": true,
"replyTo": null
},
{
"id": "2",
"name": "Второй пользователь",
"text": "Работает отлично!",
"date": "29.07.2025, 12:00:00",
"likes": 1,
"isLiked": true,
"replyTo": null
},
{
"id": "60c1",
"name": "tretiy",
"text": "ya tyta",
"date": "29.07.2025, 21:25:16",
"likes": 1,
"isLiked": true,
"replyTo": null
},
{
"id": "a6d3",
"name": "Данил",
"text": "не работают лайки :((((((((",
"date": "29.07.2025, 21:30:21",
"likes": 0,
"isLiked": false,
"replyTo": null
}
]
}
11 changes: 11 additions & 0 deletions escapeHTML.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
function escapeHTML(str) {
if (!str) return '';
return str
.replaceAll('&', '&')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#039;');
}

export { escapeHTML };
81 changes: 81 additions & 0 deletions eventHandlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { escapeHTML } from './escapeHTML.js';
import { getToken } from './api.js';
import { renderLoginComponent } from './login.js';

export function initHandlers({ onAddComment, onToggleLike, onReply, onRetry, onInputChange }) {
const commentInput = document.querySelector('.add-form-text');
const addButton = document.querySelector('.add-form-button');
const commentsList = document.querySelector('.comments');
const appEl = document.getElementById('app');

const checkInputs = () => {
if (addButton) {
addButton.disabled = !commentInput.value.trim();
}
if (onInputChange) onInputChange();
};

if (commentsList) {
commentsList.addEventListener('click', (event) => {
if (event.target.classList.contains('retry-btn')) {
onRetry();
return;
}

if (event.target.classList.contains('like-button')) {
event.preventDefault();
const commentElement = event.target.closest('.comment');
if (!commentElement) return;

const commentId = commentElement.dataset.id;
onToggleLike(commentId);
return;
}

if (event.target.classList.contains('comment-reply')) {
event.preventDefault();
const commentElement = event.target.closest('.comment');
if (!commentElement) return;

const author = commentElement.querySelector('.comment-author').textContent;
const text = commentElement.querySelector('.comment-body').textContent;
onReply(author, text);
return;
}
});
}

if (addButton && commentInput) {
addButton.addEventListener('click', (event) => {
event.preventDefault();

const text = commentInput.value.trim();

if (text.length < 3) {
alert('Комментарий должен быть не короче 3 символов');
return;
}

onAddComment({
text: escapeHTML(text)
});
});

commentInput.addEventListener('input', checkInputs);
}

const authLink = document.getElementById('auth-link');
if (authLink) {
authLink.addEventListener('click', (event) => {
event.preventDefault();
renderLoginComponent({
appEl,
onSuccess: () => {
window.location.reload();
}
});
});
}

checkInputs();
}
11 changes: 11 additions & 0 deletions formatDate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleString('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
Loading