File size: 9,005 Bytes
054d73a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ee5085
054d73a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ee5085
054d73a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ee5085
054d73a
 
 
5ee5085
 
054d73a
 
 
 
 
 
 
 
 
 
 
 
 
 
5ee5085
054d73a
 
 
 
 
 
5ee5085
054d73a
 
 
 
 
 
 
5ee5085
054d73a
 
 
 
 
 
5ee5085
054d73a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ee5085
054d73a
 
 
 
 
 
 
 
 
5ee5085
054d73a
 
 
 
 
5ee5085
 
 
 
054d73a
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import { useState } from "react";
import { GOOGLE_AUTH_URL, AUTH_STATUS_URL } from "../lib/config";

type AuthMode = 'google' | 'csv-only';

interface AuthStatusProps {
  teacherEmail: string;
  setTeacherEmail: (email: string) => void;
  isAuthenticated: boolean;
  setIsAuthenticated: (auth: boolean) => void;
}

export function AuthStatus({
  teacherEmail,
  setTeacherEmail,
  isAuthenticated,
  setIsAuthenticated,
}: AuthStatusProps) {
  const [emailInput, setEmailInput] = useState("");
  const [isChecking, setIsChecking] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [authMode, setAuthMode] = useState<AuthMode | null>(null);

  const checkAuthStatus = async (email: string) => {
    try {
      setIsChecking(true);
      const response = await fetch(`${AUTH_STATUS_URL}?teacher_email=${encodeURIComponent(email)}`);
      const data = await response.json();
      
      if (data.authenticated) {
        setTeacherEmail(email);
        setIsAuthenticated(true);
      }
      return data.authenticated;
    } catch (err) {
      console.error("Error checking auth status:", err);
      return false;
    } finally {
      setIsChecking(false);
    }
  };

  const handleConnect = async (e: React.FormEvent) => {
    e.preventDefault();
    setError(null);
    
    if (!emailInput.trim()) {
      setError("請輸入您的電子郵件地址");
      return;
    }
    
    // Check if already authenticated
    const alreadyAuth = await checkAuthStatus(emailInput);
    if (alreadyAuth) {
      return;
    }
    
    // Try to start OAuth - catch errors if not configured
    try {
      const response = await fetch(`${GOOGLE_AUTH_URL}?teacher_email=${encodeURIComponent(emailInput)}`, {
        method: 'GET',
        redirect: 'manual'
      });
      
      // If we get a redirect, OAuth is configured - follow it
      if (response.type === 'opaqueredirect' || response.status === 302) {
        window.location.href = `${GOOGLE_AUTH_URL}?teacher_email=${encodeURIComponent(emailInput)}`;
      } else if (response.status === 500) {
        const data = await response.json();
        if (data.detail?.includes('not configured')) {
          setAuthMode('csv-only');
          setError("Google OAuth 未設定。您仍可在聊天中直接上傳 CSV 檔案使用應用程式!");
          // Set as "connected" with just email for CSV fallback
          setTeacherEmail(emailInput);
          setIsAuthenticated(true);
        } else {
          setAuthMode('google');
          setError(data.detail || "Failed to connect");
        }
      } else {
        // Redirect to OAuth
        window.location.href = `${GOOGLE_AUTH_URL}?teacher_email=${encodeURIComponent(emailInput)}`;
      }
    } catch (err) {
      // Network error or CORS - just redirect
      window.location.href = `${GOOGLE_AUTH_URL}?teacher_email=${encodeURIComponent(emailInput)}`;
    }
  };

  const handleDisconnect = async () => {
    try {
      await fetch(`/auth/disconnect?teacher_email=${encodeURIComponent(teacherEmail)}`, {
        method: 'POST'
      });
      setIsAuthenticated(false);
      setTeacherEmail("");
    } catch (err) {
      console.error("Error disconnecting:", err);
    }
  };

  return (
    <section id="connect" className="py-20 px-6">
      <div className="max-w-xl mx-auto">
        <div className="card p-8">
          <div className="text-center mb-8">
            <div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-light)] flex items-center justify-center shadow-lg">
              <svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
              </svg>
            </div>
            <h2 className="font-display text-2xl font-bold text-[var(--color-text)] mb-2">
              {isAuthenticated ? "已連接!" : "連接您的 Google 帳號"}
            </h2>
            <p className="text-[var(--color-text-muted)]">
              {isAuthenticated 
                ? "您的 Google 帳號已連結。現在可以分析考試答案了。"
                : "我們只會存取您的 Google 表單回應試算表。不會在我們的伺服器上儲存任何資料。"}
            </p>
          </div>

          {isAuthenticated ? (
            <div className="space-y-4">
          <div className="flex items-center justify-center gap-3 p-4 rounded-xl bg-[var(--color-success)]/10 border border-[var(--color-success)]/20">
            <div className="w-10 h-10 rounded-full bg-[var(--color-success)]/20 flex items-center justify-center">
              <svg className="w-5 h-5 text-[var(--color-success)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
              </svg>
            </div>
            <div className="text-left">
              <div className="font-medium text-[var(--color-text)]">{teacherEmail}</div>
              <div className="text-sm text-[var(--color-text-muted)]">
                {authMode === 'csv-only' ? 'CSV 上傳模式(Google OAuth 未設定)' : 'Google 帳號已連接'}
              </div>
            </div>
          </div>
          
          {authMode === 'csv-only' && (
            <div className="p-3 rounded-lg bg-amber-50 border border-amber-200 text-amber-700 text-sm">
              <strong>注意:</strong>您可以在聊天中直接貼上 CSV 資料,或設定 Google OAuth 從 Google 表單取得資料。
            </div>
          )}
              
              <button
                onClick={handleDisconnect}
                className="w-full btn btn-outline text-red-500 border-red-200 hover:border-red-500 hover:text-red-600"
              >
                斷開帳號
              </button>
            </div>
          ) : (
            <form onSubmit={handleConnect} className="space-y-4">
              <div>
                <label htmlFor="email" className="block text-sm font-medium text-[var(--color-text)] mb-2">
                  您的學校電子郵件
                </label>
                <input
                  type="email"
                  id="email"
                  value={emailInput}
                  onChange={(e) => setEmailInput(e.target.value)}
                  placeholder="teacher@school.edu"
                  className="input"
                  required
                />
              </div>
              
              {error && (
                <div className="p-3 rounded-lg bg-red-50 border border-red-200 text-red-600 text-sm">
                  {error}
                </div>
              )}
              
              <button 
                type="submit" 
                className="w-full btn btn-primary"
                disabled={isChecking}
              >
                {isChecking ? (
                  <>
                    <svg className="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
                      <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
                      <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
                    </svg>
                    檢查中...
                  </>
                ) : (
                  <>
                    <svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
                      <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
                      <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
                      <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
                      <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
                    </svg>
                    使用 Google 繼續
                  </>
                )}
              </button>
              
              <p className="text-center text-xs text-[var(--color-text-muted)]">
                連接即表示您同意我們的{" "}
                <a href="#" className="underline hover:text-[var(--color-primary)]">服務條款</a>
                {" "}和{" "}
                <a href="#" className="underline hover:text-[var(--color-primary)]">隱私政策</a>
              </p>
            </form>
          )}
        </div>
      </div>
    </section>
  );
}