Всем привет. Тут на статью поступила жалоба от одного не особо умного человека, и статью удалил ucoz. За якобы воровство кода... И в правду я вдохновился скриптом с сайта uscript.pro, за что ему спасибо!
Так вот, я долго мучился и написал свою версию данного скрипта! Так что ловите и пользуйтесь с удовольствием, поскольку на ucoz сейчас очень мало чего годного! А у себя на сайте я собираю и тестирую почти все скрипты и по возможности переписываю, опять же, вдохновляюсь старыми идеями и скриптами!!


Этот скрипт представляет собой систему отзывов и рейтинга для сайтов на платформе uCoz. Он позволяет пользователям оставлять отзывы о других пользователях, а модераторам — управлять этим контентом.

Вот подробное описание того, что именно делает этот код:
1. Основные функции системы
* Загрузка отзывов: Скрипт подтягивает существующие отзывы из системы uCoz (страница репутации/замечаний) и отображает их в красивом, современном виде.

* Добавление отзывов: Пользователь может написать текст и выбрать тип (положительный, отрицательный или нейтральный).

При этом учитывается «вес» голоса: чем выше репутация автора, тем сильнее изменится рейтинг получателя.

* Умный рейтинг: Скрипт автоматически рассчитывает числовое значение рейтинга (от +1 до +20) в зависимости от достижений пользователя.

* Валидация: Скрипт проверяет, не пустое ли поле текста, выбран ли рейтинг и не превышен ли лимит символов (250 знаков).


2. Возможности для модераторов
Для пользователей из определенных групп (например, администраторов) скрипт включает «Панель модератора»:
* Редактирование «на лету»: Модератор может кликнуть на текст отзыва, изменить его прямо в блоке и сохранить без перезагрузки страницы.

* Удаление: Возможность быстро удалить некорректный отзыв с подтверждением действия.


3. Технические особенности (как это работает внутри)
* Безопасность: Скрипт автоматически получает ssid (специальный ключ сессии), чтобы запросы на сервер uCoz принимались как легитимные.

* Плавная анимация: Отзывы не просто «выскакивают», а появляются с эффектом затухания и прокрутки.

* Динамическая проверка: Скрипт отслеживает время (например, ограничение «один отзыв в 24 часа») и выводит соответствующие уведомления (через библиотеку Growl).

* Интерактивность: Есть счетчик оставшихся символов, который меняет цвет (зеленый → желтый → красный), если лимит текста подходит к концу.


4. Визуальные элементы
* Аватары: Если у пользователя нет своего фото, подставляется стандартная заглушка.

* Цветовая индикация: Блоки отзывов подсвечиваются разными цветами в зависимости от того, хороший это отзыв или плохой.


Установка
для начала создайте папку rep и залейте туда файлы прикреплённые какой статье

на персональной странице где будет находиться блок с отзывами разместите данный код

Код
<div class="review-section">
<div class="review-loader"></div>
</div>
<?if($_REPUTATION$='0')?><?else?>
<div class="load-more-reviews">
<button class="btn-load-reviews">Показать больше отзывов</button>
</div>
<?endif?>
<input type="hidden" class="user-id-field" value="$_USER_ID$">
<input type="hidden" class="group-id-field" value="$GROUP_ID$">
<br><br>
<h3 class="review-form-title">Написать отзыв: <span class="char-remaining"><strong id="char-remain">250</strong></span></h3>
<textarea class="review-input" name="review-text" maxlength="250" id="review-text" cols="50" placeholder="Ваш отзыв здесь" style="height: 120px;"></textarea>
<div class="review-type-label">Выберите тип отзыва:</div>
<hr class="separator">
<div class="review-type-choices">
<input type="radio" name="review-rating" value="2" class="choice-radio" id="positive-review">
<label for="positive-review" class="choice-label">Положительный</label>
<input type="radio" name="review-rating" value="1" class="choice-radio" id="negative-review">
<label for="negative-review" class="choice-label">Отрицательный</label>
<div class="review-submit">
<button class="btn-submit-review" type="submit">Отправить отзыв</button>
</div>
</div>
<script src="https://htmlstart.ucoz.net/rep/user-reputation.js"></script>
<link rel="stylesheet" type="text/css" href="https://htmlstart.ucoz.net/rep/rep_style.css" />


