Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ Desenvolva um Plugin em WordPress que implemente a funcionalidade de favoritar p
* PHP >= 5.6
* Orientado a objetos

## Dúvidas

Em caso de dúvidas, crie uma issue.
## Como Usar o Plugin

### 1. Instalação
- Faça upload do plugin para `/wp-content/plugins/`
- Ative o plugin no painel administrativo

### 2. Adicionar Botão de Favorito
No arquivo `single.php` do seu tema, adicione:
```php
<?php do_action('wp_favorites_button', get_the_ID()); ?>
```

### 3. Exibir Lista de Favoritos
Use o shortcode em qualquer página:
```
[wp_favorites_list]
```

### 4. API REST
- **Favoritar**: `POST /wp-json/wp-favorites/v1/favorite`
- **Desfavoritar**: `POST /wp-json/wp-favorites/v1/unfavorite`
- **Listar**: `GET /wp-json/wp-favorites/v1/favorites`
117 changes: 117 additions & 0 deletions wp-favorites-plugin/assets/css/wp-favorites.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* WP Favorites Plugin Styles
*/

.wp-favorites-button {
display: inline-flex;
align-items: center;
gap: 5px;
background: #0073aa;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
text-decoration: none;
line-height: 1.4;
}

.wp-favorites-button:hover {
background: #005a87;
color: white;
text-decoration: none;
}

.wp-favorites-button.favorited {
background: #d63638;
}

.wp-favorites-button.favorited:hover {
background: #b32d2e;
}

.wp-favorites-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}

.wp-favorites-icon {
font-size: 16px;
line-height: 1;
}

.wp-favorites-count {
background: rgba(255, 255, 255, 0.2);
padding: 2px 6px;
border-radius: 10px;
font-size: 12px;
margin-left: 5px;
}

.wp-favorites-message {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
border-radius: 4px;
color: white;
z-index: 9999;
font-size: 14px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
animation: wp-favorites-slide-in 0.3s ease;
}

.wp-favorites-message.success {
background: #46b450;
}

.wp-favorites-message.error {
background: #dc3232;
}

@keyframes wp-favorites-slide-in {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}

/* Loading state */
.wp-favorites-button.loading {
opacity: 0.7;
pointer-events: none;
}

.wp-favorites-button.loading .wp-favorites-icon {
animation: wp-favorites-spin 1s linear infinite;
}

@keyframes wp-favorites-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

/* Responsive */
@media (max-width: 768px) {
.wp-favorites-button {
padding: 6px 12px;
font-size: 13px;
}

.wp-favorites-message {
top: 10px;
right: 10px;
left: 10px;
font-size: 13px;
}
}
194 changes: 194 additions & 0 deletions wp-favorites-plugin/assets/js/wp-favorites.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/**
* WP Favorites Plugin JavaScript
*/

(function($) {
'use strict';

// WP Favorites Plugin namespace
window.WPFavorites = window.WPFavorites || {};

// Configuration
WPFavorites.config = {
restUrl: wpFavorites.restUrl || '/wp-json/wp-favorites/v1',
nonce: wpFavorites.nonce || '',
messages: {
favorited: 'Post added to favorites!',
unfavorited: 'Post removed from favorites!',
error: 'An error occurred. Please try again.',
loginRequired: 'Please log in to favorite posts.'
}
};

// Main class
WPFavorites.FavoritesButton = function(element, options) {
this.element = $(element);
this.options = $.extend({}, WPFavorites.config, options);
this.postId = this.element.data('post-id');
this.isFavorited = false;
this.isLoading = false;

this.init();
};

WPFavorites.FavoritesButton.prototype = {
init: function() {
this.bindEvents();
this.checkFavoriteStatus();
},

bindEvents: function() {
var self = this;
this.element.on('click', function(e) {
e.preventDefault();
self.toggleFavorite();
});
},

checkFavoriteStatus: function() {
var self = this;
$.ajax({
url: self.options.restUrl + '/favorites',
method: 'GET',
beforeSend: function(xhr) {
xhr.setRequestHeader('X-WP-Nonce', self.options.nonce);
},
success: function(response) {
if (response.success && response.favorites.includes(parseInt(self.postId))) {
self.setFavorited(true);
}
},
error: function() {
// Silent fail for status check
}
});
},

toggleFavorite: function() {
if (this.isLoading) return;

if (this.isFavorited) {
this.unfavorite();
} else {
this.favorite();
}
},

favorite: function() {
var self = this;
this.setLoading(true);

$.ajax({
url: self.options.restUrl + '/favorite',
method: 'POST',
data: JSON.stringify({
post_id: self.postId
}),
contentType: 'application/json',
beforeSend: function(xhr) {
xhr.setRequestHeader('X-WP-Nonce', self.options.nonce);
},
success: function(response) {
if (response.success) {
self.setFavorited(true);
self.showMessage(self.options.messages.favorited, 'success');
} else {
self.showMessage(response.message || self.options.messages.error, 'error');
}
},
error: function(xhr) {
var message = self.options.messages.error;
if (xhr.status === 401) {
message = self.options.messages.loginRequired;
}
self.showMessage(message, 'error');
},
complete: function() {
self.setLoading(false);
}
});
},

unfavorite: function() {
var self = this;
this.setLoading(true);

$.ajax({
url: self.options.restUrl + '/unfavorite',
method: 'POST',
data: JSON.stringify({
post_id: self.postId
}),
contentType: 'application/json',
beforeSend: function(xhr) {
xhr.setRequestHeader('X-WP-Nonce', self.options.nonce);
},
success: function(response) {
if (response.success) {
self.setFavorited(false);
self.showMessage(self.options.messages.unfavorited, 'success');
} else {
self.showMessage(response.message || self.options.messages.error, 'error');
}
},
error: function() {
self.showMessage(self.options.messages.error, 'error');
},
complete: function() {
self.setLoading(false);
}
});
},

setFavorited: function(favorited) {
this.isFavorited = favorited;
this.element.toggleClass('favorited', favorited);

var icon = this.element.find('.wp-favorites-icon');
var text = this.element.find('.wp-favorites-text');

if (favorited) {
icon.text('♥');
text.text('Favorited');
} else {
icon.text('♡');
text.text('Favorite');
}
},

setLoading: function(loading) {
this.isLoading = loading;
this.element.toggleClass('loading', loading);
this.element.prop('disabled', loading);
},

showMessage: function(message, type) {
var messageElement = $('<div>')
.addClass('wp-favorites-message ' + type)
.text(message);

$('body').append(messageElement);

setTimeout(function() {
messageElement.fadeOut(300, function() {
$(this).remove();
});
}, 3000);
}
};

// jQuery plugin
$.fn.wpFavorites = function(options) {
return this.each(function() {
if (!$(this).data('wp-favorites')) {
$(this).data('wp-favorites', new WPFavorites.FavoritesButton(this, options));
}
});
};

// Auto-initialize on document ready
$(document).ready(function() {
$('.wp-favorites-button').wpFavorites();
});

})(jQuery);
Loading