hadith_ui/pages/haditha/search/[id]/[slug]/index.vue
2025-04-19 11:24:19 +03:30

841 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
// 1. Imports
// 2. Metas
// 3. Props
// 2. Reactive State
// 3. Computed Properties
// 4. Functions / Methods
// 5. Lifecycle Hooks
// 6. Watchers
// #region imports
import hadithaApi from "@haditha/apis/hadithaApi";
import type { HadithResponseModel } from "@haditha/types/hadithType";
import type { HadithResponseShowModel } from "~/systems/hadith_ui/types/hadithType";
// #endregion imports
// #region meta
definePageMeta({
layout: false,
name: "hadithaSearchShow",
});
useHead({
title: `${import.meta.env.VITE_HADITH_PAGE_TITLE}`,
meta: [
{ name: "description", content: "کاوش با هوش مصنوعی در احادیث اسلامی" },
{
name: "msapplication-TileImage",
content: "/img/haditha/fav-icons/ms-icon-144x144.png",
},
{ name: "theme-color", content: "#ffffff" },
],
bodyAttrs: {
class: import.meta.env.VITE_HADITH_SYSTEM,
},
link: [
{
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
// #region props
// const props = defineProps({
// selectedItem: {
// type: Object,
// default() {
// return {};
// },
// },
// });
// #endregion props
// #region refs and reactives
const emit = defineEmits(["close"]);
const route = useRoute();
const router = useRouter();
const loading = ref(false);
const httpService = useNuxtApp()["$http"];
const toast = useToast();
const state = reactive({
selectedItem: {} as HadithResponseShowModel,
});
// #endregion refs and reactives
// #region methods
const fetchData = async () => {
if (loading.value) return;
loading.value = true;
let url = hadithaApi.search.show;
url = url.replace("@index_key", "dhparag");
url = url.replace("@id", route.params.id);
// fetch search list from backend(ssr)
const { data, status, error, refresh, clear } =
await useHadithaSearchComposable<HadithResponseShowModel>(url, {
method: "get",
});
if (status.value == "success") {
state.selectedItem = <HadithResponseShowModel>data.value;
}
loading.value = false;
};
fetchData();
const goToTheSearch = (type: string) => {
router.push({
name: "hadithaSearch",
});
};
const goToTheChatbot = () => {
router.push({
name: "hadithaChatBot",
});
};
const handleFavorite = async () => {
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
// if (!state.selectedItem._source.tbookmark) {
// addToFavorites(state.selectedItem);
// }
// // delete
// else {
// removeFromFavorites(state.selectedItem);
// }
};
const addToFavorites = async (item = {}) => {
let url = repoUrl() + hadithaApi.favorite.add;
url = url.replace("{{data_type}}", "bookmark");
url = url.replace("{{ref_key}}", "dhparag");
const formData = {
ref_id: item._id,
title: item._source.content,
};
httpService.postRequest(url, formData).then((res) => {
// this.updateListAnswer(index, "tbookmark", 1);
try {
toast.add({
title: "انجام شد.",
description: "به نشان شده ها افزوده شد",
color: "success",
});
} catch (err) {
console.log(err.message);
toast.add({
title: "انجام شد.",
description: "خطایی رخ داد.لطفا دوباره امتحان کنید.",
color: "error",
});
}
});
};
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);
try {
toast.add({
title: "انجام شد.",
description: "از لیست نشان شده ها حذف شد",
color: "success",
});
} catch (err) {
console.log(err.message);
toast.add({
title: "انجام شد.",
description: "خطایی رخ داد.لطفا دوباره امتحان کنید.",
color: "error",
});
}
});
};
const handlePagination = (prevNextIndicator: string) => {
if (loading.value) return;
loading.value = true;
let url = repoUrl() + hadithaApi.search.prevNextHadith;
url = url.replace("@index_key", "dhparag");
url = url.replace("@vol_id", state.selectedItem?._source?.address?.vol_id);
url = url.replace("@parag_order", state.selectedItem?._source?.parag_order);
url = url.replace("@step", prevNextIndicator);
httpService
.getRequest(url)
.then((res) => {
state.selectedItem = res.hits.hits?.[0];
})
.finally(() => (loading.value = false));
};
// const localCopyTextToClipboard = (text: string) => {
// copyTextToClipboard(text);
// };
// #endregion methods
// #region hooks
// onMounted(() => {
// console.info("mounted");
// });
// #endregion methods
</script>
<template>
<UContainer
ui="{
base: 'sm:px-6 lg:px-4',
}"
class="page-inner-container sm:px-6 lg:px-4 py-8"
>
<div class="search-show-page">
<div class="body-header">
<span class="top-left-bgi z-0"></span>
<div class="modal-title flex justify-between">
<ULink
raw
:to="{ name: 'haditha' }"
class="flex justify-center items-center me-3"
>
<img fit="auto" quality="80" src="/img/haditha/haditha-title.svg" />
</ULink>
<UButton
icon="i-haditha-close"
color="neutral"
variant="ghost"
class="close-btn"
@click="goToTheSearch('normal')"
/>
</div>
</div>
<div class="body-content">
<div class="h-full flex flex-col justify-center z-2">
<div class="bg-container h-full">
<div class="header flex">
<UButton
@click="handleFavorite"
:variant="
state.selectedItem?._source?.tbookmark ? 'soft' : 'ghost'
"
color="primary"
class="bookmark-btn"
:icon="
state.selectedItem?._source?.tbookmark
? 'i-haditha-tag-active'
: 'i-haditha-tag'
"
>
</UButton>
<div class="referene">
<span> نشانی: </span>
{{ state.selectedItem?._source?.address?.vol_title }}، صفحه
{{ state.selectedItem?._source?.address?.page_num }}
</div>
</div>
<div class="content">
<div class="search-item">
<div class="text-arabic-section">
<div class="section-header">
<span class="section-title">
{{
state.selectedItem?._source?.meta?.hadith_masoum ??
state.selectedItem?._source?.meta?.hadith_sanad
}}
</span>
<UButton
v-if="
state.selectedItem?._source?.meta?.hadith_masoum
?.length ||
state.selectedItem?._source?.meta?.hadith_sanad
"
@click="
copyTextToClipboard(
state.selectedItem?._source?.content_ar ?? ''
)
"
variant="ghost"
class="copy-btn"
label="کپی"
/>
</div>
<div
v-if="state.selectedItem?._source?.content_ar?.length"
class="arabic-text"
>
<p>
{{ state.selectedItem?._source?.content_ar ?? "" }}
</p>
</div>
</div>
<div class="separator"></div>
<div class="text-persian-section">
<div class="section-header">
<span class="section-title"> ترجمه </span>
<UButton
@click="
copyTextToClipboard(
state.selectedItem?._source?.content
)
"
variant="ghost"
class="copy-btn"
label="کپی"
/>
</div>
<p class="from">
{{
state.selectedItem?._source?.meta?.hadith_masoum ??
state.selectedItem?._source?.meta?.hadith_sanad
}}:
</p>
<p
class="persian-text"
v-html="state.selectedItem?._source?.content"
></p>
</div>
<div class="separator"></div>
<div
v-if="state.selectedItem?._source?.description?.length"
class="text-description-section"
>
<div class="section-header">
<span class="section-title"> شرح </span>
<UButton
@click="
copyTextToClipboard(
state.selectedItem?._source?.description
)
"
variant="ghost"
class="copy-btn"
label="کپی"
/>
</div>
<p
class="description-item"
v-html="state.selectedItem?._source?.description"
></p>
</div>
<div class="separator"></div>
<div class="text-description-section">
<div
v-if="state.selectedItem?._source?.ai_keywords?.length"
class="text-sm mb-1"
>
<span class=""> کلیدواژگان: </span>
<span
class="me-1 text-gray-400 font-light"
v-for="(aiClass, i) in state.selectedItem?._source
?.ai_keywords"
:key="i"
>
{{ i > 0 ? "," : "" }}
{{ aiClass }}
</span>
</div>
<div
class="text-sm"
v-if="state.selectedItem?._source?.ai_classes?.length"
>
<span class=""> دسته بندی ها: </span>
<span
class="me-1 text-gray-400 font-light"
v-for="(aiClass, i) in state.selectedItem?._source
?.ai_classes"
:key="i"
>
{{ i > 0 ? "," : "" }}
{{ aiClass.label }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="body-footer">
<div class="mt-5 z-2">
<!-- <div class="flex justify-between actions">
<UButton
disabled
class="similar-btn"
icon="i-haditha-search-3"
label="مشابه"
color="neutral"
variant="outline"
@click.prevent="goToTheSearch('similar')"
/>
<UButton
class="explore-btn"
trailing-icon="i-haditha-explore"
label="کاوش"
variant="solid"
@click.prevent="goToTheChatbot"
/>
</div> -->
<div class="flex justify-between pagination">
<UButton
@click="handlePagination('-1')"
class="prev-haditha"
label="حدیث قبل"
color=""
variant="soft"
icon="i-haditha-chevron-right"
/>
<UButton
@click="handlePagination('1')"
class="next-haditha"
label="حدیث بعد"
color=""
variant="soft"
trailing-icon="i-haditha-chevron-left"
/>
</div>
</div>
</div>
</div>
</UContainer>
</template>
<!-- because of the buttons, using without scoped. -->
<style>
.search-show-page {
.body-header {
.modal-title {
padding: 0 0.5em 1.5em;
margin-bottom: 2.5em;
.close-btn {
color: var(--ui-color-two);
/* width: 24px; */
/* height: 24px; */
padding: 0.2em;
background-color: transparent;
}
}
}
.body-content {
.header {
.bookmark-btn {
width: 49px;
height: 32px;
gap: 4px;
border-radius: 6px;
border-width: 0.5px;
padding-top: 4px;
padding-right: 12px;
padding-bottom: 4px;
padding-left: 12px;
border: 0.5px solid #d9d9d9;
/* background: #ffffff; */
display: flex;
justify-content: center;
align-items: center;
/* span { */
/* width: 19; */
/* height: 21; */
/* background: #29d985; */
/* } */
}
.referene {
margin-right: 0.5em;
width: 182;
height: 32;
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: #8a92a8;
display: flex;
align-items: center;
span {
margin-left: 0.1em;
font-family: IRANSansX;
font-weight: 300;
font-size: 12px;
line-height: 18px;
letter-spacing: 0%;
text-align: right;
color: #4d00ff;
}
}
}
.content {
/* height: calc(100dvh - 29em); */
/* overflow-y: auto; */
.search-item {
padding: 1em 0 1em 0.1em;
.section-header {
display: flex;
justify-content: space-between;
text-align: right;
margin-bottom: 0.5em;
.section-title {
font-family: IRANSansX;
font-weight: 400;
font-size: 14px;
line-height: 21px;
letter-spacing: 0%;
text-align: right;
/* Fallback color */
color: #4be8ae;
/* Gradient background */
background-image: linear-gradient(
102.02deg,
#4be8ae 7.38%,
#00a762 91.78%
);
/* Clip the background to the text */
background-clip: text;
-webkit-background-clip: text; /* For Safari */
/* Make the text transparent */
color: transparent;
-webkit-text-fill-color: transparent; /* For Safari */
}
.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: #8a92a8; */
}
}
.text-arabic-section {
padding: 2em 0;
.arabic-text {
font-family: Takrim;
font-weight: 400;
font-size: 18px;
line-height: 32px;
letter-spacing: 0%;
text-align: right;
color: var(--ui-color-two);
margin-bottom: 0.5em;
}
}
.text-persian-section {
padding: 2em 0;
.section-header {
}
.from,
.persian-text {
font-family: Takrim;
font-weight: 400;
font-size: 18px;
line-height: 30px;
letter-spacing: 0%;
text-align: right;
color: var(--ui-color-two);
}
/* .persian-text {
font-family: Takrim;
font-weight: 400;
font-size: 16px;
line-height: 22px;
letter-spacing: 0%;
text-align: right;
color: #626b84;
margin-bottom: 0.5em;
} */
}
.text-description-section {
padding: 2em 0;
.description-item {
font-family: Takrim;
font-weight: 400;
font-size: 18px;
line-height: 30px;
letter-spacing: 0%;
text-align: right;
color: var(--ui-color-two);
/*
height: 24px;
gap: 4px;
padding-top: 4px;
padding-right: 8px;
padding-bottom: 4px;
padding-left: 8px;
border-radius: 6px;
border-width: 0.5px;
border: 0.5px solid #d9d9d9;
font-family: IRANSansX;
font-weight: 300;
font-size: 10px;
line-height: 15px;
letter-spacing: 0%;
text-align: right;
color: #8a92a8; */
}
}
.separator {
/* border: 0.5px solid #eee; */
height: 1px;
background-position: center center;
background-size: contain;
background-image: linear-gradient(
90deg,
#ffffff 0%,
#a0f5ff 30.4%,
#3fc9fa 71.47%,
#a7ffe7 100%
);
}
}
}
}
.body-footer {
.actions {
margin-bottom: 1em;
.similar-btn {
width: 114;
height: 56;
gap: 8px;
border-radius: 12px;
border-width: 0.5px;
padding-top: 8px;
padding-right: 20px;
padding-bottom: 8px;
padding-left: 24px;
background: #ffffff;
border: 0.5px solid;
border-image-source: linear-gradient(
102.02deg,
#4be8ae 7.38%,
#00a762 91.78%
);
box-shadow: 0px 8px 20px 0px #0000001a;
font-family: IRANSansX;
font-weight: 400;
font-size: 15px;
line-height: 22.5px;
letter-spacing: 0%;
text-align: right;
color: var(--ui-color-two);
}
.explore-btn {
width: 118;
height: 56;
gap: 4px;
border-radius: 12px;
padding-top: 8px;
padding-right: 24px;
padding-bottom: 8px;
padding-left: 20px;
background: linear-gradient(268.94deg, #d284ff -0.65%, #4d00ff 104.59%);
box-shadow: 0px 8px 20px 0px #0000001a;
font-family: IRANSansX;
font-weight: 400;
font-size: 16px;
line-height: 24px;
letter-spacing: 0%;
text-align: right;
color: #fff;
}
}
.pagination {
padding: 0.7em 0px;
/* width: 672; */
/* height: 56; */
justify-content: space-between;
border-radius: 16px;
border-width: 0.3px;
padding-right: 32px;
padding-left: 32px;
background: #ffffff;
border: 0.3px solid #e0e0e0;
box-shadow: 0px 8px 20px 0px #0000001a;
.prev-haditha {
font-family: IRANSansX;
font-weight: 300;
font-size: 12px;
line-height: 20px;
letter-spacing: 0%;
text-align: right;
color: var(--ui-color-two);
}
.next-haditha {
font-family: IRANSansX;
font-weight: 300;
font-size: 12px;
line-height: 20px;
letter-spacing: 0%;
text-align: right;
color: var(--ui-color-two);
}
}
}
}
</style>