Compare commits

...

30 Commits

Author SHA1 Message Date
f5c54cd632 haditha pull 2025-03-18 17:56:31 +03:30
mustafa-rezae
3ee869a66b Work on hadith 2025-03-18 16:02:36 +03:30
mustafa-rezae
b21c8549ab Merge branch 'main' of https://git2.tavasi.ir/front/base_ui into main 2025-03-15 13:42:09 +03:30
mustafa-rezae
851324eb0b update files. 2025-03-15 13:41:50 +03:30
mustafa-rezae
1ccf9e09ba Update files. 2025-03-15 13:41:45 +03:30
Mehdi104797
f2fee4c45f . 2025-03-15 08:03:14 +03:30
mustafa-rezae
f00f10ca3d Update git submodule 2025-03-11 15:09:49 +03:30
mustafa-rezae
0f155d2f04 Update and debug 2025-03-11 13:46:19 +03:30
mustafa-rezae
1cae9689c5 Update nuxt.config 2025-03-06 16:31:07 +03:30
mustafa-rezae
d1373fb94c Remove extra files
Update nuxt.config
2025-03-06 14:59:42 +03:30
mustafa-rezae
be39f7377b Merge branch 'shadi/develop' of https://git2.tavasi.ir/front/base_ui into main 2025-03-04 09:14:30 +03:30
mustafa-rezae
16bb8986e0 add new fonts 2025-03-03 15:58:01 +03:30
Mehdi104797
4f6e31fed7 تغییرات 2025-03-03 13:25:54 +03:30
Mehdi104797
5ee08b4c7a . 2025-03-02 13:05:10 +03:30
Mehdi104797
5f2c8ba4e0 . 2025-03-02 11:40:02 +03:30
Mehdi104797
5100db79da . 2025-03-02 11:39:37 +03:30
mustafa-rezae
ab00e04dd8 add images form hadith 2025-03-01 16:14:53 +03:30
Mehdi104797
c71b5737cd . 2025-03-01 15:57:09 +03:30
Mehdi104797
95b2b62095 . 2025-03-01 13:16:55 +03:30
mustafa-rezae
ca6372b438 Merge branch 'shadi/develop' of https://git2.tavasi.ir/front/base_ui into main 2025-02-27 13:43:52 +03:30
mustafa-rezae
cab508f69a Merge branch 'shadi/develop' of https://git2.tavasi.ir/front/base_ui into main 2025-02-27 13:19:29 +03:30
mustafa-rezae
19a83a6367 update hadith 2025-02-27 13:19:13 +03:30
Mehdi104797
47d58ecdf4 build-hadith 2025-02-27 10:13:20 +03:30
Mehdi104797
fc1d8b6109 build-hadith 2025-02-27 09:16:48 +03:30
Mehdi104797
0d0fd2dacd . 2025-02-27 08:55:27 +03:30
Mehdi104797
606d3bdc05 build-hadith 2025-02-27 08:55:06 +03:30
Mehdi104797
436ab75d21 . 2025-02-27 06:43:31 +03:30
Mehdi104797
61f02d6883 Merge branch 'main' of https://git2.tavasi.ir/front/base_ui into shadi/develop 2025-02-27 06:43:06 +03:30
Mehdi104797
e7bbe4d628 تغییرات 2025-02-26 16:31:34 +03:30
Mehdi104797
48320d52af تغییرات داشبورد 2025-02-26 08:28:43 +03:30
118 changed files with 2236 additions and 816 deletions

View File

@ -1,5 +1,5 @@
VITE_API_NAME=api/
VITE_BASE_URL=http://192.168.23.60/
NUXT_PUBLIC_API_NAME=api/
NUXT_PUBLIC_BASE_URL=http://192.168.23.60/
VITE_APP_NAME=Hadith
@ -7,9 +7,9 @@ VITE_APP_NAME=Hadith
### Relative pathes for systems sass addressing ###
# 0 : majles.tavasi.ir 1: qanon.parliran.ir
VITE_BUILD_STATE=0
VITE_BUILD_NAME=hadith
VITE_BUILD_NAME=haditha
VITE_IDB_NAME=hadith
VITE_IDB_NAME=haditha
VITE_IDB_VERSION=1
VITE_ENABLE_IDB=1

3
.gitmodules vendored
View File

@ -7,3 +7,6 @@
[submodule "systems/hadith_ui"]
path = systems/hadith_ui
url = https://git2.tavasi.ir/front/hadith_ui.git
[submodule "systems/chat_ui"]
path = systems/chat_ui
url = https://git2.tavasi.ir/front/chat_ui.git

289
COMPOSITION_API.md Normal file
View File

@ -0,0 +1,289 @@
1. Organize Your Code into Logical Sections
Structure your component script into clear sections to improve readability. A common approach is to group related logic together.
Example Structure:
<script setup>
// 1. Imports
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
// 2. Reactive State
const count = ref(0)
const name = ref('')
// 3. Computed Properties
const doubledCount = computed(() => count.value * 2)
// 4. Functions / Methods
function increment() {
count.value++
}
// 5. Lifecycle Hooks
onMounted(() => {
console.log('Component mounted')
})
// 6. Watchers
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})
</script>
---
2. Use script setup Syntax
The <script setup> syntax is the recommended way to write Composition API components. It provides a more concise and intuitive syntax.
Example:
<script setup>
const message = ref('Hello, Vue 3!')
</script>
<template>
<p>{{ message }}</p>
</template>
---
3. Use Descriptive Variable and Function Names
Choose meaningful names for variables, functions, and computed properties to make your code self-documenting.
Example:
// Bad
const x = ref(0)
// Good
const userCount = ref(0)
---
4. Extract Reusable Logic into Composables
Move reusable logic into composables (custom hooks) to keep your components clean and promote code reuse.
Example Composable:
// composables/useCounter.js
import { ref } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
function increment() {
count.value++
}
function reset() {
count.value = initialValue
}
return {
count,
increment,
reset,
}
}
Usage in a Component:
<script setup>
import { useCounter } from '~/composables/useCounter'
const { count, increment } = useCounter()
</script>
<template>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</template>
---
5. Use ref for Primitive Values and reactive for Objects
- Use ref for primitive values (e.g., numbers, strings, booleans).
- Use reactive for objects or arrays.
Example:
const count = ref(0) // Primitive value
const user = reactive({ name: 'John', age: 30 }) // Object
---
6. Use Computed Properties for Derived State
Use computed to create reactive properties that depend on other state. This ensures the derived state is always up-to-date.
Example:
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
---
7. Use Watchers Sparingly
Watchers (watch and watchEffect) are powerful but can make your code harder to understand if overused. Prefer computed properties or event-driven updates where possible.
Example:
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})
---
8. Group Related Logic Together
Keep related reactive state, computed properties, and functions together to make your code easier to follow.
Example:
// User-related logic
const user = reactive({ name: 'John', age: 30 })
const isAdult = computed(() => user.age >= 18)
function updateUser(newName) {
user.name = newName
}
-----------
9. Use Lifecycle Hooks for Side Effects
Use lifecycle hooks (onMounted, onUnmounted, etc.) for side effects like fetching data or setting up event listeners.
Example:
onMounted(() => {
console.log('Component mounted')
fetchData()
})
onUnmounted(() => {
console.log('Component unmounted')
cleanup()
})
----------------
10. Use TypeScript for Better Type Safety
If your project uses TypeScript, leverage it to add type safety to your components.
Example:
<script setup lang="ts">
interface User {
name: string
age: number
}
const user = ref<User>({ name: 'John', age: 30 })
</script>
--------------
11. Keep Templates Clean
Avoid putting too much logic in your template. Instead, move complex logic into the script or composables.
Example:
<template>
<div>
<p>{{ fullName }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script setup>
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
function increment() {
// Logic here
}
</script>
------------
12. Use provide and inject for Prop Drilling
Avoid prop drilling by using provide and inject to share state across deeply nested components.
Example:
// Parent component
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)
// Child component
import { inject } from 'vue'
const theme = inject('theme')
-----------------
13. Use v-model for Two-Way Binding
Use v-model to simplify two-way binding between parent and child components.
Example:
<!-- Parent component -->
<template>
<ChildComponent v-model:name="userName" />
</template>
<script setup>
const userName = ref('John')
</script>
<!-- Child component -->
<template>
<input :value="name" @input="$emit('update:name', $event.target.value)" />
</template>
<script setup>
defineProps(['name'])
defineEmits(['update:name'])
</script>
----------
14. Use defineProps and defineEmits for TypeScript Support
When using TypeScript, use defineProps and defineEmits to define props and emits with type safety.
Example:
<script setup lang="ts">
const props = defineProps<{
title: string
count: number
}>()
const emit = defineEmits<{
(event: 'update:count', value: number): void
}>()
</script>
-------------
15. Use Scoped Styles
Use scoped styles (<style scoped>) to ensure styles are component-specific and dont leak into other components.
Example:
<style scoped>
.button {
background-color: blue;
}
</style>
------------------
16. Use Environment Variables
Use environment variables for configuration (e.g., API URLs) to keep your code flexible and secure.
Example:
const apiUrl = import.meta.env.VITE_API_URL
-------------
17. Write Unit Tests
Write unit tests for your components and composables to ensure they work as expected.
Example:
import { render } from '@testing-library/vue'
import MyComponent from '@/components/MyComponent.vue'
test('renders correctly', () => {
const { getByText } = render(MyComponent)
expect(getByText('Hello, Vue 3!')).toBeInTheDocument()
})

52
GIT_SUBMODULE.md Normal file
View File

@ -0,0 +1,52 @@
Add new repo:
git submodule add https://git2.tavasi.ir/front/chat_ui.git systems/chat_ui -f
Cloning a Repository with Submodules
If you clone a repository that contains submodules, you'll need to initialize and update the submodules:
git clone <repository-url>
cd <repository-directory>
git submodule init
git submodule update
Or, you can do it in one step:
git clone --recurse-submodules <repository-url>
Updating Submodules
To update a submodule to the latest commit on its branch:
git submodule update --remote
## How to Remove an Existing Submodule
If the path was previously added as a submodule, you need to remove it completely before re-adding it. Follow these steps:
1. Remove the submodule entry from .gitmodules.
2. Remove the submodule from the working tree and index: git rm --cached <path-to-submodule>.
3. Delete the submodule directory: rm -rf <path-to-submodule>.
4. Stage the .gitmodules changes: git add .gitmodules.
5. Commit the changes: git commit -m "Removed submodule <submodule-name>".
errors:
# repo already exists and is not a valid git repo
Check if the path is already tracked
git ls-files --stage systems/chat_ui (100644 <hash> 0 <path>)
If it is, remove it from the index
git rm --cached systems/chat_ui
If system/chat_ui exits, remote it(chat_ui)
rm -r systems/chat_ui
Add the submodule
git submodule add https://github.com/example/repo.git systems/chat_ui
Commit the changes
git commit -m "Added submodule at systems/chat_ui"
Verify the submodule
git submodule status
(Optional) Initialize and Update the Submodule
If the submodule was added successfully but not initialized, run:
git submodule update --init --recursive

68
app.vue
View File

