diff --git a/VERSION b/VERSION index 0914443..59ac166 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.25.0 +3.25.1 diff --git a/dump-db.sh b/dump-db.sh index 081b7f3..2833d42 100755 --- a/dump-db.sh +++ b/dump-db.sh @@ -2,14 +2,14 @@ # Скрипт для создания дампа базы данных # Использование: -# ./dump-db.sh [имя_дампа] # Дамп из .env -# ./dump-db.sh --env-file .env.prod [имя] # Дамп из указанного файла -# ./dump-db.sh production-backup # Именованный дамп из .env +# ./dump-db.sh [имя_дампа] # Дамп из .env.prod +# ./dump-db.sh --env-file .env [имя] # Дамп из указанного файла +# ./dump-db.sh production-backup # Именованный дамп из .env.prod set -e # Значения по умолчанию -DEFAULT_ENV_FILE=".env" +DEFAULT_ENV_FILE=".env.prod" ENV_FILE="$DEFAULT_ENV_FILE" DUMP_NAME="" diff --git a/play-life-backend/main.go b/play-life-backend/main.go index 0ac0960..8432884 100644 --- a/play-life-backend/main.go +++ b/play-life-backend/main.go @@ -9878,20 +9878,21 @@ func (a *App) saveWishlistConditions( // Определяем user_id для условия: // - Если условие имеет id и это условие существовало - проверяем, принадлежит ли оно текущему пользователю - // - Если условие принадлежит другому пользователю - пропускаем (не сохраняем) - // - Если условие имеет id, но не существовало (например, было только что добавлено) - игнорируем - // - Иначе - используем userID текущего пользователя + // - Если условие принадлежит другому пользователю - пропускаем (не сохраняем, так как чужие условия не редактируются) + // - Если условие имеет id, но не существовало (например, было только что добавлено) - это новое условие, используем userID текущего пользователя + // - Если условие без id - это новое условие, используем userID текущего пользователя conditionUserID := userID if condition.ID != nil { if originalUserID, exists := existingConditions[*condition.ID]; exists { - // Если условие принадлежит другому пользователю - пропускаем (не сохраняем) + // Если условие принадлежит другому пользователю - пропускаем (не сохраняем, так как чужие условия не редактируются) if originalUserID != userID { continue } + // Условие принадлежит текущему пользователю - обновляем его conditionUserID = originalUserID } else { - // Условие имеет id, но не существует в базе - пропускаем (не сохраняем) - continue + // Условие имеет id, но не существует в базе - это новое условие, используем userID текущего пользователя + conditionUserID = userID } } @@ -10051,18 +10052,35 @@ func (a *App) createWishlistHandler(w http.ResponseWriter, r *http.Request) { userID, ok := getUserIDFromContext(r) if !ok { + log.Printf("createWishlistHandler: Unauthorized") sendErrorWithCORS(w, "Unauthorized", http.StatusUnauthorized) return } + log.Printf("createWishlistHandler: userID=%d", userID) + var req WishlistRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - log.Printf("Error decoding wishlist request: %v", err) + log.Printf("createWishlistHandler: Error decoding wishlist request: %v", err) sendErrorWithCORS(w, "Invalid request body", http.StatusBadRequest) return } + log.Printf("createWishlistHandler: decoded request - name='%s', price=%v, link='%s', conditions=%d", + req.Name, req.Price, req.Link, len(req.UnlockConditions)) + + if req.UnlockConditions == nil { + log.Printf("createWishlistHandler: WARNING - UnlockConditions is nil, initializing empty slice") + req.UnlockConditions = []UnlockConditionRequest{} + } + + for i, cond := range req.UnlockConditions { + log.Printf("createWishlistHandler: condition %d - type='%s', task_id=%v, project_id=%v, required_points=%v, start_date='%v'", + i, cond.Type, cond.TaskID, cond.ProjectID, cond.RequiredPoints, cond.StartDate) + } + if strings.TrimSpace(req.Name) == "" { + log.Printf("createWishlistHandler: Name is required") sendErrorWithCORS(w, "Name is required", http.StatusBadRequest) return } @@ -10077,8 +10095,8 @@ func (a *App) createWishlistHandler(w http.ResponseWriter, r *http.Request) { var wishlistID int err = tx.QueryRow(` - INSERT INTO wishlist_items (user_id, name, price, link, completed, deleted) - VALUES ($1, $2, $3, $4, FALSE, FALSE) + INSERT INTO wishlist_items (user_id, author_id, name, price, link, completed, deleted) + VALUES ($1, $1, $2, $3, $4, FALSE, FALSE) RETURNING id `, userID, strings.TrimSpace(req.Name), req.Price, req.Link).Scan(&wishlistID) @@ -10090,19 +10108,25 @@ func (a *App) createWishlistHandler(w http.ResponseWriter, r *http.Request) { // Сохраняем условия if len(req.UnlockConditions) > 0 { - err = a.saveWishlistConditions(tx, wishlistID, userID, req.UnlockConditions) + log.Printf("createWishlistHandler: saving %d conditions", len(req.UnlockConditions)) + err = a.saveWishlistConditionsWithUserID(tx, wishlistID, userID, req.UnlockConditions) if err != nil { - log.Printf("Error saving wishlist conditions: %v", err) + log.Printf("createWishlistHandler: Error saving wishlist conditions: %v", err) sendErrorWithCORS(w, fmt.Sprintf("Error saving wishlist conditions: %v", err), http.StatusInternalServerError) return } + log.Printf("createWishlistHandler: conditions saved successfully") + } else { + log.Printf("createWishlistHandler: no conditions to save") } + log.Printf("createWishlistHandler: committing transaction") if err := tx.Commit(); err != nil { - log.Printf("Error committing transaction: %v", err) + log.Printf("createWishlistHandler: Error committing transaction: %v", err) sendErrorWithCORS(w, fmt.Sprintf("Error committing transaction: %v", err), http.StatusInternalServerError) return } + log.Printf("createWishlistHandler: transaction committed successfully") // Получаем созданное желание с условиями items, err := a.getWishlistItemsWithConditions(userID, false) @@ -10121,10 +10145,14 @@ func (a *App) createWishlistHandler(w http.ResponseWriter, r *http.Request) { } if createdItem == nil { + log.Printf("createWishlistHandler: Created item not found") sendErrorWithCORS(w, "Created item not found", http.StatusInternalServerError) return } + log.Printf("createWishlistHandler: Successfully created wishlist item id=%d, name='%s'", + createdItem.ID, createdItem.Name) + w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(createdItem) } @@ -12590,6 +12618,7 @@ func (a *App) createBoardItemHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) boardID, err := strconv.Atoi(vars["boardId"]) if err != nil { + log.Printf("createBoardItemHandler: Error parsing boardId from URL: %v, vars['boardId']='%s'", err, vars["boardId"]) sendErrorWithCORS(w, "Invalid board ID", http.StatusBadRequest) return } @@ -12617,11 +12646,21 @@ func (a *App) createBoardItemHandler(w http.ResponseWriter, r *http.Request) { var req WishlistRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Printf("createBoardItemHandler: Error decoding wishlist request: %v", err) sendErrorWithCORS(w, "Invalid request body", http.StatusBadRequest) return } + log.Printf("createBoardItemHandler: decoded request - name='%s', price=%v, link='%s', conditions=%d", + req.Name, req.Price, req.Link, len(req.UnlockConditions)) + + if req.UnlockConditions == nil { + log.Printf("createBoardItemHandler: WARNING - UnlockConditions is nil, initializing empty slice") + req.UnlockConditions = []UnlockConditionRequest{} + } + if strings.TrimSpace(req.Name) == "" { + log.Printf("createBoardItemHandler: Name is required") sendErrorWithCORS(w, "Name is required", http.StatusBadRequest) return } @@ -12642,19 +12681,25 @@ func (a *App) createBoardItemHandler(w http.ResponseWriter, r *http.Request) { `, ownerID, boardID, userID, strings.TrimSpace(req.Name), req.Price, req.Link).Scan(&itemID) if err != nil { - log.Printf("Error creating board item: %v", err) + log.Printf("createBoardItemHandler: Error creating board item: %v", err) sendErrorWithCORS(w, "Error creating item", http.StatusInternalServerError) return } + log.Printf("createBoardItemHandler: created wishlist item id=%d", itemID) + // Сохраняем условия с user_id текущего пользователя if len(req.UnlockConditions) > 0 { + log.Printf("createBoardItemHandler: saving %d conditions", len(req.UnlockConditions)) err = a.saveWishlistConditionsWithUserID(tx, itemID, userID, req.UnlockConditions) if err != nil { - log.Printf("Error saving conditions: %v", err) + log.Printf("createBoardItemHandler: Error saving wishlist conditions: %v", err) sendErrorWithCORS(w, "Error saving conditions", http.StatusInternalServerError) return } + log.Printf("createBoardItemHandler: conditions saved successfully") + } else { + log.Printf("createBoardItemHandler: no conditions to save") } if err := tx.Commit(); err != nil { @@ -12671,12 +12716,18 @@ func (a *App) createBoardItemHandler(w http.ResponseWriter, r *http.Request) { // saveWishlistConditionsWithUserID сохраняет условия с указанием user_id func (a *App) saveWishlistConditionsWithUserID(tx *sql.Tx, wishlistItemID int, userID int, conditions []UnlockConditionRequest) error { + log.Printf("saveWishlistConditionsWithUserID: wishlistItemID=%d, userID=%d, conditions=%d", + wishlistItemID, userID, len(conditions)) + for i, cond := range conditions { displayOrder := i if cond.DisplayOrder != nil { displayOrder = *cond.DisplayOrder } + log.Printf("saveWishlistConditionsWithUserID: processing condition %d - type='%s', taskID=%v, projectID=%v", + i, cond.Type, cond.TaskID, cond.ProjectID) + switch cond.Type { case "task_completion": if cond.TaskID == nil { @@ -12691,6 +12742,7 @@ func (a *App) saveWishlistConditionsWithUserID(tx *sql.Tx, wishlistItemID int, u RETURNING id `, *cond.TaskID).Scan(&taskConditionID) if err != nil { + log.Printf("saveWishlistConditionsWithUserID: error creating task condition: %v", err) return fmt.Errorf("error creating task condition: %w", err) } // Связываем с wishlist_item @@ -12699,6 +12751,7 @@ func (a *App) saveWishlistConditionsWithUserID(tx *sql.Tx, wishlistItemID int, u VALUES ($1, $2, $3, $4) `, wishlistItemID, userID, taskConditionID, displayOrder) if err != nil { + log.Printf("saveWishlistConditionsWithUserID: error linking task condition: %v", err) return fmt.Errorf("error linking task condition: %w", err) } @@ -12719,6 +12772,7 @@ func (a *App) saveWishlistConditionsWithUserID(tx *sql.Tx, wishlistItemID int, u RETURNING id `, *cond.ProjectID, *cond.RequiredPoints, startDateVal).Scan(&scoreConditionID) if err != nil { + log.Printf("saveWishlistConditionsWithUserID: error creating score condition: %v", err) return fmt.Errorf("error creating score condition: %w", err) } // Связываем с wishlist_item @@ -12727,6 +12781,7 @@ func (a *App) saveWishlistConditionsWithUserID(tx *sql.Tx, wishlistItemID int, u VALUES ($1, $2, $3, $4) `, wishlistItemID, userID, scoreConditionID, displayOrder) if err != nil { + log.Printf("saveWishlistConditionsWithUserID: error linking score condition: %v", err) return fmt.Errorf("error linking score condition: %w", err) } } diff --git a/play-life-web/package.json b/play-life-web/package.json index 8a78f38..ef9e2c8 100644 --- a/play-life-web/package.json +++ b/play-life-web/package.json @@ -1,6 +1,6 @@ { "name": "play-life-web", - "version": "3.25.0", + "version": "3.25.1", "type": "module", "scripts": { "dev": "vite", diff --git a/play-life-web/src/App.jsx b/play-life-web/src/App.jsx index 3b1f3f9..05f4526 100644 --- a/play-life-web/src/App.jsx +++ b/play-life-web/src/App.jsx @@ -653,7 +653,9 @@ function AppContent() { // Для task-form и wishlist-form явно удаляем параметры, только если нет никаких параметров // task-form может иметь taskId (редактирование), wishlistId (создание из желания), или returnTo (возврат после создания) const isTaskFormWithNoParams = tab === 'task-form' && params.taskId === undefined && params.wishlistId === undefined && params.returnTo === undefined - const isWishlistFormWithNoParams = tab === 'wishlist-form' && params.wishlistId === undefined && params.newTaskId === undefined && params.boardId === undefined + // Проверяем, что boardId не null и не undefined (null означает "нет доски", но это валидное значение) + const hasBoardId = params.boardId !== null && params.boardId !== undefined + const isWishlistFormWithNoParams = tab === 'wishlist-form' && params.wishlistId === undefined && params.newTaskId === undefined && !hasBoardId if (isTaskFormWithNoParams || isWishlistFormWithNoParams) { setTabParams({}) if (isNewTabMain) { @@ -1041,7 +1043,22 @@ function AppContent() { {/* Кнопка добавления желания (только для таба wishlist) */} {!isFullscreenTab && activeTab === 'wishlist' && (