@ma7moudsalama/falak-app 1.0.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 (37) hide show
  1. package/README.md +378 -0
  2. package/bin/falak.js +157 -0
  3. package/index.js +5 -0
  4. package/lib/scaffold.js +23 -0
  5. package/package.json +46 -0
  6. package/template/_env.example +34 -0
  7. package/template/_gitignore +8 -0
  8. package/template/firebase-rules.json +36 -0
  9. package/template/index.html +21 -0
  10. package/template/package.json +36 -0
  11. package/template/postcss.config.js +6 -0
  12. package/template/public/favicon.svg +5 -0
  13. package/template/src/App.vue +95 -0
  14. package/template/src/assets/main.css +100 -0
  15. package/template/src/components/layout/AppLayout.vue +163 -0
  16. package/template/src/composables/useAuth.js +393 -0
  17. package/template/src/composables/useCrypto.js +153 -0
  18. package/template/src/composables/useDatabase.js +341 -0
  19. package/template/src/composables/useGroq.js +237 -0
  20. package/template/src/composables/usePaymob.js +392 -0
  21. package/template/src/firebase/index.js +87 -0
  22. package/template/src/i18n/index.js +66 -0
  23. package/template/src/i18n/locales/ar.json +121 -0
  24. package/template/src/i18n/locales/en.json +121 -0
  25. package/template/src/main.js +59 -0
  26. package/template/src/router/index.js +127 -0
  27. package/template/src/stores/auth.js +14 -0
  28. package/template/src/views/AdminView.vue +67 -0
  29. package/template/src/views/DashboardView.vue +253 -0
  30. package/template/src/views/HomeView.vue +13 -0
  31. package/template/src/views/NotFoundView.vue +8 -0
  32. package/template/src/views/ProfileView.vue +134 -0
  33. package/template/src/views/auth/ForgotView.vue +57 -0
  34. package/template/src/views/auth/LoginView.vue +169 -0
  35. package/template/src/views/auth/RegisterView.vue +103 -0
  36. package/template/tailwind.config.js +41 -0
  37. package/template/vite.config.js +29 -0