@ -2,58 +2,48 @@
<UApp :locale="fa_ir">
<NuxtLoadingIndicator />
<NuxtRouteAnnouncer />
<!-- <DevOnly> -->
<!-- this component will only be rendered during development -->
<!-- <LazyDebugBar /> -->
<!-- if you ever require to have a replacement during production -->
<!-- be sure to test these using `nuxt preview` -->
<!-- <template #fallback> -->
<!-- <div>empty div for flex.justify-between</div> -->
<!-- </template> -->
<!-- </DevOnly> -->
<NuxtLayout>
<!-- this component will be rendered on client-side -->
<!-- <NuxtClientFallback
fallback-tag="span"
fallback="NuxtClientFallback"
@ssr-error="logSomeError"
> -->
<NuxtPage />
<!-- <BrokeInSsr /> -->
<!-- </NuxtClientFallback> -->
</NuxtLayout>
</UApp>
</template>
<script setup lang="ts">
import { fa_ir } from "@nuxt/ui/locale";
onMounted(() => {
// const toast = useToast();
// toast.add({
// title: "hello world",
// });
});
// import { useCounterStore } from "~/stores/counter";
// import { storeToRefs } from "pinia";
// export default {
// setup() {
// definePageMeta({
// colorMode: "light",
// });
// },
// mounted() {
// access the `store` variable anywhere in the component
// const store = useCounterStore();
// const { count, doubleCount } = storeToRefs(store);
// const { increment } = store;
// console.info(this.$config.public.TAHRIR_ASSETS_URL);
// console.info(this.$config.public.PUBLIC_JAHAT_ASSETS_URL);
// console.info(this.$config.public.VITE_APP_NAME);
// console.info(import.meta.env.VITE_APP_NAME);
// console.info(this.$config.public);
// console.info(this.$config.public.TITLE);
// console.info(this.$config.public.NODE_ENV);
// console.info(this.$config.public.VITE_APP_NAME);
// console.info(this.$config.public.VITE_BASE_URL);
// console.info(this.$config.public.VITE_BASE_URL);
// },
// setup() {
// definePageMeta({
// colorMode: "light",
// });
// },
// mounted() {
// access the `store` variable anywhere in the component
// const store = useCounterStore();
// const { count, doubleCount } = storeToRefs(store);
// const { increment } = store;
// console.info(this.$config.public.TAHRIR_ASSETS_URL);
// console.info(this.$config.public.PUBLIC_JAHAT_ASSETS_URL);
// console.info(this.$config.public.VITE_APP_NAME);
// console.info(import.meta.env.VITE_APP_NAME);
// console.info(this.$config.public);
// console.info(this.$config.public.TITLE);
// console.info(this.$config.public.NODE_ENV);
// console.info(this.$config.public.VITE_APP_NAME);
// console.info(this.$config.public.VITE_BASE_URL);
// console.info(this.$config.public.VITE_BASE_URL);
// },
// methods: {
// logSomeError(error) {
// console.info("logSomeError", error);

View File

@ -59,4 +59,7 @@
@import "common";
// responsive
@import "responsive/responsive";
@import "responsive/responsive";
// @import "tailwindcss";
// @import "@nuxt/ui";

View File

@ -58,4 +58,7 @@
@import "common";
// responsive
@import "responsive/responsive";
@import "responsive/responsive";
// @import "tailwindcss";
// @import "@nuxt/ui";

View File