JavaScript код для изучения

Код
<script>// Объект для управления отзывами by htmlstart.ucoz.net при копировании скрипта ссылка на источник обязательна!!
const ReviewSystem = {
// Конфигурация приложения
config: {
apiKey: "", // Ключ для API
defaultAvatar: "https://htmlstart.ucoz.net/img/noAvatarComment.png", // Аватар по умолчанию
moderatorGroups: "3,4", // Группы модераторов
errorEmptyText: "Пожалуйста, заполните поле отзыва", // Ошибка пустого текста
errorNoRating: "Выберите тип отзыва", // Ошибка отсутствия рейтинга
successMessage: "Отзыв успешно добавлен", // Сообщение об успехе
waitMessage: "Вы сможете оставить отзыв через 24 часа", // Ошибка ограничения времени
accessDenied: "Доступ ограничен" // Ошибка доступа
},

// Загрузка отзывов с сервера
fetchReviews: function () {
$('.btn-load-reviews').off('click').on('click', () => ReviewSystem.fetchReviews());
if (ReviewSystem.currentPage === ReviewSystem.nextPage) {
$.get(`/index/9-${ReviewSystem.userId}-${ReviewSystem.currentPage}`, function (response) {
const content = $(response).find('cmd[p=content]').text();
$('[id^=blr]', content).each(function () {
ReviewSystem.data.commentIds[ReviewSystem.commentCount] = $(this).attr('id').match(/\d+/g)[0];
ReviewSystem.data.userLinks[ReviewSystem.commentCount] = $('.banHUser', this).attr('href');
ReviewSystem.data.userIds[ReviewSystem.commentCount] = ReviewSystem.data.userLinks[ReviewSystem.commentCount].match(/\d+$/g)[0];
ReviewSystem.data.userNames[ReviewSystem.commentCount] = $('.banHUser b', this).text();
ReviewSystem.data.dates[ReviewSystem.commentCount] = $('td[width="70%"] + td', this).text();
ReviewSystem.data.ratings[ReviewSystem.commentCount] = $('[title^="Уровень"]', this).attr('title');
ReviewSystem.data.texts[ReviewSystem.commentCount] = $(`#mmtx${ReviewSystem.data.commentIds[ReviewSystem.commentCount]}`, this).text();
ReviewSystem.commentCount += 1;
});
ReviewSystem.nextPage += 1;
ReviewSystem.renderReviews(ReviewSystem.totalComments - ReviewSystem.commentLimit);
}).fail(function () {
console.error("Failed to fetch reviews");
$.growl.error({ title: "Ошибка", message: "Не удалось загрузить отзывы" });
});
} else {
ReviewSystem.renderReviews(ReviewSystem.totalComments - ReviewSystem.commentLimit);
}
},

// Рендеринг отзывов
renderReviews: function (index) {
$.get(`/api/index/8-${ReviewSystem.data.userIds[index]}?apikey=${ReviewSystem.config.apiKey}`, function (data) {
const commentId = ReviewSystem.data.commentIds[index];
const userLink = ReviewSystem.data.userLinks[index];
const userId = ReviewSystem.data.userIds[index];
const userName = ReviewSystem.data.userNames[index];
const commentDate = ReviewSystem.data.dates[index];
let rating = ReviewSystem.data.ratings[index];
const commentText = ReviewSystem.data.texts[index];
const userData = {};
let avatar, moderatorControls = "", ratingClass;

if (index < ReviewSystem.commentCount) {
$('name', data).each(function () {
userData[$(this).text()] = $(this).next().text();
});
avatar = userData['USER_AVATAR'] || ReviewSystem.config.defaultAvatar;

if (rating.includes('+')) {
rating = rating.match(/\+\d+/g)[0];
ratingClass = "positive-rating";
} else if (rating.includes('-')) {
rating = rating.match(/\-\d+/g)[0];
ratingClass = "negative-rating";
} else {
rating = 0;
ratingClass = "neutral-rating";
}

if (ReviewSystem.config.moderatorGroups.indexOf(ReviewSystem.groupId) !== -1) {
moderatorControls = `
<div class="mod-panel mod-hover top-left">
<div class="mod-toggle" role="button"></div>
<ul class="mod-list">
<li class="mod-edit"><a href="javascript:void(0)" onclick="ReviewSystem.editReview(${commentId});return false;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg> <div class="mod-tooltip">Редактировать отзыв</div></a></li>
<li class="mod-delete"><a href="javascript:void(0)" onclick="ReviewSystem.deleteReview(${commentId});return false;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg> <div class="mod-tooltip">Удалить отзыв</div></a></li>
</ul>
</div>`;
}

$('.review-section').append(`
<div class="comment-block ${ratingClass}" id="cmnt${commentId}">
<div class="user-details">
<div class="avatar-wrapper">
<img src="${avatar}" alt="Avatar" />
<span class="rating-indicator"><span class="rating-text" title="${rating} к рейтингу от ${userName}">${rating}</span></span>
</div>
<div class="comment-body">
<div class="comment-header">
<span class="user-name"><a href="${userLink}">${userName}</a></span>
<span class="comment-date">${commentDate}</span>
${moderatorControls}
</div>
<p class="comment-text">${commentText}</p>
</div>
<div class="clear-fix"></div>
</div>
<br><hr class="separator">
</div>`);

index += 1;
if (index < ReviewSystem.totalComments) {
ReviewSystem.renderReviews(index);
} else {
$('.review-section .comment-block').css({ display: 'block' });
ReviewSystem.displayComments(0);
}
} else {
ReviewSystem.checkRemaining(index);
ReviewSystem.currentPage += 1;
$('.review-section .comment-block').css({ display: 'block' });
ReviewSystem.displayComments(0);
}
ReviewSystem.bindModeratorEvents();
}).fail(function () {
console.error("Failed to fetch user data");
$.growl.error({ title: "Ошибка", message: "Не удалось загрузить данные пользователя" });
});
},

// Анимация отображения комментариев
displayComments: function (index) {
if (index < ReviewSystem.commentLimit) {
$('.comment-block:not(.visible)').eq(index).animate({ opacity: 1 }, 400);
} else {
ReviewSystem.totalComments += ReviewSystem.commentLimit - ReviewSystem.commentDiff;
ReviewSystem.commentDiff = 0;
$('.comment-block:not(.visible)').addClass('visible');
$('.btn-load-reviews').off('click').on('click', () => ReviewSystem.fetchReviews());
return false;
}
setTimeout(function () {
ReviewSystem.displayComments(index + 1);
}, 200);
},

// Проверка оставшихся данных
checkRemaining: function (index) {
if (index >= ReviewSystem.commentCount && ReviewSystem.commentCount % 50 !== 0) {
$('.btn-load-reviews').remove();
} else {
ReviewSystem.commentDiff = ReviewSystem.totalComments - ReviewSystem.commentCount;
$.get(`/index/9-${ReviewSystem.userId}-${ReviewSystem.nextPage}`, function (response) {
const content = $(response).find('cmd[p=content]').text();
const commentId = $('[id^=blr]', content).attr('id').match(/\d+/g)[0];
if (commentId === ReviewSystem.data.commentIds[0]) {
$('.btn-load-reviews').remove();
}
}).fail(function () {
console.error("Failed to check remaining comments");
});
}
},

// Редактирование отзыва  
editReview: function (reviewId) {
console.log(`Editing review ${reviewId}`);
$('#cmnt' + reviewId + ' .comment-text').attr({ contenteditable: 'true' }).css({ color: '#1992ed', border: '1px dashed #1992ed', padding: '5px' }).focus();
$('#cmnt' + reviewId + ' .comment-text').blur(function () {
$.get("/index/14", function (session) {
$.post('/index/', {
c: reviewId,
a: '26',
p: '2',
s: ReviewSystem.userId,
reason: $('#cmnt' + reviewId + ' .comment-text').text(),
ssid: $("input[name='ssid']", session).val()
}, function () {
$('#cmnt' + reviewId + ' .comment-text').attr({ contenteditable: 'false' }).css({ color: '#000', border: 'none', padding: '0' });
console.log(`Review ${reviewId} updated`);
}).fail(function (xhr, status, error) {
console.error(`Failed to update review ${reviewId}: ${error}`);
$.growl.error({ title: "Ошибка", message: "Не удалось обновить отзыв" });
});
}).fail(function () {
console.error("Failed to fetch SSID for edit");
$.growl.error({ title: "Ошибка", message: "Не удалось получить данные для редактирования" });
});
});
},

// Удаление отзыва
deleteReview: function (reviewId) {
console.log(`Deleting review ${reviewId}`);
if (confirm('Вы подтверждаете удаление?')) {
$.get("/index/14", function (session) {
$.post('/index/26-' + ReviewSystem.userId + '-' + reviewId + '-1', function () {
$('#cmnt' + reviewId).animate({ opacity: 0 }, 400, function () {
$(this).animate({ height: 0 }, 400, function () {
$(this).remove();
console.log(`Review ${reviewId} deleted`);
});
});
}).fail(function (xhr, status, error) {
console.error(`Failed to delete review ${reviewId}: ${error}`);
$.growl.error({ title: "Ошибка", message: "Не удалось удалить отзыв" });
});
}).fail(function () {
console.error("Failed to fetch SSID for delete");
$.growl.error({ title: "Ошибка", message: "Не удалось получить данные для удаления" });
});
}
},

// Привязка событий для панели модератора
bindModeratorEvents: function () {
$('.mod-panel .mod-toggle').off('click').on('click', function (e) {
e.stopPropagation();
const $parent = $(this).parent('.mod-panel');
$('.mod-panel').not($parent).removeClass('active');
$parent.toggleClass('active');
});

$(document).off('click').on('click', function (e) {
if (!$(e.target).closest('.mod-panel').length) {
$('.mod-panel').removeClass('active');
}
});
},

userId: 0,
groupId: 0,
currentPage: 1,
nextPage: 1,
commentCount: 0,
commentLimit: 7,
totalComments: 7,
commentDiff: 0,
data: {
commentIds: [],
userLinks: [],
userIds: [],
userNames: [],
dates: [],
ratings: [],
texts: []
}
};

