create-nara 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +17 -0
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.js +50 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +3 -0
  6. package/dist/template.d.ts +8 -0
  7. package/dist/template.js +68 -0
  8. package/package.json +28 -0
  9. package/templates/base/.env.example +3 -0
  10. package/templates/base/tsconfig.json +14 -0
  11. package/templates/minimal/routes/web.ts +11 -0
  12. package/templates/minimal/server.ts +10 -0
  13. package/templates/svelte/resources/js/app.ts +12 -0
  14. package/templates/svelte/resources/js/components/DarkModeToggle.svelte +67 -0
  15. package/templates/svelte/resources/js/components/Header.svelte +240 -0
  16. package/templates/svelte/resources/js/components/NaraIcon.svelte +3 -0
  17. package/templates/svelte/resources/js/components/Pagination.svelte +55 -0
  18. package/templates/svelte/resources/js/components/UserModal.svelte +234 -0
  19. package/templates/svelte/resources/js/components/helper.ts +300 -0
  20. package/templates/svelte/resources/js/pages/auth/forgot-password.svelte +97 -0
  21. package/templates/svelte/resources/js/pages/auth/login.svelte +138 -0
  22. package/templates/svelte/resources/js/pages/auth/register.svelte +176 -0
  23. package/templates/svelte/resources/js/pages/auth/reset-password.svelte +106 -0
  24. package/templates/svelte/resources/js/pages/dashboard.svelte +224 -0
  25. package/templates/svelte/resources/js/pages/landing.svelte +446 -0
  26. package/templates/svelte/resources/js/pages/profile.svelte +368 -0
  27. package/templates/svelte/resources/js/pages/users.svelte +260 -0
  28. package/templates/svelte/resources/views/inertia.html +12 -0
  29. package/templates/svelte/routes/web.ts +17 -0
  30. package/templates/svelte/server.ts +12 -0
  31. package/templates/svelte/vite.config.ts +19 -0
  32. package/templates/vue/resources/js/app.ts +14 -0
  33. package/templates/vue/resources/js/components/DarkModeToggle.vue +81 -0
  34. package/templates/vue/resources/js/components/Header.vue +251 -0
  35. package/templates/vue/resources/js/components/NaraIcon.vue +5 -0
  36. package/templates/vue/resources/js/components/Pagination.vue +71 -0
  37. package/templates/vue/resources/js/components/UserModal.vue +276 -0
  38. package/templates/vue/resources/js/components/index.ts +5 -0
  39. package/templates/vue/resources/js/pages/auth/forgot-password.vue +105 -0
  40. package/templates/vue/resources/js/pages/auth/login.vue +142 -0
  41. package/templates/vue/resources/js/pages/auth/register.vue +183 -0
  42. package/templates/vue/resources/js/pages/auth/reset-password.vue +115 -0
  43. package/templates/vue/resources/js/pages/dashboard.vue +233 -0
  44. package/templates/vue/resources/js/pages/landing.vue +358 -0
  45. package/templates/vue/resources/js/pages/profile.vue +370 -0
  46. package/templates/vue/resources/js/pages/users.vue +264 -0
  47. package/templates/vue/resources/views/inertia.html +12 -0
  48. package/templates/vue/routes/web.ts +17 -0
  49. package/templates/vue/server.ts +12 -0
  50. package/templates/vue/vite.config.ts +19 -0
