aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/ui/src/components/LoginModal.svelte
blob: 1886cd9feaac8fbb65b795215e9d22dda8885cd8 (plain) (blame)
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
<script lang="ts">
  import { open } from "@tauri-apps/plugin-shell";
  import { authState } from "../stores/auth.svelte";

  function openLink(url: string) {
    open(url);
  }
</script>

{#if authState.isLoginModalOpen}
  <div
    class="fixed inset-0 z-[60] flex items-center justify-center bg-white/80 dark:bg-black/80 backdrop-blur-sm p-4"
  >
    <div
      class="bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded-xl shadow-2xl p-6 w-full max-w-md animate-in fade-in zoom-in-0 duration-200"
    >
      <div class="flex justify-between items-center mb-6">
        <h2 class="text-2xl font-bold text-gray-900 dark:text-white">Login</h2>
        <button
          onclick={() => authState.closeLoginModal()}
          class="text-zinc-500 hover:text-black dark:hover:text-white transition group"
        >
          ✕
        </button>
      </div>

      {#if authState.loginMode === "select"}
        <div class="space-y-4">
          <button
            onclick={() => authState.startMicrosoftLogin()}
            class="w-full flex items-center justify-center gap-3 bg-gray-100 hover:bg-gray-200 dark:bg-[#2F2F2F] dark:hover:bg-[#3F3F3F] text-gray-900 dark:text-white p-4 rounded-lg font-bold border border-transparent hover:border-zinc-400 dark:hover:border-zinc-500 transition-all group"
          >
            <!-- Microsoft Logo SVG -->
            <svg
              class="w-5 h-5"
              viewBox="0 0 23 23"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
              ><path fill="#f35325" d="M1 1h10v10H1z" /><path
                fill="#81bc06"
                d="M12 1h10v10H12z"
              /><path fill="#05a6f0" d="M1 12h10v10H1z" /><path
                fill="#ffba08"
                d="M12 12h10v10H12z"
              /></svg
            >
            Microsoft Account
          </button>

          <div class="relative py-2">
            <div class="absolute inset-0 flex items-center">
              <div class="w-full border-t border-zinc-200 dark:border-zinc-700"></div>
            </div>
            <div class="relative flex justify-center text-xs uppercase">
              <span class="bg-white dark:bg-zinc-900 px-2 text-zinc-400 dark:text-zinc-500">OR</span>
            </div>
          </div>

          <div class="space-y-2">
            <input
              type="text"
              bind:value={authState.offlineUsername}
              placeholder="Offline Username"
              class="w-full bg-gray-50 border-zinc-200 dark:bg-zinc-950 dark:border-zinc-700 rounded p-3 text-gray-900 dark:text-white focus:border-indigo-500 outline-none"
              onkeydown={(e) => e.key === "Enter" && authState.performOfflineLogin()}
            />
            <button
              onclick={() => authState.performOfflineLogin()}
              class="w-full bg-gray-200 hover:bg-gray-300 dark:bg-zinc-800 dark:hover:bg-zinc-700 text-gray-700 dark:text-zinc-300 p-3 rounded font-medium transition-colors"
            >
              Offline Login
            </button>
          </div>
        </div>
      {:else if authState.loginMode === "microsoft"}
        <div class="text-center">
          {#if authState.msLoginLoading && !authState.deviceCodeData}
            <div class="py-8 text-zinc-400 animate-pulse">
              Starting login flow...
            </div>
          {:else if authState.deviceCodeData}
            <div class="space-y-4">
              <p class="text-sm text-gray-500 dark:text-zinc-400">1. Go to this URL:</p>
              <button
                onclick={() =>
                    authState.deviceCodeData && openLink(authState.deviceCodeData.verification_uri)}
                class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-500 dark:hover:text-indigo-300 underline break-all font-mono text-sm"
              >
                {authState.deviceCodeData.verification_uri}
              </button>

              <p class="text-sm text-gray-500 dark:text-zinc-400 mt-2">2. Enter this code:</p>
              <div
                class="bg-gray-50 dark:bg-zinc-950 p-4 rounded border border-zinc-200 dark:border-zinc-700 font-mono text-2xl tracking-widest text-center select-all cursor-pointer hover:border-indigo-500 transition-colors dark:text-white text-gray-900"
                role="button"
                tabindex="0"
                onkeydown={(e) => e.key === 'Enter' && navigator.clipboard.writeText(authState.deviceCodeData?.user_code || "")}
                onclick={() =>
                  navigator.clipboard.writeText(
                    authState.deviceCodeData?.user_code || ""
                  )}
              >
                {authState.deviceCodeData.user_code}
              </div>
              <p class="text-xs text-zinc-500">Click code to copy</p>

              <div class="pt-6 space-y-3">
                   <div class="flex flex-col items-center gap-3">
                       <div class="animate-spin rounded-full h-6 w-6 border-2 border-zinc-300 dark:border-zinc-600 border-t-indigo-500"></div>
                       <span class="text-sm text-gray-600 dark:text-zinc-400 font-medium break-all text-center">{authState.msLoginStatus}</span>
                   </div>
                   <p class="text-xs text-zinc-600">This window will update automatically.</p>
              </div>
              
              <button
                onclick={() => { authState.stopPolling(); authState.loginMode = "select"; }}
                class="text-xs text-zinc-500 hover:text-zinc-300 mt-6 underline"
                >Cancel</button
              >
            </div>
          {/if}
        </div>
      {/if}
    </div>
  </div>
{/if}