Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c901691e9 | |||
| 53e44a3263 | |||
| 5db5eb0e87 |
179
controller.js
Normal file
179
controller.js
Normal file
@@ -0,0 +1,179 @@
|
||||
import { LetterModel } from './model.js';
|
||||
import { LetterView } from './view.js';
|
||||
|
||||
export class LetterController {
|
||||
constructor(model, view) {
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
this.users = [];
|
||||
this.folders = [];
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
const [letters, users] = await Promise.all([
|
||||
this.model.getAllLetters(),
|
||||
this.model.getUsers()
|
||||
]);
|
||||
|
||||
this.users = users;
|
||||
this.view.renderLetters(letters, users);
|
||||
this.bindEvents();
|
||||
} catch (error) {
|
||||
console.error('Ошибка инициализации:', error);
|
||||
}
|
||||
}
|
||||
async loadLetters(users) {
|
||||
try {
|
||||
const letters = await this.model.getAllLetters();
|
||||
this.view.renderLetters(letters, users); // Убедитесь, что users передается
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки:', error);
|
||||
this.view.showError('Не удалось загрузить письма');
|
||||
}
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
if (e.target.id === 'addLetterBtn' || e.target.closest('#addLetterBtn')) {
|
||||
this.view.showAddForm(this.users, this.folders);
|
||||
}
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.id === 'addLetterBtn' || e.target.closest('#addLetterBtn')) {
|
||||
this.view.showAddForm();
|
||||
} else if (e.target.id === 'cancelAdd' || e.target.closest('#cancelAdd')) {
|
||||
this.view.hideAddForm();
|
||||
} else if (e.target.classList.contains('delete-btn') || e.target.closest('.delete-btn')) {
|
||||
const id = e.target.dataset.id || e.target.closest('.delete-btn').dataset.id;
|
||||
this.deleteLetter(id);
|
||||
} else if (e.target.classList.contains('edit-btn') || e.target.closest('.edit-btn')) {
|
||||
const id = e.target.dataset.id || e.target.closest('.edit-btn').dataset.id;
|
||||
this.editLetter(id);
|
||||
} else if (e.target.id === 'cancelEdit' || e.target.closest('#cancelEdit')) {
|
||||
this.view.hideEditForm();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('submit', async (e) => {
|
||||
if (e.target.id === 'letterForm') {
|
||||
e.preventDefault();
|
||||
await this.handleFormSubmit();
|
||||
} else if (e.target.id === 'editLetterFormElement') {
|
||||
e.preventDefault();
|
||||
await this.handleEditFormSubmit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async editLetter(id) {
|
||||
try {
|
||||
console.log('Editing letter with ID:', id);
|
||||
const letters = await this.model.getAllLetters();
|
||||
console.log('All letters:', letters);
|
||||
|
||||
const letter = letters.find(l => l.id == id);
|
||||
console.log('Found letter:', letter);
|
||||
|
||||
if (letter) {
|
||||
this.view.showEditForm(letter);
|
||||
} else {
|
||||
this.view.showError('Письмо не найдено');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при получении письма:', error);
|
||||
this.view.showError(`Не удалось загрузить письмо для редактирования: ${error.message}`);
|
||||
}
|
||||
}
|
||||
async handleFormSubmit() {
|
||||
const subject = document.getElementById('subject').value.trim();
|
||||
const username = document.getElementById('senderName').value.trim();
|
||||
const message = document.getElementById('message').value.trim();
|
||||
|
||||
if (!username) {
|
||||
this.view.showError('Введите имя пользователя для email');
|
||||
return;
|
||||
}
|
||||
|
||||
const senderName = username + '@mail.ru';
|
||||
|
||||
try {
|
||||
const newLetter = await this.model.createLetter({
|
||||
subject,
|
||||
senderName,
|
||||
message,
|
||||
date: new Date().toLocaleDateString('ru-RU'),
|
||||
folderId: 1
|
||||
});
|
||||
|
||||
// Добавляем новое письмо в начало списка
|
||||
const container = document.getElementById('lettersContainer');
|
||||
const alert = container.querySelector('.alert');
|
||||
if (alert) container.removeChild(alert);
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card mb-3 letter-card';
|
||||
card.innerHTML = `
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">${subject}</h5>
|
||||
<p class="card-text"><strong>От:</strong> ${senderName}</p>
|
||||
<p class="card-text">${message}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">${new Date().toLocaleDateString('ru-RU')}</small>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-primary edit-btn me-2" data-id="${newLetter.id}">
|
||||
<i class="bi bi-pencil"></i> Редактировать
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger delete-btn" data-id="${newLetter.id}">
|
||||
<i class="bi bi-trash"></i> Удалить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.insertBefore(card, container.firstChild);
|
||||
|
||||
this.view.hideAddForm();
|
||||
} catch (error) {
|
||||
console.error('Ошибка при создании письма:', error);
|
||||
this.view.showError('Не удалось создать письмо');
|
||||
}
|
||||
}
|
||||
async handleEditFormSubmit() {
|
||||
const id = document.getElementById('editLetterForm').dataset.id;
|
||||
const subject = document.getElementById('editSubject').value.trim();
|
||||
const senderName = document.getElementById('editSenderName').value.trim();
|
||||
const message = document.getElementById('editMessage').value.trim();
|
||||
|
||||
try {
|
||||
await this.model.updateLetter(id, { subject, senderName, message });
|
||||
window.location.reload(); // Просто перезагружаем страницу
|
||||
} catch (error) {
|
||||
console.log('Ошибка при редактировании:', error); // Только лог в консоль
|
||||
}
|
||||
}
|
||||
async updateLetter(id, letterData) {
|
||||
const response = await fetch(`${this.apiUrl}/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...letterData,
|
||||
date: new Date().toLocaleDateString('ru-RU'),
|
||||
folderId: 1
|
||||
}),
|
||||
});
|
||||
return await response.json();
|
||||
}
|
||||
async deleteLetter(id) {
|
||||
if (confirm('Вы уверены, что хотите удалить это письмо?')) {
|
||||
try {
|
||||
await this.model.deleteLetter(id);
|
||||
await this.loadLetters();
|
||||
} catch (error) {
|
||||
console.error('Ошибка удаления:', error);
|
||||
this.view.showError('Не удалось удалить письмо');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
db.json
Normal file
53
db.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"_headers": {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE"
|
||||
},
|
||||
"letters": [
|
||||
{
|
||||
"id": "0dbb",
|
||||
"subject": "rta",
|
||||
"senderName": "КАМЕНЩИК",
|
||||
"message": "321",
|
||||
"date": "14.05.2025",
|
||||
"folderId": 1
|
||||
},
|
||||
{
|
||||
"id": "cf4b",
|
||||
"subject": "Gtgt",
|
||||
"senderName": "CyberLoh",
|
||||
"message": "Я у тебя за спиной",
|
||||
"date": "14.05.2025",
|
||||
"folderId": 1
|
||||
},
|
||||
{
|
||||
"id": "19a3",
|
||||
"subject": "Gnoms",
|
||||
"senderName": "BigBrain",
|
||||
"message": "561",
|
||||
"date": "14.05.2025",
|
||||
"folderId": 1
|
||||
},
|
||||
{
|
||||
"id": "5272",
|
||||
"subject": "6565",
|
||||
"senderName": "Try",
|
||||
"message": "5ggtgt",
|
||||
"date": "14.05.2025",
|
||||
"folderId": 1
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Иван Иванов",
|
||||
"email": "ivan@example.com"
|
||||
}
|
||||
],
|
||||
"folders": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Входящие"
|
||||
}
|
||||
]
|
||||
}
|
||||
82
model.js
Normal file
82
model.js
Normal file
@@ -0,0 +1,82 @@
|
||||
export class LetterModel {
|
||||
constructor() {
|
||||
this.apiUrl = 'http://localhost:3000';
|
||||
this.currentUserId = 1;
|
||||
}
|
||||
|
||||
async getUsers() {
|
||||
const response = await fetch(`${this.apiUrl}/users`);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Получить все папки
|
||||
async getFolders() {
|
||||
const response = await fetch(`${this.apiUrl}/folders`);
|
||||
return await response.json();
|
||||
}
|
||||
// Получить все письма (с данными о пользователях и папках)
|
||||
async getAllLetters() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${this.apiUrl}/letters?_expand=user&_expand=folder`
|
||||
);
|
||||
if (!response.ok) throw new Error('Ошибка загрузки писем');
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Ошибка в getAllLetters:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Получить письма конкретного пользователя
|
||||
async getLettersByUserId(userId) {
|
||||
const response = await fetch(
|
||||
`${this.apiUrl}/letters?userId=${userId}&_expand=folder`
|
||||
);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Получить письма из конкретной папки
|
||||
async getLettersByFolderId(folderId) {
|
||||
const response = await fetch(
|
||||
`${this.apiUrl}/letters?folderId=${folderId}&_expand=user`
|
||||
);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Создать письмо
|
||||
async createLetter(letterData) {
|
||||
const response = await fetch(`${this.apiUrl}/letters`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...letterData,
|
||||
userId: this.currentUserId, // Автоматически добавляем userId
|
||||
date: new Date().toLocaleDateString('ru-RU'), // Форматируем дату
|
||||
folderId: letterData.folderId || 1 // Добавляем folderId по умолчанию
|
||||
}),
|
||||
});
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Обновить письмо
|
||||
async updateLetter(id, letterData) {
|
||||
const response = await fetch(`${this.apiUrl}/letters/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(letterData),
|
||||
});
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Удалить письмо
|
||||
async deleteLetter(id) {
|
||||
await fetch(`${this.apiUrl}/letters/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
}
|
||||
5025
package-lock.json
generated
Normal file
5025
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
39
package.json
Normal file
39
package.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "mysite",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "concurrently \"json-server --watch db.json --port 3000\" \"live-server\"",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "http-server -p 3000 ./dist/",
|
||||
"prod": "npm-run-all build serve",
|
||||
"lint": "eslint …"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-prettier": "10.0.2",
|
||||
"eslint-plugin-html": "8.1.2",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-prettier": "5.2.3",
|
||||
"http-server": "14.1.1",
|
||||
"npm-run-all": "4.1.5",
|
||||
"vite": "6.2.0",
|
||||
"concurrently": "^8.2.0",
|
||||
"live-server": "^1.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.3.3",
|
||||
"bootstrap-icons": "1.11.3",
|
||||
"json-server": "^0.17.0"
|
||||
}
|
||||
}
|
||||
219
view.js
Normal file
219
view.js
Normal file
@@ -0,0 +1,219 @@
|
||||
export class LetterView {
|
||||
constructor() {
|
||||
this.app = document.getElementById('app');
|
||||
}
|
||||
|
||||
renderLetters(letters, users) {
|
||||
// Сначала создаем контейнер для формы и списка писем
|
||||
let html = `
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h3 class="mb-0">Список писем</h3>
|
||||
<button id="addLetterBtn" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Добавить письмо
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Форма добавления письма (изначально скрыта) -->
|
||||
<div id="addLetterForm" class="card mb-3" style="display: none;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Новое письмо</h5>
|
||||
<form id="letterForm">
|
||||
<div class="mb-3">
|
||||
<label for="subject" class="form-label">Тема</label>
|
||||
<input type="text" class="form-control" id="subject" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="senderName" class="form-label">От кого</label>
|
||||
<input type="text" class="form-control" id="senderName" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="message" class="form-label">Сообщение</label>
|
||||
<textarea class="form-control" id="message" rows="5" required></textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary flex-grow-1">Добавить</button>
|
||||
<button type="button" id="cancelAdd" class="btn btn-outline-secondary">Отмена</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Контейнер для списка писем -->
|
||||
<div id="lettersContainer" class="mb-4"></div>
|
||||
`;
|
||||
|
||||
this.app.innerHTML = html;
|
||||
|
||||
const container = document.getElementById('lettersContainer');
|
||||
if (letters.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="alert alert-info">
|
||||
Нет писем для отображения
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
letters.forEach(letter => {
|
||||
const user = users.find(u => u.id === letter.userId);
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card mb-3 letter-card';
|
||||
card.innerHTML = `
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">${letter.subject}</h5>
|
||||
<p class="card-text"><strong>От:</strong> ${letter.senderName}</p>
|
||||
${user ? `<p class="card-text"><strong>Пользователь:</strong> ${user.name}</p>` : ''}
|
||||
<p class="card-text">${letter.message}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">${letter.date}</small>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-primary edit-btn me-2" data-id="${letter.id}">
|
||||
<i class="bi bi-pencil"></i> Редактировать
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger delete-btn" data-id="${letter.id}">
|
||||
<i class="bi bi-trash"></i> Удалить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(card);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
showAddForm(users = [], folders = []) {
|
||||
const folderOptions = folders.map(f => `<option value="${f.id}">${f.name}</option>`).join('');
|
||||
|
||||
const formHtml = `
|
||||
<div id="addLetterForm" class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Новое письмо</h5>
|
||||
<form id="letterForm">
|
||||
<div class="mb-3">
|
||||
<label for="subject" class="form-label">Тема</label>
|
||||
<input type="text" class="form-control" id="subject" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="senderName" class="form-label">Отправитель (имя)</label>
|
||||
<input type="text" class="form-control" id="senderName" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="folderSelect" class="form-label">Папка</label>
|
||||
<select id="folderSelect" class="form-select" required>
|
||||
<option disabled selected>Выберите папку</option>
|
||||
${folderOptions}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="message" class="form-label">Сообщение</label>
|
||||
<textarea id="message" class="form-control" rows="5" required></textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary flex-grow-1">Добавить</button>
|
||||
<a href="page2.html" class="btn btn-outline-secondary">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
this.app.innerHTML = formHtml;
|
||||
}
|
||||
|
||||
showEditForm(letter) {
|
||||
const form = document.getElementById('editLetterForm');
|
||||
if (!form) {
|
||||
this.createEditForm(letter);
|
||||
} else {
|
||||
document.getElementById('editSubject').value = letter.subject;
|
||||
document.getElementById('editSenderName').value = letter.senderName;
|
||||
document.getElementById('editMessage').value = letter.message;
|
||||
form.dataset.id = letter.id;
|
||||
form.style.display = 'block';
|
||||
}
|
||||
document.getElementById('editSubject').focus();
|
||||
}
|
||||
|
||||
hideEditForm() {
|
||||
const form = document.getElementById('editLetterForm');
|
||||
if (form) {
|
||||
form.style.display = 'none';
|
||||
form.reset();
|
||||
}
|
||||
}
|
||||
createAddForm() {
|
||||
const formHtml = `
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Новое письмо</h5>
|
||||
<form id="letterForm">
|
||||
<div class="mb-3">
|
||||
<label for="subject" class="form-label">Тема</label>
|
||||
<input type="text" class="form-control" id="subject" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="senderName" class="form-label">От кого</label>
|
||||
<input type="text" class="form-control" id="senderName" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="message" class="form-label">Сообщение</label>
|
||||
<textarea class="form-control" id="message" rows="5" required></textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary flex-grow-1">Добавить</button>
|
||||
<a href="page2.html" class="btn btn-outline-secondary">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
this.app.innerHTML = formHtml;
|
||||
}
|
||||
createEditForm(letter) {
|
||||
const formHtml = `
|
||||
<div id="editLetterForm" class="card mb-3" style="display: none;" data-id="${letter.id}">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Редактирование письма</h5>
|
||||
<form id="editLetterFormElement">
|
||||
<div class="mb-3">
|
||||
<label for="editSubject" class="form-label">Тема</label>
|
||||
<input type="text" class="form-control" id="editSubject" value="${letter.subject}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editSenderName" class="form-label">От кого</label>
|
||||
<input type="text" class="form-control" id="editSenderName" value="${letter.senderName}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editMessage" class="form-label">Сообщение</label>
|
||||
<textarea class="form-control" id="editMessage" rows="5" required>${letter.message}</textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary flex-grow-1">Сохранить</button>
|
||||
<button type="button" id="cancelEdit" class="btn btn-outline-secondary">Отмена</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
this.app.insertAdjacentHTML('beforeend', formHtml);
|
||||
}
|
||||
|
||||
hideAddForm() {
|
||||
document.getElementById('addLetterForm').style.display = 'none';
|
||||
document.getElementById('letterForm').reset();
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
const loader = document.createElement('div');
|
||||
loader.className = 'loader';
|
||||
loader.innerHTML = 'Сохранение изменений...';
|
||||
this.app.appendChild(loader);
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
const loader = document.querySelector('.loader');
|
||||
if (loader) loader.remove();
|
||||
}
|
||||
}
|
||||
BIN
Отчёт4.docx
Normal file
BIN
Отчёт4.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user