@@ -0,0 +1,276 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, onUnmounted } from 'vue';
3
+
4
+ interface UserForm {
5
+ name: string;
6
+ email: string;
7
+ phone?: string;
8
+ is_admin: boolean;
9
+ is_verified: boolean;
10
+ password?: string;
11
+ }
12
+
13
+ const props = defineProps<{
14
+ show: boolean;
15
+ mode?: 'create' | 'edit';
16
+ form: UserForm;
17
+ isSubmitting?: boolean;
18
+ }>();
19
+
20
+ const emit = defineEmits<{
21
+ (e: 'close'): void;
22
+ (e: 'submit', form: UserForm): void;
23
+ }>();
24
+
25
+ const handleClose = () => {
26
+ emit('close');
27
+ };
28
+
29
+ const handleSubmit = () => {
30
+ emit('submit', props.form);
31
+ };
32
+
33
+ const handleBackdropClick = (e: MouseEvent) => {
34
+ if (e.target === e.currentTarget) {
35
+ handleClose();
36
+ }
37
+ };
38
+
39
+ const handleKeyDown = (e: KeyboardEvent) => {
40
+ if (e.key === 'Escape' && props.show) {
41
+ handleClose();
42
+ }
43
+ };
44
+
45
+ onMounted(() => {
46
+ window.addEventListener('keydown', handleKeyDown);
47
+ });
48
+
49
+ onUnmounted(() => {
50
+ window.removeEventListener('keydown', handleKeyDown);
51
+ });
52
+ </script>
53
+
54
+ <template>
55
+ <Teleport to="body">
56
+ <Transition
57
+ enter-active-class="transition duration-300 ease-out"
58
+ enter-from-class="opacity-0"
59
+ enter-to-class="opacity-100"
60
+ leave-active-class="transition duration-200 ease-in"
61
+ leave-from-class="opacity-100"
62
+ leave-to-class="opacity-0"
63
+ >
64
+ <div
65
+ v-if="show"
66
+ class="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6"
67
+ @click="handleBackdropClick"
68
+ role="dialog"
69
+ aria-modal="true"
70
+ aria-labelledby="modal-title"
71
+ tabindex="-1"
72
+ >
73
+ <!-- Background Blur & Overlay -->
74
+ <div class="absolute inset-0 bg-black/70 backdrop-blur-md"></div>
75
+
76
+ <!-- Decorative Elements -->
77
+ <div class="absolute top-1/4 left-1/4 w-96 h-96 bg-accent-500/10 rounded-full blur-3xl pointer-events-none"></div>
78
+ <div class="absolute bottom-1/4 right-1/4 w-64 h-64 bg-primary-500/10 rounded-full blur-3xl pointer-events-none"></div>
79
+
80
+ <!-- Modal Container -->
81
+ <Transition
82
+ appear
83
+ enter-active-class="transition duration-500 ease-out-back"
84
+ enter-from-class="opacity-0 translate-y-12 scale-95"
85
+ enter-to-class="opacity-100 translate-y-0 scale-100"
86
+ >
87
+ <div class="relative w-full max-w-lg">
88
+ <!-- Glowing Border Effect -->
89
+ <div class="absolute -inset-px bg-gradient-to-br from-accent-500/50 via-transparent to-primary-500/50 rounded-3xl blur-sm opacity-60"></div>
90
+
91
+ <!-- Modal Content -->
92
+ <div class="relative bg-white dark:bg-surface-dark rounded-3xl border border-slate-200 dark:border-white/10 overflow-hidden shadow-2xl">
93
+
94
+ <!-- Header -->
95
+ <div class="relative px-8 pt-8 pb-6 border-b border-slate-100 dark:border-white/5">
96
+ <!-- Decorative Line -->
97
+ <div class="absolute top-0 left-8 right-8 h-px bg-gradient-to-r from-transparent via-accent-500/50 to-transparent"></div>
98
+
99
+ <div class="flex items-start justify-between">
100
+ <div>
101
+ <p class="text-[10px] font-bold uppercase tracking-[0.3em] text-accent-600 dark:text-accent-400 mb-2">
102
+ {{ mode === 'create' ? 'New Entry' : 'Edit Entry' }}
103
+ </p>
104
+ <h3 id="modal-title" class="text-2xl font-bold tracking-tight text-slate-900 dark:text-white">
105
+ {{ mode === 'create' ? 'Create User' : 'Update User' }}
106
+ </h3>
107
+ </div>
108
+
109
+ <!-- Close Button -->
110
+ <button
111
+ class="group w-10 h-10 flex items-center justify-center rounded-full border border-slate-200 dark:border-white/10 hover:border-red-500/50 hover:bg-red-500/5 transition-all duration-300"
112
+ @click="handleClose"
113
+ :disabled="isSubmitting"
114
+ aria-label="Close modal"
115
+ >
116
+ <svg class="w-4 h-4 text-slate-400 group-hover:text-red-500 transition-colors" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
117
+ <path d="M18 6L6 18M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/>
118
+ </svg>
119
+ </button>
120
+ </div>
121
+ </div>
122
+
123
+ <!-- Form -->
124
+ <form class="p-8 space-y-6" @submit.prevent="handleSubmit">
125
+
126
+ <!-- Name Field -->
127
+ <div class="group">
128
+ <label for="user-name" class="block text-[10px] font-bold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400 mb-3">
129
+ Full Name
130
+ </label>
131
+ <div class="relative">
132
+ <input
133
+ id="user-name"
134
+ class="w-full px-0 py-3 bg-transparent border-0 border-b-2 border-slate-200 dark:border-white/10 text-lg text-slate-900 dark:text-white placeholder-slate-300 dark:placeholder-slate-600 focus:border-accent-500 dark:focus:border-accent-400 focus:ring-0 outline-none transition-colors"
135
+ type="text"
136
+ v-model="form.name"
137
+ placeholder="Enter full name"
138
+ />
139
+ <div class="absolute bottom-0 left-0 w-0 h-0.5 bg-accent-500 group-focus-within:w-full transition-all duration-500"></div>
140
+ </div>
141
+ </div>
142
+
143
+ <!-- Email Field -->
144
+ <div class="group">
145
+ <label for="user-email" class="block text-[10px] font-bold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400 mb-3">
146
+ Email Address
147
+ </label>
148
+ <div class="relative">
149
+ <input
150
+ id="user-email"
151
+ class="w-full px-0 py-3 bg-transparent border-0 border-b-2 border-slate-200 dark:border-white/10 text-lg text-slate-900 dark:text-white placeholder-slate-300 dark:placeholder-slate-600 focus:border-accent-500 dark:focus:border-accent-400 focus:ring-0 outline-none transition-colors"
152
+ type="email"
153
+ v-model="form.email"
154
+ placeholder="user@example.com"
155
+ />
156
+ </div>
157
+ </div>
158
+
159
+ <!-- Phone Field -->
160
+ <div class="group">
161
+ <label for="user-phone" class="block text-[10px] font-bold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400 mb-3">
162
+ Phone Number
163
+ <span class="text-slate-400 dark:text-slate-600 font-normal normal-case tracking-normal ml-1">(optional)</span>
164
+ </label>
165
+ <div class="relative">
166
+ <input
167
+ id="user-phone"
168
+ class="w-full px-0 py-3 bg-transparent border-0 border-b-2 border-slate-200 dark:border-white/10 text-lg text-slate-900 dark:text-white placeholder-slate-300 dark:placeholder-slate-600 focus:border-purple-500 dark:focus:border-purple-400 focus:ring-0 outline-none transition-colors font-mono"
169
+ type="text"
170
+ v-model="form.phone"
171
+ placeholder="+62 xxx xxxx xxxx"
172
+ />
173
+ </div>
174
+ </div>
175
+
176
+ <!-- Role & Status Toggles -->
177
+ <div class="grid grid-cols-2 gap-4 pt-2">
178
+ <!-- Admin Toggle -->
179
+ <label class="group relative flex items-center gap-4 p-4 rounded-2xl border border-slate-200 dark:border-white/10 hover:border-accent-500/30 cursor-pointer transition-all duration-300" :class="{ 'bg-accent-500/5 border-accent-500/30': form.is_admin }">
180
+ <div class="relative">
181
+ <input
182
+ type="checkbox"
183
+ class="sr-only peer"
184
+ v-model="form.is_admin"
185
+ />
186
+ <div class="w-12 h-7 bg-slate-200 dark:bg-white/10 rounded-full peer-checked:bg-accent-500 transition-colors"></div>
187
+ <div class="absolute top-0.5 left-0.5 w-6 h-6 bg-white rounded-full shadow-md transform peer-checked:translate-x-5 transition-transform"></div>
188
+ </div>
189
+ <div>
190
+ <p class="text-sm font-bold text-slate-900 dark:text-white">Admin</p>
191
+ <p class="text-[10px] text-slate-400 dark:text-slate-500">Full access</p>
192
+ </div>
193
+ </label>
194
+
195
+ <!-- Verified Toggle -->
196
+ <label class="group relative flex items-center gap-4 p-4 rounded-2xl border border-slate-200 dark:border-white/10 hover:border-primary-500/30 cursor-pointer transition-all duration-300" :class="{ 'bg-primary-500/5 border-primary-500/30': form.is_verified }">
197
+ <div class="relative">
198
+ <input
199
+ type="checkbox"
200
+ class="sr-only peer"
201
+ v-model="form.is_verified"
202
+ />
203
+ <div class="w-12 h-7 bg-slate-200 dark:bg-white/10 rounded-full peer-checked:bg-primary-500 transition-colors"></div>
204
+ <div class="absolute top-0.5 left-0.5 w-6 h-6 bg-white rounded-full shadow-md transform peer-checked:translate-x-5 transition-transform"></div>
205
+ </div>
206
+ <div>
207
+ <p class="text-sm font-bold text-slate-900 dark:text-white">Verified</p>
208
+ <p class="text-[10px] text-slate-400 dark:text-slate-500">Email confirmed</p>
209
+ </div>
210
+ </label>
211
+ </div>
212
+
213
+ <!-- Password Field -->
214
+ <div class="group pt-2">
215
+ <label for="user-password" class="block text-[10px] font-bold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400 mb-3">
216
+ {{ mode === 'create' ? 'Password' : 'New Password' }}
217
+ <span class="text-slate-400 dark:text-slate-600 font-normal normal-case tracking-normal ml-1">(optional)</span>
218
+ </label>
219
+ <div class="relative">
220
+ <input
221
+ id="user-password"
222
+ class="w-full px-0 py-3 bg-transparent border-0 border-b-2 border-slate-200 dark:border-white/10 text-lg text-slate-900 dark:text-white placeholder-slate-300 dark:placeholder-slate-600 focus:border-accent-500 dark:focus:border-accent-400 focus:ring-0 outline-none transition-colors"
223
+ type="password"
224
+ v-model="form.password"
225
+ :placeholder="mode === 'create' ? 'Leave empty to use email' : 'Leave empty to keep current'"
226
+ />
227
+ </div>
228
+ </div>
229
+
230
+ <!-- Actions -->
231
+ <div class="flex items-center justify-end gap-4 pt-6 border-t border-slate-100 dark:border-white/5">
232
+ <button
233
+ type="button"
234
+ class="px-6 py-3 text-xs font-bold uppercase tracking-wider text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors disabled:opacity-50"
235
+ @click="handleClose"
236
+ :disabled="isSubmitting"
237
+ >
238
+ Cancel
239
+ </button>
240
+ <button
241
+ type="submit"
242
+ class="group relative px-8 py-3 bg-slate-900 dark:bg-white text-white dark:text-black text-xs font-bold uppercase tracking-wider rounded-full overflow-hidden hover:scale-105 active:scale-95 transition-transform disabled:opacity-50 disabled:hover:scale-100"
243
+ :disabled="isSubmitting"
244
+ >
245
+ <span class="relative z-10 flex items-center gap-2">
246
+ <template v-if="isSubmitting">
247
+ <svg class="w-4 h-4 animate-spin" viewBox="0 0 24 24" fill="none">
248
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
249
+ <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>
250
+ </svg>
251
+ Saving...
252
+ </template>
253
+ <template v-else>
254
+ {{ mode === 'create' ? 'Create User' : 'Save Changes' }}
255
+ <svg class="w-4 h-4 transform group-hover:translate-x-1 transition-transform" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
256
+ <path d="M5 12h14M12 5l7 7-7 7" stroke-linecap="round" stroke-linejoin="round"/>
257
+ </svg>
258
+ </template>
259
+ </span>
260
+ <div class="absolute inset-0 bg-gradient-to-r from-accent-500 to-primary-500 transform translate-y-full group-hover:translate-y-0 transition-transform duration-300"></div>
261
+ </button>
262
+ </div>
263
+ </form>
264
+ </div>
265
+ </div>
266
+ </Transition>
267
+ </div>
268
+ </Transition>
269
+ </Teleport>
270
+ </template>
271
+
272
+ <style scoped>
273
+ .ease-out-back {
274
+ transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
275
+ }
276
+ </style>
@@ -0,0 +1,5 @@
1
+ export { default as DarkModeToggle } from './DarkModeToggle.vue';
2
+ export { default as Header } from './Header.vue';
3
+ export { default as NaraIcon } from './NaraIcon.vue';
4
+ export { default as Pagination } from './Pagination.vue';
5
+ export { default as UserModal } from './UserModal.vue';
@@ -0,0 +1,105 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted, watch } from 'vue';
3
+ import { Link } from '@inertiajs/vue3';
4
+ import axios from 'axios';
5
+ import { api, Toast } from '../../utils/helper';
6
+ import NaraIcon from '../../components/NaraIcon.vue';
7
+ import DarkModeToggle from '../../components/DarkModeToggle.vue';
8
+
9
+ const props = defineProps<{
10
+ error?: string;
11
+ }>();
12
+
13
+ const form = ref({
14
+ email: '',
15
+ phone: '',
16
+ });
17
+
18
+ const success = ref(false);
19
+ const processing = ref(false);
20
+
21
+ onMounted(() => {
22
+ if (props.error) {
23
+ Toast(props.error, 'error');
24
+ }
25
+ });
26
+
27
+ watch(() => props.error, (newError) => {
28
+ if (newError) {
29
+ Toast(newError, 'error');
30
+ }
31
+ });
32
+
33
+ async function submit() {
34
+ processing.value = true;
35
+ const result = await api(() => axios.post("/forgot-password", form.value));
36
+ processing.value = false;
37
+
38
+ if (result.success) {
39
+ success.value = true;
40
+ form.value.email = "";
41
+ form.value.phone = "";
42
+ }
43
+ }
44
+ </script>
45
+
46
+ <template>
47
+ <section class="min-h-screen bg-gradient-to-b from-slate-950 via-slate-900 to-slate-950 text-slate-50">
48
+ <!-- Header with dark mode toggle -->
49
+ <header class="fixed inset-x-0 top-0 z-40 border-b border-slate-800/60 bg-slate-950/80 backdrop-blur-xl">
50
+ <div class="max-w-6xl mx-auto flex h-16 items-center justify-between px-4 sm:h-20 sm:px-6 lg:px-8">
51
+ <Link href="/" class="flex items-center gap-2">
52
+ <img src="/nara.png" alt="Nara logo" class="h-7 w-7 rounded-lg object-cover" />
53
+ <div class="flex flex-col leading-tight">
54
+ <span class="text-sm font-semibold tracking-tight text-slate-50">Nara</span>
55
+ <span class="text-[10px] uppercase tracking-[0.22em] text-slate-500">TypeScript framework</span>
56
+ </div>
57
+ </Link>
58
+ <div class="flex items-center gap-3">
59
+ <DarkModeToggle />
60
+ </div>
61
+ </div>
62
+ </header>
63
+
64
+ <div class="flex flex-col items-center justify-center px-6 py-8 mx-auto min-h-screen lg:py-0">
65
+ <div class="flex items-center mb-6 text-2xl font-semibold text-slate-50">
66
+ <NaraIcon />
67
+ </div>
68
+ <div class="w-full max-w-md rounded-3xl border border-slate-800/80 bg-slate-900/70 backdrop-blur-xl shadow-[0_24px_80px_rgba(15,23,42,0.8)] md:mt-0 sm:max-w-md xl:p-0">
69
+ <div class="p-6 space-y-4 md:space-y-6 sm:p-8">
70
+ <h1 class="text-xl font-bold leading-tight tracking-tight text-slate-50 md:text-2xl">
71
+ Reset Password
72
+ </h1>
73
+
74
+ <div v-if="success" class="p-4 mb-4 text-sm text-green-400 rounded-lg bg-green-900/50" role="alert">
75
+ Link reset password telah dikirim ke email atau nomor telepon Anda.
76
+ </div>
77
+
78
+ <form class="space-y-4 md:space-y-6" @submit.prevent="submit">
79
+ <div>
80
+ <label for="email" class="block mb-2 text-sm font-medium text-slate-200">Email atau Nomor Telepon</label>
81
+ <input
82
+ v-model="form.email"
83
+ type="text"
84
+ name="email"
85
+ id="email"
86
+ class="bg-slate-900/70 border border-slate-700 text-slate-50 sm:text-sm rounded-lg focus:ring-2 focus:ring-primary-400 focus:border-primary-400 focus:outline-none block w-full py-2.5 px-3 placeholder-slate-500"
87
+ placeholder="email@example.com atau 08xxxxxxxxxx"
88
+ required
89
+ />
90
+ </div>
91
+
92
+ <button type="submit" :disabled="processing"
93
+ class="w-full text-sm font-medium rounded-full px-5 py-2.5 text-slate-950 bg-primary-400 hover:bg-primary-300 focus:ring-4 focus:outline-none focus:ring-primary-300 disabled:opacity-50">
94
+ {{ processing ? 'Mengirim...' : 'Kirim Link Reset Password' }}
95
+ </button>
96
+
97
+ <p class="text-sm font-light text-slate-400">
98
+ Ingat password Anda? <Link href="/login" class="font-medium text-primary-400 hover:underline">Login disini</Link>
99
+ </p>
100
+ </form>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </section>
105
+ </template>
@@ -0,0 +1,142 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, watch } from 'vue';
3
+ import { useForm, Link } from '@inertiajs/vue3';
4
+ import { Toast } from '../../utils/helper';
5
+ import NaraIcon from '../../components/NaraIcon.vue';
6
+
7
+ const props = defineProps<{
8
+ error?: string;
9
+ }>();
10
+
11
+ const form = useForm({
12
+ email: '',
13
+ password: '',
14
+ });
15
+
16
+ onMounted(() => {
17
+ if (props.error) {
18
+ Toast(props.error, 'error');
19
+ }
20
+ });
21
+
22
+ watch(() => props.error, (newError) => {
23
+ if (newError) {
24
+ Toast(newError, 'error');
25
+ }
26
+ });
27
+
28
+ function submit() {
29
+ form.post('/login');
30
+ }
31
+ </script>
32
+
33
+ <template>
34
+ <div class="min-h-screen bg-white dark:bg-black text-slate-900 dark:text-slate-100 flex overflow-hidden">
35
+
36
+ <!-- Left Panel: Form -->
37
+ <div class="w-full lg:w-1/2 flex flex-col justify-between p-8 lg:p-12 z-10 relative">
38
+ <div>
39
+ <Link href="/" class="text-2xl font-bold tracking-tighter flex items-center gap-2">
40
+ <NaraIcon />
41
+ <span>NARA.</span>
42
+ </Link>
43
+ </div>
44
+
45
+ <div class="max-w-md w-full mx-auto">
46
+ <h1 class="text-4xl lg:text-5xl font-bold tracking-tight mb-2">Welcome Back.</h1>
47
+ <p class="text-slate-500 dark:text-slate-400 mb-10 text-lg">Enter your credentials to access the grid.</p>
48
+
49
+ <!-- Google Login Button -->
50
+ <div class="flex flex-col space-y-4 mb-8">
51
+ <a href="/google/redirect"
52
+ class="group relative w-full flex items-center justify-center px-6 py-4 border border-slate-200 dark:border-white/10 rounded-xl bg-slate-50 dark:bg-white/5 hover:bg-slate-100 dark:hover:bg-white/10 transition-all duration-300">
53
+ <svg class="h-5 w-5 mr-3 transition-transform group-hover:scale-110" viewBox="0 0 24 24">
54
+ <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" fill="#4285F4"/>
55
+ <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" fill="#34A853"/>
56
+ <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" fill="#FBBC05"/>
57
+ <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" fill="#EA4335"/>
58
+ </svg>
59
+ <span class="font-medium text-slate-700 dark:text-slate-200">Continue with Google</span>
60
+ </a>
61
+
62
+ <div class="relative py-2">
63
+ <div class="absolute inset-0 flex items-center">
64
+ <div class="w-full border-t border-slate-200 dark:border-white/10"></div>
65
+ </div>
66
+ <div class="relative flex justify-center text-xs uppercase tracking-widest">
67
+ <span class="bg-white dark:bg-black px-4 text-slate-400">Or via email</span>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ <form class="space-y-5" @submit.prevent="submit">
73
+ <div class="space-y-1">
74
+ <label for="email" class="block text-sm font-medium text-slate-700 dark:text-slate-300 ml-1">Email</label>
75
+ <input v-model="form.email" required type="text" name="email" id="email"
76
+ class="w-full px-5 py-4 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl focus:ring-2 focus:ring-primary-500/50 focus:border-primary-500 outline-none transition-all dark:text-white placeholder:text-slate-400"
77
+ placeholder="nara@example.com" >
78
+ <span v-if="form.errors.email" class="text-xs text-red-500 ml-1">{{ form.errors.email }}</span>
79
+ </div>
80
+
81
+ <div class="space-y-1">
82
+ <label for="password" class="block text-sm font-medium text-slate-700 dark:text-slate-300 ml-1">Password</label>
83
+ <input v-model="form.password" required type="password" name="password" id="password"
84
+ placeholder="••••••••"
85
+ class="w-full px-5 py-4 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl focus:ring-2 focus:ring-primary-500/50 focus:border-primary-500 outline-none transition-all dark:text-white placeholder:text-slate-400" >
86
+ <span v-if="form.errors.password" class="text-xs text-red-500 ml-1">{{ form.errors.password }}</span>
87
+ </div>
88
+
89
+ <div class="flex items-center justify-end">
90
+ <Link href="/forgot-password" class="text-sm font-medium text-slate-500 hover:text-primary-500 transition-colors">Forgot password?</Link>
91
+ </div>
92
+
93
+ <button type="submit" :disabled="form.processing"
94
+ class="group w-full relative overflow-hidden rounded-xl bg-slate-900 dark:bg-white text-white dark:text-black font-bold text-lg py-4 transition-transform active:scale-[0.98]">
95
+ <span class="relative z-10">{{ form.processing ? 'LOGGING IN...' : 'LOGIN' }}</span>
96
+ <div class="absolute inset-0 bg-primary-500 transform translate-y-full transition-transform duration-300 group-hover:translate-y-0"></div>
97
+ </button>
98
+
99
+ <p class="text-center text-slate-500 dark:text-slate-400 mt-6">
100
+ Don't have an account? <Link href="/register" class="font-bold text-primary-600 dark:text-primary-400 hover:underline">Sign up</Link>
101
+ </p>
102
+ </form>
103
+ </div>
104
+
105
+ <div class="text-xs text-slate-400 dark:text-slate-600">
106
+ © 2025 NARA INC.
107
+ </div>
108
+ </div>
109
+
110
+ <!-- Right Panel: Visual -->
111
+ <div class="hidden lg:flex w-1/2 bg-surface-card-dark relative overflow-hidden items-center justify-center">
112
+ <div class="absolute inset-0 bg-[url('https://grainy-gradients.vercel.app/noise.svg')] opacity-30"></div>
113
+
114
+ <!-- Abstract blobs -->
115
+ <div class="absolute top-0 right-0 w-[600px] h-[600px] bg-primary-500/20 rounded-full blur-[120px] -translate-y-1/2 translate-x-1/2"></div>
116
+ <div class="absolute bottom-0 left-0 w-[600px] h-[600px] bg-accent-500/10 rounded-full blur-[120px] translate-y-1/2 -translate-x-1/2"></div>
117
+
118
+ <div class="relative z-10 max-w-lg text-center p-12">
119
+ <h2 class="text-6xl font-bold tracking-tighter text-white mb-6 leading-[0.9]">
120
+ PURE <br/>
121
+ <span class="text-transparent bg-clip-text bg-gradient-to-r from-primary-400 to-info-300">VELOCITY</span>
122
+ </h2>
123
+ <p class="text-xl text-slate-400 font-serif italic">
124
+ "The only thing faster than Nara is the thought of using it."
125
+ </p>
126
+
127
+ <!-- Code Snippet Decoration -->
128
+ <div class="mt-12 text-left bg-black/50 backdrop-blur-md rounded-xl border border-white/10 p-6 font-mono text-xs text-slate-300 shadow-2xl transform rotate-3 hover:rotate-0 transition-transform duration-500">
129
+ <div class="flex gap-2 mb-4">
130
+ <div class="w-3 h-3 rounded-full bg-red-500"></div>
131
+ <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
132
+ <div class="w-3 h-3 rounded-full bg-green-500"></div>
133
+ </div>
134
+ <div>
135
+ <span class="text-purple-400">const</span> <span class="text-blue-400">future</span> = <span class="text-purple-400">await</span> nara.<span class="text-yellow-300">load</span>();<br/>
136
+ <span class="text-slate-500">// Welcome to the new standard.</span>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ </template>