@@ -0,0 +1,134 @@
1
+ <template>
2
+ <AppLayout>
3
+ <div class="max-w-2xl mx-auto space-y-6">
4
+ <h2 class="text-xl font-bold text-gray-900 dark:text-white">{{ $t('user.profile') }}</h2>
5
+
6
+ <!-- Profile card -->
7
+ <div class="card space-y-6">
8
+ <!-- Avatar -->
9
+ <div class="flex items-center gap-4">
10
+ <Avatar
11
+ :image="currentUser?.photoURL || undefined"
12
+ :label="!currentUser?.photoURL ? initials : undefined"
13
+ shape="circle"
14
+ size="xlarge"
15
+ class="shrink-0"
16
+ />
17
+ <div>
18
+ <p class="font-semibold text-gray-900 dark:text-white">{{ currentUser?.displayName || '—' }}</p>
19
+ <p class="text-sm text-gray-500">{{ currentUser?.email }}</p>
20
+ <span class="badge-info badge mt-1">{{ $t(`user.roles.${userRole}`) }}</span>
21
+ </div>
22
+ </div>
23
+
24
+ <!-- Edit form -->
25
+ <form @submit.prevent="saveProfile" class="space-y-4">
26
+ <div>
27
+ <label class="label">{{ $t('auth.displayName') }}</label>
28
+ <InputText v-model="form.displayName" class="w-full" />
29
+ </div>
30
+ <div>
31
+ <label class="label">{{ $t('auth.email') }}</label>
32
+ <InputText :value="currentUser?.email" disabled class="w-full opacity-60" />
33
+ </div>
34
+ <div class="flex justify-end">
35
+ <Button type="submit" :label="$t('common.save')" :loading="isLoading" />
36
+ </div>
37
+ </form>
38
+ </div>
39
+
40
+ <!-- Change password card -->
41
+ <div class="card space-y-4">
42
+ <h3 class="font-semibold text-gray-900 dark:text-white">{{ $t('user.changePassword') }}</h3>
43
+
44
+ <Message v-if="pwError" severity="error" :closable="false">{{ pwError }}</Message>
45
+ <Message v-if="pwSuccess" severity="success" :closable="false">Password updated!</Message>
46
+
47
+ <form @submit.prevent="savePassword" class="space-y-4">
48
+ <div>
49
+ <label class="label">Current Password</label>
50
+ <Password v-model="pw.current" :feedback="false" toggleMask class="w-full" inputClass="w-full" />
51
+ </div>
52
+ <div>
53
+ <label class="label">New Password</label>
54
+ <Password v-model="pw.next" toggleMask class="w-full" inputClass="w-full" />
55
+ </div>
56
+ <div class="flex justify-end">
57
+ <Button type="submit" :label="$t('common.save')" :loading="isLoading" severity="secondary" />
58
+ </div>
59
+ </form>
60
+ </div>
61
+
62
+ <!-- Danger zone -->
63
+ <div class="card border-red-200 dark:border-red-900 space-y-3">
64
+ <h3 class="font-semibold text-red-600">Danger Zone</h3>
65
+ <p class="text-sm text-gray-500">{{ $t('user.deleteAccountConfirm') }}</p>
66
+ <Button
67
+ :label="$t('user.deleteAccount')"
68
+ severity="danger"
69
+ outlined
70
+ @click="confirmDelete"
71
+ />
72
+ </div>
73
+ </div>
74
+ </AppLayout>
75
+ </template>
76
+
77
+ <script setup>
78
+ import { reactive, ref, computed } from 'vue'
79
+ import { useRouter } from 'vue-router'
80
+ import { useI18n } from 'vue-i18n'
81
+ import { useToast } from 'primevue/usetoast'
82
+ import { useConfirm } from 'primevue/useconfirm'
83
+ import InputText from 'primevue/inputtext'
84
+ import Password from 'primevue/password'
85
+ import Button from 'primevue/button'
86
+ import Avatar from 'primevue/avatar'
87
+ import Message from 'primevue/message'
88
+ import AppLayout from '@/components/layout/AppLayout.vue'
89
+ import { useAuth } from '@/composables/useAuth.js'
90
+
91
+ const { t } = useI18n()
92
+ const router = useRouter()
93
+ const toast = useToast()
94
+ const confirm = useConfirm()
95
+ const { currentUser, userRole, isLoading, updateUserProfile, changePassword, deleteAccount } = useAuth()
96
+
97
+ const form = reactive({ displayName: currentUser.value?.displayName || '' })
98
+ const pw = reactive({ current: '', next: '' })
99
+ const pwError = ref('')
100
+ const pwSuccess = ref(false)
101
+
102
+ const initials = computed(() => {
103
+ const n = currentUser.value?.displayName || currentUser.value?.email || '?'
104
+ return n.slice(0, 2).toUpperCase()
105
+ })
106
+
107
+ async function saveProfile() {
108
+ const result = await updateUserProfile({ displayName: form.displayName })
109
+ if (result.success) toast.add({ severity: 'success', summary: t('common.success'), life: 2000 })
110
+ }
111
+
112
+ async function savePassword() {
113
+ pwError.value = ''
114
+ pwSuccess.value = false
115
+ const result = await changePassword({ currentPassword: pw.current, newPassword: pw.next })
116
+ if (result.success) { pwSuccess.value = true; pw.current = ''; pw.next = '' }
117
+ else pwError.value = result.error
118
+ }
119
+
120
+ function confirmDelete() {
121
+ confirm.require({
122
+ message: t('user.deleteAccountConfirm'),
123
+ header: t('user.deleteAccount'),
124
+ icon: 'pi pi-exclamation-triangle',
125
+ rejectLabel: t('common.cancel'),
126
+ acceptLabel: t('common.delete'),
127
+ acceptClass: 'p-button-danger',
128
+ accept: async () => {
129
+ await deleteAccount()
130
+ router.push('/')
131
+ }
132
+ })
133
+ }
134
+ </script>
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <div class="min-h-screen bg-gradient-to-br from-primary-50 to-blue-100 dark:from-gray-950 dark:to-gray-900
3
+ flex items-center justify-center p-4">
4
+ <div class="w-full max-w-md">
5
+ <div class="card">
6
+ <div class="text-center mb-8">
7
+ <div class="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-primary-600 mb-4">
8
+ <i class="pi pi-lock-open text-white text-2xl" />
9
+ </div>
10
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-white">{{ $t('auth.resetPassword') }}</h1>
11
+ <p class="text-sm text-gray-500 mt-1">{{ $t('auth.resetPasswordDesc') }}</p>
12
+ </div>
13
+
14
+ <Message v-if="sent" severity="success" :closable="false" class="mb-4">
15
+ ✅ Reset link sent! Check your inbox.
16
+ </Message>
17
+ <Message v-if="authError" severity="error" :closable="false" class="mb-4">
18
+ {{ authError }}
19
+ </Message>
20
+
21
+ <form v-if="!sent" @submit.prevent="handleReset" class="space-y-4">
22
+ <div>
23
+ <label class="label">{{ $t('auth.email') }}</label>
24
+ <InputText v-model="email" type="email" class="w-full" :placeholder="$t('auth.email')" />
25
+ </div>
26
+ <Button type="submit" :label="$t('auth.resetPassword')" :loading="isLoading" class="w-full" />
27
+ </form>
28
+
29
+ <RouterLink to="/login" class="flex items-center justify-center gap-2 text-sm text-primary-600
30
+ hover:underline mt-6">
31
+ <i class="pi pi-arrow-left text-xs" />
32
+ {{ $t('auth.backToLogin') }}
33
+ </RouterLink>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup>
40
+ import { ref } from 'vue'
41
+ import { useI18n } from 'vue-i18n'
42
+ import InputText from 'primevue/inputtext'
43
+ import Button from 'primevue/button'
44
+ import Message from 'primevue/message'
45
+ import { useAuth } from '@/composables/useAuth.js'
46
+
47
+ const { t } = useI18n()
48
+ const { resetPassword, authError, isLoading } = useAuth()
49
+ const email = ref('')
50
+ const sent = ref(false)
51
+
52
+ async function handleReset() {
53
+ if (!email.value) return
54
+ const result = await resetPassword(email.value)
55
+ if (result.success) sent.value = true
56
+ }
57
+ </script>
@@ -0,0 +1,169 @@
1
+ <template>
2
+ <div class="min-h-screen bg-gradient-to-br from-primary-50 to-blue-100 dark:from-gray-950 dark:to-gray-900
3
+ flex items-center justify-center p-4">
4
+ <div class="w-full max-w-md">
5
+ <!-- Card -->
6
+ <div class="card">
7
+ <!-- Header -->
8
+ <div class="text-center mb-8">
9
+ <div class="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-primary-600 mb-4">
10
+ <span class="text-white font-bold text-2xl">F</span>
11
+ </div>
12
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-white">{{ $t('nav.login') }}</h1>
13
+ <p class="text-sm text-gray-500 mt-1">{{ $t('app.tagline') }}</p>
14
+ </div>
15
+
16
+ <!-- Error alert -->
17
+ <Message v-if="authError" severity="error" :closable="false" class="mb-4">
18
+ {{ authError }}
19
+ </Message>
20
+
21
+ <!-- Email/Password Form -->
22
+ <form @submit.prevent="handleLogin" class="space-y-4">
23
+ <div>
24
+ <label class="label">{{ $t('auth.email') }}</label>
25
+ <InputText
26
+ v-model="form.email"
27
+ type="email"
28
+ :placeholder="$t('auth.email')"
29
+ class="w-full"
30
+ :invalid="!!errors.email"
31
+ autocomplete="email"
32
+ />
33
+ <small v-if="errors.email" class="text-red-500 text-xs mt-1">{{ errors.email }}</small>
34
+ </div>
35
+
36
+ <div>
37
+ <div class="flex items-center justify-between mb-1">
38
+ <label class="label !mb-0">{{ $t('auth.password') }}</label>
39
+ <RouterLink to="/forgot-password" class="text-xs text-primary-600 hover:underline">
40
+ {{ $t('auth.forgotPassword') }}
41
+ </RouterLink>
42
+ </div>
43
+ <Password
44
+ v-model="form.password"
45
+ :placeholder="$t('auth.password')"
46
+ :feedback="false"
47
+ toggleMask
48
+ class="w-full"
49
+ inputClass="w-full"
50
+ :invalid="!!errors.password"
51
+ autocomplete="current-password"
52
+ />
53
+ <small v-if="errors.password" class="text-red-500 text-xs mt-1">{{ errors.password }}</small>
54
+ </div>
55
+
56
+ <div class="flex items-center gap-2">
57
+ <Checkbox v-model="form.remember" inputId="remember" binary />
58
+ <label for="remember" class="text-sm text-gray-600 dark:text-gray-400 cursor-pointer">
59
+ {{ $t('auth.rememberMe') }}
60
+ </label>
61
+ </div>
62
+
63
+ <Button
64
+ type="submit"
65
+ :label="$t('nav.login')"
66
+ :loading="isLoading"
67
+ class="w-full"
68
+ />
69
+ </form>
70
+
71
+ <!-- Divider -->
72
+ <div class="flex items-center gap-3 my-6">
73
+ <div class="flex-1 border-t" />
74
+ <span class="text-xs text-gray-400">{{ $t('auth.orContinueWith') }}</span>
75
+ <div class="flex-1 border-t" />
76
+ </div>
77
+
78
+ <!-- OAuth Buttons -->
79
+ <div class="grid grid-cols-2 gap-3">
80
+ <Button
81
+ @click="handleGoogle"
82
+ :loading="isLoading"
83
+ outlined
84
+ class="w-full"
85
+ >
86
+ <template #icon>
87
+ <svg class="w-4 h-4" viewBox="0 0 24 24">
88
+ <path fill="#4285F4" 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"/>
89
+ <path fill="#34A853" 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"/>
90
+ <path fill="#FBBC05" 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"/>
91
+ <path fill="#EA4335" 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"/>
92
+ </svg>
93
+ </template>
94
+ Google
95
+ </Button>
96
+
97
+ <Button
98
+ @click="handleFacebook"
99
+ :loading="isLoading"
100
+ outlined
101
+ class="w-full"
102
+ >
103
+ <template #icon>
104
+ <svg class="w-4 h-4" fill="#1877F2" viewBox="0 0 24 24">
105
+ <path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
106
+ </svg>
107
+ </template>
108
+ Facebook
109
+ </Button>
110
+ </div>
111
+
112
+ <!-- Register link -->
113
+ <p class="text-center text-sm text-gray-600 dark:text-gray-400 mt-6">
114
+ {{ $t('auth.noAccount') }}
115
+ <RouterLink to="/register" class="text-primary-600 font-medium hover:underline ms-1">
116
+ {{ $t('nav.register') }}
117
+ </RouterLink>
118
+ </p>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </template>
123
+
124
+ <script setup>
125
+ import { reactive, ref } from 'vue'
126
+ import { useRouter, useRoute } from 'vue-router'
127
+ import { useI18n } from 'vue-i18n'
128
+ import { useToast } from 'primevue/usetoast'
129
+ import InputText from 'primevue/inputtext'
130
+ import Password from 'primevue/password'
131
+ import Button from 'primevue/button'
132
+ import Checkbox from 'primevue/checkbox'
133
+ import Message from 'primevue/message'
134
+ import { useAuth } from '@/composables/useAuth.js'
135
+
136
+ const { t } = useI18n()
137
+ const router = useRouter()
138
+ const route = useRoute()
139
+ const toast = useToast()
140
+ const { login, loginWithGoogle, loginWithFacebook, authError, isLoading } = useAuth()
141
+
142
+ const form = reactive({ email: '', password: '', remember: false })
143
+ const errors = reactive({ email: '', password: '' })
144
+
145
+ function validate() {
146
+ errors.email = !form.email ? t('common.required') : ''
147
+ errors.password = !form.password ? t('common.required') : ''
148
+ return !errors.email && !errors.password
149
+ }
150
+
151
+ async function handleLogin() {
152
+ if (!validate()) return
153
+ const result = await login({ email: form.email, password: form.password })
154
+ if (result.success) {
155
+ toast.add({ severity: 'success', summary: t('auth.loginSuccess'), life: 2000 })
156
+ router.push(route.query.redirect || '/dashboard')
157
+ }
158
+ }
159
+
160
+ async function handleGoogle() {
161
+ const result = await loginWithGoogle()
162
+ if (result.success) router.push(route.query.redirect || '/dashboard')
163
+ }
164
+
165
+ async function handleFacebook() {
166
+ const result = await loginWithFacebook()
167
+ if (result.success) router.push(route.query.redirect || '/dashboard')
168
+ }
169
+ </script>
@@ -0,0 +1,103 @@
1
+ <template>
2
+ <div class="min-h-screen bg-gradient-to-br from-primary-50 to-blue-100 dark:from-gray-950 dark:to-gray-900
3
+ flex items-center justify-center p-4">
4
+ <div class="w-full max-w-md">
5
+ <div class="card">
6
+ <!-- Header -->
7
+ <div class="text-center mb-8">
8
+ <div class="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-primary-600 mb-4">
9
+ <span class="text-white font-bold text-2xl">F</span>
10
+ </div>
11
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-white">{{ $t('nav.register') }}</h1>
12
+ </div>
13
+
14
+ <Message v-if="authError" severity="error" :closable="false" class="mb-4">
15
+ {{ authError }}
16
+ </Message>
17
+
18
+ <form @submit.prevent="handleRegister" class="space-y-4">
19
+ <div>
20
+ <label class="label">{{ $t('auth.displayName') }}</label>
21
+ <InputText v-model="form.displayName" type="text" class="w-full"
22
+ :placeholder="$t('auth.displayName')" :invalid="!!errors.displayName" />
23
+ <small v-if="errors.displayName" class="text-red-500 text-xs">{{ errors.displayName }}</small>
24
+ </div>
25
+
26
+ <div>
27
+ <label class="label">{{ $t('auth.email') }}</label>
28
+ <InputText v-model="form.email" type="email" class="w-full"
29
+ :placeholder="$t('auth.email')" :invalid="!!errors.email" />
30
+ <small v-if="errors.email" class="text-red-500 text-xs">{{ errors.email }}</small>
31
+ </div>
32
+
33
+ <div>
34
+ <label class="label">{{ $t('auth.password') }}</label>
35
+ <Password v-model="form.password" toggleMask class="w-full" inputClass="w-full"
36
+ :placeholder="$t('auth.password')" :invalid="!!errors.password" />
37
+ <small v-if="errors.password" class="text-red-500 text-xs">{{ errors.password }}</small>
38
+ </div>
39
+
40
+ <div>
41
+ <label class="label">{{ $t('auth.confirmPassword') }}</label>
42
+ <Password v-model="form.confirm" :feedback="false" toggleMask class="w-full" inputClass="w-full"
43
+ :placeholder="$t('auth.confirmPassword')" :invalid="!!errors.confirm" />
44
+ <small v-if="errors.confirm" class="text-red-500 text-xs">{{ errors.confirm }}</small>
45
+ </div>
46
+
47
+ <Button type="submit" :label="$t('nav.register')" :loading="isLoading" class="w-full" />
48
+ </form>
49
+
50
+ <p class="text-center text-sm text-gray-600 dark:text-gray-400 mt-6">
51
+ {{ $t('auth.hasAccount') }}
52
+ <RouterLink to="/login" class="text-primary-600 font-medium hover:underline ms-1">
53
+ {{ $t('nav.login') }}
54
+ </RouterLink>
55
+ </p>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </template>
60
+
61
+ <script setup>
62
+ import { reactive } from 'vue'
63
+ import { useRouter } from 'vue-router'
64
+ import { useI18n } from 'vue-i18n'
65
+ import { useToast } from 'primevue/usetoast'
66
+ import InputText from 'primevue/inputtext'
67
+ import Password from 'primevue/password'
68
+ import Button from 'primevue/button'
69
+ import Message from 'primevue/message'
70
+ import { useAuth } from '@/composables/useAuth.js'
71
+ import { useSubscription } from '@/composables/usePaymob.js'
72
+
73
+ const { t } = useI18n()
74
+ const router = useRouter()
75
+ const toast = useToast()
76
+ const { register, authError, isLoading } = useAuth()
77
+ const { initFreeplan } = useSubscription()
78
+
79
+ const form = reactive({ displayName: '', email: '', password: '', confirm: '' })
80
+ const errors = reactive({ displayName: '', email: '', password: '', confirm: '' })
81
+
82
+ function validate() {
83
+ errors.displayName = !form.displayName ? t('common.required') : ''
84
+ errors.email = !form.email ? t('common.required') : ''
85
+ errors.password = form.password.length < 8 ? t('auth.weakPassword') : ''
86
+ errors.confirm = form.password !== form.confirm ? t('auth.passwordMismatch') : ''
87
+ return !Object.values(errors).some(Boolean)
88
+ }
89
+
90
+ async function handleRegister() {
91
+ if (!validate()) return
92
+ const result = await register({
93
+ email: form.email,
94
+ password: form.password,
95
+ displayName: form.displayName
96
+ })
97
+ if (result.success) {
98
+ await initFreeplan(result.user.uid)
99
+ toast.add({ severity: 'success', summary: t('auth.registerSuccess'), life: 4000 })
100
+ router.push('/dashboard')
101
+ }
102
+ }
103
+ </script>
@@ -0,0 +1,41 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: [
4
+ './index.html',
5
+ './src/**/*.{vue,js,ts,jsx,tsx}',
6
+ './node_modules/primevue/**/*.{vue,js,ts}'
7
+ ],
8
+ darkMode: 'class',
9
+ theme: {
10
+ extend: {
11
+ colors: {
12
+ primary: {
13
+ 50: '#eff6ff',
14
+ 100: '#dbeafe',
15
+ 200: '#bfdbfe',
16
+ 300: '#93c5fd',
17
+ 400: '#60a5fa',
18
+ 500: '#3b82f6',
19
+ 600: '#2563eb',
20
+ 700: '#1d4ed8',
21
+ 800: '#1e40af',
22
+ 900: '#1e3a8a',
23
+ 950: '#172554'
24
+ }
25
+ },
26
+ fontFamily: {
27
+ sans: ['Inter', 'ui-sans-serif', 'system-ui'],
28
+ arabic: ['Cairo', 'ui-sans-serif', 'system-ui']
29
+ }
30
+ }
31
+ },
32
+ plugins: [
33
+ // Uncomment if needed:
34
+ // require('@tailwindcss/forms'),
35
+ // require('@tailwindcss/typography'),
36
+ ],
37
+ // Prevent Tailwind from conflicting with PrimeVue
38
+ corePlugins: {
39
+ preflight: true
40
+ }
41
+ }
@@ -0,0 +1,29 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+ import path from 'path'
4
+
5
+ export default defineConfig({
6
+ plugins: [vue()],
7
+ resolve: {
8
+ alias: {
9
+ '@': path.resolve(__dirname, './src')
10
+ }
11
+ },
12
+ server: {
13
+ port: 5173,
14
+ host: true
15
+ },
16
+ build: {
17
+ outDir: 'dist',
18
+ sourcemap: false,
19
+ rollupOptions: {
20
+ output: {
21
+ manualChunks: {
22
+ 'firebase': ['firebase/app', 'firebase/auth', 'firebase/database'],
23
+ 'primevue': ['primevue'],
24
+ 'vendor': ['vue', 'vue-router', 'pinia', 'vue-i18n']
25
+ }
26
+ }
27
+ }
28
+ }
29
+ })