1+ <template >
2+ <div class =" min-h-screen bg-gray-50 dark:bg-gray-900" >
3+ <div class =" flex min-h-screen items-center justify-center p-6" >
4+ <div class =" w-full max-w-md" >
5+ <!-- Logo and App Name -->
6+ <div class =" mb-8 text-center" >
7+ <div class =" mb-4 flex items-center justify-center" >
8+ <div class =" rounded-xl bg-gray-900 p-3 dark:bg-gray-100" >
9+ <svg class =" h-10 w-10 text-white dark:text-gray-900" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
10+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
11+ </svg >
12+ </div >
13+ </div >
14+ <h1 class =" text-3xl font-semibold text-gray-900 dark:text-white" >Welcome to Clueless</h1 >
15+ <p class =" mt-2 text-gray-600 dark:text-gray-400" >AI-powered meeting assistant</p >
16+ </div >
17+
18+ <!-- Progress Indicator -->
19+ <div v-if =" showSteps" class =" mb-6 flex items-center justify-center space-x-2" >
20+ <div v-for =" i in 2" :key =" i"
21+ :class =" ['h-1.5 w-20 rounded-full transition-all duration-300',
22+ i <= currentStep ? 'bg-gray-900 dark:bg-gray-100' : 'bg-gray-200 dark:bg-gray-700']" >
23+ </div >
24+ </div >
25+
26+ <!-- Main Card -->
27+ <div class =" rounded-xl bg-white shadow-sm ring-1 ring-gray-200 dark:bg-gray-800 dark:ring-gray-700" >
28+ <div class =" p-6" >
29+ <!-- Step 1: API Key -->
30+ <div v-if =" currentStep === 1" >
31+ <h2 class =" mb-6 text-xl font-medium text-gray-900 dark:text-white" >Setup OpenAI API</h2 >
32+
33+ <div class =" space-y-4" >
34+ <div >
35+ <label class =" mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300" >
36+ API Key
37+ </label >
38+ <div class =" relative" >
39+ <input
40+ v-model =" apiKey"
41+ :type =" showApiKey ? 'text' : 'password'"
42+ placeholder =" sk-..."
43+ class =" w-full rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-gray-900 placeholder-gray-400 focus:border-gray-400 focus:outline-none focus:ring-1 focus:ring-gray-400 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-500"
44+ @input =" validateApiKey"
45+ />
46+ <button
47+ @click =" showApiKey = !showApiKey"
48+ type =" button"
49+ class =" absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
50+ >
51+ <svg v-if =" !showApiKey" class =" h-5 w-5" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
52+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
53+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
54+ </svg >
55+ <svg v-else class =" h-5 w-5" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
56+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
57+ </svg >
58+ </button >
59+ </div >
60+ <Transition
61+ enter-active-class =" transition duration-200 ease-out"
62+ enter-from-class =" opacity-0"
63+ enter-to-class =" opacity-100"
64+ leave-active-class =" transition duration-150 ease-in"
65+ leave-from-class =" opacity-100"
66+ leave-to-class =" opacity-0"
67+ >
68+ <p v-if =" apiKeyError" class =" mt-1.5 text-sm text-red-600 dark:text-red-400" >
69+ {{ apiKeyError }}
70+ </p >
71+ <p v-else-if =" apiKeyValid" class =" mt-1.5 text-sm text-green-600 dark:text-green-400" >
72+ ✓ Valid API key format
73+ </p >
74+ </Transition >
75+ </div >
76+
77+ <div class =" rounded-lg bg-gray-50 p-3 dark:bg-gray-900/50" >
78+ <p class =" text-sm text-gray-600 dark:text-gray-400" >
79+ Need an API key?
80+ <a href =" https://platform.openai.com/api-keys" target =" _blank" class =" font-medium text-gray-900 underline hover:text-gray-700 dark:text-gray-100 dark:hover:text-gray-300" >
81+ Get one from OpenAI
82+ </a >
83+ </p >
84+ </div >
85+ </div >
86+
87+ <div class =" mt-6 flex justify-end" >
88+ <button
89+ @click =" saveApiKey"
90+ :disabled =" !apiKeyValid || isValidating"
91+ :class =" ['rounded-lg px-6 py-2.5 text-sm font-medium transition-all',
92+ apiKeyValid && !isValidating
93+ ? 'bg-gray-900 text-white hover:bg-gray-800 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200'
94+ : 'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-gray-800 dark:text-gray-600']"
95+ >
96+ <span v-if =" isValidating" class =" flex items-center" >
97+ <svg class =" mr-2 h-4 w-4 animate-spin" fill =" none" viewBox =" 0 0 24 24" >
98+ <circle class =" opacity-25" cx =" 12" cy =" 12" r =" 10" stroke =" currentColor" stroke-width =" 4" ></circle >
99+ <path class =" 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" ></path >
100+ </svg >
101+ Validating...
102+ </span >
103+ <span v-else >Continue</span >
104+ </button >
105+ </div >
106+ </div >
107+
108+ <!-- Step 2: GitHub Star -->
109+ <div v-else-if =" currentStep === 2" >
110+ <div class =" text-center" >
111+ <div class =" mb-4 inline-flex rounded-full bg-gray-100 p-3 dark:bg-gray-700" >
112+ <svg class =" h-8 w-8 text-gray-700 dark:text-gray-300" fill =" currentColor" viewBox =" 0 0 24 24" >
113+ <path d =" M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
114+ </svg >
115+ </div >
116+ <h2 class =" mb-2 text-xl font-medium text-gray-900 dark:text-white" >Support Clueless</h2 >
117+ <p class =" mb-6 text-sm text-gray-600 dark:text-gray-400" >
118+ If you find Clueless helpful, please consider starring our repository on GitHub. It helps others discover the project!
119+ </p >
120+
121+ <button
122+ @click =" openGitHub"
123+ class =" mb-4 inline-flex items-center rounded-lg bg-gray-900 px-4 py-2.5 text-sm font-medium text-white hover:bg-gray-800 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200"
124+ >
125+ <svg class =" mr-2 h-4 w-4" fill =" currentColor" viewBox =" 0 0 16 16" >
126+ <path d =" M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25z" />
127+ </svg >
128+ Star on GitHub
129+ </button >
130+
131+ <Transition
132+ enter-active-class =" transition duration-200 ease-out"
133+ enter-from-class =" opacity-0 scale-95"
134+ enter-to-class =" opacity-100 scale-100"
135+ >
136+ <p v-if =" hasStarred" class =" mb-4 text-sm text-green-600 dark:text-green-400" >
137+ ✓ Thank you for your support!
138+ </p >
139+ </Transition >
140+ </div >
141+
142+ <div class =" mt-6 flex items-center justify-between" >
143+ <button
144+ @click =" currentStep = 1"
145+ class =" text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
146+ >
147+ Back
148+ </button >
149+ <button
150+ @click =" completeOnboarding"
151+ class =" rounded-lg bg-gray-900 px-6 py-2.5 text-sm font-medium text-white hover:bg-gray-800 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200"
152+ >
153+ Get Started
154+ </button >
155+ </div >
156+ </div >
157+ </div >
158+ </div >
159+ </div >
160+ </div >
161+ </div >
162+ </template >
163+
164+ <script setup lang="ts">
165+ import { ref , computed } from ' vue'
166+ import { router } from ' @inertiajs/vue3'
167+ import axios from ' axios'
168+
169+ const currentStep = ref (1 )
170+ const apiKey = ref (' ' )
171+ const showApiKey = ref (false )
172+ const apiKeyValid = ref (false )
173+ const apiKeyError = ref (' ' )
174+ const isValidating = ref (false )
175+ const hasStarred = ref (false )
176+
177+ const showSteps = computed (() => currentStep .value > 0 )
178+
179+ const validateApiKey = () => {
180+ const key = apiKey .value .trim ()
181+
182+ if (! key ) {
183+ apiKeyValid .value = false
184+ apiKeyError .value = ' '
185+ return
186+ }
187+
188+ if (! key .startsWith (' sk-' )) {
189+ apiKeyValid .value = false
190+ apiKeyError .value = ' API key should start with "sk-"'
191+ return
192+ }
193+
194+ if (key .length < 20 ) {
195+ apiKeyValid .value = false
196+ apiKeyError .value = ' API key seems too short'
197+ return
198+ }
199+
200+ apiKeyValid .value = true
201+ apiKeyError .value = ' '
202+ }
203+
204+ const saveApiKey = async () => {
205+ if (! apiKeyValid .value || isValidating .value ) return
206+
207+ isValidating .value = true
208+
209+ try {
210+ const response = await axios .post (' /api/openai/api-key' , {
211+ api_key: apiKey .value
212+ })
213+
214+ if (response .data .success ) {
215+ currentStep .value = 2
216+ } else {
217+ apiKeyError .value = ' Failed to save API key. Please try again.'
218+ }
219+ } catch (error ) {
220+ apiKeyError .value = ' Invalid API key or connection error. Please check and try again.'
221+ } finally {
222+ isValidating .value = false
223+ }
224+ }
225+
226+ const openGitHub = async () => {
227+ try {
228+ // Use NativePHP API endpoint to open in default browser
229+ await axios .post (' /api/open-external' , {
230+ url: ' https://github.com/vijaythecoder/clueless'
231+ })
232+ hasStarred .value = true
233+ } catch (error ) {
234+ console .error (' Failed to open GitHub:' , error )
235+ // Fallback for web browser
236+ window .open (' https://github.com/vijaythecoder/clueless' , ' _blank' )
237+ hasStarred .value = true
238+ }
239+ }
240+
241+ const completeOnboarding = () => {
242+ // Navigate to realtime agent
243+ router .visit (' /realtime-agent' )
244+ }
245+ </script >
0 commit comments