// Добавление нового отзыва
function addReview(userId, commentText, rating) {
$.get(`/api/index/8?apikey=${ReviewSystem.config.apiKey}`, function (data) {
const commentId = Math.floor(Math.random() * 1000000);
const userLink = `/index/8-${userId}`;
let userName = "";
const commentDate = "Только что";
let ratingValue = rating;
let weight = 1;
const userData = {};
let avatar, moderatorControls = "", ratingClass;

$('name', data).each(function () {
userData[$(this).text()] = $(this).next().text();
});
avatar = userData['USER_AVATAR'] || ReviewSystem.config.defaultAvatar;
userName = userData['USER_USERNAME'];

if (userData['USER_REPUTATION'] > 10) weight = 2;
if (userData['USER_REPUTATION'] > 50) weight = 3;
if (userData['USER_REPUTATION'] > 100) weight = 4;
if (userData['USER_REPUTATION'] > 200) weight = 5;
if (userData['USER_REPUTATION'] > 300) weight = 6;
if (userData['USER_REPUTATION'] > 500) weight = 7;
if (userData['USER_REPUTATION'] > 700) weight = 8;
if (userData['USER_REPUTATION'] > 900) weight = 9;
if (userData['USER_REPUTATION'] > 1000) weight = 10;
if (userData['USER_REPUTATION'] > 2500) weight = 15;
if (userData['USER_REPUTATION'] > 5000) weight = 20;

if (ratingValue == 2) {
ratingValue = `+${weight}`;
ratingClass = "positive-rating";
} else {
ratingValue = `-${weight}`;
ratingClass = "negative-rating";
}
if (rating == 0) {
ratingValue = "0";
ratingClass = "neutral-rating";
}

if (ReviewSystem.config.moderatorGroups.indexOf(ReviewSystem.groupId) !== -1) {
moderatorControls = `
<div class="mod-panel mod-hover top-left">
<div class="mod-toggle" role="button"></div>
<ul class="mod-list">
<li class="mod-edit"><a href="javascript:void(0)" onclick="ReviewSystem.editReview(${commentId});return false;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg> <div class="mod-tooltip">Редактировать отзыв</div></a></li>
<li class="mod-delete"><a href="javascript:void(0)" onclick="ReviewSystem.deleteReview(${commentId});return false;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg> <div class="mod-tooltip">Удалить отзыв</div></a></li>
</ul>
</div>`;
}

$('.review-section').prepend(`
<div class="comment-block ${ratingClass}" id="cmnt${commentId}">
<div class="user-details">
<div class="avatar-wrapper">
<img src="${avatar}" alt="Avatar" />
<span class="rating-indicator"><span class="rating-text" title="${ratingValue} к рейтингу от ${userName}">${ratingValue}</span></span>
</div>
<div class="comment-body">
<div class="comment-header">
<span class="user-name"><a href="${userLink}">${userName}</a></span>
<span class="comment-date">${commentDate}</span>
${moderatorControls}
</div>
<p class="comment-text">${commentText}</p>
</div>
<div class="clear-fix"></div>
</div>
<br><hr class="separator">
</div>`);

$('.comment-block:not(.visible)').eq(0).toggle(400, function () {
$('.comment-block:not(.visible)').eq(0).animate({ opacity: 1 }, 400);
});

ReviewSystem.bindModeratorEvents();
}).fail(function () {
console.error("Failed to fetch user data for new review");
$.growl.error({ title: "Ошибка", message: "Не удалось добавить отзыв" });
});
}

