Pull and merge from shadi and baghi

This commit is contained in:
mustafa-rezae 2025-04-20 16:01:16 +03:30
commit 2cff09d0a0
11 changed files with 431 additions and 253 deletions

View File

@ -1,7 +1,7 @@
export default { export default {
search: { search: {
list: "repo/monir/search/@index_key/@search_type/@type_key/@listkey/@field_collapsed/@offset/@limit/@q=none", list: "repo/monir/search/@index_key/@search_type/@type_key/@listkey/@field_collapsed/@offset/@limit/@q=none",
show: "repo/public/get/byid/@index_key/@id", show: "repo/public/get/byid/@index_key/@id/tbookmark",
synonym: "synonym/get/words", synonym: "synonym/get/words",
prevNextHadith: "monir/next/@index_key/@vol_id/@parag_order/@step", prevNextHadith: "monir/next/@index_key/@vol_id/@parag_order/@step",
getDataTree: "@appname/book/tree/@offset/@limit/@vol_id/@q" getDataTree: "@appname/book/tree/@offset/@limit/@vol_id/@q"

View File

@ -56,7 +56,7 @@ const search_type = ref("normal");
const type_key = ref("hadith"); const type_key = ref("hadith");
const typeModelValue = ref("normal"); const typeModelValue = ref("normal");
const typeModelValueFa = ref(""); const typeModelValueFa = ref("");
const showclearButton = ref(false); // const showclearButton = ref(false);
// If you want to share state across multiple components, // If you want to share state across multiple components,
// you can use the same key in useState. Nuxt will ensure // you can use the same key in useState. Nuxt will ensure
@ -168,17 +168,17 @@ const clearSimilar = () => {
// const onUpdateModel = (newVal: boolean | InputMenuItem | any) => { // const onUpdateModel = (newVal: boolean | InputMenuItem | any) => {
// console.info("onUpdateModel", newVal); // console.info("onUpdateModel", newVal);
// }; // };
const searchButtonIcon = computed(() => { // const searchButtonIcon = computed(() => {
return showclearButton.value ? "i-lucide-x" : "i-haditha-search"; // return showclearButton.value ? "i-lucide-x" : "i-haditha-search";
}); // });
const handleSearchClearButton = ()=>{ // const handleSearchClearButton = ()=>{
showclearButton.value = true; // showclearButton.value = true;
sendQuery(); // sendQuery();
} // }
const onKeyDown = () => { const onKeyDown = () => {
showclearButton.value = false; // showclearButton.value = false;
// clearTimeout(typingTimer.value); // clearTimeout(typingTimer.value);
}; };
@ -234,6 +234,12 @@ const sendQuery = async (payload = {}) => {
return await httpService return await httpService
.postRequest(url, payload) .postRequest(url, payload)
.then((res) => { .then((res) => {
emit("response-ready", {
res: res,
searchQuery: searchTerm.value,
disableAutoRedirect: true,
});
// pass res and search query to the parent. // pass res and search query to the parent.
loading.value = false; loading.value = false;
@ -243,31 +249,29 @@ const sendQuery = async (payload = {}) => {
// close the history dropdown menu // close the history dropdown menu
open.value = false; open.value = false;
// show clear button // show clear button
if (showclearButton.value) { // if (showclearButton.value) {
searchTerm.value = ""; // searchTerm.value = "";
emit("response-ready", { // emit("response-ready", {
res: { // res: {
hits:{ // hits:{
total:0, // total:0,
hits:[] // hits:[]
} // }
}, // },
searchQuery: searchTerm.value, // searchQuery: searchTerm.value,
disableAutoRedirect: true, // disableAutoRedirect: true,
}); // });
} else { // } else {
emit("response-ready", { // emit("response-ready", {
res: res, // res: res,
searchQuery: searchTerm.value, // searchQuery: searchTerm.value,
disableAutoRedirect: true, // disableAutoRedirect: true,
}); // });
} // }
showclearButton.value = !showclearButton.value; // showclearButton.value = !showclearButton.value;
// store search phrase // store search phrase
useStorage("searchPhrase", searchTerm.value); useStorage("searchPhrase", searchTerm.value);
@ -444,10 +448,10 @@ onMounted(() => {
</div> </div>
<UButton <UButton
class="my-trailing-button" class="my-trailing-button"
:class="{ 'close-mode': showclearButton }" @click.prevent="sendQuery"
@click.prevent="handleSearchClearButton" icon="i-haditha-search"
:icon="searchButtonIcon"
> >
<!-- // @click.prevent="handleSearchClearButton" // :icon="searchButtonIcon" -->
<!-- <UIcon name="i-lucide-search" /> --> <!-- <UIcon name="i-lucide-search" /> -->
</UButton> </UButton>
<!-- </client-only> --> <!-- </client-only> -->
@ -605,7 +609,7 @@ onMounted(() => {
{{ state.phrase.label }} {{ state.phrase.label }}
<UIcon <UIcon
v-if="search_type == 'phrase'" v-if="search_type == 'phrase'"
@click.self="search_type = 'normal'" @click.self="setType('normal')"
name="i-haditha-close-bg-circle" name="i-haditha-close-bg-circle"
size="20px" size="20px"
> >

View File

@ -33,6 +33,7 @@ const goToLibraryShow = (item) => {
<UCard <UCard
v-if="props.list?.length" v-if="props.list?.length"
v-for="(item, index) in props.list" v-for="(item, index) in props.list"
class="mx-auto"
:key="index" :key="index"
variant="solid" variant="solid"
:ui="{ :ui="{
@ -114,5 +115,16 @@ const goToLibraryShow = (item) => {
text-align: right; text-align: right;
color: #444444; color: #444444;
} }
@media screen and (max-width: 719.99px) {
.library-list {
height: calc(100dvh - 13em);
}
.page-header {
margin-top: 4em;
margin-right: 2em;
margin-bottom: 1em;
}
}
} }
</style> </style>

View File

@ -126,7 +126,7 @@ const leftItem = computed(() => [
const isMobile = ref(false); const isMobile = ref(false);
const rerenderNavigation = ref(1); const rerenderNavigation = ref(1);
const { isAuthenticatedGetter } = useAuthStore(); const { isAuthenticatedGetter, isRealUserGetter } = useAuthStore();
const setMenu = () => { const setMenu = () => {
if (isAuthenticatedGetter) { if (isAuthenticatedGetter) {
@ -152,6 +152,9 @@ const setMenu = () => {
setMenu(); setMenu();
// if(!(isAuthenticatedGetter && isRealUserGetter))
// items.value = items.value.filter((item) => item.to != "/haditha/favorites");
onMounted(() => { onMounted(() => {
if (window?.outerWidth < 991) { if (window?.outerWidth < 991) {
isMobile.value = true; isMobile.value = true;
@ -340,7 +343,7 @@ onMounted(() => {
} }
} }
@media screen and (max-width: 719.99px) { @media screen and (max-width: 991.99px) {
.fixed { .fixed {
.my-navbar { .my-navbar {
height: 4.8em; // 76px; height: 4.8em; // 76px;

View File

@ -1,4 +1,6 @@
<script setup> <script setup>
import hadithaApi from "@haditha/apis/hadithaApi";
const httpService = useNuxtApp()["$http"];
const props = defineProps({ const props = defineProps({
list: { list: {
default() { default() {
@ -16,6 +18,7 @@ const props = defineProps({
}, },
}); });
const router = useRouter(); const router = useRouter();
const route = useRoute();
// const modal = useModal(); // const modal = useModal();
// const isModalOpen = ref(false); // const isModalOpen = ref(false);
@ -24,7 +27,7 @@ function openModal(selectedItem) {
// modal.open(SearchShow, { title: "Welcome" }); // modal.open(SearchShow, { title: "Welcome" });
// isModalOpen.value = true; // isModalOpen.value = true;
const slug = selectedItem._source.content const slug = selectedItem?._source?.content
.split(" ") .split(" ")
.filter((v, i) => i < 4) .filter((v, i) => i < 4)
.join("-"); .join("-");
@ -32,7 +35,7 @@ function openModal(selectedItem) {
router.push({ router.push({
name: "hadithaSearchShow", name: "hadithaSearchShow",
params: { params: {
id: selectedItem._source.id, id: selectedItem?._source?.id,
slug: slug, slug: slug,
}, },
query: { query: {
@ -55,44 +58,83 @@ function openModal(selectedItem) {
// const SearchShow = defineAsyncComponent(() => // const SearchShow = defineAsyncComponent(() =>
// import("@haditha/components/haditha/search-page/SearchShow.vue") // import("@haditha/components/haditha/search-page/SearchShow.vue")
// ); // );
const removeFromFavorites = async (item = {}, index = 0) => {
let url = repoUrl() + hadithaApi.favorite.deleteByRefid;
url = url.replace("{{data_type}}", "bookmark");
url = url.replace("{{index_key}}", "dhparag");
url = url.replace("{{ref_id}}", item._id);
const formData = {
ref_id: item._id,
title: item?._source?.title,
};
httpService.postRequest(url, formData).then((res) => {
// this.updateListAnswer(index, "tbookmark", 0);
});
};
</script> </script>
<template> <template>
<div <div class="search-list firefox-scrollbar">
v-if="props?.list?.length" <div
class="search-list-item" v-if="props?.list?.length"
v-for="(item, index) in props.list" class="search-list-item"
:key="index" v-for="(item, index) in props.list"
> :key="index"
<p @click="openModal(item)" class="from-person"> >
{{ <div class="flex justify-between mt-4 mb-2">
item?._source?.meta?.hadith_masoum ?? <a
item?._source?.meta?.hadith_sanad ?? :href="`/haditha/search/${item?._source?.id}/${
"بدون عنوان" item?._source?.meta?.hadith_masoum ??
}} item?._source?.meta?.hadith_sanad
</p> }`"
<p @click="openModal(item)" class="arabic-text"> @click.prevent="openModal(item)"
{{ item?._source?.content_ar }} class="from-person block"
</p> >
<p {{
class="persian-text" item?._source?.meta?.hadith_masoum ??
v-html="item?.highlight?.['content.fa'] ?? item?._source?.content" item?._source?.meta?.hadith_sanad
></p> }}
<div class="flex justify-end"> </a>
<p class="reference"> <UButton
{{ item?._source?.address?.vol_title }}، صفحه v-if="route.name == 'hadithaFavorites'"
{{ item?._source?.address?.page_num }} @click="removeFromFavorites(item)"
</p> variant="ghost"
</div> color="error"
</div> class="copy-btn"
label="حذف"
/>
</div>
<no-data <a
class="h-full w-full flex flex-col justify-center items-center" @click.prevent="openModal(item)"
v-else class="arabic-text block"
> :href="`/haditha/search/${item?._source?.id}/${item?._source?.content_ar}`"
<img fit="auto" quality="80" placeholder :src="props.noDataIcon" /> >
<p class="no-data-text">{{ props.noDataText }}</p> {{ item?._source?.content_ar }}
</no-data> </a>
<a
@click.prevent="openModal(item)"
:href="`/haditha/search/${item?._source?.id}/${item?._source?.content}`"
class="persian-text block"
v-html="item?.highlight?.['content.fa'] ?? item?._source?.content"
></a>
<div class="flex justify-end">
<p class="reference">
{{ item?._source?.address?.vol_title }}، صفحه
{{ item?._source?.address?.page_num }}
</p>
</div>
</div>
<no-data
class="h-full w-full flex flex-col justify-center items-center"
v-else
>
<img fit="auto" quality="80" placeholder :src="props.noDataIcon" />
<p class="no-data-text">{{ props.noDataText }}</p>
</no-data>
</div>
<!-- <UModal <!-- <UModal
v-model:open="isModalOpen" v-model:open="isModalOpen"
@ -147,11 +189,77 @@ function openModal(selectedItem) {
color: var(--ui-color-two); color: var(--ui-color-two);
margin-bottom: 0.5em; margin-bottom: 0.5em;
&:hover, .search-list-item {
&:focus, .from-person {
&:active { font-family: IRANSansX;
cursor: pointer; font-weight: 300;
background-color: #fafafa; font-size: 0.75rem; /*12px*/
line-height: 1.125rem; /*18px*/
letter-spacing: 0%;
text-align: right;
color: #00a762; /* #4be8ae 7.38% */
margin-bottom: 0.5em;
&:hover,
&:focus,
&:active {
cursor: pointer;
background-color: #fafafa;
}
}
.arabic-text {
font-family: Takrim;
font-weight: 400;
font-size: 1.125rem; /*18px*/
line-height: 2rem; /*23px*/
letter-spacing: 0%;
text-align: right;
color: var(--ui-color-two);
margin-bottom: 0.5em;
&:hover,
&:focus,
&:active {
cursor: pointer;
background-color: #fafafa;
}
}
.persian-text {
font-family: Takrim;
font-weight: 400;
font-size: 1rem; /*16px*/
line-height: 1.375rem; /*22px*/
letter-spacing: 0%;
text-align: right;
color: #626b84;
margin-bottom: 0.5em;
}
.reference {
height: 24px;
gap: 4px;
padding-top: 0.25em; /*4px*/
padding-right: 0.5em; /*8px*/
padding-bottom: 0.25em; /*4px*/
padding-left: 0.5em; /*8px*/
border-radius: 6px; /*18px*/
border-width: 0.5px;
border: 0.5px solid #d9d9d9;
font-family: IRANSansX;
font-weight: 300;
font-size: 0.625rem; /*10px*/
line-height: 0.9rem; /*15px*/
letter-spacing: 0%;
text-align: right;
color: #8a92a8;
&:hover,
&:focus,
&:active {
cursor: pointer;
background-color: #fafafa;
}
}
} }
} }
.persian-text { .persian-text {
@ -177,8 +285,8 @@ function openModal(selectedItem) {
font-family: IRANSansX; font-family: IRANSansX;
font-weight: 300; font-weight: 300;
font-size: 0.625rem; /*10px*/ font-size: 1rem;
line-height: 0.9rem; /*15px*/ line-height: 1.5rem; /*24px*/
letter-spacing: 0%; letter-spacing: 0%;
text-align: right; text-align: right;
color: #8a92a8; color: #8a92a8;
@ -267,4 +375,28 @@ function openModal(selectedItem) {
.modal-overlay { .modal-overlay {
background: #00000033; background: #00000033;
} }
</style> --> .copy-btn {
padding: 0.2em 1em;
/* width: 44px; */
/* height: 24px; */
gap: 4px;
border-radius: 6px;
border-width: 0.5px;
padding-top: 4px;
padding-right: 12px;
padding-bottom: 4px;
padding-left: 12px;
/* background: #ffffff; */
/* border: 0.5px solid #d9d9d9; */
font-family: IRANSansX;
font-weight: 300;
font-size: 12px;
line-height: 18px;
letter-spacing: 0%;
text-align: right;
/* color: #a71111; */
}
</style>

View File

@ -15,6 +15,9 @@ const router = useRouter();
import hadithaApi from "@haditha/apis/hadithaApi"; import hadithaApi from "@haditha/apis/hadithaApi";
import type { HadithResponseModel, Hits } from "@haditha/types/hadithType"; import type { HadithResponseModel, Hits } from "@haditha/types/hadithType";
import type { Hit } from "~/systems/hadith_ui/types/hadithType"; import type { Hit } from "~/systems/hadith_ui/types/hadithType";
import headLinks from "@haditha/json/haditha/headLinks";
import headMetas from "@haditha/json/haditha/headMetas";
// #endregion imports // #endregion imports
// #region meta // #region meta
@ -28,94 +31,12 @@ useHead({
title: `${import.meta.env.VITE_HADITH_PAGE_TITLE} | ${route.params.slug}`, title: `${import.meta.env.VITE_HADITH_PAGE_TITLE} | ${route.params.slug}`,
meta: [ meta: [
{ name: "description", content: "کاوش با هوش مصنوعی در احادیث اسلامی" }, { name: "description", content: "کاوش با هوش مصنوعی در احادیث اسلامی" },
{ ...headMetas,
name: "msapplication-TileImage",
content: "/img/haditha/fav-icons/ms-icon-144x144.png",
},
{ name: "theme-color", content: "#ffffff" },
], ],
bodyAttrs: { bodyAttrs: {
class: import.meta.env.VITE_HADITH_SYSTEM, class: import.meta.env.VITE_HADITH_SYSTEM,
}, },
link: [ link: headLinks,
{
rel: "icon",
type: "image/x-icon",
href: "/img/haditha/fav-icons/favicon.ico",
},
{ rel: "manifest", href: "/img/haditha/fav-icons/manifest.json" },
// rel: icon
{
rel: "icon",
type: "image/png",
sizes: "16x16",
href: "/img/haditha/fav-icons/favicon-16x16.png",
},
{
rel: "icon",
type: "image/png",
sizes: "32x32",
href: "/img/haditha/fav-icons/favicon-32x32.png",
},
{
rel: "icon",
type: "image/png",
sizes: "96x96",
href: "/img/haditha/fav-icons/favicon-96x96.png",
},
{
rel: "icon",
sizes: "192x192",
type: "image/png",
href: "/img/haditha/fav-icons/android-icon-192x192.png",
},
// rel: apple
{
rel: "apple-touch-icon",
sizes: "57x57",
href: "/img/haditha/fav-icons/apple-icon-57x57.png",
},
{
rel: "apple-touch-icon",
sizes: "60x60",
href: "/img/haditha/fav-icons/android-icon-60x60.png",
},
{
rel: "apple-touch-icon",
sizes: "72x72",
href: "/img/haditha/fav-icons/android-icon-72x72.png",
},
{
rel: "apple-touch-icon",
sizes: "76x76",
href: "/img/haditha/fav-icons/android-icon-76x76.png",
},
{
rel: "apple-touch-icon",
sizes: "114x114",
href: "/img/haditha/fav-icons/android-icon-114x114.png",
},
{
rel: "apple-touch-icon",
sizes: "120x120",
href: "/img/haditha/fav-icons/android-icon-120x120.png",
},
{
rel: "apple-touch-icon",
sizes: "144x144",
href: "/img/haditha/fav-icons/android-icon-144x144.png",
},
{
rel: "apple-touch-icon",
sizes: "152x152",
href: "/img/haditha/fav-icons/android-icon-152x152.png",
},
{
rel: "apple-touch-icon",
sizes: "180x180",
href: "/img/haditha/fav-icons/android-icon-180x180.png",
},
],
}); });
// #endregion imports // #endregion imports
// #region props // #region props
@ -155,6 +76,7 @@ const props = defineProps({
const isModalOpen = ref(false); const isModalOpen = ref(false);
const loading = ref(false); const loading = ref(false);
const httpService = useNuxtApp()["$http"]; const httpService = useNuxtApp()["$http"];
const page_num = ref(1);
const state = reactive({ const state = reactive({
selectedItem: [] as Hit[], selectedItem: [] as Hit[],
@ -214,27 +136,13 @@ const state = reactive({
const pageIsLessThanOne = computed(() => { const pageIsLessThanOne = computed(() => {
console.info("pageIsLessThanOne"); console.info("pageIsLessThanOne");
let isDisabled = true; return page_num.value <= 1;
if (state.selectedItem.length)
return state.selectedItem[0]._source.address.page_num <= 1;
return isDisabled;
}); });
const pageIsBiggerThanTotal = computed(() => { const pageIsBiggerThanTotal = computed(() => {
console.info("pageIsBiggerThanTotal"); console.info("pageIsBiggerThanTotal");
const page_count = +route.query.page_count;
let isDisabled = true; return page_num.value + 1 >= page_count;
if (
state.selectedItem.length &&
state.selectedItem[0]._source.address.page_num
)
return (
state.selectedItem?.[0]._source.address.page_num + 1 >=
+route.query.page_count
);
return isDisabled;
}); });
const page_num = ref(1);
// #endregion refs and reactives // #endregion refs and reactives
// #region methods // #region methods
@ -242,12 +150,16 @@ const fetchData = async () => {
if (loading.value) return; if (loading.value) return;
loading.value = true; loading.value = true;
let page_first = +route.query.page_first;
const volId = route.params.id;
let url = repoUrl() + hadithaApi.library.show; let url = repoUrl() + hadithaApi.library.show;
url = url.replace("@appname", "monir"); url = url.replace("@appname", "monir");
url = url.replace("@page_start", route.query.page_first); url = url.replace("@page_start", 0);
url = url.replace("@page_end", +route.query.page_first + 1); url = url.replace("@page_end", 1);
url = url.replace("@vol_id", route.params.id.toString()); url = url.replace("@vol_id", volId);
console.info("urlurlurlurl", url);
// fetch search list from backend(ssr) // fetch search list from backend(ssr)
const { data, status, error, refresh, clear } = const { data, status, error, refresh, clear } =
await useHadithaSearchComposable<HadithResponseModel>(url, { await useHadithaSearchComposable<HadithResponseModel>(url, {
@ -292,45 +204,41 @@ const handlePagination = (
if (loading.value) return; if (loading.value) return;
loading.value = true; loading.value = true;
if (state.selectedItem?.length) { const volId = route.params.id;
const isPageBiggerThanOne = const page_count = +route.query.page_count;
state.selectedItem[0]._source.address.page_num + prevNextIndicator > 0; const page_first = +route.query.page_first;
const isPageLessThanTotal = const isPageBiggerThanOne = +page_num.value + prevNextIndicator > 0;
state.selectedItem[0]._source.address.page_num + prevNextIndicator < const isPageLessThanTotal = +page_num.value + prevNextIndicator < page_count;
+route.query.page_count;
if (!(isPageBiggerThanOne && isPageLessThanTotal)) return; if (!(isPageBiggerThanOne && isPageLessThanTotal)) return;
}
let url = repoUrl() + hadithaApi.library.prevNextHadith; let url = repoUrl() + hadithaApi.library.prevNextHadith;
url = url.replace("@index_key", "dhparag"); url = url.replace("@index_key", "dhparag");
url = url.replace("@vol_id", state.selectedItem?.[0]._source.address.vol_id); url = url.replace("@vol_id", volId);
url = url.replace( url = url.replace("@page_num", (pageNumber ?? page_num.value).toString());
"@page_num",
(pageNumber ?? state.selectedItem?.[0]._source.address.page_num).toString()
);
url = url.replace("@step", prevNextIndicator.toString()); url = url.replace("@step", prevNextIndicator.toString());
page_num.value += prevNextIndicator;
httpService httpService
.getRequest(url) .getRequest(url)
.then((res: HadithResponseModel) => { .then((res: HadithResponseModel) => {
state.selectedItem = res.hits.hits; state.selectedItem = res.hits.hits;
page_num.value = res.hits.hits[0]._source.address.page_num ?? 1;
}) })
.finally(() => (loading.value = false)); .finally(() => (loading.value = false));
}; };
const handlePageChange = () => { const handlePageChange = () => {
handlePagination(1, page_num.value); handlePagination(1, +page_num.value);
}; };
const getDataTree = () => { const getDataTree = () => {
let id = route.params.id; const volId = route.params.id;
let url = repoUrl() + hadithaApi.search.getDataTree; let url = repoUrl() + hadithaApi.search.getDataTree;
url = url.replace("@appname", "monir"); url = url.replace("@appname", "monir");
url = url.replace("@offset", 0); url = url.replace("@offset", 0);
url = url.replace("@limit", 10000); url = url.replace("@limit", 10000);
url = url.replace("@vol_id", id); url = url.replace("@vol_id", volId);
url = url.replace("@q", "none"); url = url.replace("@q", "none");
httpService.getRequest(url).then((res) => { httpService.getRequest(url).then((res) => {
@ -350,6 +258,9 @@ const prepareTreeData = (data) => {
getDataTree(); getDataTree();
onMounted(() => {
page_num.value = +route.query.page_first;
});
// #endregion methods // #endregion methods
// components declaration // components declaration
@ -684,7 +595,7 @@ const UTree = defineAsyncComponent(
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 3em /*36px*/; width: 4em /*36px*/;
height: 3em /*36px*/; height: 3em /*36px*/;
gap: 4px; gap: 4px;
border-radius: 12px; border-radius: 12px;

View File

@ -119,7 +119,10 @@ const CardList = defineAsyncComponent(
<img fit="auto" quality="80" src="/img/haditha/haditha-title.svg" /> <img fit="auto" quality="80" src="/img/haditha/haditha-title.svg" />
</div> </div>
<div ref="el" class="library-list grid grid-cols-5 gap-x-28 gap-y-12"> <div
ref="el"
class="library-list grid grid-cols-2 gap-x-15 gap-y-12 md:grid-cols-3 md:gap-x-28 md:gap-y-12 lg:grid-cols-5 lg:gap-x-28 lg:gap-y-12 mx-6"
>
<!-- Client-side loaded content --> <!-- Client-side loaded content -->
<card-list <card-list
v-if="loadedItems.length" v-if="loadedItems.length"
@ -139,6 +142,7 @@ const CardList = defineAsyncComponent(
background: #f7fffd; background: #f7fffd;
.library-list-contianer { .library-list-contianer {
height: 100%;
margin-top: 10em; margin-top: 10em;
max-width: 1200px; max-width: 1200px;
width: 100%; width: 100%;
@ -176,4 +180,19 @@ const CardList = defineAsyncComponent(
} }
} }
} }
@media screen and (max-width: 991.99px) {
.search-box-container {
padding-top: 0em;
}
.library-list {
height: calc(100dvh - 13em);
}
.page-header {
margin-top: 4em;
margin-right: 2em;
margin-bottom: 1em;
}
}
</style> </style>

View File

@ -30,7 +30,9 @@ const NavigationMenu = defineAsyncComponent(() =>
<UContainer class="page-inner-container sm:px-6 lg:px-4"> <UContainer class="page-inner-container sm:px-6 lg:px-4">
<navigation-menu></navigation-menu> <navigation-menu></navigation-menu>
<div class="page-header pt-38 pb-4 flex justify-between items-center"> <div
class="page-header pt-10 md:pt-10 lg:pt-38 pb-4 flex justify-between items-center"
>
<div class="flex items-center"> <div class="flex items-center">
<h1 class="m-0 title">درباره</h1> <h1 class="m-0 title">درباره</h1>
<img <img
@ -42,36 +44,36 @@ const NavigationMenu = defineAsyncComponent(() =>
</div> </div>
</div> </div>
<div class="page-content p-6"> <div class="page-content p-5">
<p> <p>
حدیثا، یک موتور جستجوی پیشرفته و تخصصی در حوزه احادیث اسلامی است که حدیثا، یک موتور جستجوی پیشرفته و تخصصی در حوزه احادیث اسلامی است که
با هدف دسترسی آسان و سریع به منابع معتبر حدیثی طراحی شده است. این با هدف دسترسی آسان و سریع به منابع معتبر حدیثی طراحی شده است. این
پلتفرم، بستری است برای پژوهشگران، علاقهمندان به معارف اسلامی، و پلتفرم، بستری است برای پژوهشگران، علاقهمندان به معارف اسلامی، و
تمامی افرادی که به دنبال دسترسی به گنجینه ارزشمند احادیث نبوی و تمامی افرادی که به دنبال دسترسی به گنجینه ارزشمند احادیث نبوی و
اهلبیت (علیهمالسلام) هستند. اهلبیت (علیهمالسلام) هستند.
</p> </p>
<p> <p>
ما در حدیثا تلاش میکنیم تا با بهرهگیری از فناوریهای نوین و منابع ما در حدیثا تلاش میکنیم تا با بهرهگیری از فناوریهای نوین و منابع
اصیل اسلامی، بهترین تجربه جستجو را به کاربران ارائه دهیم. تمامی اصیل اسلامی، بهترین تجربه جستجو را به کاربران ارائه دهیم. تمامی
محتوای این موتور جستجو از کتب معتبر حدیثی و منابع شناختهشده استخراج محتوای این موتور جستجو از کتب معتبر حدیثی و منابع شناختهشده استخراج
شده و با دقت علمی بالا ساماندهی شده است. شده و با دقت علمی بالا ساماندهی شده است.
</p> </p>
<p>اهداف ما شامل موارد زیر است:</p> <p>اهداف ما شامل موارد زیر است:</p>
<ul class="list-disc list-inside"> <ul class="list-disc list-inside">
<li>ارائه محتوای معتبر و دستهبندیشده از احادیث اسلامی.</li> <li>ارائه محتوای معتبر و دستهبندیشده از احادیث اسلامی.</li>
<li>تسهیل دسترسی به معارف اسلامی برای پژوهشگران و عموم مردم.</li> <li>تسهیل دسترسی به معارف اسلامی برای پژوهشگران و عموم مردم.</li>
<li>کمک به ترویج فرهنگ و اخلاق اسلامی از طریق انتشار احادیث.</li> <li>کمک به ترویج فرهنگ و اخلاق اسلامی از طریق انتشار احادیث.</li>
</ul> </ul>
<p> <p>
ما به حریم خصوصی کاربران خود احترام میگذاریم و متعهد به ارائه ما به حریم خصوصی کاربران خود احترام میگذاریم و متعهد به ارائه
خدماتی با کیفیت و دقیق هستیم. حدیثا پلی است میان شما و کلام گهربار خدماتی با کیفیت و دقیق هستیم. حدیثا پلی است میان شما و کلام گهربار
اهلبیت و پیامبر اسلام (صلیاللهعلیهوآله). اهلبیت و پیامبر اسلام (صلیاللهعلیهوآله).
</p> </p>
<p>به خانواده حدیثا بپیوندید و در مسیر دانش و معرفت گام بردارید.</p> <p>به خانواده حدیثا بپیوندید و در مسیر دانش و معرفت گام بردارید.</p>
</div> </div>
</UContainer> </UContainer>
</div> </div>
@ -97,17 +99,24 @@ const NavigationMenu = defineAsyncComponent(() =>
} }
.page-content { .page-content {
height: calc(100dvh - 13em);
overflow: auto;
color: var(--ui-color-two); color: var(--ui-color-two);
p { p {
font-family: IRANSansX; font-family: IRANSansX;
font-weight: 300; font-weight: 300;
font-size: 14px; font-size: 14px;
line-height: 21px; line-height: 21px;
letter-spacing: 0%; letter-spacing: 0%;
text-align: right; text-align: right;
} }
} }
} }
} }
@media screen and (max-width: 719.99px) {
.page-content{
height: calc(100dvh - 12em);
}
}
</style> </style>

View File

@ -32,7 +32,9 @@ const NavigationMenu = defineAsyncComponent(() =>
<navigation-menu></navigation-menu> <navigation-menu></navigation-menu>
<div class="page-header pt-38 pb-4 flex justify-between items-center"> <div
class="page-header pt-10 md:pt-10 lg:pt-38 pb-4 flex justify-between items-center"
>
<div class="flex items-center"> <div class="flex items-center">
<h1 class="m-0 title">تماس با ما</h1> <h1 class="m-0 title">تماس با ما</h1>
</div> </div>
@ -89,8 +91,10 @@ const NavigationMenu = defineAsyncComponent(() =>
left: 0; left: 0;
background-image: url("../../../assets/haditha/images/modal-top-bgi.png"); background-image: url("../../../assets/haditha/images/modal-top-bgi.png");
backdrop-filter: blur(54px); backdrop-filter: blur(54px);
width: 447px; width: 100%;
height: 447px; max-width: 447px;
height: 100%;
max-height: 447px;
top: 0; top: 0;
left: 0; left: 0;
@ -105,8 +109,11 @@ const NavigationMenu = defineAsyncComponent(() =>
bottom: 0; bottom: 0;
background-image: url("../../../assets/haditha/images/modal-bttom-right-bgi.png"); background-image: url("../../../assets/haditha/images/modal-bttom-right-bgi.png");
backdrop-filter: blur(54px); backdrop-filter: blur(54px);
width: 438px; width: 100%;
height: 238px;
max-width: 438px;
height: 100%;
max-height: 238px;
mix-blend-mode: Multiply; mix-blend-mode: Multiply;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@ -120,8 +127,11 @@ const NavigationMenu = defineAsyncComponent(() =>
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 458px; width: 100%;
height: 239px; max-width: 458px;
height: 100%;
max-height: 239px;
mix-blend-mode: Multiply; mix-blend-mode: Multiply;
background-image: url("../../../assets/haditha/images/modal-bottom-left-bgi.png"); background-image: url("../../../assets/haditha/images/modal-bottom-left-bgi.png");
background-repeat: no-repeat; background-repeat: no-repeat;

View File

@ -30,7 +30,9 @@ const NavigationMenu = defineAsyncComponent(() =>
<UContainer class="page-inner-container sm:px-6 lg:px-4"> <UContainer class="page-inner-container sm:px-6 lg:px-4">
<navigation-menu></navigation-menu> <navigation-menu></navigation-menu>
<div class="page-header pt-38 pb-4 flex justify-between items-center"> <div
class="page-header pt-10 md:pt-10 lg:pt-38 pb-4 flex justify-between items-center"
>
<div class="flex items-center"> <div class="flex items-center">
<h1 class="m-0 title">قوانین و مقررات</h1> <h1 class="m-0 title">قوانین و مقررات</h1>
<img <img
@ -42,7 +44,7 @@ const NavigationMenu = defineAsyncComponent(() =>
</div> </div>
</div> </div>
<div class="page-content p-6"> <div class="page-content p-5">
<ul class="list-decimal list-inside"> <ul class="list-decimal list-inside">
<li> <li>
احترام به حقوق معنوی: تمامی محتواهای ارائهشده در این سایت متعلق احترام به حقوق معنوی: تمامی محتواهای ارائهشده در این سایت متعلق
@ -110,8 +112,9 @@ const NavigationMenu = defineAsyncComponent(() =>
} }
.page-content { .page-content {
height: calc(100dvh - 13em);
overflow: auto;
color: var(--ui-color-two); color: var(--ui-color-two);
ul li { ul li {
font-family: IRANSansX; font-family: IRANSansX;
font-weight: 300; font-weight: 300;
@ -123,4 +126,9 @@ const NavigationMenu = defineAsyncComponent(() =>
} }
} }
} }
@media screen and (max-width: 719.99px) {
.page-content{
height: calc(100dvh - 12em);
}
}
</style> </style>

View File

@ -132,6 +132,7 @@ const route = useRoute();
const router = useRouter(); const router = useRouter();
const loading = ref(false); const loading = ref(false);
const httpService = useNuxtApp()["$http"]; const httpService = useNuxtApp()["$http"];
const toast = useToast();
const state = reactive({ const state = reactive({
selectedItem: {} as HadithResponseShowModel, selectedItem: {} as HadithResponseShowModel,
@ -174,7 +175,13 @@ const goToTheChatbot = () => {
}; };
const handleFavorite = async () => { const handleFavorite = async () => {
addToFavorites(state.selectedItem); if (state.selectedItem?._source?.tbookmark) {
await removeFromFavorites(state.selectedItem);
state.selectedItem._source.tbookmark = false;
} else {
await addToFavorites(state.selectedItem);
state.selectedItem._source.tbookmark = true;
}
// // add // // add
// if (!state.selectedItem._source.tbookmark) { // if (!state.selectedItem._source.tbookmark) {
@ -196,6 +203,21 @@ const addToFavorites = async (item = {}) => {
}; };
httpService.postRequest(url, formData).then((res) => { httpService.postRequest(url, formData).then((res) => {
// this.updateListAnswer(index, "tbookmark", 1); // this.updateListAnswer(index, "tbookmark", 1);
try {
toast.add({
title: "انجام شد.",
description: "به نشان شده ها افزوده شد",
color: "success",
});
} catch (err) {
console.log(err.message);
toast.add({
title: "انجام شد.",
description: "خطایی رخ داد.لطفا دوباره امتحان کنید.",
color: "error",
});
}
}); });
}; };
@ -211,6 +233,21 @@ const removeFromFavorites = async (item = {}, index = 0) => {
}; };
httpService.postRequest(url, formData).then((res) => { httpService.postRequest(url, formData).then((res) => {
// this.updateListAnswer(index, "tbookmark", 0); // this.updateListAnswer(index, "tbookmark", 0);
try {
toast.add({
title: "انجام شد.",
description: "از لیست نشان شده ها حذف شد",
color: "success",
});
} catch (err) {
console.log(err.message);
toast.add({
title: "انجام شد.",
description: "خطایی رخ داد.لطفا دوباره امتحان کنید.",
color: "error",
});
}
}); });
}; };
@ -220,8 +257,8 @@ const handlePagination = (prevNextIndicator: string) => {
let url = repoUrl() + hadithaApi.search.prevNextHadith; let url = repoUrl() + hadithaApi.search.prevNextHadith;
url = url.replace("@index_key", "dhparag"); url = url.replace("@index_key", "dhparag");
url = url.replace("@vol_id", state.selectedItem._source.address.vol_id); url = url.replace("@vol_id", state.selectedItem?._source?.address?.vol_id);
url = url.replace("@parag_order", state.selectedItem._source.parag_order); url = url.replace("@parag_order", state.selectedItem?._source?.parag_order);
url = url.replace("@step", prevNextIndicator); url = url.replace("@step", prevNextIndicator);
httpService httpService
@ -305,14 +342,19 @@ const handlePagination = (prevNextIndicator: string) => {
<span class="section-title"> <span class="section-title">
{{ {{
state.selectedItem?._source?.meta?.hadith_masoum ?? state.selectedItem?._source?.meta?.hadith_masoum ??
"بدون عنوان" state.selectedItem?._source?.meta?.hadith_sanad
}} }}
</span> </span>
<UButton <UButton
v-if="
state.selectedItem?._source?.meta?.hadith_masoum
?.length ||
state.selectedItem?._source?.meta?.hadith_sanad
"
@click=" @click="
copyTextToClipboard( copyTextToClipboard(
state.selectedItem?._source?.meta?.hadith_masoum ?? '' state.selectedItem?._source?.content_ar ?? ''
) )
" "
variant="ghost" variant="ghost"
@ -320,8 +362,13 @@ const handlePagination = (prevNextIndicator: string) => {
label="کپی" label="کپی"
/> />
</div> </div>
<div class="arabic-text"> <div
<p>بدون متن عربی</p> v-if="state.selectedItem?._source?.content_ar?.length"
class="arabic-text"
>
<p>
{{ state.selectedItem?._source?.content_ar ?? "" }}
</p>
</div> </div>
</div> </div>
@ -345,7 +392,7 @@ const handlePagination = (prevNextIndicator: string) => {
<p class="from"> <p class="from">
{{ {{
state.selectedItem?._source?.meta?.hadith_masoum ?? state.selectedItem?._source?.meta?.hadith_masoum ??
"بدون عنوان" state.selectedItem?._source?.meta?.hadith_sanad
}}: }}:
</p> </p>
<p <p
@ -356,13 +403,16 @@ const handlePagination = (prevNextIndicator: string) => {
<div class="separator"></div> <div class="separator"></div>
<div class="text-description-section"> <div
v-if="state.selectedItem?._source?.description?.length"
class="text-description-section"
>
<div class="section-header"> <div class="section-header">
<span class="section-title"> شرح </span> <span class="section-title"> شرح </span>
<UButton <UButton
@click=" @click="
copyTextToClipboard( copyTextToClipboard(
state.selectedItem?._source?.content state.selectedItem?._source?.description
) )
" "
variant="ghost" variant="ghost"
@ -372,14 +422,17 @@ const handlePagination = (prevNextIndicator: string) => {
</div> </div>
<p <p
class="description-item" class="description-item"
v-html="state.selectedItem?._source?.content" v-html="state.selectedItem?._source?.description"
></p> ></p>
</div> </div>
<div class="separator"></div> <div class="separator"></div>
<div class="text-description-section"> <div class="text-description-section">
<div class="text-sm mb-1"> <div
v-if="state.selectedItem?._source?.ai_keywords?.length"
class="text-sm mb-1"
>
<span class=""> کلیدواژگان: </span> <span class=""> کلیدواژگان: </span>
<span <span
class="me-1 text-gray-400 font-light" class="me-1 text-gray-400 font-light"
@ -392,7 +445,10 @@ const handlePagination = (prevNextIndicator: string) => {
</span> </span>
</div> </div>
<div class="text-sm"> <div
class="text-sm"
v-if="state.selectedItem?._source?.ai_classes?.length"
>
<span class=""> دسته بندی ها: </span> <span class=""> دسته بندی ها: </span>
<span <span
class="me-1 text-gray-400 font-light" class="me-1 text-gray-400 font-light"
@ -457,7 +513,11 @@ const handlePagination = (prevNextIndicator: string) => {
<!-- because of the buttons, using without scoped. --> <!-- because of the buttons, using without scoped. -->
<style> <style>
.page-inner-container{
height: 100%;
}
.search-show-page { .search-show-page {
.body-header { .body-header {
.modal-title { .modal-title {
padding: 0 0.5em 1.5em; padding: 0 0.5em 1.5em;
@ -535,8 +595,8 @@ const handlePagination = (prevNextIndicator: string) => {
} }
} }
.content { .content {
/* height: calc(100dvh - 29em); */ height: calc(100dvh - 15em);
/* overflow-y: auto; */ overflow-y: auto;
.search-item { .search-item {
padding: 1em 0 1em 0.1em; padding: 1em 0 1em 0.1em;
@ -781,4 +841,14 @@ const handlePagination = (prevNextIndicator: string) => {
} }
} }
} }
@media screen and (max-width: 719.99px) {
.search-show-page {
.body-content {
.content {
height: calc(100dvh - 15em);
overflow-y: auto;
}
}
}
}
</style> </style>