Files
play-life/play-life-web/src/components/TodoistIntegration.jsx
Play Life Bot 713f6020f6
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 6s
fix: use authFetch for Todoist OAuth connect to send auth header
2026-01-02 15:40:06 +03:00

195 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react'
import { useAuth } from './auth/AuthContext'
import './Integrations.css'
function TodoistIntegration({ onBack }) {
const { authFetch } = useAuth()
const [connected, setConnected] = useState(false)
const [todoistEmail, setTodoistEmail] = useState('')
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const [message, setMessage] = useState('')
useEffect(() => {
checkStatus()
// Проверяем URL параметры для сообщений
const params = new URLSearchParams(window.location.search)
const integration = params.get('integration')
const status = params.get('status')
if (integration === 'todoist') {
if (status === 'connected') {
setMessage('✅ Todoist успешно подключен!')
// Очищаем URL параметры
window.history.replaceState({}, '', window.location.pathname)
} else if (status === 'error') {
const errorMsg = params.get('message') || 'Произошла ошибка'
setError(errorMsg)
window.history.replaceState({}, '', window.location.pathname)
}
}
}, [])
const checkStatus = async () => {
try {
setLoading(true)
setError('')
const response = await authFetch('/api/integrations/todoist/status')
if (!response.ok) {
throw new Error('Ошибка при проверке статуса')
}
const data = await response.json()
setConnected(data.connected || false)
if (data.connected && data.todoist_email) {
setTodoistEmail(data.todoist_email)
}
} catch (error) {
console.error('Error checking status:', error)
setError(error.message || 'Не удалось проверить статус')
} finally {
setLoading(false)
}
}
const handleConnect = async () => {
try {
setLoading(true)
setError('')
// Получаем URL для редиректа через авторизованный запрос
const response = await authFetch('/api/integrations/todoist/oauth/connect')
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(errorData.error || 'Ошибка при подключении Todoist')
}
const data = await response.json()
if (data.auth_url) {
// Делаем редирект на Todoist OAuth
window.location.href = data.auth_url
} else {
throw new Error('URL для авторизации не получен')
}
} catch (error) {
console.error('Error connecting Todoist:', error)
setError(error.message || 'Не удалось подключить Todoist')
setLoading(false)
}
}
const handleDisconnect = async () => {
if (!window.confirm('Вы уверены, что хотите отключить Todoist?')) {
return
}
try {
setLoading(true)
setError('')
const response = await authFetch('/api/integrations/todoist/disconnect', {
method: 'DELETE',
})
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(errorData.error || 'Ошибка при отключении')
}
setConnected(false)
setTodoistEmail('')
setMessage('Todoist отключен')
} catch (error) {
console.error('Error disconnecting:', error)
setError(error.message || 'Не удалось отключить Todoist')
} finally {
setLoading(false)
}
}
return (
<div className="p-4 md:p-6">
<button className="close-x-button" onClick={onBack} title="Закрыть">
</button>
<h1 className="text-2xl font-bold mb-6">Todoist интеграция</h1>
{message && (
<div className="bg-green-50 border border-green-200 rounded-lg p-4 mb-6 text-green-800">
{message}
</div>
)}
{error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6 text-red-800">
{error}
</div>
)}
{loading ? (
<div className="text-gray-500">Загрузка...</div>
) : connected ? (
<div>
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
<h2 className="text-lg font-semibold mb-4">Статус подключения</h2>
<div className="space-y-3">
<div className="flex items-center gap-2">
<span className="text-green-600 font-semibold"> Todoist подключен</span>
</div>
{todoistEmail && (
<div>
<span className="text-gray-600">Email: </span>
<span className="font-medium">{todoistEmail}</span>
</div>
)}
</div>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6 mb-6">
<h3 className="text-lg font-semibold mb-3 text-blue-900">
Как это работает
</h3>
<p className="text-gray-700 mb-2">
Todoist подключен! Закрывайте задачи в Todoist они автоматически
появятся в Play Life.
</p>
<p className="text-gray-600 text-sm">
Никаких дополнительных настроек не требуется. Просто закрывайте задачи
в Todoist, и они будут обработаны автоматически.
</p>
</div>
<button
onClick={handleDisconnect}
className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
>
Отключить Todoist
</button>
</div>
) : (
<div>
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
<h2 className="text-lg font-semibold mb-4">Подключение Todoist</h2>
<p className="text-gray-700 mb-4">
Подключите свой Todoist аккаунт для автоматической обработки закрытых задач.
</p>
<button
onClick={handleConnect}
className="px-6 py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors font-semibold"
>
Подключить Todoist
</button>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6">
<h3 className="text-lg font-semibold mb-3 text-blue-900">
Что нужно сделать
</h3>
<ol className="list-decimal list-inside space-y-2 text-gray-700">
<li>Нажмите кнопку "Подключить Todoist"</li>
<li>Авторизуйтесь в Todoist</li>
<li>Готово! Закрытые задачи будут автоматически обрабатываться</li>
</ol>
</div>
</div>
)}
</div>
)
}
export default TodoistIntegration