// Обработчик отправки отзыва
$('.btn-submit-review').click(function () {
const userId = $('.user-id-field').val();
const commentText = $('.review-input').val();
const rating = $('input[name="review-rating"]:checked').val();
const captchaCode = $('.review-form [name="code"]');
const secretKey = $('.review-form [name="seckey"]');

if (!commentText) {
$.growl.warning({ title: "Ошибка", message: ReviewSystem.config.errorEmptyText });
return false;
}
if (!rating) {
$.growl.warning({ title: "Ошибка", message: ReviewSystem.config.errorNoRating });
return false;
}

$.get("/index/14", function (response) {
$.post('/index/', {
a: '23',
t: '1',
s: userId,
reason: commentText,
act: rating,
code: captchaCode,
seckey: secretKey,
ref: '',
ssid: $("input[name='ssid']", response).val()
}, function (data) {
const result = $(data).find('cmd[p=innerHTML]').text();
if (result.includes('myWinLoadSD')) {
$('.review-input').val("");
addReview(userId, commentText, rating);
$.growl.notice({ title: "Успех", message: ReviewSystem.config.successMessage });
} else if (result.includes('myWinLoadSF')) {
$.growl.error({ title: "Ошибка", message: ReviewSystem.config.waitMessage });
} else if (result.includes('Доступ запрещен')) {
$.growl.error({ title: "Ошибка", message: ReviewSystem.config.accessDenied });
}
}).fail(function () {
console.error("Failed to submit review");
$.growl.error({ title: "Ошибка", message: "Не удалось отправить отзыв" });
});
}).fail(function () {
console.error("Failed to fetch SSID for review submission");
$.growl.error({ title: "Ошибка", message: "Не удалось отправить отзыв" });
});
});

