kaze 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +42 -0
  4. data/bin/kaze +11 -0
  5. data/lib/kaze/commands/install_command.rb +63 -0
  6. data/lib/kaze/commands/install_inertia_stacks.rb +151 -0
  7. data/lib/kaze/commands.rb +2 -0
  8. data/lib/kaze/version.rb +3 -0
  9. data/lib/kaze.rb +9 -0
  10. data/stubs/default/Procfile.dev +3 -0
  11. data/stubs/default/app/assets/stylesheets/application.css +1 -0
  12. data/stubs/default/app/assets/stylesheets/application.tailwind.css +3 -0
  13. data/stubs/default/app/controllers/application_controller.rb +5 -0
  14. data/stubs/default/app/controllers/auth/authenticated_session_controller.rb +28 -0
  15. data/stubs/default/app/controllers/auth/new_password_controller.rb +18 -0
  16. data/stubs/default/app/controllers/auth/password_reset_link_controller.rb +17 -0
  17. data/stubs/default/app/controllers/auth/registered_user_controller.rb +19 -0
  18. data/stubs/default/app/controllers/concerns/authenticate.rb +34 -0
  19. data/stubs/default/app/controllers/concerns/handle_inertia_requests.rb +9 -0
  20. data/stubs/default/app/controllers/concerns/verify_csrf_token.rb +24 -0
  21. data/stubs/default/app/controllers/dashboard_controller.rb +5 -0
  22. data/stubs/default/app/controllers/password_controller.rb +11 -0
  23. data/stubs/default/app/controllers/profile_controller.rb +31 -0
  24. data/stubs/default/app/controllers/welcome_controller.rb +10 -0
  25. data/stubs/default/app/forms/application_form.rb +9 -0
  26. data/stubs/default/app/forms/auth/login_form.rb +18 -0
  27. data/stubs/default/app/forms/auth/new_password_form.rb +21 -0
  28. data/stubs/default/app/forms/auth/register_form.rb +7 -0
  29. data/stubs/default/app/forms/auth/send_password_reset_link_form.rb +22 -0
  30. data/stubs/default/app/forms/delete_user_form.rb +5 -0
  31. data/stubs/default/app/forms/update_password_form.rb +6 -0
  32. data/stubs/default/app/forms/update_profile_information_form.rb +6 -0
  33. data/stubs/default/app/mailers/application_mailer.rb +11 -0
  34. data/stubs/default/app/mailers/user_mailer.rb +8 -0
  35. data/stubs/default/app/models/application_record.rb +3 -0
  36. data/stubs/default/app/models/concerns/can_reset_password.rb +5 -0
  37. data/stubs/default/app/models/current.rb +3 -0
  38. data/stubs/default/app/models/user.rb +11 -0
  39. data/stubs/default/app/validators/current_password_validator.rb +5 -0
  40. data/stubs/default/app/validators/email_validator.rb +7 -0
  41. data/stubs/default/app/validators/lowercase_validator.rb +5 -0
  42. data/stubs/default/app/validators/uniqueness_validator.rb +24 -0
  43. data/stubs/default/app/views/layouts/mailer.html.erb +374 -0
  44. data/stubs/default/app/views/layouts/mailer.text.erb +11 -0
  45. data/stubs/default/app/views/user_mailer/reset_password.html.erb +39 -0
  46. data/stubs/default/bin/dev +16 -0
  47. data/stubs/default/bin/vite +27 -0
  48. data/stubs/default/config/routes.rb +27 -0
  49. data/stubs/default/config/vite.json +16 -0
  50. data/stubs/default/db/migrate/20240101000000_create_users.rb +14 -0
  51. data/stubs/default/db/migrate/20240101000001_create_delayed_jobs.rb +22 -0
  52. data/stubs/inertia-react-ts/app/javascript/Components/ApplicationLogo.tsx +12 -0
  53. data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +14 -0
  54. data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +17 -0
  55. data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +99 -0
  56. data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +9 -0
  57. data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +9 -0
  58. data/stubs/inertia-react-ts/app/javascript/Components/Modal.tsx +68 -0
  59. data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +18 -0
  60. data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +17 -0
  61. data/stubs/inertia-react-ts/app/javascript/Components/ResponsiveNavLink.tsx +16 -0
  62. data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +18 -0
  63. data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +30 -0
  64. data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +131 -0
  65. data/stubs/inertia-react-ts/app/javascript/Layouts/GuestLayout.tsx +19 -0
  66. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +52 -0
  67. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +98 -0
  68. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +118 -0
  69. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +74 -0
  70. data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +22 -0
  71. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +33 -0
  72. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +100 -0
  73. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.tsx +114 -0
  74. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.tsx +84 -0
  75. data/stubs/inertia-react-ts/app/javascript/Pages/Welcome.tsx +66 -0
  76. data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +34 -0
  77. data/stubs/inertia-react-ts/app/javascript/entrypoints/bootstrap.ts +4 -0
  78. data/stubs/inertia-react-ts/app/javascript/types/global.d.ts +7 -0
  79. data/stubs/inertia-react-ts/app/javascript/types/index.d.ts +12 -0
  80. data/stubs/inertia-react-ts/app/javascript/types/vite-env.d.ts +1 -0
  81. data/stubs/inertia-react-ts/app/views/layouts/application.html.erb +26 -0
  82. data/stubs/inertia-react-ts/config/tailwind.config.js +22 -0
  83. data/stubs/inertia-react-ts/package.json +26 -0
  84. data/stubs/inertia-react-ts/tsconfig.json +19 -0
  85. data/stubs/inertia-react-ts/vite.config.ts +13 -0
  86. data/stubs/inertia-vue-ts/app/javascript/Components/ApplicationLogo.vue +8 -0
  87. data/stubs/inertia-vue-ts/app/javascript/Components/Checkbox.vue +29 -0
  88. data/stubs/inertia-vue-ts/app/javascript/Components/DangerButton.vue +7 -0
  89. data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +75 -0
  90. data/stubs/inertia-vue-ts/app/javascript/Components/DropdownLink.vue +16 -0
  91. data/stubs/inertia-vue-ts/app/javascript/Components/InputError.vue +13 -0
  92. data/stubs/inertia-vue-ts/app/javascript/Components/InputLabel.vue +12 -0
  93. data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +96 -0
  94. data/stubs/inertia-vue-ts/app/javascript/Components/NavLink.vue +21 -0
  95. data/stubs/inertia-vue-ts/app/javascript/Components/PrimaryButton.vue +7 -0
  96. data/stubs/inertia-vue-ts/app/javascript/Components/ResponsiveNavLink.vue +21 -0
  97. data/stubs/inertia-vue-ts/app/javascript/Components/SecondaryButton.vue +19 -0
  98. data/stubs/inertia-vue-ts/app/javascript/Components/TextInput.vue +23 -0
  99. data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +155 -0
  100. data/stubs/inertia-vue-ts/app/javascript/Layouts/GuestLayout.vue +20 -0
  101. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +60 -0
  102. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +93 -0
  103. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +106 -0
  104. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +89 -0
  105. data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +22 -0
  106. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +42 -0
  107. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +98 -0
  108. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.vue +108 -0
  109. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.vue +78 -0
  110. data/stubs/inertia-vue-ts/app/javascript/Pages/Welcome.vue +56 -0
  111. data/stubs/inertia-vue-ts/app/javascript/entrypoints/application.ts +34 -0
  112. data/stubs/inertia-vue-ts/app/javascript/entrypoints/bootstrap.ts +4 -0
  113. data/stubs/inertia-vue-ts/app/javascript/types/global.d.ts +13 -0
  114. data/stubs/inertia-vue-ts/app/javascript/types/index.d.ts +12 -0
  115. data/stubs/inertia-vue-ts/app/javascript/types/vite-env.d.ts +1 -0
  116. data/stubs/inertia-vue-ts/app/views/layouts/application.html.erb +25 -0
  117. data/stubs/inertia-vue-ts/config/tailwind.config.js +22 -0
  118. data/stubs/inertia-vue-ts/package.json +24 -0
  119. data/stubs/inertia-vue-ts/tsconfig.json +19 -0
  120. data/stubs/inertia-vue-ts/vite.config.ts +13 -0
  121. metadata +205 -0