@ -1,5 +1,7 @@
@import "src/assets/common/scss/mixin";
@import "src/assets/common/scss/placeholder";
// @import "src/assets/common/scss/mixin";
// @import "src/assets/common/scss/placeholder";
@import "@/assets/common/scss/mixin";
@import "@/assets/common/scss/placeholder";
.admin-system,.task-system,.admin-panel{
//@import "main";

View File

@ -1,5 +1,7 @@
@import "src/assets/common/scss/mixin";
@import "src/assets/common/scss/placeholder";
// @import "src/assets/common/scss/mixin";
// @import "src/assets/common/scss/placeholder";
@import "@/assets/common/scss/mixin";
@import "@/assets/common/scss/placeholder";
.admin-panel {
.pages-content-container {

View File

@ -106,7 +106,9 @@
<script>
import authMixin from "~/mixins/authMixin";
import { required, minLength } from "vuelidate/lib/validators";
import { required,minLength } from '@vuelidate/validators'
import useVuelidate from '@vuelidate/core'
import { mapState } from "pinia";

View File

@ -5,13 +5,13 @@
"{{ userFullname(getFullName) }}" عزیز، خوش آمدید.
</div>
<div v-else> 0{{ getMobile }} </div>
<div v-else>0{{ getMobile }}</div>
</div>
<div class="m-sign-up__text">رمز پیامک شده را وارد کنید</div>
<div class="m-sign-up__form">
<div class="m-sign-up__form-row m-sign-up__simple-input">
<div
class="form-group "
class="form-group"
:class="{ 'form-group--error': $v.code.$error }"
>
<input
@ -96,7 +96,7 @@
</div>
<div class="m-sign-up__button-container">
<a @click.prevent="goBack" :href="$t('Back')">{{ $t("Back") }}</a>
<button v-on:click="doActivate">
<button v-on:click="doActivate">
{{ $t("Verify") }}
</button>
</div>
@ -106,7 +106,8 @@
<script>
import authMixin from "~/mixins/authMixin";
import { required, minLength } from "vuelidate/lib/validators";
import { required, minLength } from "@vuelidate/validators";
import { useCommonStore } from "~/stores/commonStore";
import { useAuthStore } from "~/stores/authStore";
import { mapState } from "pinia";
@ -147,7 +148,11 @@ export default {
};
},
computed: {
...mapState(useAuthStore,["getMobile", "getFullName", "getResetPasswordState"]),
...mapState(useAuthStore, [
"getMobile",
"getFullName",
"getResetPasswordState",
]),
},
methods: {
resetErrors() {
@ -183,14 +188,14 @@ export default {
vm.mySwalToast({
title: res.message,
icon: "success",
});
});
const { $eventBus } = useNuxtApp();
$eventBus.emit(
'authenticated-by-modal',
this.$route.query["invite-id"]
);
"authenticated-by-modal",
this.$route.query["invite-id"]
);
// vm.$router.push({
// name: "dashboard",
// });
@ -226,7 +231,7 @@ export default {
const vm = this;
var timeleft = timeInSecond;
var downloadTimer = setInterval(function() {
var downloadTimer = setInterval(function () {
if (timeleft <= 0) {
clearInterval(downloadTimer);
vm.$refs.countdown.innerHTML = "ارسال مجدد";
@ -250,7 +255,7 @@ export default {
display: inline-block !important;
text-align: center !important;
border: none !important;
background:unset !important;
background: unset !important;
&[disabled] {
min-width: 4em !important;
}

View File

@ -43,7 +43,7 @@ import {
minLength,
maxLength,
between,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
export default {
name: "forget",

View File

@ -156,7 +156,7 @@ import {
minLength,
maxLength,
between,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
export default {
mounted() {

View File

@ -116,7 +116,7 @@ import {
minLength,
maxLength,
between,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
import { mapState, mapActions } from "pinia";
import { useCommonStore } from "~/stores/commonStore";

View File

@ -207,7 +207,7 @@ import {
maxLength,
between,
sameAs,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
export default {
mounted(){

View File

@ -93,7 +93,7 @@ import {
maxLength,
between,
sameAs,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
export default {
validations: {

View File

@ -110,7 +110,7 @@ import {
required,
minLength,
sameAs,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
import { mapState, mapActions } from "pinia";
import { useCommonStore } from "~/stores/commonStore";
import { useAuthStore } from "~/stores/authStore";

View File

@ -34,12 +34,8 @@
<script>
import authMixin from "~/mixins/authMixin";
import {
required,
minLength,
maxLength,
between,
} from "vuelidate/lib/validators";
import { required, minLength, maxLength, between } from "@vuelidate/validators";
export default {
name: "forget",
@ -75,7 +71,7 @@ export default {
title: res.message,
icon: "success",
});
this.$router.push({
name: "resetpass",
params: { usernameemail: vm.usernameemail },

View File

@ -116,7 +116,7 @@ import {
minLength,
maxLength,
between,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
import { mapState, mapActions } from "pinia";
import { useCommonStore } from "~/stores/commonStore";

View File

@ -146,7 +146,7 @@ import {
maxLength,
between,
sameAs,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
export default {
validations: {

View File

@ -107,7 +107,7 @@ import {
required,
minLength,
sameAs,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
import { mapState, mapActions } from "pinia";
export default {

View File

@ -44,7 +44,7 @@ import {
minLength,
maxLength,
between,
} from "vuelidate/lib/validators";
} from "@vuelidate/validators";
export default {
validations: {

View File

@ -63,8 +63,8 @@
<div class="row">
<div class="col-sm-6 mb-2 mb-sm-0 col-md mb-sm-2 mb-md-0">
<div class="link-item-container">
<a
:href="urlResolver('qq309202')"
<NuxtLink
:to="urlResolver('qq309202')"
target="_blank"
class="btn link-item"
>
@ -80,13 +80,13 @@
<div class="more-text-container">
<span class="more-text"> مشاهده بیشتر </span>
</div>
</a>
</NuxtLink>
</div>
</div>
<div class="col-sm-6 mb-2 mb-sm-0 col-md mb-sm-2 mb-md-0">
<div class="link-item-container">
<a
:href="urlResolver('qq308235')"
<NuxtLink
:to="urlResolver('qq308235')"
target="_blank"
class="btn link-item"
>
@ -100,13 +100,13 @@
<div class="more-text-container">
<span class="more-text"> مشاهده بیشتر </span>
</div>
</a>
</NuxtLink>
</div>
</div>
<div class="col-sm-6 mb-2 mb-sm-0 col-md">
<div class="link-item-container">
<a
:href="urlResolver('qq38162')"
<NuxtLink
:to="urlResolver('qq38162')"
target="_blank"
class="btn link-item"
>
@ -120,13 +120,13 @@
<div class="more-text-container">
<span class="more-text"> مشاهده بیشتر </span>
</div>
</a>
</NuxtLink>
</div>
</div>
<div class="col-sm-6 mb-2 mb-sm-0 col-md">
<div class="link-item-container">
<a
:href="urlResolver('qq233930')"
<NuxtLink
:to="urlResolver('qq233930')"
target="_blank"
class="btn link-item"
>
@ -140,7 +140,7 @@
<div class="more-text-container">
<span class="more-text"> مشاهده بیشتر </span>
</div>
</a>
</NuxtLink>
</div>
</div>
</div>
@ -156,7 +156,8 @@ import searchApi from "~/apis/searchApi";
import { mapState, mapActions } from "pinia";
import { useCommonStore } from "~/stores/commonStore";
import { useStorage } from "@vueuse/core";
import {clearBodyClass} from "@manuals/utilities"
import { clearBodyClass } from "@manuals/utilities";
import { useRouter } from "vue-router";
export default {
beforeMount() {
@ -164,7 +165,7 @@ export default {
},
mounted() {
this.setBodyClass("majles-user-panel");
let localStoageSearchSchema = useStorage("searchSchema",undefined).value;
let localStoageSearchSchema = useStorage("searchSchema", undefined).value;
if (localStoageSearchSchema) {
let searchSchema = JSON.parse(localStoageSearchSchema);
this.schemasSetter(searchSchema);
@ -255,7 +256,9 @@ export default {
},
urlResolver(_id) {
return "";
const router = `search/qasection/${_id}/show`;
return router;
// const routeData = this.$router.resolve({
// path: "navigation",
// params: {
@ -272,7 +275,6 @@ export default {
<style lang="scss">
@import "../../../assets/majles/scss/majles";
.custom-class {
.dropdown-toggle {
color: rgba(0, 0, 0, 0.5) !important;

View File

@ -4,7 +4,7 @@
>{{ getLabel() }}:</label
>
<div>
<codemirror
<!-- <codemirror
:options="cmOptions"
class="markdown-preview"
v-model="textValue"
@ -12,7 +12,7 @@
:id="localFormElement.key"
:name="localFormElement.key"
ref="myCm"
></codemirror>
></codemirror> -->
</div>
</div>
</template>
@ -21,20 +21,20 @@
import { mapState } from "pinia";
import formBuilderMixin from "@mixins/formBuilderMixin";
import { codemirror } from "vue-codemirror";
import "vue-codemirror/node_modules/codemirror/lib/codemirror.css";
import "vue-codemirror/node_modules/codemirror/mode/markdown/markdown.js";
import "vue-codemirror/node_modules/codemirror/mode/javascript/javascript.js";
import "vue-codemirror/node_modules/codemirror/mode/css/css.js";
import "vue-codemirror/node_modules/codemirror/mode/vue/vue.js";
import "vue-codemirror/node_modules/codemirror/mode/htmlmixed/htmlmixed.js";
import "vue-codemirror/node_modules/codemirror/addon/edit/closebrackets.js";
import "vue-codemirror/node_modules/codemirror/addon/edit/matchbrackets.js";
import "vue-codemirror/node_modules/codemirror/addon/edit/closetag.js";
import "vue-codemirror/node_modules/codemirror/addon/display/placeholder.js";
import "vue-codemirror/node_modules/codemirror/addon/scroll/simplescrollbars.js";
import "vue-codemirror/node_modules/codemirror/addon/scroll/simplescrollbars.css";
import "vue-codemirror/node_modules/codemirror/addon/selection/active-line.js";
// import { codemirror } from "vue-codemirror";
// import "vue-codemirror/node_modules/codemirror/lib/codemirror.css";
// import "vue-codemirror/node_modules/codemirror/mode/markdown/markdown.js";
// import "vue-codemirror/node_modules/codemirror/mode/javascript/javascript.js";
// import "vue-codemirror/node_modules/codemirror/mode/css/css.js";
// import "vue-codemirror/node_modules/codemirror/mode/vue/vue.js";
// import "vue-codemirror/node_modules/codemirror/mode/htmlmixed/htmlmixed.js";
// import "vue-codemirror/node_modules/codemirror/addon/edit/closebrackets.js";
// import "vue-codemirror/node_modules/codemirror/addon/edit/matchbrackets.js";
// import "vue-codemirror/node_modules/codemirror/addon/edit/closetag.js";
// import "vue-codemirror/node_modules/codemirror/addon/display/placeholder.js";
// import "vue-codemirror/node_modules/codemirror/addon/scroll/simplescrollbars.js";
// import "vue-codemirror/node_modules/codemirror/addon/scroll/simplescrollbars.css";
// import "vue-codemirror/node_modules/codemirror/addon/selection/active-line.js";
export default {
mixins: [formBuilderMixin],

View File

@ -1,8 +1,8 @@
<template>
<div class="row form-group" :key="$attrs.key">
<label :for="localFormElement?.key" class="col-md-3">{{
localFormElement?.label
}}:</label>
<label :for="localFormElement?.key" class="col-md-3"
>{{ localFormElement?.label }}:</label
>
<div v-if="isInitial" class="dropbox">
<input
@ -57,8 +57,8 @@ const STATUS_INITIAL = 0,
STATUS_SUCCESS = 2,
STATUS_FAILED = 3;
import { upload } from "@services/fileUploadService";
import { wait } from "@utilities/utilities.js";
// import { upload } from "@services/fileUploadService.js";
import { wait } from "~/manuals/utilities.js";
import formBuilderMixin from "@mixins/formBuilderMixin";
export default {
@ -107,16 +107,16 @@ export default {
// upload data to the server
this.currentStatus = STATUS_SAVING;
upload(formData)
.then(wait(1500)) // DEV ONLY: wait for 1.5s
.then((x) => {
this.uploadedFiles = [].concat(x);
this.currentStatus = STATUS_SUCCESS;
})
.catch((err) => {
this.uploadError = err.response;
this.currentStatus = STATUS_FAILED;
});
// upload(formData)
// .then(wait(1500)) // DEV ONLY: wait for 1.5s
// .then((x) => {
// this.uploadedFiles = [].concat(x);
// this.currentStatus = STATUS_SUCCESS;
// })
// .catch((err) => {
// this.uploadError = err.response;
// this.currentStatus = STATUS_FAILED;
// });
},
reset() {
// reset form to initial state
@ -133,7 +133,7 @@ export default {
.dropbox {
outline: 2px dashed grey; /* the dash box */
outline-offset: -10px;
background: lightcyan;
background: lightcyan;
color: dimgray;
padding: 10px 10px;
min-height: 200px; /* minimum height */

View File

@ -92,7 +92,7 @@ export default {
// datePicker: VuePersianDatetimePicker,
datePicker: () =>
import(
"vue-persian-datetime-picker"
"vue3-persian-datetime-picker"
),
},
};

View File

@ -30,13 +30,15 @@
<script>
import formBuilderMixin from "@mixins/formBuilderMixin";
import HttpService from "@services/httpService";
// import HttpService from "@services/httpService";
export default {
// props:["listOptions"],
mixins: [formBuilderMixin],
created() {
this.httpService = useNuxtApp()["$http"];
},
mounted() {
setTimeout(() => {
this.setOptions();
@ -45,7 +47,7 @@ export default {
},
data() {
return {
httpService: undefined,
httpService: {},
optionRendering: false,
listOptions: [],
};
@ -173,7 +175,7 @@ export default {
if (this.optionRendering) return;
let vm = this;
this.httpService = new HttpService();
// this.httpService = new HttpService();
this.optionRendering = true;

View File

@ -1,299 +1,309 @@
<template>
<aside
class="nav-sidebar"
:class="[
{
' js-sidebar-collapsed ': isSidebarCollapsed,
' sidebar-expanded-mobile ': isMobile && !isSidebarCollapsed,
' sidebar-collapsed-desktop ': isDesktop && isSidebarCollapsed,
},
buildName() + '-sidebar',
]"
>
<!-- #region mobile-header -->
<div class="mobile-header pt-2 p-0">
<NuxtLink to="/" classes="btn mobile-close-sidebar">
<img
:src="logo"
:alt="appLongTitle()"
class="img-fluid"
style="width: 2em; filter: invert(0)"
/>
{{ appShortTitle() }}
</NuxtLink>
<button-component
@click="sidebarCollapsedSetter(true)"
classes="mobile-close-sidebar"
buttonText=""
>
<span class="tavasi tavasi-Component-71--1"></span>
</button-component>
</div>
<!-- #endregion mobile-header -->
<!-- #region sidebar -->
<div
class="nav-sidebar-inner-scroll d-flex flex-column firefox-scrollbar justify-content-between"
<client-only>
<aside
class="nav-sidebar"
:class="[
{
' js-sidebar-collapsed ': isSidebarCollapsed,
' sidebar-expanded-mobile ': isMobile && !isSidebarCollapsed,
' sidebar-collapsed-desktop ': isDesktop && isSidebarCollapsed,
},
buildName() + '-sidebar',
]"
>
<ClientOnly>
<ul class="sidebar-top-level-items">
<template v-for="(menuItem, mainKey) in menu">
<li
v-if="isShowMenuItem(mainKey, menuItem)"
class="mb-1"
@mouseenter="emitCustomEvent($event, 'in')"
@mouseleave="emitCustomEvent($event, 'out')"
v-for="(item, key, index) in menuItem"
:key="index"
:class="['color-' + item.color]"
>
<template v-if="isShowMenuItem(key, item)">
<template v-if="item?.subMenu?.length">
<NuxtLink
:to="{ name: item.link }"
class="has-sub-items gl-link"
:class="{ active: $route.name.startsWith(item.link) }"
>
<!-- <i class="nav-icon-container" :class="item.icon"></i> -->
<svg
class="nav-icon-container"
:class="'icon icon-' + item.icon"
>
<use :xlink:href="'#icon-' + item.icon"></use>
</svg>
<span class="nav-item-name">
{{ $t(translateKey(item)) }}
</span>
</NuxtLink>
<!-- #region mobile-header -->
<ul class="sidebar-sub-level-items li-child" :id="item.link">
<li class="fly-out-top-item active gl-link">
<span class="fly-out-top-item-container">
<strong class="fly-out-top-item-name">
{{ $t(translateKey(item)) }}
</strong>
<div class="mobile-header pt-2 p-0">
<NuxtLink to="/" classes="btn mobile-close-sidebar">
<img
:src="logo"
:alt="appLongTitle()"
class="img-fluid"
style="width: 2em; filter: invert(0)"
/>
{{ appShortTitle() }}
</NuxtLink>
<button-component
@click="sidebarCollapsedSetter(true)"
classes="mobile-close-sidebar"
buttonText=""
>
<span class="tavasi tavasi-Component-71--1"></span>
</button-component>
</div>
<!-- #endregion mobile-header -->
<!-- #region sidebar -->
<div
class="nav-sidebar-inner-scroll d-flex flex-column firefox-scrollbar justify-content-between"
>
<ClientOnly>
<ul class="sidebar-top-level-items">
<template v-for="(menuItem, mainKey) in menu">
<li
v-if="isShowMenuItem(mainKey, menuItem)"
class="mb-1"
@mouseenter="emitCustomEvent($event, 'in')"
@mouseleave="emitCustomEvent($event, 'out')"
v-for="(item, key, index) in menuItem"
:key="index"
:class="['color-' + item.color]"
>
<template v-if="isShowMenuItem(key, item)">
<template v-if="item?.subMenu?.length">
<NuxtLink
:to="{ name: item.link }"
class="has-sub-items gl-link"
:class="{ active: $route.name.startsWith(item.link) }"
>
<!-- <i class="nav-icon-container" :class="item.icon"></i> -->
<svg
class="nav-icon-container"
:class="'icon icon-' + item.icon"
>
<use :xlink:href="'#icon-' + item.icon"></use>
</svg>
<span class="nav-item-name">
{{ $t(translateKey(item)) }}
</span>
</li>
<li class="divider fly-out-top-item"></li>
</NuxtLink>
<li
class="li-child"
v-for="(child, j) in item.subMenu"
:key="j"
<ul
class="sidebar-sub-level-items li-child"
:id="item.link"
>
<template v-if="child?.subMenu?.length">
<NuxtLink
:to="{ name: child.link }"
class="has-sub-items gl-link"
exact-path
>
<!-- <i class="nav-icon-container" :class="child.icon"></i> -->
<svg
class="nav-icon-container"
:class="'icon icon-' + child.icon"
>
<use :xlink:href="'#icon-' + child.icon"></use>
</svg>
<span class="nav-item-name">
{{ $t(translateKey(child)) }}
</span>
</NuxtLink>
<li class="fly-out-top-item active gl-link">
<span class="fly-out-top-item-container">
<strong class="fly-out-top-item-name">
{{ $t(translateKey(item)) }}
</strong>
</span>
</li>
<li class="divider fly-out-top-item"></li>
<ul class="sidebar-sub-level-items" :id="child.link">
<li class="fly-out-top-item active gl-link">
<span class="fly-out-top-item-container">
<strong class="fly-out-top-item-name">
{{ $t(translateKey(child)) }}
</strong>
</span>
</li>
<li class="divider fly-out-top-item"></li>
<li
v-for="(descendant, k) in child.subMenu"
@click.prevent="navigateTo(descendant)"
:key="k"
<li
class="li-child"
v-for="(child, j) in item.subMenu"
:key="j"
>
<template v-if="child?.subMenu?.length">
<NuxtLink
:to="{ name: child.link }"
class="has-sub-items gl-link"
exact-path
>
<NuxtLink
exact-path
:to="{ name: descendant.link }"
:title="descendant.title"
class="gl-link btn sub-level-item"
<!-- <i class="nav-icon-container" :class="child.icon"></i> -->
<svg
class="nav-icon-container"
:class="'icon icon-' + child.icon"
>
<!-- <svg-icon-component :iconName="descendant.icon" /> -->
<svg
class="nav-icon-container"
:class="'icon icon-' + descendant.icon"
<use :xlink:href="'#icon-' + child.icon"></use>
</svg>
<span class="nav-item-name">
{{ $t(translateKey(child)) }}
</span>
</NuxtLink>
<ul class="sidebar-sub-level-items" :id="child.link">
<li class="fly-out-top-item active gl-link">
<span class="fly-out-top-item-container">
<strong class="fly-out-top-item-name">
{{ $t(translateKey(child)) }}
</strong>
</span>
</li>
<li class="divider fly-out-top-item"></li>
<li
v-for="(descendant, k) in child.subMenu"
@click.prevent="navigateTo(descendant)"
:key="k"
>
<NuxtLink
exact-path
:to="{ name: descendant.link }"
:title="descendant.title"
class="gl-link btn sub-level-item"
>
<use
:xlink:href="'#icon-' + descendant.icon"
></use>
</svg>
<!-- <i
<!-- <svg-icon-component :iconName="descendant.icon" /> -->
<svg
class="nav-icon-container"
:class="'icon icon-' + descendant.icon"
>
<use
:xlink:href="'#icon-' + descendant.icon"
></use>
</svg>
<!-- <i
class="nav-icon-container "
:class="descendant.icon"
>
<span class="path1"></span
><span class="path2"></span>
</i> -->
<span class="nav-item-name gl-link me-1">
{{ descendant.title }}
</span>
</NuxtLink>
</li>
</ul>
</template>
<span class="nav-item-name gl-link me-1">
{{ descendant.title }}
</span>
</NuxtLink>
</li>
</ul>
</template>
<NuxtLink
v-else
exact-path
:to="{ name: child.link }"
:title="$t(translateKey(child))"
class="gl-link btn sub-level-item"
>
<svg-icon-component :iconName="child.icon" />
<svg
class="nav-icon-container"
:class="'icon icon-' + child.icon"
<NuxtLink
v-else
exact-path
:to="{ name: child.link }"
:title="$t(translateKey(child))"
class="gl-link btn sub-level-item"
>
<use :xlink:href="'#icon-' + child.icon"></use>
</svg>
<!-- <i class="nav-icon-container " :class="child.icon">
<svg-icon-component :iconName="child.icon" />
<svg
class="nav-icon-container"
:class="'icon icon-' + child.icon"
>
<use :xlink:href="'#icon-' + child.icon"></use>
</svg>
<!-- <i class="nav-icon-container " :class="child.icon">
<span class="path1"></span><span class="path2"></span>
</i> -->
<span class="nav-item-name gl-link me-1">
{{ $t(translateKey(child)) }}
</span>
</NuxtLink>
</li>
</ul>
</template>
<span class="nav-item-name gl-link me-1">
{{ $t(translateKey(child)) }}
</span>
</NuxtLink>
</li>
</ul>
</template>
<NuxtLink
v-else
:to="{ name: item.link }"
:title="$t(translateKey(item))"
class="gl-link"
exact-path
>
<!-- <svg-icon-component :iconName="item.icon" /> -->
<svg
class="nav-icon-container"
:class="'icon icon-' + item.icon"
<NuxtLink
v-else
:to="{ name: item.link }"
:title="$t(translateKey(item))"
class="gl-link"
exact-path
>
<use :xlink:href="'#icon-' + item.icon"></use>
</svg>
<!-- <i class="nav-icon-container" :class="item.icon">
<!-- <svg-icon-component :iconName="item.icon" /> -->
<svg
class="nav-icon-container"
:class="'icon icon-' + item.icon"
>
<use :xlink:href="'#icon-' + item.icon"></use>
</svg>
<!-- <i class="nav-icon-container" :class="item.icon">
<span class="path1"></span><span class="path2"></span
></i> -->
<span class="nav-item-name">
{{ $t(translateKey(item)) }}
</span>
</NuxtLink>
</template>
</li>
</template>
</ul>
<!-- <template v-if="isSidebarCollapsed"> -->
<div class="mb-5 mb-md-0">
<ul class="sidebar-top-level-items m-0">
<li
v-if="!isMajlesBuild()"
class=""
@mouseenter="emitCustomEvent($event, 'in')"
@mouseleave="emitCustomEvent($event, 'out')"
>
<a class="has-sub-items gl-link">
<svg
v-if="isGuest && showRegister"
class="icon icon-personal"
>
<use
class="icon-personal"
xlink:href="#icon-personal"
></use>
</svg>
<img
v-else
class="nav-icon-container img-fluid user-avatar"
:src="avatar"
/>
<span class="nav-item-name">
{{ $t(translateKey(item)) }}
{{ currentUser?.user_data?.first_name }}&nbsp;&nbsp;
</span>
</NuxtLink>
</template>
</li>
</template>
</ul>
</a>
<!-- <template v-if="isSidebarCollapsed"> -->
<div class="mb-5 mb-md-0">
<ul class="sidebar-top-level-items m-0">
<li
v-if="!isMajlesBuild()"
class=""
@mouseenter="emitCustomEvent($event, 'in')"
@mouseleave="emitCustomEvent($event, 'out')"
>
<a class="has-sub-items gl-link">
<svg v-if="isGuest && showRegister" class="icon icon-personal">
<use class="icon-personal" xlink:href="#icon-personal"></use>
</svg>
<img
v-else
class="nav-icon-container img-fluid user-avatar"
:src="avatar"
/>
<span class="nav-item-name">
{{ currentUser?.user_data?.first_name }}&nbsp;&nbsp;
</span>
</a>
<ul class="sidebar-sub-level-items">
<!-- <li class="fly-out-top-item gl-link">
<ul class="sidebar-sub-level-items">
<!-- <li class="fly-out-top-item gl-link">
<h6 class="bg-white text-center py-3">
{{ userFullname(currentUser?.user_data) }}
</h6>
</li> -->
<li class="fly-out-top-item gl-link">
<NuxtLink
v-can="'admin_dahsboard'"
:to="{ name: 'admin' }"
class="dropdown-item"
exact-path
>{{ $t("admin") }}</NuxtLink
>
</li>
<li class="fly-out-top-item gl-link">
<NuxtLink
v-can="'admin_dahsboard'"
:to="{ name: 'admin' }"
class="dropdown-item"
exact-path
>{{ $t("admin") }}</NuxtLink
>
</li>
<li class="fly-out-top-item gl-link">
<li class="fly-out-top-item gl-link">
<a
href="install PWA"
title="نصب سامانه - در صفحه گوشی یا رایانه"
class="dropdown-item"
type="button"
@click.prevent="checkBeforeInstallingPwa()"
>
{{ $t("InstallSystem") }}
</a>
</li>
<li class="fly-out-top-item gl-link">
<a
href="exit"
@click.prevent="logout()"
class="dropdown-item"
>
{{ $t("Exit") }}
</a>
</li>
<li class="fly-out-top-item gl-link">
<a
href="/refresh-page"
v-if="isNewVersionAvailable"
@click="reloadPage()"
class="dropdown-item"
>
<svg style="height: 1em" class="icon icon-reset-form">
<use xlink:href="#icon-reset-form"></use>
</svg>
بروزرسانی
</a>
</li>
<li class="fly-out-top-item gl-link">
<a
v-if="isGuest && showRegister"
title="ثبت نام"
href="/login"
class="dropdown-item"
>ثبت نام
</a>
</li>
</ul>
</li>
<template>
<li class="">
<a
href="install PWA"
title="نصب سامانه - در صفحه گوشی یا رایانه"
class="dropdown-item"
@click.prevent="sidebarCollapsedSetter(!isSidebarCollapsed)"
class="toggle-sidebar-button"
role="button"
title="بازکردن"
type="button"
@click.prevent="checkBeforeInstallingPwa()"
>
{{ $t("InstallSystem") }}
</a>
</li>
<li class="fly-out-top-item gl-link">
<a
href="exit"
@click.prevent="logout()"
class="dropdown-item"
>
{{ $t("Exit") }}
</a>
</li>
<li class="fly-out-top-item gl-link">
<a
href="/refresh-page"
v-if="isNewVersionAvailable"
@click="reloadPage()"
class="dropdown-item"
>
<svg style="height: 1em" class="icon icon-reset-form">
<use xlink:href="#icon-reset-form"></use>
<svg class="icon icon-chevron-double-left">
<use xlink:href="#icon-chevron-double-left"></use>
</svg>
بروزرسانی
</a>
</li>
<li class="fly-out-top-item gl-link">
<a
v-if="isGuest && showRegister"
title="ثبت نام"
href="/login"
class="dropdown-item"
>ثبت نام
</a>
</li>
</ul>
</li>
<template>
<li class="">
<a
@click.prevent="sidebarCollapsedSetter(!isSidebarCollapsed)"
class="toggle-sidebar-button"
role="button"
title="بازکردن"
type="button"
>
<svg class="icon icon-chevron-double-left">
<use xlink:href="#icon-chevron-double-left"></use>
</svg>
<!-- <svg
<!-- <svg
class="icon icon-chevron-double-left"
data-testid="chevron-double-lg-left-icon"
>
@ -301,43 +311,44 @@
href="@assets/common/img/icons.svg#chevron-double-lg-left"
></use>
</svg> -->
<span class="collapse-text me-2">{{ $t("Close") }}</span>
</a>
</li>
</template>
</ul>
</div>
</ClientOnly>
</div>
<span class="collapse-text me-2">{{ $t("Close") }}</span>
</a>
</li>
</template>
</ul>
</div>
</ClientOnly>
</div>
<!-- #endregion sidebar -->
<!-- #endregion sidebar -->
<!-- #region base-modal -->
<base-modal-v2
v-if="uploadForFirstTime"
@close="closeModal"
@canel="closeModal()"
:showHeaderCloseButton="true"
:modalTitle="modalTitle"
class="borhan-modal"
modalSize="modal-lg"
height="30em"
overflow="hidden"
:showSaveButton="false"
:showCloseButton="false"
>
<settings
:uploadForFirstTime="uploadForFirstTime"
:menuId="menuId"
<!-- #region base-modal -->
<base-modal-v2
v-if="uploadForFirstTime"
@close="closeModal"
@canel="closeModal()"
@close="closeModal()"
@after-select-file="afterSelectFile()"
></settings>
</base-modal-v2>
<!-- #endregion base-modal -->
:showHeaderCloseButton="true"
:modalTitle="modalTitle"
class="borhan-modal"
modalSize="modal-lg"
height="30em"
overflow="hidden"
:showSaveButton="false"
:showCloseButton="false"
>
<settings
:uploadForFirstTime="uploadForFirstTime"
:menuId="menuId"
@canel="closeModal()"
@close="closeModal()"
@after-select-file="afterSelectFile()"
></settings>
</base-modal-v2>
<!-- #endregion base-modal -->
<SvgIconComponent></SvgIconComponent>
</aside>
<SvgIconComponent></SvgIconComponent>
</aside>
</client-only>
</template>
<script>
import { mapState, mapActions } from "pinia";

View File

@ -0,0 +1,80 @@
<template>
<div>
<input type="file" accept="image/*" @change="onFileChange" />
<div v-if="imageSrc" class="cropper-container">
<img ref="image" :src="imageSrc" alt="Image to crop" />
</div>
<button v-if="imageSrc" @click="cropImage">Crop and Upload</button>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
const imageSrc = ref(null);
const image = ref(null);
let cropper = null;
const onFileChange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
imageSrc.value = event.target.result;
initCropper();
};
reader.readAsDataURL(file);
}
};
const initCropper = () => {
if (cropper) {
cropper.destroy();
}
cropper = new Cropper(image.value, {
aspectRatio: 1, // Set aspect ratio (e.g., 1 for square)
viewMode: 1, // Restrict the crop box to the size of the image
});
};
const cropImage = () => {
if (cropper) {
const croppedCanvas = cropper.getCroppedCanvas();
croppedCanvas.toBlob((blob) => {
uploadImage(blob);
});
}
};
const uploadImage = async (blob) => {
const formData = new FormData();
formData.append('image', blob, 'cropped-image.png');
try {
const response = await $fetch('/api/upload', {
method: 'POST',
body: formData,
});
alert('Image uploaded successfully!');
console.log('Upload response:', response);
} catch (error) {
console.error('Error uploading image:', error);
alert('Error uploading image');
}
};
onBeforeUnmount(() => {
if (cropper) {
cropper.destroy();
}
});
</script>
<style scoped>
.cropper-container {
max-width: 100%;
margin-top: 20px;
}
</style>

View File

@ -86,13 +86,13 @@ import repoApi from "~/apis/repoApi";
import { mapState } from "pinia";
import { useCommonStore } from "~/stores/commonStore";
import SelectComponentDefault from "~/components/SelectComponentDefault.vue";
import SelectComponent from "~/components/SelectComponent.vue";
import InputComponent from "~/components/InputComponent.vue";
import LabelComponent from "~/components/LabelComponent.vue";
import tagsComponent from "~/components/tagsComponent.vue";
import DateComponent from "~/components/DateComponent.vue";
import TextareaComponent from "~/components/TextareaComponent.vue";
import SelectComponentDefault from "~/components/other/SelectComponentDefault.vue";
import SelectComponent from "~/components/other/SelectComponent.vue";
import InputComponent from "~/components/other/InputComponent.vue";
import LabelComponent from "~/components/other/LabelComponent.vue";
import tagsComponent from "~/components/other/tagsComponent.vue";
import DateComponent from "~/components/other/DateComponent.vue";
import TextareaComponent from "~/components/other/TextareaComponent.vue";
/**
* @vue-data {Object} [listUpdatedText = {}] - متنهای بهروزشده در لیست.
* @vue-data {undefined} [httpService = undefined] - سرویس HTTP برای درخواستها.

View File

@ -0,0 +1,26 @@
// composables/useHadithaSearchComposable.js
import { useStorage } from "@vueuse/core";
export const useHadithaSearchComposable = <T>(url: string, options = {}) => {
// Get the authentication token (e.g., from a cookie or local storage)
// const token = useCookie('auth-token') // Assuming you store the token in a cookie
let token = useStorage("id_token", "GuestAccess");
const config = useRuntimeConfig();
console.info(config);
const baseURL =
config.public.NUXT_PUBLIC_BASE_URL + config.public.NUXT_PUBLIC_API_NAME;
// Add headers to the request
const headers = {
Authorization: token.value,
...options.headers, // Merge with any existing headers
};
// Use useFetch with the headers
return useFetch<T>(url, {
...options,
baseURL,
headers,
});
};

View File

@ -1,44 +1,44 @@
import { useStorage } from "@vueuse/core";
// import { useStorage } from "@vueuse/core";
import { ref } from "vue";
import { useCommonStore } from "~/stores/commonStore";
// import { ref } from "vue";
// import { useCommonStore } from "~/stores/commonStore";
export function useMyComposable() {
const count = ref(0);
// export function useMyComposable() {
// const count = ref(0);
const increment = () => {
count.value++;
};
// const increment = () => {
// count.value++;
// };
const decrement = () => {
count.value--;
};
// const decrement = () => {
// count.value--;
// };
return {
count,
increment,
decrement,
};
}
// return {
// count,
// increment,
// decrement,
// };
// }
export function useGetSchema() {
const commonStore = useCommonStore();
// export function useGetSchema() {
// const commonStore = useCommonStore();
try {
return useAsyncData("shema", () =>
$fetch("/api/ali", {
headers: {
headers: { Authorization: useStorage("id_token") },
},
method: "post",
body: {
organ: commonStore.organNameGetter,
system: "search",
build_state: buildState(),
},
})
);
} catch (err) {
console.info(err);
}
}
// try {
// return useAsyncData("shema", () =>
// $fetch("/api/ali", {
// headers: {
// headers: { Authorization: useStorage("id_token") },
// },
// method: "post",
// body: {
// organ: commonStore.organNameGetter,
// system: "search",
// build_state: buildState(),
// },
// })
// );
// } catch (err) {
// console.info(err);
// }
// }

View File

@ -1,17 +0,0 @@
<script setup>
import { clearBodyClass } from "@manuals/utilities";
onMounted(() => {
clearBodyClass();
});
onUnmounted(() => {
clearBodyClass();
});
</script>
<template>
<main class="h-full">
<slot name="named-slot"></slot>
<slot></slot>
</main>
</template>

View File

@ -1,167 +1,177 @@
<!-- layouts/default.vue -->
<template>
<div>
<header v-if="buildName() != 'majles'">
<!-- //////////////////// begin navbar //////////////////// -->
<nav
class="navbar navbar-expand-md navbar-light bg-light"
:class="{ expanded: !isSidebarCollapsed }"
>
<!-- sidebar menu toggler -->
<button-component
@click="sidebarCollapsedSetter(false)"
classes="p-0 d-lg-none"
buttonText=""
<!-- //////////////////// begin navbar //////////////////// -->
<template v-if="$attrs.showNavbar">
<header v-if="buildName() != 'majles'">
<nav
class="navbar navbar-expand-md navbar-light bg-light"
:class="{ expanded: !isSidebarCollapsed }"
>
<svg class="icon icon-Component-68--1">
<use xlink:href="#icon-Component-68--1"></use>
</svg>
</button-component>
<a
class="navbar-brand d-none d-lg-inline-block"
style="color: #00b6e3"
>{{ navTitle }}</a
>
<a class="navbar-brand d-lg-none" style="color: #00b6e3">{{
navShortTitle
}}</a>
<button
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<!-- sidebar menu toggler -->
<button-component
@click="sidebarCollapsedSetter(false)"
classes="p-0 d-lg-none"
buttonText=""
>
<svg class="icon icon-Component-68--1">
<use xlink:href="#icon-Component-68--1"></use>
</svg>
</button-component>
<a
class="navbar-brand d-none d-lg-inline-block"
style="color: #00b6e3"
>{{ navTitle }}</a
>
<a class="navbar-brand d-lg-none" style="color: #00b6e3">{{
navShortTitle
}}</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse" id="navbarSupportedContent">
<div class="collapse-mobile-header">
<div>
<img
src="assets/common/img/logo/gray-logo.png"
alt="هم فهمی"
class="img-fluid"
style="width: 2em; filter: invert(0)"
/>
هم فهمی
<div class="navbar-collapse" id="navbarSupportedContent">
<div class="collapse-mobile-header">
<div>
<img
src="assets/common/img/logo/gray-logo.png"
alt="هم فهمی"
class="img-fluid"
style="width: 2em; filter: invert(0)"
/>
هم فهمی
</div>
<button
class="btn p-0 me-auto"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<svg class="icon icon-Component-71--1">
<use xlink:href="#icon-Component-71--1"></use>
</svg>
</button>
</div>
<button
class="btn p-0 mr-auto"
type="button"
data-toggle="collapse"
data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<svg class="icon icon-Component-71--1">
<use xlink:href="#icon-Component-71--1"></use>
</svg>
</button>
</div>
<ul class="navbar-nav ml-auto mr-md-5">
<!-- <li class="nav-item active">
<ul class="navbar-nav ms-auto me-md-5">
<!-- <li class="nav-item active">
<a class="nav-link" href="#"
>پیشخوان <span class="sr-only">(current)</span></a
>
</li> -->
<!-- <li class="nav-item">
<!-- <li class="nav-item">
<a class="nav-link" href="#">سامانهها</a>
</li> -->
<li class="nav-item dropdown">
<a
class="nav-link dropdown-toggle"
id="navbarDropdown"
role="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{{ $t("Systems") }}
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<!-- <my-system class="list" layout="list"></my-system> -->
</div>
</li>
<li class="nav-item dropdown">
<a
class="nav-link dropdown-toggle"
href="#"
id="navbarDropdown"
role="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{{ $t("Portal") }}
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<li class="nav-item dropdown">
<a
class="dropdown-item"
href="http://pub.hamfahmi.ir/public"
target="_blank"
>{{ $t("PersonalFolder") }}</a
class="nav-link dropdown-toggle"
id="navbarDropdown"
role="button"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{{ $t("Systems") }}
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<!-- <my-system class="list" layout="list"></my-system> -->
</div>
</li>
<li class="nav-item dropdown">
<a
class="dropdown-item"
href="http://pub.hamfahmi.ir/wiki"
target="_blank"
>{{ $t("WikiFarhangestan") }}</a
class="nav-link dropdown-toggle"
href="#"
id="navbarDropdown"
role="button"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
<a
class="dropdown-item"
href="http://pub.hamfahmi.ir:91/"
target="_blank"
>{{ $t("ResearchPortal") }}</a
>
<a
class="dropdown-item"
href="http://pub.hamfahmi.ir:94/"
target="_blank"
>{{ $t("ResearchSoftware") }}</a
>
</div>
</li>
</ul>
{{ $t("Portal") }}
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a
class="dropdown-item"
href="http://pub.hamfahmi.ir/public"
target="_blank"
>{{ $t("PersonalFolder") }}</a
>
<a
class="dropdown-item"
href="http://pub.hamfahmi.ir/wiki"
target="_blank"
>{{ $t("WikiFarhangestan") }}</a
>
<a
class="dropdown-item"
href="http://pub.hamfahmi.ir:91/"
target="_blank"
>{{ $t("ResearchPortal") }}</a
>
<a
class="dropdown-item"
href="http://pub.hamfahmi.ir:94/"
target="_blank"
>{{ $t("ResearchSoftware") }}</a
>
</div>
</li>
</ul>
<ul class="navbar-nav ms-md-3 me-auto">
<li class="nav-item">
<notification></notification>
</li>
<li class="nav-item">
<select-language-dropdown
toggleClass="dropdown-toggle"
></select-language-dropdown>
</li>
<li class="nav-item">
<user-avatar-dropdown
class="position-static"
></user-avatar-dropdown>
</li>
</ul>
</div>
</nav>
</header>
</template>
<!-- //////////////////// end navbar //////////////////// -->
<ul class="navbar-nav ml-md-3 mr-auto">
<li class="nav-item">
<notification></notification>
</li>
<li class="nav-item">
<select-language-dropdown
toggleClass="dropdown-toggle"
></select-language-dropdown>
</li>
<li class="nav-item">
<user-avatar-dropdown
class="position-static"
></user-avatar-dropdown>
</li>
</ul>
</div>
</nav>
<!-- //////////////////// end navbar //////////////////// -->
</header>
<!-- <the-sidebar :showUserAvatar="true" :menu="menu"></the-sidebar> -->
<the-sidebar
:showUserAvatar="true"
:menu="$attrs.menu"
@statusPag="statusPag"
></the-sidebar>
<main class="main-page__content" :class="{ expanded: !isSidebarCollapsed }">
<!-- <div class="pages list-page"> -->
<!-- <div class="pages-content align-items-stretch p-0"> -->
<!-- <div class="flex-grow-1"> -->
<slot></slot>
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
</main>
<client-only>
<main
class="main-page__content"
:class="{ expanded: !isSidebarCollapsed }"
>
<!-- <div class="pages list-page"> -->
<!-- <div class="pages-content align-items-stretch p-0"> -->
<!-- <div class="flex-grow-1"> -->
<slot></slot>
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
</main>
</client-only>
</div>
</template>
@ -169,7 +179,7 @@
import { mapState, mapActions } from "pinia";
import { useCommonStore } from "~/stores/commonStore";
import {clearBodyClass} from "@manuals/utilities"
import { clearBodyClass } from "@manuals/utilities";
export default {
mounted() {
// this.setBodyClass("default-dashboard");

View File

@ -1,4 +1,4 @@
// middleware/redirect.js
export default defineNuxtRouteMiddleware((to, from) => {
if (buildName() == "hadith" && to.path == '/') return navigateTo({ name: "hadith" });
if (buildName() == "haditha" && to.path == '/') return navigateTo({ name: "haditha" });
});

View File

@ -33,6 +33,8 @@ export default {
]),
// callFrom : page | modal
async authBase(methodName, callFrom = "page") {
const toast = useToast();
// this.$v.$touch();
const route = useRoute();
const router = useRouter();
@ -51,16 +53,19 @@ export default {
this[methodName]()
.then((res) => {
// mySwalToast({
// title: res.message,
// icon: "success",
// timer: 2000,
// });
toast.add({
title: res.message,
icon: "success",
timer: 2000,
});
if (callFrom == "modal") {
// fired event cached in the Group.vue and ChatList.vue
const { $eventBus } = useNuxtApp();
$eventBus.emit("authenticated-by-modal", route.query["invite-id"]);
$eventBus.emit(
"authenticated-by-modal",
route.query["invite-id"]
);
}
// if redirected from chat system to relogin.
else {
@ -81,10 +86,12 @@ export default {
}
})
.catch((err) => {
mySwalToast({
toast.add({
title: err.message,
icon: "error",
timer: 2000,
});
this.resetCaptcha();
})
.finally(() => {

358
mixins/formBuilderMixin.js Normal file
View File

@ -0,0 +1,358 @@
export default {
props: ["formElement", "keyValue"],
data() {
return {
options: {
controls: [
// "play-large", // The large play button in the center
//'restart', // Restart playback
// "rewind", // Rewind by the seek time (default 10 seconds)
"play", // Play/pause playback
// "fast-forward", // Fast forward by the seek time (default 10 seconds)
"progress", // The progress bar and scrubber for playback and buffering
"current-time", // The current time of playback
// "duration", // The full duration of the media
"mute", // Toggle mute
"volume", // Volume control
// "captions", // Toggle captions
"settings", // Settings menu
// "pip", // Picture-in-picture (currently Safari only)
// "airplay", // Airplay (currently Safari only)
"download", // Show a download button with a link to either the current source or a custom URL you specify in your options
// "fullscreen", // Toggle fullscreen
],
speed: { selected: 1, options: [0.75, 1, 1.5, 2] },
},
hasError: false,
localFormElement: {},
textValue: "",
emit_disable: false,
textValuePrev: "",
textDate: [],
objectValue: {}, // for select component {text, value}
isUpdateitem: false,
renderKey:1,
};
},
beforeMount() {
this.$root.$on("catch-form-builder-event", this.listenEventBus);
},
beforeDestroy() {
this.$root.$off("catch-form-builder-event", this.listenEventBus);
},
mounted() {
// this.textValue = this.value;
// this.textItem = this.value
this.disableEmitTemprory(2000);
this.localFormElement = this.formElement;
this.textValuePrev = this.localFormElement.value;
this.initTextValue();
this.textValuePrev = this.textValue;
// this.date = this.textValue; // for DateCompnent
},
watch: {
formElement: {
handler(newValue) {
this.localFormElement = newValue;
},
deep: true,
nested: true,
},
textValue(newVal) {
// console.log(this.localFormElement.key, newVal)
if (this.textValuePrev == newVal) {
return;
}
let [emitValue, emitValueObject] = this.parseValueToEmit(newVal)
// if (!this.emit_disable)
// console.log(this.localFormElement.key, newVal, emitValue, this.localFormElement.value)
if(this.localFormElement.value != emitValue)
this.$emit("take-value", emitValue);
if (emitValueObject) this.parseAffectedToEventBus(emitValueObject); // for select only
else this.parseAffectedToEventBus(emitValue);
},
},
methods: {
incrementRenderKey(){
this.renderKey = this.renderKey +1
this.$forceUpdate();
},
parseValueToEmit(newVal){
let emitValue = newVal;
let emitValueObject = undefined;
if (this.localFormElement?.type == "date") {
// console.info(newVal);
emitValue = newVal;
try {
if (this.localFormElement?.savetype == "timestamp") {
let dtStr = newVal;
// const dtStr = "2012/02/26";
const [y, m, d] = dtStr.split(/-|\//);
const date = new Date(y, m - 1, d);
// emitValue = date.toISOString();
emitValue = date.getTime()();
}
// if( this.localFormElement?.subtype == 'time' )
// return "HH:mm"
} catch {
emitValue = newVal;
}
// console.info(emitValue);
}
// input , textarea if multi items
else if (this.localFormElement?.isarray) {
let delim = this.localFormElement?.delimiter
? this.localFormElement.delimiter
: "-";
emitValue = [];
newVal
.split(delim)
.filter((el) => el.trim())
.forEach((el) => {
emitValue.push(el.trim());
});
// console.log(emitValue)
} else if (this.localFormElement.type == "select" && this.listOptions) {
// if(this.localFormElement.key == 'city'){
// }
emitValue = newVal;
//برای ذدخیره در بک کدام مقدار را بفرستد
if (this.localFormElement.value_save) {
emitValueObject = this.listOptions.find(
(item) => item.value == newVal
);
if (emitValueObject && this.localFormElement.value_save == "title") {
emitValue = emitValueObject.title ?? emitValueObject.value;
} else if (
emitValueObject &&
this.localFormElement.value_save == "object"
) {
emitValue = {
id: emitValueObject.value,
title: emitValueObject.title,
};
}
}
} else if (this.localFormElement?.type == "number"){
if(this.localFormElement?.step != 1){
emitValue = parseFloat(newVal)
}
else
emitValue = parseInt(newVal)
}
else emitValue = newVal;
return [emitValue, emitValueObject]
},
listenEventBus({ value, affectedTo }) {
// console.log(affectedTo.key, this.localFormElement.key);
if (affectedTo.key === this.localFormElement.key) {
try {
// console.log(this.localFormElement.key, affectedTo, value);
// console.log(this[affectedTo.action])
this[affectedTo.action](value);
} catch (err) {}
}
},
parseAffectedToEventBus(newValue) {
if (!this.localFormElement["affected-to"]) return;
// console.log("parseAffectedToEvent", newValue);
// console.log(newValue);
let affectedTo = this.localFormElement["affected-to"];
// console.log(affectedTo);
affectedTo.forEach((item_affectedTo) => {
let value = newValue;
if (
typeof newValue == "object" &&
item_affectedTo?.param &&
newValue[item_affectedTo?.param]
)
value = newValue[item_affectedTo?.param];
if (item_affectedTo?.emit) {
this.$emit( "action-affected-item", { action:item_affectedTo.emit, key: item_affectedTo.key, value});
// console.log("action-affected-item", { action:item_affectedTo.emit, key: item_affectedTo.key, value})
} else if (item_affectedTo?.action) {
this.$root.$emit("catch-form-builder-event", {
value: value,
affectedTo: item_affectedTo,
});
}
});
},
disableEmitTemprory(timeout = 1000) {
this.emit_disable = true;
setTimeout(() => {
this.emit_disable = false;
}, timeout);
},
clearErrors() {
return true;
},
getLabel() {
let label = this.localFormElement.label;
if (this.localFormElement?.required == "1") label = "*" + label;
return label;
},
validate(ev) {
const value = ev.target.value;
if (this.localFormElement.validate) {
// چک کردن برای وجود الگو
if (value) {
let pattern = this.localFormElement.validate;
// اگر الگوی ورودی معتبر باشد، تنظیم می‌کنیم که مقدار مطابقت دارد یا خیر
this.hasError = new RegExp(pattern).test(value);
} else {
// اگر مقدار ورودی خالی باشد، خطا بر اساس الزامی بودن فیلد تعیین می‌شود
this.hasError = this.localFormElement?.required ? true : false;
}
} else {
// اگر الگوی validate وجود نداشته باشد، کاری انجام نمی‌دهیم
this.hasError = false;
}
// اعلان خطا به والد (component اصلی)
this.$emit("take-value-validate", this.hasError);
},
initTextValue(newValue=undefined) {
// console.log('initTextValue', this.localFormElement.key, newValue)
let value = "";
if(newValue)
value = newValue
else if(this.localFormElement.value)
value = this.localFormElement.value;
else if(this.localFormElement.value_defualt)
value = this.localFormElement.value_defualt
// در همه حالت نباید این باشد اگر لازم شد باید پیش فرض تنظیم شود
// ذخیره هم نمیشود ...!!!
// if (!value && this.localFormElement.type == "select") {
// value = this.listOptions.length > 0 ? this.listOptions[0].value : "";
// }
// console.log(this.localFormElement.key, value)
var mValue = value ?? "";
if (this.localFormElement?.type == "date") {
// console.info("setTextValue", mValue);
try {
var date = "";
if (mValue != "") {
if (this.localFormElement?.savetype == "timestamp") {
let timeWithMiliSeconds = mValue;
if ( this.isUnixTimestamp(mValue) && mValue.toString().length <= 11) {
timeWithMiliSeconds = mValue * 1000;
}
date = this.$moment(new Date(timeWithMiliSeconds)).format(
"YYYY-MM-DD HH:mm"
);
} else if (
this.localFormElement?.savetype == "datetime" ||
this.localFormElement?.savetype == "date"
) {
date = this.$moment(new Date(mValue)).format("YYYY-MM-DD HH:mm");
} else date = mValue;
}
this.textDate = date;
mValue = date
} catch {
this.textDate = mValue;
}
} else if (this.localFormElement?.isarray) {
if (value && Array.isArray(value)) {
let delim = this.localFormElement?.delimiter
? this.localFormElement.delimiter
: "-";
mValue = value.join(delim);
} else mValue = "";
} else if (this.localFormElement.type == "select") {
if (this.localFormElement?.value_save == "title" && this.listOptions) {
// console.log('value_defualt', this.localFormElement.value_defualt)
let finded = this.listOptions.find((item) => {
if (item.title == value) {
return item;
}
});
if (finded) {
mValue = finded.value;
// console.log('finded', finded)
} else {
mValue =
this.listOptions.length > 0 ? this.listOptions[0].value : "";
}
} else if (this.localFormElement?.value_save == "object") {
mValue = value.value;
} else mValue = value ?? "";
} else {
mValue = value ?? "";
}
//نیازی به ارسال تغییر در واچ نیست
if(this.localFormElement.value == value )
this.textValuePrev == value
this.textValue = mValue
// console.log(' this.textValue', this.localFormElement.key, this.textValue)
},
isUnixTimestamp(value) {
// Check if the value is a number
if (typeof value !== "number" && !/^\d+$/.test(value)) {
return false;
}
// Convert to number if it's a string
value = parseInt(value);
// Unix timestamps are non-negative integers
return !isNaN(value) && value >= 0 && value % 1 === 0;
},
sendTextValue() {
this.$emit("keydown", {
text: this.textValue,
key: this.keyValue,
isUpdate: this.isUpdateitem,
});
},
////ااز هیچا فراخوانی نمیشه لذا کامنت کردم
//فقط برای select
// takeSelectValue($event, type) {
// this.objectValue = {
// value: $event.target.value,
// text: $event.target.selectedOptions[0].text,
// };
// console.log('takeSelectValue',this.localFormElement.key , objectValue)
// this.$emit("take-select-object", {
// type: type,
// objectValue: this.objectValue,
// });
// },
},
};

View File

@ -1,5 +1,5 @@
import { mapState, mapActions } from "pinia";
import { useCommonStore } from "@stores/commonStore";
import { useCommonStore } from "~/stores/commonStore";
export default {
created() {

View File

@ -2,6 +2,11 @@
import fs from "fs-extra";
import path from "path";
import chat from "./routes/chat";
import haditha from "./routes/haditha";
import search from "./routes/search";
import research from "./routes/research";
const envs = import.meta.env;
let sassEnvVariables = "";
@ -13,20 +18,19 @@ for (let e in envs) {
// dynamically importing styles.
const buildName = import.meta.env.VITE_BUILD_NAME;
let mainStyle = `~/assets/common/scss/${buildName}-styles.scss`;
if (buildName == "hadith")
mainStyle = `~/systems/${buildName}_ui/assets/${buildName}/scss/${buildName}.css`;
export default defineNuxtConfig({
hooks: {
"build:before": () => {
if (buildName == "hadith") {
if (buildName == "haditha") {
const sourceDir = path.resolve(
__dirname,
`systems/${buildName}_ui/assets/${buildName}/images`
`systems/${buildName.substring(
0,
buildName.length - 1
)}_ui/assets/${buildName}/images`
); // Source folder (e.g., assets/images)
const targetDir = path.resolve(__dirname, "public/img"); // Target folder (e.g., public/images)
const targetDir = path.resolve(__dirname, `public/img/${buildName}`); // Target folder (e.g., public/images)
// Ensure the target directory exists
fs.ensureDirSync(targetDir);
@ -40,96 +44,15 @@ export default defineNuxtConfig({
"pages:extend"(pages) {
// Add custom routes
pages.push(
{
name: "search",
path: "/search",
file: "~/systems/search_ui/pages/search/index.vue",
},
{
name: "searchNavigation",
path: "/search/lists",
file: "~/systems/search_ui/pages/search/lists.vue",
},
{
name: "searchChart",
path: "/search/charts",
file: "~/systems/search_ui/pages/search/charts.vue",
},
{
name: "showEntity",
path: "/search/:key/:id/show",
file: "~/systems/search_ui/pages/search/(show)/[key]/[id]/index.vue",
},
{
name: "navigationView",
path: "/search/:key/:id/list",
file: "~/systems/search_ui/pages/search/(show)/[key]/[id]/index.vue",
},
{
name: "entityResearch",
path: "/search/:key/:id/research",
file: "~/systems/search_ui/pages/search/(show)/[key]/[id]/index.vue",
},
{
name: "entityResearch",
path: "/search/:key/:id/detail",
file: "~/systems/search_ui/pages/search/(show)/[key]/[id]/index.vue",
},
// --------------------- start: hadith routes ---------------------
{
name: "hadith",
path: "/hadith",
file: "~/systems/hadith_ui/pages/hadith/index.vue",
},
{
name: "hadithSearch",
path: "/hadith/search",
file: "~/systems/hadith_ui/pages/hadith/search/index.vue",
},
{
name: "hadithChatBot",
path: "/hadith/chat-bot",
file: "~/systems/hadith_ui/pages/hadith/chat-bot.vue",
},
{
name: "hadithFavorites",
path: "/hadith/favorites",
file: "~/systems/hadith_ui/pages/hadith/favorites/index.vue",
},
{
name: "hadithLibrary",
path: "/hadith/library",
file: "~/systems/hadith_ui/pages/hadith/library/index.vue",
},
{
name: "hadithLibraryShow",
path: "/hadith/library/:id/:slug?",
file: "~/systems/hadith_ui/pages/hadith/library/[id]/[slug]/index.vue",
},
{
name: "hadithAbout",
path: "/hadith/about-us",
file: "~/systems/hadith_ui/pages/hadith/public-pages/about-us.vue",
},
{
name: "hadithContact",
path: "/hadith/contact-us",
file: "~/systems/hadith_ui/pages/hadith/public-pages/ContactUs.vue",
},
{
name: "hadithRules",
path: "/hadith/rules",
file: "~/systems/hadith_ui/pages/hadith/public-pages/rules.vue",
}
// --------------------- end: hadith routes ---------------------
);
pages.push(...search, ...research, ...haditha, ...chat);
},
},
nitro: {
compressPublicAssets: true,
prerender: {
routes: ["/haditha/about-us", "/haditha/contact"], // Pre-render these routes
},
},
colorMode: {
@ -172,6 +95,17 @@ export default defineNuxtConfig({
"@nuxt/image",
// "@nuxtjs/supabase",
],
icon: {
customCollections: [
{
prefix: "haditha",
dir: `./systems/${buildName.substring(
0,
buildName.length - 1
)}_ui/assets/${buildName}/font-icons`,
},
],
},
// ui: {
// prefix: 'Nuxt', //Use the prefix option to change the prefix of the components. Default: U
// fonts: false, // Use the fonts option to enable or disable the @nuxt/fonts module. Default: true
@ -185,29 +119,10 @@ export default defineNuxtConfig({
// debug: process.env.NODE_ENV === 'development',
// mode: 'init'
// },
image: {
inject: true,
quality: 80,
format: ["webp", "jpeg", "jpg", "png", "gif", "avif"],
screens: {
xs: 320,
sm: 576,
md: 768,
lg: 1199,
xl: 1400,
xxl: 1600,
"2xl": 1920,
},
presets: {
avatar: {
modifiers: {
format: "jpg",
width: 25,
height: 24,
},
},
},
},
// image: {
// provider: "ipx",
// quality: 100,
// },
i18n: {
vueI18n: "./i18n.config.ts", // if you are using custom path, default
},
@ -233,7 +148,7 @@ export default defineNuxtConfig({
},
},
css: [
mainStyle,
customStyleLoader(),
// "vue3-persian-datetime-picker/src/picker/assets/scss/style.scss",
],
alias: {
@ -253,7 +168,8 @@ export default defineNuxtConfig({
"@pages": "~/pages",
"@search": "~/systems/search_ui",
"@research": "~/systems/research_ui",
"@hadith": "~/systems/hadith_ui",
"@haditha": "~/systems/hadith_ui",
"@chat": "~/systems/chat_ui",
},
vite: {
resolve: {},
@ -275,6 +191,10 @@ export default defineNuxtConfig({
},
},
},
build: {
analyze: true,
// transpile: ["echarts", "resize-detector", "echarts-wordcloud"],
},
// postcss: {
// plugins: {
// tailwindcss: {},
@ -419,3 +339,13 @@ export default defineNuxtConfig({
// console.log(runtimeConfig.public.apiBase)
// Resolve ~, ~~, @ and @@ aliases located within layers with respect to their layer source and root directories.
function customStyleLoader() {
let mainStyle = `~/assets/common/scss/${buildName}-styles.scss`;
if (buildName == "haditha")
mainStyle = `~/systems/${buildName.substring(
0,
buildName.length - 1
)}_ui/assets/${buildName}/scss/${buildName}.css`;
return mainStyle;
}

207
package-lock.json generated
View File

@ -17,6 +17,8 @@
"@vueup/vue-quill": "^1.2.0",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
"codemirror": "^6.0.1",
"cropperjs": "^2.0.0",
"docx": "^9.1.1",
"echarts": "^5.5.1",
"echarts-wordcloud": "^2.1.0",
@ -29,6 +31,8 @@
"i18next-vue": "^5.0.0",
"jalali-moment": "^3.3.11",
"jquery": "^3.7.1",
"lucide": "^0.477.0",
"lucide-vue-next": "^0.477.0",
"mammoth": "^1.8.0",
"mitt": "^3.0.1",
"npm": "^11.1.0",
@ -42,6 +46,7 @@
"vue-draggable-next": "^2.2.1",
"vue-echarts": "^7.0.3",
"vue-i18n": "^10.0.1",
"vue-image-upload-resize": "^2.3.0",
"vue-jalali-moment": "^1.0.0",
"vue-jstree": "^2.1.6",
"vue-multiselect": "^3.0.0",
@ -53,9 +58,12 @@
"vue3-persian-datetime-picker": "^1.2.2",
"vue3-tree-vue": "^2.0.11",
"vuejs-paginate": "^2.1.0",
"vuejs-paginate-next": "^1.0.2"
"vuejs-paginate-next": "^1.0.2",
"zod": "^3.24.2"
},
"devDependencies": {
"@iconify-json/lucide": "^1.2.28",
"@iconify-json/vscode-icons": "^1.2.16",
"@nuxt/test-utils": "^3.14.4",
"@nuxtjs/i18n": "^9.0.0-rc.2",
"@types/bootstrap": "^5.2.10",
@ -667,7 +675,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.3.tgz",
"integrity": "sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
@ -712,7 +719,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.3.tgz",
"integrity": "sha512-GSGfKxCo867P7EX1k2LoCrjuQFeqVgPGRRsSl4J4c0KMkD+k1y6WYvTQkzv0iZ8JhLJDujEvlnMchv4CZQLh3Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.35.0",
@ -724,7 +730,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.8.tgz",
"integrity": "sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
@ -748,6 +753,126 @@
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@cropper/element": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element/-/element-2.0.0.tgz",
"integrity": "sha512-lsthn0nQq73GExUE7Mg/ss6Q3RXADGDv055hxoLFwvl/wGHgy6ZkYlfLZ/VmgBHC6jDK5IgPBFnqrPqlXWSGBA==",
"license": "MIT",
"dependencies": {
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-canvas": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-canvas/-/element-canvas-2.0.0.tgz",
"integrity": "sha512-GPtGJgSm92crJhhhwUsaMw3rz2KfJWWSz7kRAlufFEV/EHTP5+6r6/Z1BCGRna830i+Avqbm435XLOtA7PVJwA==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-crosshair": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-crosshair/-/element-crosshair-2.0.0.tgz",
"integrity": "sha512-KfPfyrdeFvUC31Ws7ATtcalWWSaMtrC6bMoCipZhqbUOE7wZoL4ecDSL6BUOZxPa74awZUqfzirCDjHvheBfyw==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-grid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-grid/-/element-grid-2.0.0.tgz",
"integrity": "sha512-i78SQ0IJTLFveKX6P7svkfMYVdgHrQ8ZmmEw8keFy9n1ZVbK+SK0UHK5FNMRNI/gtVhKJOGEnK/zeyjUdj4Iyw==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-handle": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-handle/-/element-handle-2.0.0.tgz",
"integrity": "sha512-ZJvW+0MkK9E8xYymGdoruaQn2kwjSHFpNSWinjyq6csuVQiCPxlX5ovAEDldmZ9MWePPtWEi3vLKQOo2Yb0T8g==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-image": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-image/-/element-image-2.0.0.tgz",
"integrity": "sha512-9BxiTS/aHRmrjopaFQb9mQQXmx4ruhYHGkDZMVz24AXpMFjUY6OpqrWse/WjzD9tfhMFvEdu17b3VAekcAgpeg==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-selection": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-selection/-/element-selection-2.0.0.tgz",
"integrity": "sha512-ensNnbIfJsJ8bhbJTH/RXtk2URFvTOO4TvfRk461n2FPEC588D7rwBmUJxQg74IiTi4y1JbCI+6j+4LyzYBLCQ==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-image": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-shade": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-shade/-/element-shade-2.0.0.tgz",
"integrity": "sha512-jv/2bbNZnhU4W+T4G0c8ADocLIZvQFTXgCf2RFDNhI5UVxurzWBnDdb8Mx8LnVplnkTqO+xUmHZYve0CwgWo+Q==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-selection": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-viewer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-viewer/-/element-viewer-2.0.0.tgz",
"integrity": "sha512-zY+3VRN5TvpM8twlphYtXw0tzJL2VgzeK7ufhL1BixVqOdRxwP13TprYIhqwGt9EW/SyJZUiaIu396T89kRX8A==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-image": "^2.0.0",
"@cropper/element-selection": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/elements": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/elements/-/elements-2.0.0.tgz",
"integrity": "sha512-PQkPo1nUjxLFUQuHYu+6atfHxpX9B41Xribao6wpvmvmNIFML6LQdNqqWYb6LyM7ujsu71CZdBiMT5oetjJVoQ==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-crosshair": "^2.0.0",
"@cropper/element-grid": "^2.0.0",
"@cropper/element-handle": "^2.0.0",
"@cropper/element-image": "^2.0.0",
"@cropper/element-selection": "^2.0.0",
"@cropper/element-shade": "^2.0.0",
"@cropper/element-viewer": "^2.0.0"
}
},
"node_modules/@cropper/utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/utils/-/utils-2.0.0.tgz",
"integrity": "sha512-cprLYr+7kK3faGgoOsTW9gIn5sefDr2KwOmgyjzIXk+8PLpW8FgFKEg5FoWfRD5zMAmkCBuX6rGKDK3VdUEGrg==",
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
@ -1463,6 +1588,26 @@
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@iconify-json/lucide": {
"version": "1.2.28",
"resolved": "https://registry.npmjs.org/@iconify-json/lucide/-/lucide-1.2.28.tgz",
"integrity": "sha512-SPeAoh1YKhALpNSto/FBspf3GZ0eAr3ka/OI7cJJ5HljLzycIITv0s4Rd2+sRVzFtD1I991mJJmYohQeOSZ+Og==",
"dev": true,
"license": "ISC",
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify-json/vscode-icons": {
"version": "1.2.16",
"resolved": "https://registry.npmjs.org/@iconify-json/vscode-icons/-/vscode-icons-1.2.16.tgz",
"integrity": "sha512-hstc2yVq2UJ6v6FrgjftzXRvphGZBsKxvSeXoFLP1Hgx89TPZKrGE5SV6vqsoeIlLYaQ7OZbXmAoVGroTfGmVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify/collections": {
"version": "1.0.516",
"resolved": "https://registry.npmjs.org/@iconify/collections/-/collections-1.0.516.tgz",
@ -6302,6 +6447,12 @@
"integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==",
"license": "MIT"
},
"node_modules/blueimp-canvas-to-blob": {
"version": "3.29.0",
"resolved": "https://registry.npmjs.org/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz",
"integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==",
"license": "MIT"
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@ -6845,7 +6996,6 @@
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/commands": "^6.0.0",
@ -7112,8 +7262,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/croner": {
"version": "9.0.0",
@ -7133,6 +7282,16 @@
"cronstrue": "bin/cli.js"
}
},
"node_modules/cropperjs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-2.0.0.tgz",
"integrity": "sha512-TO2j0Qre01kPHbow4FuTrbdEB4jTmGRySxW49jyEIqlJZuEBfrvCTT0vC3eRB2WBXudDfKi1Onako6DKWKxeAQ==",
"license": "MIT",
"dependencies": {
"@cropper/elements": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/cross-fetch": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz",
@ -10934,6 +11093,21 @@
"dev": true,
"license": "MIT"
},
"node_modules/lucide": {
"version": "0.477.0",
"resolved": "https://registry.npmjs.org/lucide/-/lucide-0.477.0.tgz",
"integrity": "sha512-lEgt1ni9sKohbLsYWSnM3mKVRkvUrJ3Ijj01RAdM5F0iyvxkCcy3gPrb1G2ux6zE4y20ptcitfMa0OOFXI57gw==",
"license": "ISC"
},
"node_modules/lucide-vue-next": {
"version": "0.477.0",
"resolved": "https://registry.npmjs.org/lucide-vue-next/-/lucide-vue-next-0.477.0.tgz",
"integrity": "sha512-C7azIKO7aJKf5MD7OIzV7NRDtnjXH3KSXfyJgGqRIMemuMzW/9esuMxDXIXDhBZcJgRtXMUN0FcJwOJZQ8SywA==",
"license": "ISC",
"peerDependencies": {
"vue": ">=3.0.1"
}
},
"node_modules/magic-regexp": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/magic-regexp/-/magic-regexp-0.8.0.tgz",
@ -20410,6 +20584,16 @@
"vue": "^3.0.0"
}
},
"node_modules/vue-image-upload-resize": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/vue-image-upload-resize/-/vue-image-upload-resize-2.3.0.tgz",
"integrity": "sha512-ElzyAVCluiDt8xSZkSK9FbuLY5mr23JlO4cHyyRo86RfEGLWtvXrTq7vYOgDstT8rNBmnTgNCkPKc0rpmyl1yA==",
"license": "MIT",
"dependencies": {
"blueimp-canvas-to-blob": "^3.14.0",
"vue": "^2.5.17"
}
},
"node_modules/vue-jalali-moment": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/vue-jalali-moment/-/vue-jalali-moment-1.0.0.tgz",
@ -21013,6 +21197,15 @@
"node": ">= 14"
}
},
"node_modules/zod": {
"version": "3.24.2",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zrender": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz",

View File

@ -11,8 +11,8 @@
"postinstall": "nuxt prepare",
"dev-tavasi": "env-cmd -f .env.tavasi nuxt dev --host --inspect",
"dev-monir": "env-cmd -f .env.monir nuxt dev --host --inspect",
"dev-hadith": "env-cmd -f .env.hadith nuxt dev --host --inspect",
"build-hadith": "env-cmd -f .env.hadith nuxt build"
"dev-haditha": "env-cmd -f .env.haditha nuxt dev --host --inspect",
"build-haditha": "env-cmd -f .env.haditha nuxt build"
},
"dependencies": {
"@nuxt/image": "^1.8.1",
@ -25,6 +25,8 @@
"@vueup/vue-quill": "^1.2.0",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
"codemirror": "^6.0.1",
"cropperjs": "^2.0.0",
"docx": "^9.1.1",
"echarts": "^5.5.1",
"echarts-wordcloud": "^2.1.0",
@ -37,6 +39,8 @@
"i18next-vue": "^5.0.0",
"jalali-moment": "^3.3.11",
"jquery": "^3.7.1",
"lucide": "^0.477.0",
"lucide-vue-next": "^0.477.0",
"mammoth": "^1.8.0",
"mitt": "^3.0.1",
"npm": "^11.1.0",
@ -50,6 +54,7 @@
"vue-draggable-next": "^2.2.1",
"vue-echarts": "^7.0.3",
"vue-i18n": "^10.0.1",
"vue-image-upload-resize": "^2.3.0",
"vue-jalali-moment": "^1.0.0",
"vue-jstree": "^2.1.6",
"vue-multiselect": "^3.0.0",
@ -61,9 +66,12 @@
"vue3-persian-datetime-picker": "^1.2.2",
"vue3-tree-vue": "^2.0.11",
"vuejs-paginate": "^2.1.0",
"vuejs-paginate-next": "^1.0.2"
"vuejs-paginate-next": "^1.0.2",
"zod": "^3.24.2"
},
"devDependencies": {
"@iconify-json/lucide": "^1.2.28",
"@iconify-json/vscode-icons": "^1.2.16",
"@nuxt/test-utils": "^3.14.4",
"@nuxtjs/i18n": "^9.0.0-rc.2",
"@types/bootstrap": "^5.2.10",

View File

@ -5,7 +5,7 @@
<!-- majles dashbaord -->
<search-section v-if="buildName() == 'majles'"></search-section>
<!-- monir dashboard -->
<default-dashboard></default-dashboard>
<default-dashboard v-else></default-dashboard>
<!-- </ClientOnly> -->
</NuxtLayout>
</div>

View File

@ -13,7 +13,7 @@ export default defineNuxtPlugin((nuxtApp) => {
onRequest({ request, options, error }) {
options.baseURL =
import.meta.env.VITE_BASE_URL +
import.meta.env.VITE_API_NAME +
import.meta.env.NUXT_PUBLIC_API_NAME+
options.baseURL;
if (token) {

View File

@ -3,12 +3,13 @@ import { useStorage } from "@vueuse/core";
export default defineNuxtPlugin((nuxtApp) => {
let token = useStorage("id_token", "GuestAccess").value;
const config = useRuntimeConfig();
const api = $fetch.create({
onRequest({ request, options, error }) {
options.baseURL =
import.meta.env.VITE_BASE_URL +
import.meta.env.VITE_API_NAME +
config.public.NUXT_PUBLIC_BASE_URL +
config.public.NUXT_PUBLIC_API_NAME +
options.baseURL;
if (token) {
@ -40,13 +41,14 @@ export default defineNuxtPlugin((nuxtApp) => {
getRequest: (url, options = {}) => api(url, { method: "GET", ...options }),
postRequest: (url, body, options = {}) =>
api(url, { method: "POST", body, ...options }),
deleteRequest: (url, options = {}) => api(url, { method: "DELETE", ...options }),
deleteRequest: (url, options = {}) =>
api(url, { method: "DELETE", ...options }),
};
// Expose to useNuxtApp().$api and useNuxtApp().$http
return {
provide: {
http
http,
},
};
});

View File

@ -4,23 +4,28 @@
// createApp.use(Vuelidate);
import { useVuelidate } from '@vuelidate/core'
import { required, minLength } from '@vuelidate/validators'
// import { required, minLength } from '@vuelidate/validators'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(() => {
// // Example of global setup, but typically you'd handle this in the component
// const state = reactive({
// firstName: ''
// })
// const rules = {
// firstName: {
// required,
// minLength: minLength(2)
// }
// }
// const v$ = useVuelidate(rules, state)
// return v$;
// // Return the v$ instance or any other necessary setup
})
})
// export default defineNuxtPlugin((nuxtApp) => {
// nuxtApp.vueApp.use((useVuelidate) => {
// // // Example of global setup, but typically you'd handle this in the component
// // const state = reactive({
// // firstName: ''
// // })
// // const rules = {
// // firstName: {
// // required,
// // minLength: minLength(2)
// // }
// // }
// // const v$ = useVuelidate(rules, state)
// // return v$;
// // // Return the v$ instance or any other necessary setup
// })
// })
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.provide('vuelidate', useVuelidate)
})

View File

Before

Width:  |  Height:  |  Size: 490 KiB

After

Width:  |  Height:  |  Size: 490 KiB

View File

Before

Width:  |  Height:  |  Size: 783 KiB

After

Width:  |  Height:  |  Size: 783 KiB

View File

Before

Width:  |  Height:  |  Size: 263 KiB

After

Width:  |  Height:  |  Size: 263 KiB

View File

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 203 KiB

View File

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 186 KiB

View File

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 198 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 550 B

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 269 KiB

After

Width:  |  Height:  |  Size: 269 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 304 KiB

After

Width:  |  Height:  |  Size: 304 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,9 @@
<svg width="16" height="27" viewBox="0 0 16 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.79031 13.8107C5.19508 12.8313 7.08122 10.8959 8 8.47239C8.91878 10.8959 10.8049 12.8313 13.2097 13.8107L13.5599 13.9533C10.9698 14.8404 8.92567 16.8559 8 19.427C7.07433 16.8559 5.03017 14.8404 2.44014 13.9533L2.79031 13.8107Z" stroke="url(#paint0_linear_67_6564)" stroke-linejoin="round"/>
<defs>
<linearGradient id="paint0_linear_67_6564" x1="15.0921" y1="7" x2="0.0931545" y2="7.27651" gradientUnits="userSpaceOnUse">
<stop stop-color="#D284FF"/>
<stop offset="1" stop-color="#4D00FF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 630 B

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Some files were not shown because too many files have changed in this diff Show More