// Инициализация страницы
$(document).ready(function () {
$('.review-section').animate({ opacity: 0 }, 200, function () {
$('.review-section').html("").css({ opacity: 1 });
$('.review-form-data').load('/index/23-1', function (response) {
const inputs = $('.review-form fieldset:nth-child(3) > input');
const images = $('.review-form fieldset:nth-child(3) > img');
$('.review-form').append(inputs).append(images);
});
ReviewSystem.userId = $('.user-id-field').val();
ReviewSystem.groupId = $('.group-id-field').val();
ReviewSystem.fetchReviews();
});

ReviewSystem.bindModeratorEvents();
});

// Обработчик ввода текста
const charCounter = document.getElementById('char-remain');
const textField = document.getElementById('review-text');

textField.addEventListener('keyup', function () {
const charsLeft = 250 - textField.value.length;
charCounter.textContent = charsLeft;

if (charsLeft > 80) {
charCounter.className = 'char-remaining';
} else if (charsLeft < 30) {
charCounter.className = 'char-remaining danger';
} else {
charCounter.className = 'char-remaining warning';
}

if (charsLeft < 0) {
textField.value = textField.value.substring(0, 250);
charCounter.textContent = '0';
}
}, false);
//разработчик by jaguar37rus, site htmlstart.ucoz.net</script>


