5.9.0: Статус Отклонено для желаний
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m20s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m20s
This commit is contained in:
@@ -478,6 +478,7 @@ type WishlistItem struct {
|
|||||||
Link *string `json:"link,omitempty"`
|
Link *string `json:"link,omitempty"`
|
||||||
Unlocked bool `json:"unlocked"`
|
Unlocked bool `json:"unlocked"`
|
||||||
Completed bool `json:"completed"`
|
Completed bool `json:"completed"`
|
||||||
|
Rejected bool `json:"rejected"`
|
||||||
FirstLockedCondition *UnlockConditionDisplay `json:"first_locked_condition,omitempty"`
|
FirstLockedCondition *UnlockConditionDisplay `json:"first_locked_condition,omitempty"`
|
||||||
MoreLockedConditions int `json:"more_locked_conditions,omitempty"`
|
MoreLockedConditions int `json:"more_locked_conditions,omitempty"`
|
||||||
LockedConditionsCount int `json:"locked_conditions_count,omitempty"` // Общее количество заблокированных условий
|
LockedConditionsCount int `json:"locked_conditions_count,omitempty"` // Общее количество заблокированных условий
|
||||||
@@ -4532,6 +4533,7 @@ func main() {
|
|||||||
protected.HandleFunc("/api/wishlist/{id}/image", app.deleteWishlistImageHandler).Methods("DELETE", "OPTIONS")
|
protected.HandleFunc("/api/wishlist/{id}/image", app.deleteWishlistImageHandler).Methods("DELETE", "OPTIONS")
|
||||||
protected.HandleFunc("/api/wishlist/{id}/complete", app.completeWishlistHandler).Methods("POST", "OPTIONS")
|
protected.HandleFunc("/api/wishlist/{id}/complete", app.completeWishlistHandler).Methods("POST", "OPTIONS")
|
||||||
protected.HandleFunc("/api/wishlist/{id}/uncomplete", app.uncompleteWishlistHandler).Methods("POST", "OPTIONS")
|
protected.HandleFunc("/api/wishlist/{id}/uncomplete", app.uncompleteWishlistHandler).Methods("POST", "OPTIONS")
|
||||||
|
protected.HandleFunc("/api/wishlist/{id}/reject", app.rejectWishlistHandler).Methods("POST", "OPTIONS")
|
||||||
protected.HandleFunc("/api/wishlist/{id}/copy", app.copyWishlistHandler).Methods("POST", "OPTIONS")
|
protected.HandleFunc("/api/wishlist/{id}/copy", app.copyWishlistHandler).Methods("POST", "OPTIONS")
|
||||||
|
|
||||||
// Group suggestions
|
// Group suggestions
|
||||||
@@ -11996,6 +11998,7 @@ func (a *App) getWishlistItemsWithConditions(userID int, includeCompleted bool)
|
|||||||
wi.image_path,
|
wi.image_path,
|
||||||
wi.link,
|
wi.link,
|
||||||
wi.completed,
|
wi.completed,
|
||||||
|
wi.rejected,
|
||||||
wi.group_name,
|
wi.group_name,
|
||||||
wc.id AS condition_id,
|
wc.id AS condition_id,
|
||||||
wc.display_order,
|
wc.display_order,
|
||||||
@@ -12035,6 +12038,7 @@ func (a *App) getWishlistItemsWithConditions(userID int, includeCompleted bool)
|
|||||||
var price sql.NullFloat64
|
var price sql.NullFloat64
|
||||||
var imagePath, link sql.NullString
|
var imagePath, link sql.NullString
|
||||||
var completed bool
|
var completed bool
|
||||||
|
var rejected bool
|
||||||
var groupName sql.NullString
|
var groupName sql.NullString
|
||||||
|
|
||||||
var conditionID, displayOrder sql.NullInt64
|
var conditionID, displayOrder sql.NullInt64
|
||||||
@@ -12048,7 +12052,7 @@ func (a *App) getWishlistItemsWithConditions(userID int, includeCompleted bool)
|
|||||||
var startDate sql.NullTime
|
var startDate sql.NullTime
|
||||||
|
|
||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
&itemID, &name, &price, &imagePath, &link, &completed,
|
&itemID, &name, &price, &imagePath, &link, &completed, &rejected,
|
||||||
&groupName,
|
&groupName,
|
||||||
&conditionID, &displayOrder,
|
&conditionID, &displayOrder,
|
||||||
&taskConditionID, &scoreConditionID, &conditionUserID,
|
&taskConditionID, &scoreConditionID, &conditionUserID,
|
||||||
@@ -12066,6 +12070,7 @@ func (a *App) getWishlistItemsWithConditions(userID int, includeCompleted bool)
|
|||||||
ID: itemID,
|
ID: itemID,
|
||||||
Name: name,
|
Name: name,
|
||||||
Completed: completed,
|
Completed: completed,
|
||||||
|
Rejected: rejected,
|
||||||
UnlockConditions: []UnlockConditionDisplay{},
|
UnlockConditions: []UnlockConditionDisplay{},
|
||||||
}
|
}
|
||||||
if price.Valid {
|
if price.Valid {
|
||||||
@@ -12973,6 +12978,7 @@ func (a *App) getWishlistItemHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
wi.image_path,
|
wi.image_path,
|
||||||
wi.link,
|
wi.link,
|
||||||
wi.completed,
|
wi.completed,
|
||||||
|
wi.rejected,
|
||||||
wi.group_name,
|
wi.group_name,
|
||||||
wc.id AS condition_id,
|
wc.id AS condition_id,
|
||||||
wc.display_order,
|
wc.display_order,
|
||||||
@@ -13013,6 +13019,7 @@ func (a *App) getWishlistItemHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
var imagePath sql.NullString
|
var imagePath sql.NullString
|
||||||
var link sql.NullString
|
var link sql.NullString
|
||||||
var completed bool
|
var completed bool
|
||||||
|
var rejected bool
|
||||||
var groupName sql.NullString
|
var groupName sql.NullString
|
||||||
var conditionID sql.NullInt64
|
var conditionID sql.NullInt64
|
||||||
var displayOrder sql.NullInt64
|
var displayOrder sql.NullInt64
|
||||||
@@ -13028,7 +13035,7 @@ func (a *App) getWishlistItemHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
var startDate sql.NullTime
|
var startDate sql.NullTime
|
||||||
|
|
||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
&itemID, &name, &price, &imagePath, &link, &completed, &groupName,
|
&itemID, &name, &price, &imagePath, &link, &completed, &rejected, &groupName,
|
||||||
&conditionID, &displayOrder, &taskConditionID, &scoreConditionID, &conditionUserID,
|
&conditionID, &displayOrder, &taskConditionID, &scoreConditionID, &conditionUserID,
|
||||||
&taskID, &taskName, &taskNextShowAt, &projectID, &projectName, &requiredPoints, &startDate,
|
&taskID, &taskName, &taskNextShowAt, &projectID, &projectName, &requiredPoints, &startDate,
|
||||||
)
|
)
|
||||||
@@ -13043,6 +13050,7 @@ func (a *App) getWishlistItemHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
ID: itemID,
|
ID: itemID,
|
||||||
Name: name,
|
Name: name,
|
||||||
Completed: completed,
|
Completed: completed,
|
||||||
|
Rejected: rejected,
|
||||||
UnlockConditions: []UnlockConditionDisplay{},
|
UnlockConditions: []UnlockConditionDisplay{},
|
||||||
}
|
}
|
||||||
if price.Valid {
|
if price.Valid {
|
||||||
@@ -13369,6 +13377,7 @@ func (a *App) updateWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
wi.image_path,
|
wi.image_path,
|
||||||
wi.link,
|
wi.link,
|
||||||
wi.completed,
|
wi.completed,
|
||||||
|
wi.rejected,
|
||||||
wi.group_name,
|
wi.group_name,
|
||||||
wc.id AS condition_id,
|
wc.id AS condition_id,
|
||||||
wc.display_order,
|
wc.display_order,
|
||||||
@@ -13409,6 +13418,7 @@ func (a *App) updateWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
var imagePath sql.NullString
|
var imagePath sql.NullString
|
||||||
var link sql.NullString
|
var link sql.NullString
|
||||||
var completed bool
|
var completed bool
|
||||||
|
var rejected bool
|
||||||
var groupName sql.NullString
|
var groupName sql.NullString
|
||||||
var conditionID sql.NullInt64
|
var conditionID sql.NullInt64
|
||||||
var displayOrder sql.NullInt64
|
var displayOrder sql.NullInt64
|
||||||
@@ -13423,7 +13433,7 @@ func (a *App) updateWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
var startDate sql.NullTime
|
var startDate sql.NullTime
|
||||||
|
|
||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
&itemID, &name, &price, &imagePath, &link, &completed, &groupName,
|
&itemID, &name, &price, &imagePath, &link, &completed, &rejected, &groupName,
|
||||||
&conditionID, &displayOrder, &taskConditionID, &scoreConditionID, &conditionUserID,
|
&conditionID, &displayOrder, &taskConditionID, &scoreConditionID, &conditionUserID,
|
||||||
&taskID, &taskName, &projectID, &projectName, &requiredPoints, &startDate,
|
&taskID, &taskName, &projectID, &projectName, &requiredPoints, &startDate,
|
||||||
)
|
)
|
||||||
@@ -13445,6 +13455,7 @@ func (a *App) updateWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
ID: itemID,
|
ID: itemID,
|
||||||
Name: name,
|
Name: name,
|
||||||
Completed: completed,
|
Completed: completed,
|
||||||
|
Rejected: rejected,
|
||||||
UnlockConditions: []UnlockConditionDisplay{},
|
UnlockConditions: []UnlockConditionDisplay{},
|
||||||
}
|
}
|
||||||
if price.Valid {
|
if price.Valid {
|
||||||
@@ -14036,7 +14047,7 @@ func (a *App) uncompleteWishlistHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
_, err = a.DB.Exec(`
|
_, err = a.DB.Exec(`
|
||||||
UPDATE wishlist_items
|
UPDATE wishlist_items
|
||||||
SET completed = FALSE, updated_at = NOW()
|
SET completed = FALSE, rejected = FALSE, updated_at = NOW()
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`, itemID)
|
`, itemID)
|
||||||
|
|
||||||
@@ -14053,6 +14064,76 @@ func (a *App) uncompleteWishlistHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rejectWishlistHandler отклоняет желание (completed=true, rejected=true)
|
||||||
|
func (a *App) rejectWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == "OPTIONS" {
|
||||||
|
setCORSHeaders(w)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setCORSHeaders(w)
|
||||||
|
|
||||||
|
userID, ok := getUserIDFromContext(r)
|
||||||
|
if !ok {
|
||||||
|
sendErrorWithCORS(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
itemID, err := strconv.Atoi(vars["id"])
|
||||||
|
if err != nil {
|
||||||
|
sendErrorWithCORS(w, "Invalid wishlist ID", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем доступ к желанию
|
||||||
|
hasAccess, _, _, err := a.checkWishlistAccess(itemID, userID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
sendErrorWithCORS(w, "Wishlist item not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error checking wishlist access: %v", err)
|
||||||
|
sendErrorWithCORS(w, fmt.Sprintf("Error checking wishlist access: %v", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !hasAccess {
|
||||||
|
sendErrorWithCORS(w, "Access denied", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = a.DB.Exec(`
|
||||||
|
UPDATE wishlist_items
|
||||||
|
SET completed = TRUE, rejected = TRUE, updated_at = NOW()
|
||||||
|
WHERE id = $1
|
||||||
|
`, itemID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error rejecting wishlist item: %v", err)
|
||||||
|
sendErrorWithCORS(w, fmt.Sprintf("Error rejecting wishlist item: %v", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// При отклонении желания удаляем все связанные с ним задачи
|
||||||
|
result, err := a.DB.Exec(`
|
||||||
|
UPDATE tasks
|
||||||
|
SET deleted = TRUE
|
||||||
|
WHERE wishlist_id = $1 AND deleted = FALSE
|
||||||
|
`, itemID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error deleting tasks for rejected wishlist item %d: %v", itemID, err)
|
||||||
|
} else {
|
||||||
|
rowsAffected, _ := result.RowsAffected()
|
||||||
|
log.Printf("Rejected wishlist item %d: deleted %d tasks", itemID, rowsAffected)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"success": true,
|
||||||
|
"message": "Wishlist item rejected successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// copyWishlistHandler копирует желание
|
// copyWishlistHandler копирует желание
|
||||||
func (a *App) copyWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
func (a *App) copyWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
@@ -15224,6 +15305,7 @@ func (a *App) getBoardCompletedHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
wi.image_path,
|
wi.image_path,
|
||||||
wi.link,
|
wi.link,
|
||||||
wi.completed,
|
wi.completed,
|
||||||
|
wi.rejected,
|
||||||
wi.group_name AS item_group_name,
|
wi.group_name AS item_group_name,
|
||||||
wc.id AS condition_id,
|
wc.id AS condition_id,
|
||||||
wc.display_order,
|
wc.display_order,
|
||||||
@@ -15267,6 +15349,7 @@ func (a *App) getBoardCompletedHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
var imagePath sql.NullString
|
var imagePath sql.NullString
|
||||||
var link sql.NullString
|
var link sql.NullString
|
||||||
var completed bool
|
var completed bool
|
||||||
|
var rejected bool
|
||||||
var itemGroupName sql.NullString
|
var itemGroupName sql.NullString
|
||||||
var conditionID sql.NullInt64
|
var conditionID sql.NullInt64
|
||||||
var displayOrder sql.NullInt64
|
var displayOrder sql.NullInt64
|
||||||
@@ -15282,7 +15365,7 @@ func (a *App) getBoardCompletedHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
var userName sql.NullString
|
var userName sql.NullString
|
||||||
|
|
||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
&itemID, &name, &price, &imagePath, &link, &completed, &itemGroupName,
|
&itemID, &name, &price, &imagePath, &link, &completed, &rejected, &itemGroupName,
|
||||||
&conditionID, &displayOrder, &taskConditionID, &scoreConditionID, &userIDCond,
|
&conditionID, &displayOrder, &taskConditionID, &scoreConditionID, &userIDCond,
|
||||||
&taskID, &taskName, &projectID, &projectName, &requiredPoints, &startDate, &userName,
|
&taskID, &taskName, &projectID, &projectName, &requiredPoints, &startDate, &userName,
|
||||||
)
|
)
|
||||||
@@ -15297,6 +15380,7 @@ func (a *App) getBoardCompletedHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
ID: itemID,
|
ID: itemID,
|
||||||
Name: name,
|
Name: name,
|
||||||
Completed: completed,
|
Completed: completed,
|
||||||
|
Rejected: rejected,
|
||||||
UnlockConditions: []UnlockConditionDisplay{},
|
UnlockConditions: []UnlockConditionDisplay{},
|
||||||
}
|
}
|
||||||
if price.Valid {
|
if price.Valid {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- Remove rejected column from wishlist_items
|
||||||
|
DROP INDEX IF EXISTS idx_wishlist_items_rejected;
|
||||||
|
ALTER TABLE wishlist_items DROP COLUMN IF EXISTS rejected;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- Add rejected column to wishlist_items
|
||||||
|
ALTER TABLE wishlist_items ADD COLUMN rejected BOOLEAN DEFAULT FALSE;
|
||||||
|
|
||||||
|
-- Create index for filtering by rejected status
|
||||||
|
CREATE INDEX idx_wishlist_items_rejected ON wishlist_items(rejected) WHERE rejected = TRUE;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "play-life-web",
|
"name": "play-life-web",
|
||||||
"version": "5.8.0",
|
"version": "5.9.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -188,6 +188,31 @@
|
|||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-status-indicator {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.25rem;
|
||||||
|
left: 0.25rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
z-index: 10;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-status-completed {
|
||||||
|
background: #27ae60;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-status-rejected {
|
||||||
|
background: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.wishlist .card-menu-button {
|
.wishlist .card-menu-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.25rem;
|
top: 0.25rem;
|
||||||
|
|||||||
@@ -616,6 +616,20 @@ function Wishlist({ onNavigate, refreshTrigger = 0, isActive = false, initialBoa
|
|||||||
className={`wishlist-card ${isFaded ? 'faded' : ''}`}
|
className={`wishlist-card ${isFaded ? 'faded' : ''}`}
|
||||||
onClick={() => handleItemClick(item)}
|
onClick={() => handleItemClick(item)}
|
||||||
>
|
>
|
||||||
|
{item.completed && (
|
||||||
|
<div className={`card-status-indicator ${item.rejected ? 'card-status-rejected' : 'card-status-completed'}`}>
|
||||||
|
{item.rejected ? (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<polyline points="20 6 9 17 4 12"></polyline>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
className="card-menu-button"
|
className="card-menu-button"
|
||||||
onClick={(e) => handleMenuClick(item, e)}
|
onClick={(e) => handleMenuClick(item, e)}
|
||||||
|
|||||||
@@ -246,6 +246,7 @@
|
|||||||
.wishlist-detail-edit-button,
|
.wishlist-detail-edit-button,
|
||||||
.wishlist-detail-complete-button,
|
.wishlist-detail-complete-button,
|
||||||
.wishlist-detail-uncomplete-button,
|
.wishlist-detail-uncomplete-button,
|
||||||
|
.wishlist-detail-reject-button,
|
||||||
.wishlist-detail-delete-button {
|
.wishlist-detail-delete-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.75rem 1.5rem;
|
padding: 0.75rem 1.5rem;
|
||||||
@@ -267,6 +268,12 @@
|
|||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wishlist-detail-action-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.wishlist-detail-complete-button {
|
.wishlist-detail-complete-button {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: #27ae60;
|
background-color: #27ae60;
|
||||||
@@ -283,6 +290,22 @@
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wishlist-detail-reject-button {
|
||||||
|
flex: 1;
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wishlist-detail-reject-button:hover:not(:disabled) {
|
||||||
|
background-color: #c0392b;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wishlist-detail-reject-button:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.wishlist-detail-create-task-button {
|
.wishlist-detail-create-task-button {
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId, onClose, p
|
|||||||
if (onNavigate) {
|
if (onNavigate) {
|
||||||
onNavigate('wishlist')
|
onNavigate('wishlist')
|
||||||
}
|
}
|
||||||
|
onClose?.()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error completing wishlist:', err)
|
console.error('Error completing wishlist:', err)
|
||||||
setToastMessage({ text: err.message || 'Ошибка при завершении', type: 'error' })
|
setToastMessage({ text: err.message || 'Ошибка при завершении', type: 'error' })
|
||||||
@@ -86,6 +87,34 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId, onClose, p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleReject = async () => {
|
||||||
|
if (!wishlistItem || !wishlistItem.unlocked) return
|
||||||
|
|
||||||
|
setIsCompleting(true)
|
||||||
|
try {
|
||||||
|
const response = await authFetch(`${API_URL}/${wishlistId}/reject`, {
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Ошибка при отклонении')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onRefresh) {
|
||||||
|
onRefresh()
|
||||||
|
}
|
||||||
|
if (onNavigate) {
|
||||||
|
onNavigate('wishlist')
|
||||||
|
}
|
||||||
|
onClose?.()
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error rejecting wishlist:', err)
|
||||||
|
setToastMessage({ text: err.message || 'Ошибка при отклонении', type: 'error' })
|
||||||
|
} finally {
|
||||||
|
setIsCompleting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleUncomplete = async () => {
|
const handleUncomplete = async () => {
|
||||||
if (!wishlistItem || !wishlistItem.completed) return
|
if (!wishlistItem || !wishlistItem.completed) return
|
||||||
|
|
||||||
@@ -103,6 +132,7 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId, onClose, p
|
|||||||
onRefresh()
|
onRefresh()
|
||||||
}
|
}
|
||||||
fetchWishlistDetail()
|
fetchWishlistDetail()
|
||||||
|
onClose?.()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error uncompleting wishlist:', err)
|
console.error('Error uncompleting wishlist:', err)
|
||||||
setToastMessage({ text: err.message || 'Ошибка при возобновлении желания', type: 'error' })
|
setToastMessage({ text: err.message || 'Ошибка при возобновлении желания', type: 'error' })
|
||||||
@@ -687,13 +717,22 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId, onClose, p
|
|||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
<div className="wishlist-detail-action-buttons">
|
||||||
<button
|
<button
|
||||||
onClick={handleComplete}
|
onClick={handleComplete}
|
||||||
disabled={isCompleting}
|
disabled={isCompleting}
|
||||||
className="wishlist-detail-complete-button"
|
className="wishlist-detail-complete-button"
|
||||||
>
|
>
|
||||||
{isCompleting ? 'Завершение...' : 'Завершить'}
|
{isCompleting ? '...' : 'Завершить'}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleReject}
|
||||||
|
disabled={isCompleting}
|
||||||
|
className="wishlist-detail-reject-button"
|
||||||
|
>
|
||||||
|
{isCompleting ? '...' : 'Отклонить'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||||
<button
|
<button
|
||||||
onClick={handleCreateTask}
|
onClick={handleCreateTask}
|
||||||
|
|||||||
Reference in New Issue
Block a user