diff --git a/VERSION b/VERSION index 6a6a3d8..24ba9a3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.1 +2.7.0 diff --git a/play-life-backend/main.go b/play-life-backend/main.go index 79d9024..b533c96 100644 --- a/play-life-backend/main.go +++ b/play-life-backend/main.go @@ -3569,6 +3569,7 @@ func main() { protected.HandleFunc("/project/priority", app.setProjectPriorityHandler).Methods("POST", "OPTIONS") protected.HandleFunc("/project/move", app.moveProjectHandler).Methods("POST", "OPTIONS") protected.HandleFunc("/project/delete", app.deleteProjectHandler).Methods("POST", "OPTIONS") + protected.HandleFunc("/project/create", app.createProjectHandler).Methods("POST", "OPTIONS") protected.HandleFunc("/d2dc349a-0d13-49b2-a8f0-1ab094bfba9b", app.getFullStatisticsHandler).Methods("GET", "OPTIONS") // Integrations @@ -4974,6 +4975,10 @@ type ProjectDeleteRequest struct { ID int `json:"id"` } +type ProjectCreateRequest struct { + Name string `json:"name"` +} + func (a *App) moveProjectHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "OPTIONS" { setCORSHeaders(w) @@ -5207,6 +5212,71 @@ func (a *App) deleteProjectHandler(w http.ResponseWriter, r *http.Request) { }) } +func (a *App) createProjectHandler(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 + } + + var req ProjectCreateRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Printf("Error decoding create project request: %v", err) + sendErrorWithCORS(w, "Invalid request body", http.StatusBadRequest) + return + } + + if req.Name == "" { + sendErrorWithCORS(w, "name is required", http.StatusBadRequest) + return + } + + // Проверяем, существует ли уже проект с таким именем + var existingID int + err := a.DB.QueryRow(` + SELECT id FROM projects + WHERE name = $1 AND user_id = $2 AND deleted = FALSE + `, req.Name, userID).Scan(&existingID) + + if err == nil { + // Проект уже существует + sendErrorWithCORS(w, "Project with this name already exists", http.StatusConflict) + return + } else if err != sql.ErrNoRows { + log.Printf("Error checking project existence: %v", err) + sendErrorWithCORS(w, fmt.Sprintf("Error checking project existence: %v", err), http.StatusInternalServerError) + return + } + + // Создаем новый проект + var projectID int + err = a.DB.QueryRow(` + INSERT INTO projects (name, deleted, user_id) + VALUES ($1, FALSE, $2) + RETURNING id + `, req.Name, userID).Scan(&projectID) + + if err != nil { + log.Printf("Error creating project: %v", err) + sendErrorWithCORS(w, fmt.Sprintf("Error creating project: %v", err), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]interface{}{ + "message": "Project created successfully", + "project_id": projectID, + "project_name": req.Name, + }) +} + func (a *App) todoistWebhookHandler(w http.ResponseWriter, r *http.Request) { // Логирование входящего запроса log.Printf("=== Todoist Webhook Request ===") diff --git a/play-life-web/src/components/ProjectPriorityManager.jsx b/play-life-web/src/components/ProjectPriorityManager.jsx index ab15348..b3b9232 100644 --- a/play-life-web/src/components/ProjectPriorityManager.jsx +++ b/play-life-web/src/components/ProjectPriorityManager.jsx @@ -25,6 +25,104 @@ import { useAuth } from './auth/AuthContext' const PROJECTS_API_URL = '/projects' const PRIORITY_UPDATE_API_URL = '/project/priority' const PROJECT_MOVE_API_URL = '/project/move' +const PROJECT_CREATE_API_URL = '/project/create' + +// Компонент экрана добавления проекта +function AddProjectScreen({ onClose, onSuccess }) { + const { authFetch } = useAuth() + const [projectName, setProjectName] = useState('') + const [isSubmitting, setIsSubmitting] = useState(false) + const [error, setError] = useState(null) + + const handleSubmit = async () => { + if (!projectName.trim()) { + setError('Введите название проекта') + return + } + + setIsSubmitting(true) + setError(null) + + try { + const response = await authFetch(PROJECT_CREATE_API_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + name: projectName.trim(), + }), + }) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(errorText || 'Ошибка при создании проекта') + } + + onSuccess() + } catch (err) { + console.error('Ошибка создания проекта:', err) + setError(err.message || 'Ошибка при создании проекта') + } finally { + setIsSubmitting(false) + } + } + + return ( +