css код для изучения

Код
<style>.review-section {
background: linear-gradient(145deg, #f9fafb, #e5e7eb);
border-radius: 12px;
padding: 24px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
margin: 20px auto;
max-width: 800px;
position: relative;
overflow: hidden;
}

.review-loader {
display: flex;
justify-content: center;
align-items: center;
height: 120px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><circle cx="25" cy="25" r="20" stroke="rgba(59,130,246,0.5)" stroke-width="5" fill="none"><animate attributeName="r" from="20" to="25" dur="0.8s" repeatCount="indefinite"/></circle></svg>') no-repeat center center / 40px;
opacity: 0.7;
}

.load-more-reviews {
text-align: center;
margin: 20px 0;
}

.btn-load-reviews {
background: #3b82f6;
color: #fff;
padding: 12px 30px;
border: none;
border-radius: 10px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: background 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease;
}

.btn-load-reviews:hover {
background: #2563eb;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}

.review-form-title {
font-size: 28px;
color: #1f2937;
font-weight: 700;
margin: 20px 0 15px;
display: flex;
align-items: center;
gap: 12px;
}

.char-remaining {
font-size: 20px;
font-weight: 600;
color: #3b82f6;
transition: color 0.3s ease;
}

.char-remaining.warning {
color: #facc15;
}

.char-remaining.danger {
color: #ef4444;
}

.review-input {
width: 94%;
padding: 16px;
border: 1px solid #d1d5db;
border-radius: 10px;
font-size: 16px;
line-height: 1.5;
resize: none;
background: #fff;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.review-input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 8px rgba(59, 130, 246, 0.3);
}

.review-type-label {
font-size: 18px;
color: #4b5563;
margin: 15px 0 10px;
font-weight: 500;
}

.separator {
border: 0;
height: 1px;
background: linear-gradient(to right, transparent, #d1d5db, transparent);
margin: 20px 0;
}

.review-type-choices {
display: flex;
gap: 16px;
margin-bottom: 15px;
}

.choice-radio {
display: none;
}

.choice-label {
padding: 12px 24px;
background: #f3f4f6;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
color: #374151;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
}

.choice-radio:checked + .choice-label {
background: #3b82f6;
color: #fff;
}

.choice-label:hover {
background: #e5e7eb;
}

.review-submit {
text-align: right;
}

.btn-submit-review {
background: #10b981;
color: #fff;
padding: 12px 30px;
border: none;
border-radius: 10px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: background 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease;
}

.btn-submit-review:hover {
background: #059669;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
}

.comment-block {
display: block;
opacity: 0;
margin-bottom: 20px;
transition: opacity 0.4s ease;
}

.comment-block.visible {
opacity: 1;
}

.user-details {
display: flex;
gap: 10px;
padding: 16px;
border-radius: 10px;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: transform 0.3s ease;
}

.user-details:hover {
transform: translateY(-2px);
}

.avatar-wrapper {
flex-shrink: 0;
position: relative;
}

.avatar-wrapper img {
width: 50px;
height: 50px;
border-radius: 50%;
object-fit: cover;
border: 2px solid #e5e7eb;
}

.rating-indicator {
display: inline-block;
margin-left: 12px;
position: absolute;
left: -20px;
}

.rating-text {
font-size: 14px;
font-weight: 600;
border-radius: 50%;
color: #fff;
width: 25px;
height: 25px;
display: flex;
justify-content: center;
flex-wrap: nowrap;
align-items: center;
}

.positive-rating .rating-text {
background: #10b981;
}

.negative-rating .rating-text {
background: #ef4444;
}

.neutral-rating .rating-text {
background: #6b7280;
}

.comment-body {
flex-grow: 1;
}

.comment-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}

.user-name a {
color: #3b82f6;
text-decoration: none;
font-weight: 600;
font-size: 16px;
}

.user-name a:hover {
text-decoration: underline;
}

.comment-date {
color: #6b7280;
font-size: 14px;
font-style: italic;
}

.comment-text {
margin: 0;
color: #374151;
font-size: 16px;
line-height: 1.6;
}

.comment-text[contenteditable="true"] {
border: 1px dashed #1992ed;
padding: 5px;
color: #1992ed;
}

.mod-panel {
position: relative;
display: inline-block;
margin-left: 12px;
}

.mod-toggle {
width: 28px;
height: 28px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/></svg>') no-repeat center;
background-size: 20px;
cursor: pointer;
color: #6b7280;
transition: transform 0.3s ease, color 0.3s ease;
}

.mod-panel.active .mod-toggle {
transform: rotate(45deg);
color: #3b82f6;
}

.mod-list {
display: none;
position: absolute;
top: 100%;
left: 0;
background: #fff;
border-radius: 8px;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
list-style: none;
padding: 8px 0;
min-width: 160px;
z-index: 100;
opacity: 0;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease;
}

.mod-panel.active .mod-list {
display: block;
opacity: 1;
transform: translateY(0);
}

.mod-list li {
padding: 10px 16px;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}

.mod-list li:hover {
background: #f3f4f6;
}

.mod-list li a {
display: flex;
align-items: center;
gap: 8px;
text-decoration: none;
color: inherit;
}

.mod-tooltip {
font-size: 14px;
color: #374151;
font-weight: 500;
}

.mod-list li:hover .mod-tooltip {
color: #3b82f6;
}

@media (max-width: 600px) {
.review-section {
padding: 16px;
margin: 10px;
}

.review-form-title {
font-size: 24px;
}

.review-input {
font-size: 14px;
padding: 12px;
}

.review-type-choices {
flex-direction: column;
gap: 10px;
}

.choice-label {
width: 100%;
text-align: center;
}

.user-details {
flex-direction: column;
gap: 12px;
}

.avatar-wrapper {
text-align: center;
}

.comment-header {
flex-direction: column;
align-items: flex-start;
}

.mod-panel {
margin-left: 0;
}

.mod-list {
left: auto;
right: 0;
}
}</style>
Система управления репутацией пользователей в виде отзывов скрипт ucoz
Размер: 6.0 Kb

Комментарии

Минимальная длина комментария - 50 знаков. комментарии модерируются
HTMLSTART » Скрипты UCOZ » Персональной страница » Система управления репутацией пользователей в виде отзывов скрипт ucoz