@@ -0,0 +1,98 @@
1
+ <script setup lang="ts">
2
+ import DangerButton from '@/Components/DangerButton.vue';
3
+ import InputError from '@/Components/InputError.vue';
4
+ import InputLabel from '@/Components/InputLabel.vue';
5
+ import Modal from '@/Components/Modal.vue';
6
+ import SecondaryButton from '@/Components/SecondaryButton.vue';
7
+ import TextInput from '@/Components/TextInput.vue';
8
+ import { useForm } from '@inertiajs/vue3';
9
+ import { nextTick, ref } from 'vue';
10
+ import { profile_destroy_path } from '@/routes';
11
+
12
+ const confirmingUserDeletion = ref(false);
13
+ const passwordInput = ref<HTMLInputElement | null>(null);
14
+
15
+ const form = useForm({
16
+ password: '',
17
+ });
18
+
19
+ const confirmUserDeletion = () => {
20
+ confirmingUserDeletion.value = true;
21
+
22
+ nextTick(() => passwordInput.value?.focus());
23
+ };
24
+
25
+ const deleteUser = () => {
26
+ form.delete(profile_destroy_path(), {
27
+ preserveScroll: true,
28
+ onSuccess: () => closeModal(),
29
+ onError: () => passwordInput.value?.focus(),
30
+ onFinish: () => {
31
+ form.reset();
32
+ },
33
+ });
34
+ };
35
+
36
+ const closeModal = () => {
37
+ confirmingUserDeletion.value = false;
38
+
39
+ form.reset();
40
+ };
41
+ </script>
42
+
43
+ <template>
44
+ <section class="space-y-6">
45
+ <header>
46
+ <h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">Delete Account</h2>
47
+
48
+ <p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
49
+ Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting
50
+ your account, please download any data or information that you wish to retain.
51
+ </p>
52
+ </header>
53
+
54
+ <DangerButton @click="confirmUserDeletion">Delete Account</DangerButton>
55
+
56
+ <Modal :show="confirmingUserDeletion" @close="closeModal">
57
+ <div class="p-6">
58
+ <h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
59
+ Are you sure you want to delete your account?
60
+ </h2>
61
+
62
+ <p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
63
+ Once your account is deleted, all of its resources and data will be permanently deleted. Please
64
+ enter your password to confirm you would like to permanently delete your account.
65
+ </p>
66
+
67
+ <div class="mt-6">
68
+ <InputLabel for="password" value="Password" class="sr-only" />
69
+
70
+ <TextInput
71
+ id="password"
72
+ ref="passwordInput"
73
+ v-model="form.password"
74
+ type="password"
75
+ class="mt-1 block w-3/4"
76
+ placeholder="Password"
77
+ @keyup.enter="deleteUser"
78
+ />
79
+
80
+ <InputError :message="form.errors.password" class="mt-2" />
81
+ </div>
82
+
83
+ <div class="mt-6 flex justify-end">
84
+ <SecondaryButton @click="closeModal"> Cancel </SecondaryButton>
85
+
86
+ <DangerButton
87
+ class="ms-3"
88
+ :class="{ 'opacity-25': form.processing }"
89
+ :disabled="form.processing"
90
+ @click="deleteUser"
91
+ >
92
+ Delete Account
93
+ </DangerButton>
94
+ </div>
95
+ </div>
96
+ </Modal>
97
+ </section>
98
+ </template>
@@ -0,0 +1,108 @@
1
+ <script setup lang="ts">
2
+ import InputError from '@/Components/InputError.vue';
3
+ import InputLabel from '@/Components/InputLabel.vue';
4
+ import PrimaryButton from '@/Components/PrimaryButton.vue';
5
+ import TextInput from '@/Components/TextInput.vue';
6
+ import { useForm } from '@inertiajs/vue3';
7
+ import { ref } from 'vue';
8
+ import { password_update_path } from '@/routes';
9
+
10
+ const passwordInput = ref<HTMLInputElement | null>(null);
11
+ const currentPasswordInput = ref<HTMLInputElement | null>(null);
12
+
13
+ const form = useForm({
14
+ current_password: '',
15
+ password: '',
16
+ password_confirmation: '',
17
+ });
18
+
19
+ const updatePassword = () => {
20
+ form.put(password_update_path(), {
21
+ preserveScroll: true,
22
+ onSuccess: () => {
23
+ form.reset();
24
+ },
25
+ onError: () => {
26
+ if (form.errors.password) {
27
+ form.reset('password', 'password_confirmation');
28
+ passwordInput.value?.focus();
29
+ }
30
+ if (form.errors.current_password) {
31
+ form.reset('current_password');
32
+ currentPasswordInput.value?.focus();
33
+ }
34
+ },
35
+ });
36
+ };
37
+ </script>
38
+
39
+ <template>
40
+ <section>
41
+ <header>
42
+ <h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">Update Password</h2>
43
+
44
+ <p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
45
+ Ensure your account is using a long, random password to stay secure.
46
+ </p>
47
+ </header>
48
+
49
+ <form @submit.prevent="updatePassword" class="mt-6 space-y-6">
50
+ <div>
51
+ <InputLabel for="current_password" value="Current Password" />
52
+
53
+ <TextInput
54
+ id="current_password"
55
+ ref="currentPasswordInput"
56
+ v-model="form.current_password"
57
+ type="password"
58
+ class="mt-1 block w-full"
59
+ autocomplete="current-password"
60
+ />
61
+
62
+ <InputError :message="form.errors.current_password" class="mt-2" />
63
+ </div>
64
+
65
+ <div>
66
+ <InputLabel for="password" value="New Password" />
67
+
68
+ <TextInput
69
+ id="password"
70
+ ref="passwordInput"
71
+ v-model="form.password"
72
+ type="password"
73
+ class="mt-1 block w-full"
74
+ autocomplete="new-password"
75
+ />
76
+
77
+ <InputError :message="form.errors.password" class="mt-2" />
78
+ </div>
79
+
80
+ <div>
81
+ <InputLabel for="password_confirmation" value="Confirm Password" />
82
+
83
+ <TextInput
84
+ id="password_confirmation"
85
+ v-model="form.password_confirmation"
86
+ type="password"
87
+ class="mt-1 block w-full"
88
+ autocomplete="new-password"
89
+ />
90
+
91
+ <InputError :message="form.errors.password_confirmation" class="mt-2" />
92
+ </div>
93
+
94
+ <div class="flex items-center gap-4">
95
+ <PrimaryButton :disabled="form.processing">Save</PrimaryButton>
96
+
97
+ <Transition
98
+ enter-active-class="transition ease-in-out"
99
+ enter-from-class="opacity-0"
100
+ leave-active-class="transition ease-in-out"
101
+ leave-to-class="opacity-0"
102
+ >
103
+ <p v-if="form.recentlySuccessful" class="text-sm text-gray-600 dark:text-gray-400">Saved.</p>
104
+ </Transition>
105
+ </div>
106
+ </form>
107
+ </section>
108
+ </template>
@@ -0,0 +1,78 @@
1
+ <script setup lang="ts">
2
+ import InputError from '@/Components/InputError.vue';
3
+ import InputLabel from '@/Components/InputLabel.vue';
4
+ import PrimaryButton from '@/Components/PrimaryButton.vue';
5
+ import TextInput from '@/Components/TextInput.vue';
6
+ import { Link, useForm, usePage } from '@inertiajs/vue3';
7
+ import { profile_update_path } from '@/routes';
8
+
9
+ defineProps<{
10
+ mustVerifyEmail?: Boolean;
11
+ status?: String;
12
+ }>();
13
+
14
+ const user = usePage().props.auth.user;
15
+
16
+ const form = useForm({
17
+ name: user.name,
18
+ email: user.email,
19
+ });
20
+ </script>
21
+
22
+ <template>
23
+ <section>
24
+ <header>
25
+ <h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">Profile Information</h2>
26
+
27
+ <p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
28
+ Update your account's profile information and email address.
29
+ </p>
30
+ </header>
31
+
32
+ <form @submit.prevent="form.patch(profile_update_path())" class="mt-6 space-y-6">
33
+ <div>
34
+ <InputLabel for="name" value="Name" />
35
+
36
+ <TextInput
37
+ id="name"
38
+ type="text"
39
+ class="mt-1 block w-full"
40
+ v-model="form.name"
41
+ required
42
+ autofocus
43
+ autocomplete="name"
44
+ />
45
+
46
+ <InputError class="mt-2" :message="form.errors.name" />
47
+ </div>
48
+
49
+ <div>
50
+ <InputLabel for="email" value="Email" />
51
+
52
+ <TextInput
53
+ id="email"
54
+ type="email"
55
+ class="mt-1 block w-full"
56
+ v-model="form.email"
57
+ required
58
+ autocomplete="username"
59
+ />
60
+
61
+ <InputError class="mt-2" :message="form.errors.email" />
62
+ </div>
63
+
64
+ <div class="flex items-center gap-4">
65
+ <PrimaryButton :disabled="form.processing">Save</PrimaryButton>
66
+
67
+ <Transition
68
+ enter-active-class="transition ease-in-out"
69
+ enter-from-class="opacity-0"
70
+ leave-active-class="transition ease-in-out"
71
+ leave-to-class="opacity-0"
72
+ >
73
+ <p v-if="form.recentlySuccessful" class="text-sm text-gray-600 dark:text-gray-400">Saved.</p>
74
+ </Transition>
75
+ </div>
76
+ </form>
77
+ </section>
78
+ </template>
@@ -0,0 +1,56 @@
1
+ <script setup lang="ts">
2
+ import { Head, Link } from '@inertiajs/vue3';
3
+ import { dashboard_path, login_path, register_path } from '@/routes';
4
+
5
+ defineProps<{
6
+ railsVersion: string;
7
+ rubyVersion: string;
8
+ }>();
9
+ </script>
10
+
11
+ <template>
12
+ <Head title="Welcome" />
13
+ <div
14
+ class="flex flex-col items-center justify-center bg-[#F0E7E9] bg-center bg-cover text-[#261B23] not-italic font-normal leading-tight min-h-screen text-center"
15
+ style="background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEwMjQiIHZpZXdCb3g9IjAgMCAxNDQwIDEwMjQiIHdpZHRoPSIxNDQwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Im0xNDQwIDUxMC4wMDA2NDh2LTUxMC4wMDA2NDhoLTE0NDB2Mzg0LjAwMDY0OGM0MTcuMzExOTM5IDEzMS4xNDIxNzkgODkxIDE3MS41MTMgMTQ0MCAxMjZ6IiBmaWxsPSIjZmZmIi8+PC9zdmc+);font-family: Sans-Serif;font-size: calc(0.9em + 0.5vw);"
16
+ >
17
+ <header class="absolute top-0 right-0 grid grid-cols-2 items-center gap-2 py-10 lg:grid-cols-3">
18
+ <div class="-mx-3 flex flex-1 justify-end">
19
+ <Link
20
+ href="dashboard_path()"
21
+ class="rounded-md px-3 py-2 text-black ring-1 ring-transparent transition hover:text-black/70 focus:outline-none focus-visible:ring-[#FF2D20] dark:text-white dark:hover:text-white/80 dark:focus-visible:ring-white"
22
+ v-if="$page.props.auth.user"
23
+ >
24
+ Dashboard
25
+ </Link>
26
+ <template v-else>
27
+ <Link
28
+ :href="login_path()"
29
+ class="rounded-md px-3 py-2 text-black ring-1 ring-transparent transition hover:text-black/70 focus:outline-none focus-visible:ring-[#FF2D20] dark:text-white dark:hover:text-white/80 dark:focus-visible:ring-white"
30
+ >
31
+ Log in
32
+ </Link>
33
+ <Link
34
+ :href="register_path()"
35
+ class="rounded-md px-3 py-2 text-black ring-1 ring-transparent transition hover:text-black/70 focus:outline-none focus-visible:ring-[#FF2D20] dark:text-white dark:hover:text-white/80 dark:focus-visible:ring-white"
36
+ >
37
+ Register
38
+ </Link>
39
+ </template>
40
+ </div>
41
+ </header>
42
+ <nav style="font-size: 0;height: 20vw;line-height: 0;max-height: 280px;max-width: 280px;min-height: 86px;min-width: 86px;width: 20vw;">
43
+ <a
44
+ class="bg-[#D30001] hover:bg-[#261B23] flex rounded-full" href="https://rubyonrails.org" target="_blank"
45
+ style="transition: background 0.25s cubic-bezier(0.33, 1, 0.68, 1);filter: drop-shadow(0 20px 13px rgb(0 0 0 / 0.03)) drop-shadow(0 8px 5px rgb(0 0 0 / 0.08));"
46
+ >
47
+ <img class="h-auto max-w-full w-full cursor-pointer" alt="" src="data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjExMiIgdmlld0JveD0iMCAwIDExMiAxMTIiIHdpZHRoPSIxMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTEwMC4wODIzNTcgNDguOTk5NTQwM3Y0LjExMzUwMzRoLTcuMzAwMzM4OHYxLjg5ODU0aDMuNjg0MDcxN2MxLjk3MjUwOTEgMCA0LjA3MjUzNDEgMS40NjY0MzExIDQuMTk3OTk3MSAzLjk2NjUxMjRsLjAwNTkxMy4yMzczOTc3djEuNTgyMTE2N2MtLjA4NzgyNCAzLjAwNzk1OS0yLjU0MzEyMTEgNC4xMzkwMDE4LTQuMDcxNTM4OSA0LjIwMTE3NzNsLS4xMzIzNzEyLjAwMjczMjhoLTcuMzkwNzQ1MXYtNC4wOTA5MDE4bDcuNDgxMTUxOC0uMDIyNjAxNnYtMS45ODg5NDY3aC0zLjQ4MDY1NjdjLTEuNzc1MjU4MyAwLTQuMDgxODMyMS0xLjMzODkxNTMtNC4yMTk5OTQyLTMuOTU0OTIwMWwtLjAwNjUxNzYtLjI0ODk5di0xLjQyMzkwNWMwLTIuNjk4MjQwMiAyLjI3ODIxMjktNC4xODI4NTMgNC4wNjU0NjQtNC4yNjc4NDkxbC4xNjEwNDc4LS4wMDM4NjZ6bS0xOC42OTE1NzkgMHYxMS44NjU4NzUyaDYuMTcwMjU1MXY0LjEzNjEwNTFoLTEwLjczNTc5MTl2LTE2LjAwMTk4MDN6bS02LjQ0MTQ3NTEgMHYxNi4wMDE5ODAzaC00LjU4ODEzODV2LTE2LjAwMTk4MDN6bS0xNS4yMTA5MjIgMGg0LjQwNzMyNTFsLjE4NzcyNC4wMDY2ODEyYy4wMzM4NjA5LjAwMjI3MzUuMDY4OTQ1Ny4wMDUxNDk5LjEwNTE2MDEuMDA4NjY0M2wuMjMwMDg3NS4wMjkwMTc4YzEuMjkxMzgyOS4xOTg1NDc2IDMuNTQ2MzgxNiAxLjEyMTM4MTYgMy42NzUxNzA4IDMuOTA2ODU4N2wuMDA1NzY3Ny4yNTI2ODgxdjExLjc5ODA3MDJoLTQuMjcxNzE1MXYtMi44MjUyMDg0aC00LjEzNjEwNTF2Mi44MjUyMDg0aC00LjQwNzMyNTF2LTExLjc5ODA3MDJjMC0xLjMxODQzMDYgMS4wMDQwODI2LTQuMDQ2ODQ5NSAzLjk0Njg5OS00LjE5NzQxMXptLTE5LjczOTg1MTktLjAwMjc1ODEgOC41ODAxODU3LjAwMDU3NDkuMjM4OTIzNC4wMzY3NDYyLjE0Nzk0NTkuMDI5NDE3LjI3NDExNjEuMDY1MzU4Mi4yMTI1MTk4LjA1OTkwNjguMjMyMzIyNS4wNzQ3MTgyYy4wNDAxNzkzLjAxMzc3MDQuMDgxMDQwMi4wMjgyMjAxLjEyMjUxMS4wNDMzODA0bC4yNTU1NjkxLjA5OTczNzYuMjY2NzYxOC4xMTgyODUyYzEuMzU2MDUxNS42NDAyODY2IDMuMDAyNjg1NSAyLjAzMjE1ODMgMy4wMDI2ODU1IDUuMDE2Mjk0IDAgMy4xODMwNzgxLTEuNTQ2NTI0MSA0LjUwMTQ4OTktMi42OTg4MzYyIDUuMDQyMDQ3N2wtLjIxMDk2NTEuMDkyNTA0NS0uMTk4ODgyOC4wNzU4NjU1LS4xODM5NTc3LjA2MDgxODYtLjE2NjE4OTguMDQ3MzYzNi0uMjA5NzUwOC4wNDkzMDAxLS4yNTk2NDkzLjA0MTgzNjMgNS4wMDU2ODk1IDUuMDUwNTgzNmgtNi4zNzQ5NTg5bC0zLjcyNjIwODMtMy44NjA4OTA2djMuODYwODkwNmgtNC4zMDk4MzE0em0yMi4wMDcwNjAzLTMuMDg2OTE5My40NDQ5OTE3LjQyMjY4MDctLjI0Mjk2OC4xNzc5ODgxLS4zNDgyMzMxLjI2OTMzNjUtLjE5OTg1NzMuMTYyOTIwNGMtNS4yMzk4OTY3LTMuODc0NTcxNS05LjY0NzY0NTQtNS40MjIxNDA5LTEzLjE5NzExOS01LjgxNTM1MzdsLS41MjU5ODA3LS4wNTA0NzA4Yy0xLjAzOTA3NDItLjA4NDIxMjYtMi4wMDA3ODYxLS4wNjk4MTAxLTIuODg0NDMwMi4wMTE1NDYybC0uNDM1MzEzMi4wNDYxMTExYy0uMDcxNDY2OC4wMDg1NjYyLS4xNDIzOTA3LjAxNzU2MDgtLjIxMjc3MTIuMDI2OTY1M2wtLjQxNTc1OTUuMDYxMjAwNS0uNDAyNjk5MS4wNzAxNjA3LS4zODk2MTk0LjA3ODI0MTUtLjM3NjUxOTguMDg1NDQyN2MtLjA2MTY2MDYuMDE0NzkxNy0uMTIyNzc0Ni4wMjk4NDY4LS4xODMzNDE1LjA0NTE0N2wtLjM1NjgzMzkuMDk0NTk1Ny0uMzQzNjg1NC4wOTk1OTgyLS4zMzA1MTc0LjEwMzcyMTMtLjMxNzMyOTcuMTA2OTY0OS0uMzA0MTIyNS4xMDkzMjktLjI5MDg5NTYuMTEwODEzNy0uNDExNTAwMi4xNjcwODAzLS41MDIxOTAyLjIxOTc2MTUtLjY1MzM3MzYuMzA4ODk1Ni0uNjEwMTA1LjMwMDU2NzVjLTQuNjk4ODg2NiAyLjYyMTk3ODgtNi44NTM5Mjk0IDYuODQ2MDcxMS03Ljc2ODIwMDIgMTAuODg2NjQ2M2wtLjEyMjMxMDEuNTc1NzgzOGMtLjAzODA1MDQuMTkxMzgzMy0uMDczNDI3OC4zODIxNTc2LS4xMDYyNzMyLjU3MjEzMDFsLS4wOTEwODA0LjU2NzMxOTYtLjA3NjczMTguNTYxMzUyMS0uMDYzMjI3NS41NTQyMjc3Yy0uMDA5NDU5NC4wOTE3MTM0LS4wMTgzOTE0LjE4MzA4MTctLjAyNjgxMzQuMjc0MDgwOGwtLjA0NDU1NC41NDEzNzIyLS4wMzMxNjAxLjUzMTM1NTZjLS4wMDQ2MjQyLjA4NzY2MDMtLjAwODgwODcuMTc0ODU1MS0uMDEyNTcxMy4yNjE1NjAybC0uMDE3NjUyNS41MTQxNjE4LS4wMDgzNjkzLjUwMTI1MzEuMDAwMDY5Ni40ODcxODc2LjAxNDA4MDYuNzAxODc3OS4wMjgzMTk1LjY2MzcyMjkuMDM5NzA4OS42MjE2NjM3LjA0ODI0OTIuNTc1Njk5OS4wNTM5NDAyLjUyNTgzMTcuMDc1ODQ4MS42MTY3ODc5LjA3NDE0NjIuNTExOTM3LjA4MDAwMDIuNDc4Mzc1Ni4wNjg1OTk1LjM1NDA4NjNoLTE3Ljg1NTMxN2wuMDU4NDktLjQ0MTcyNTMuMDQ2MzM2OC0uMjk1MTQ0OC4wNjMwMjA2LS4zNjQ3MjQyLjExMzI4MTktLjU4OTIwNjkuMTA5Mzc4Ny0uNTE1MDkwNy4xODIwMjY1LS43Nzg1MDg4LjE2NjEzNzYtLjY0ODY3NzIuMTI1OTIzMy0uNDYxNTk0My4yMTMwNDc0LS43MzM4MjI1LjE1ODk2NTUtLjUxNTUwNDIuMTczMTcyOC0uNTM1NDY5OS4xODc5NDYzLS41NTQ1MjI2LjIwMzI4NTctLjU3MjY2Mi4yMTkxOTEzLS41ODk4ODgzLjIzNTY2MjgtLjYwNjIwMTQuMjUyNzAwNC0uNjIxNjAxMmMuMDQzNTY4LS4xMDQ4MzI4LjA4Nzg2OTQtLjIxMDI2OTEuMTMyOTE2Mi0uMzE2MjkwMWwuMjc5MzE4LS42NDI5ODg3LjI5Nzc3MDctLjY1NjEwNTYuMzE2Nzg5NC0uNjY4MzA5Mi4zMzYzNzQtLjY3OTU5OTcuMzU2NTI0OS0uNjg5OTc3Yy4zNjY3ODg5LS42OTQ4NjExLjc2NDY1MjktMS40MDM5MTg0IDEuMTk2MTM5My0yLjEyMzA2MjQgNC43NDYzNTAxLTcuOTEwNTgzNCAxMi44Mzc3NDY5LTEzLjkwMDAyNTIgMTkuNDE0ODMyLTE0LjQ4NzY2ODYgNS4wNDUzODA2LS41MDU0MDk0IDkuODkyNTQzNi45Mjc2ODIzIDEzLjk0NDM2MjggMi44Nzk2NDM1bC42NDk5ODU4LjMyMDg1ODIuNjM1NDc2OC4zMjg2MDkzLjYyMDQwMTkuMzM1MDE1Yy4xMDIxMTI2LjA1NjI5NDkuMjAzNTczNi4xMTI4MDA3LjMwNDM3MS4xNjk0ODkybC41OTY3Mjg2LjM0MjEwMTMuNTgwMjM5LjM0NTE0MzkuNTYzMTgzNi4zNDY4NDExLjU0NTU2MjQuMzQ3MTkzMi41MjczNzUzLjM0NjE5OTkuNTA4NjIyMy4zNDM4NjE0Yy4wODMxNzYyLjA1NzA0MDYuMTY1NTQ3NS4xMTM5Mjc3LjI0NzEwMi4xNzA2MzMzbC40Nzk0MzIuMzM3ODMxMi40NTkyNjQ0LjMzMjEyOTQuNDM4NTMxLjMyNTA4MjUuNDE3MjMxNy4zMTY2OTAyLjU4NDY3MzYuNDU2MzU2OS41MzM1Njc0LjQyOTkwNzEuNjI4NjIzNy41MjQyMTc5LjY0NjM0MzIuNTYxNDAxNXptLTMwLjcwMDEwNTYgMTQuNTcxMzI0MyAyLjQ0MDk4MDEuODgxNDY1Yy4xMTMwMDgzLjg4NTIzMTkuMjczMTAzNCAxLjcyMzM3NzEuNDQxMDQ2NCAyLjQ4ODI3NjFsLjEwMTM5MzYuNDQ5OTQwNi0yLjcxMjIwMDEtLjk3MTg3MTctLjAzMzgzNDctLjIxMjE2MTgtLjA2NjA0MjEtLjQ3NTU4NDMtLjA2MTE2MDEtLjU0MTIxOTVjLS4wNDgwMjg1LS40Nzc0NjAyLS4wODc1ODE0LTEuMDE5OTAwMy0uMTEwMTgzMS0xLjYxODg0NDR6bTMxLjUwNjcyMzktNy42NjE5NjUyaC0xLjUxNDMxMTdjLS45NDAwMjM4IDAtMS4yMzkxMjI0LjQwOTc3Mi0xLjMzNDI5MDEuNjcwNTM2bC0uMDI4MjI2Ni4wOTUwMzM5Yy0uMDAzMjM3LjAxNDA3My0uMDA1ODI2Ni4wMjcxNTI2LS4wMDc4OTgyLjAzOTA3MzJsLS4wMDgxNTczLjA3MTUyMzktLjAwMDEyOTUgMy45MTUzODY0aDQuMTM2MTA1MWwtLjAwMTg1MzMtMy45MzQ3Njk0LS4wMTAyNjEyLS4wNjY5OTEzLS4wMjU3MjA3LS4wOTg3MjQxYy0uMDgzNTM1Ny0uMjU5MTUwNy0uMzUwOTEzNS0uNjkxMDY4Ni0xLjIwNTI1NjUtLjY5MTA2ODZ6bS01MC40OTIxMjQyLjMzOTAyNSAyLjU5OTE5MTcuOTQ5MjctLjQwODY0MS45NTE1ODM5Yy0uMjEyNDg4Mi40OTk0NjQ0LS40Mjc3Nzk2IDEuMDE0ODE0Mi0uNjAzMzU3NSAxLjQ1Nzc0NWwtLjExODA4NDkuMzAyODcxMi0yLjU5OTE5MTctLjk0OTI3LjEzMjM2NjItLjM0NTMwMzMuMjU4Mzg4OS0uNjM5MjAzNC4zMDU3MTExLS43MjgxOTc0LjMyNTQ4NzUtLjc1MzYyNDRjLjAzNjI0MzgtLjA4Mjc5NDMuMDcyMzQzNy0uMTY0ODgyMy4xMDgxMjk3LS4yNDU4NzE2em0zNS40NDUxMjA5LS4xNDM0NDQ5aC0zLjQ1Njg0Mzl2My42NTg4NjczaDMuNDM0Mzk2OGwuMDU0NzEwNi0uMDI1MzkyLjA4NjU5ODQtLjA0ODg5ODMuMTE0NzUzNi0uMDc4NjgyMmMuMjkyNjQyOC0uMjIxMTQ0OC43MzE2MDcxLS43MTQ5Nzk3LjczMTYwNzEtMS42ODc2ODQ3cy0uNDI4OTg3OC0xLjQ1NjU2MzQtLjcxNDk3OTctMS42NzEwNTczbC0uMTEyMTQ1NS0uMDc2MDc0MS0uMDg0NjMwMy0uMDQ2OTMwMXptLTE1LjQ0MjY0NTYtLjc2MDYyMTggMS42MjczMjAxIDEuMjg4Mjk1MWMtLjE4MDgxMzQuNzA1MTcyLS4zMTgyMzE1IDEuNDEwMzQ0LS40MTIyNTQ1IDIuMTE1NTE2bC0uMDYyMzgwNi41Mjg4NzktMS44MzA3MzUtMS40NDY1MDY3Yy4xODA4MTM0LS44MTM2Ni4zODQyMjg0LTEuNjQ5OTIxNy42NzgwNS0yLjQ4NjE4MzR6bTQuMDAwNDk1MS02LjMwNTg2NTEgMS4wMTcwNzUgMS41MzY5MTM0Yy0uMzk3Nzg5My40MTU4NzA3LS43NjY2NDg1LjgzMTc0MTMtMS4wOTUwMDU1IDEuMjcwNzU2MWwtLjIzODQ5MjguMzMzOTYyMy0xLjA4NDg4MDEtMS42MjczMjAxYy40MDY4My0uNTE5ODM4My44ODE0NjUxLTEuMDM5Njc2NyAxLjQwMTMwMzQtMS41MTQzMTE3em0tMTYuMTgyNzkzNi0zLjM0NTA0NjcgMS42MDQ3MTgzIDEuNDAxMzAzNGMtLjQwNjgzLjQyMzc4MTItLjgwMDk0NjUuODcyOTg5NC0xLjE3MjgxNDYgMS4zMjg1NTQybC0uMzY0MDk4Ny40NTY5Nzc1LTEuNzQwMzI4NC0xLjQ5MTcxMDFjLjUxOTgzODMtLjU2NTA0MTYgMS4wODQ4OC0xLjEzMDA4MzMgMS42NzI1MjM0LTEuNjk1MTI1em0yMi4zOTgyNTIxLS4wOTA0MDY3LjQ5NzIzNjYgMS40OTE3MTAxYy0uNTI0MzU4Ni4xNjI3MzItMS4wNDg3MTczLjM2ODg1OTItMS41NzMwNzYuNjA2ODA5NWwtLjM5MzI2OS4xODQyNDg4LS41MTk4Mzg0LTEuNTU5NTE1Yy41NjUwNDE3LS4yNDg2MTg0IDEuMjIwNDkwMS0uNDk3MjM2NyAxLjk4ODk0NjgtLjcyMzI1MzR6bTUuMjg4NzktLjU0MjQ0Yy41Nzg2MDI3LjAzNjE2MjcgMS4xNzE2NzA1LjEwMTI1NTUgMS43NzkyMDMzLjIwNjg1MDVsLjQ1ODM2MTguMDg2OTcxMi0uMDkwNDA2NyAxLjQwMTMwMzRjLS41OTY2ODQtLjEyNjU2OTQtMS4xOTMzNjgtLjIwOTc0MzUtMS43OTAwNTItLjI0OTUyMjRsLS40NDc1MTMtLjAyMTY5NzZ6bS0xOC41NTU5Njg2LTYuMjM4MDYwMSAxLjAxNzA3NSAxLjU1OTUxNWMtLjQ0MDczMjUuMjIwMzY2My0uODY4NzUxNi40NjYxNTk0LTEuMzAzMTI3NC43Mjc4NDQzbC0uNDM3MjAxLjI2NjYyOTEtMS4wMzk2NzY3LTEuNTgyMTE2N2MuNjEwMjQ1LS4zNjE2MjY3IDEuMTk3ODg4NC0uNjc4MDUgMS43NjI5MzAxLS45NzE4NzE3em0xOC41MTA3NjUzLjYzMjg0NjcuMDkwNDA2Ny0xLjQ5MTcxLjQ0MzUwNzMuMTMwNTU2NC4zODM5ODAzLjEyMDMyNTIuMzI4NTQ1OC4xMDk5MDc5LjI3NzIwMzcuMDk5MzA0OC4zMjg0OTE1LjEyODY2OS4yOTY0ODguMTMyNzExMi4xMzQxNDUxLjA2OTU4MzgtLjA5MDQwNjcgMS41MTQzMTE3Yy0uNDgyMTY4OS0uMTk1ODgxMS0uOTY0MzM3OC0uMzgxNzE3LTEuNDUzMjAzNS0uNTU3NTA3OHptLTguNTQzNDMwMS0yLjgyNTIwODQuNDUyMDMzMyAxLjM3ODcwMTdoLS4yMjYwMTY3Yy0uNDkxNTg2MiAwLS45ODMxNzI1LjAxMjcxMzQtMS40NzQ3NTg4LjA0NzY3NTRsLS40OTE1ODYyLjA0MjczMTMtLjQyOTQzMTctMS4zMzM0OTg0Yy43NDU4NTUtLjA5MDQwNjcgMS40NjkxMDg0LS4xMzU2MSAyLjE2OTc2MDEtLjEzNTYxeiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==" />
48
+ </a>
49
+ </nav>
50
+
51
+ <ul class="bottom-0 left-0 list-none mx-8 mt-0 mb-8 right-0 absolute">
52
+ <li><strong>Rails version:</strong> {{ railsVersion }}</li>
53
+ <li><strong>Ruby version:</strong> {{ rubyVersion }}</li>
54
+ </ul>
55
+ </div>
56
+ </template>
@@ -0,0 +1,34 @@
1
+ import './bootstrap'
2
+ import '../../assets/builds/tailwind.css'
3
+
4
+ import { createApp, h, DefineComponent } from 'vue';
5
+ import { createInertiaApp } from '@inertiajs/vue3';
6
+
7
+ async function resolvePageComponent<T>(path: string|string[], pages: Record<string, Promise<T> | (() => Promise<T>)>): Promise<T> {
8
+ for (const p of (Array.isArray(path) ? path : [path])) {
9
+ const page = pages[p]
10
+
11
+ if (typeof page === 'undefined') {
12
+ continue
13
+ }
14
+
15
+ return typeof page === 'function' ? page() : page
16
+ }
17
+
18
+ throw new Error(`Page not found: ${path}`)
19
+ }
20
+
21
+ const appName = import.meta.env.VITE_APP_NAME || 'Rails';
22
+
23
+ createInertiaApp({
24
+ title: (title) => `${title} - ${appName}`,
25
+ resolve: (name) => resolvePageComponent(`../Pages/${name}.vue`, import.meta.glob<DefineComponent>('../Pages/**/*.vue')),
26
+ setup({ el, App, props, plugin }) {
27
+ createApp({ render: () => h(App, props) })
28
+ .use(plugin)
29
+ .mount(el);
30
+ },
31
+ progress: {
32
+ color: '#4B5563',
33
+ },
34
+ });
@@ -0,0 +1,4 @@
1
+ import axios from 'axios';
2
+ window.axios = axios;
3
+
4
+ window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
@@ -0,0 +1,13 @@
1
+ import { PageProps as InertiaPageProps } from '@inertiajs/core';
2
+ import { AxiosInstance } from 'axios';
3
+ import { PageProps as AppPageProps } from './';
4
+
5
+ declare global {
6
+ interface Window {
7
+ axios: AxiosInstance;
8
+ }
9
+ }
10
+
11
+ declare module '@inertiajs/core' {
12
+ interface PageProps extends InertiaPageProps, AppPageProps {}
13
+ }
@@ -0,0 +1,12 @@
1
+ export interface User {
2
+ id: number;
3
+ name: string;
4
+ email: string;
5
+ email_verified_at: string;
6
+ }
7
+
8
+ export type PageProps<T extends Record<string, unknown> = Record<string, unknown>> = T & {
9
+ auth: {
10
+ user: User;
11
+ };
12
+ };
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+
7
+ <title><%= content_for(:title) || "Rails" %></title>
8
+
9
+ <%= csrf_meta_tags %>
10
+ <%= csp_meta_tag %>
11
+
12
+ <!-- Fonts -->
13
+ <link rel="preconnect" href="https://fonts.bunny.net">
14
+ <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
15
+
16
+ <%= vite_client_tag %>
17
+ <%= vite_typescript_tag 'application.ts', defer: true %>
18
+ </head>
19
+
20
+ <body class="font-sans antialiased">
21
+ <div class="min-h-screen bg-gray-100">
22
+ <%= yield %>
23
+ </div>
24
+ </body>
25
+ </html>
@@ -0,0 +1,22 @@
1
+ import defaultTheme from 'tailwindcss/defaultTheme';
2
+ import forms from '@tailwindcss/forms';
3
+
4
+ /** @type {import('tailwindcss').Config} */
5
+ export default {
6
+ content: [
7
+ './public/*.html',
8
+ './app/helpers/**/*.rb',
9
+ './app/views/**/*',
10
+ './app/javascript/**/*.vue',
11
+ ],
12
+
13
+ theme: {
14
+ extend: {
15
+ fontFamily: {
16
+ sans: ['Figtree', ...defaultTheme.fontFamily.sans],
17
+ },
18
+ },
19
+ },
20
+
21
+ plugins: [forms],
22
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "scripts": {
5
+ "dev": "vite",
6
+ "build": "tsc && bin/rails tailwindcss:build && vite build"
7
+ },
8
+ "devDependencies": {
9
+ "@headlessui/vue": "^1.7.22",
10
+ "@inertiajs/vue3": "^1.1.0",
11
+ "@tailwindcss/forms": "^0.5.7",
12
+ "@types/node": "^18.13.0",
13
+ "@vitejs/plugin-vue": "^5.0.5",
14
+ "autoprefixer": "^10.4.12",
15
+ "axios": "^1.6.4",
16
+ "postcss": "^8.4.31",
17
+ "tailwindcss": "^3.2.1",
18
+ "typescript": "^5.0.2",
19
+ "vite": "^5.2.11",
20
+ "vite-plugin-rails": "^0.5.0",
21
+ "vue": "^3.4.27",
22
+ "vue-tsc": "^2.0.21"
23
+ }
24
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowJs": true,
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "preserve",
7
+ "strict": true,
8
+ "isolatedModules": true,
9
+ "target": "ESNext",
10
+ "esModuleInterop": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "noEmit": true,
13
+ "skipLibCheck": true,
14
+ "paths": {
15
+ "@/*": ["./app/javascript/*"]
16
+ }
17
+ },
18
+ "include": ["app/javascript/**/*.ts", "app/javascript/**/*.d.ts", "app/javascript/**/*.vue"]
19
+ }
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vite'
2
+ import Rails from 'vite-plugin-rails'
3
+ import vue from '@vitejs/plugin-vue'
4
+
5
+ export default defineConfig({
6
+ resolve: {
7
+ dedupe: ['axios']
8
+ },
9
+ plugins: [
10
+ Rails(),
11
+ vue(),
12
+ ],
13
+ })