253 lines
9.6 KiB
Vue
253 lines
9.6 KiB
Vue
<script setup lang="ts">
|
|
// import
|
|
|
|
import useGetComments from "~/composables/api/product/useGetComments";
|
|
import useCreateComment from "~/composables/api/product/useCreateComment";
|
|
import useRateProduct from "~/composables/api/product/useRateProduct";
|
|
import { useAuth } from "~/composables/api/auth/useAuth";
|
|
|
|
// props
|
|
|
|
type Props = {
|
|
product: Product;
|
|
};
|
|
|
|
const props = defineProps<Props>();
|
|
|
|
// state
|
|
|
|
const route = useRoute();
|
|
|
|
const id = route.params.id as string | undefined;
|
|
|
|
const { token } = useAuth();
|
|
const userTitle = ref("");
|
|
const userComment = ref("");
|
|
const selectedRating = ref(5);
|
|
|
|
const showMoreComments = ref(false);
|
|
|
|
const { data: comments, refetch: refetchComments } = useGetComments(id);
|
|
const { mutateAsync: createComment, isPending: isCreateCommentPending } = useCreateComment(id);
|
|
const { mutateAsync: rateProduct, isPending: isRateProductPending } = useRateProduct(props.product.slug);
|
|
|
|
watch(
|
|
() => props.product.user_rating,
|
|
(newUserRating) => {
|
|
if (token.value && newUserRating !== null) {
|
|
selectedRating.value = newUserRating;
|
|
} else {
|
|
selectedRating.value = 5;
|
|
}
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
|
|
const canSubmitComment = computed(() => {
|
|
return userTitle.value.trim().length > 0 && userComment.value.trim().length > 0;
|
|
});
|
|
|
|
const hasUserRated = computed(() => {
|
|
return token.value && props.product.user_rating !== null;
|
|
});
|
|
|
|
// methods
|
|
|
|
const submitComment = async () => {
|
|
if (!canSubmitComment.value) {
|
|
return;
|
|
}
|
|
|
|
const promises: Promise<any>[] = [
|
|
createComment({
|
|
title: userTitle.value,
|
|
content: userComment.value,
|
|
}),
|
|
];
|
|
|
|
// Only submit rating if user hasn't rated before
|
|
if (!hasUserRated.value) {
|
|
promises.push(
|
|
rateProduct({
|
|
rating: selectedRating.value,
|
|
}),
|
|
);
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
|
|
userTitle.value = "";
|
|
userComment.value = "";
|
|
selectedRating.value = 5;
|
|
|
|
await refetchComments();
|
|
};
|
|
|
|
// computed
|
|
|
|
const limitedComments = computed(() => {
|
|
if (showMoreComments.value) {
|
|
return comments.value?.results;
|
|
}
|
|
|
|
return comments.value?.results.slice(0, 3);
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<section
|
|
v-if="!!comments"
|
|
class="bg-slate-50"
|
|
>
|
|
<div class="flex relative gap-8 my-42 container max-lg:flex-col">
|
|
<div
|
|
class="sticky top-0 flex flex-col gap-6 lg:min-w-100 h-fit bg-white p-8 rounded-xl border-[0.5px] border-slate-200"
|
|
>
|
|
<h3 class="typo-h-6 max-sm:text-xl md:typo-h-5 lg:typo-h-4">نظرات کاربران</h3>
|
|
<!-- <div class="flex flex-col gap-2">
|
|
<Rating :rate="props.product.rating" />
|
|
<span class="typo-p-sm"> بر اساس {{ comments?.count }} نظر </span>
|
|
</div> -->
|
|
<form
|
|
@submit.prevent="submitComment"
|
|
class="flex flex-col gap-6"
|
|
>
|
|
<div
|
|
v-if="hasUserRated"
|
|
class="flex flex-col gap-3 px-4 py-3 bg-white rounded-lg border border-slate-300"
|
|
>
|
|
<div class="flex flex-col gap-2">
|
|
<span class="typo-p-xs text-slate-700 font-semibold">امتیاز قبلی شما</span>
|
|
<div class="flex items-center gap-1">
|
|
<button
|
|
v-for="star in 5"
|
|
:key="`prev-${star}`"
|
|
type="button"
|
|
disabled
|
|
class="size-9 rounded-full flex-center cursor-not-allowed opacity-75"
|
|
:class="star <= (props.product.user_rating || 0) ? 'bg-amber-50' : 'bg-slate-50'"
|
|
>
|
|
<Icon
|
|
name="ci:star-solid"
|
|
class="size-5"
|
|
:class="
|
|
star <= (props.product.user_rating || 0)
|
|
? '**:fill-yellow-500'
|
|
: '**:fill-slate-300'
|
|
"
|
|
/>
|
|
</button>
|
|
<span class="typo-p-sm font-semibold text-slate-600 mr-2">{{
|
|
props.product.user_rating
|
|
}}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="flex flex-col gap-2"
|
|
>
|
|
<span class="typo-p-sm text-slate-500">امتیاز شما</span>
|
|
<div class="flex items-center gap-1">
|
|
<button
|
|
v-for="star in 5"
|
|
:key="star"
|
|
type="button"
|
|
class="size-9 rounded-full flex-center transition-colors"
|
|
:class="star <= selectedRating ? 'bg-amber-50' : 'bg-slate-50'"
|
|
:disabled="!token || isCreateCommentPending || isRateProductPending"
|
|
@click="selectedRating = star"
|
|
>
|
|
<Icon
|
|
name="ci:star-solid"
|
|
class="size-5"
|
|
:class="star <= selectedRating ? '**:fill-yellow-500' : '**:fill-slate-300'"
|
|
/>
|
|
</button>
|
|
<span class="typo-p-sm font-semibold text-slate-500 mr-2">{{ selectedRating }}</span>
|
|
</div>
|
|
</div>
|
|
<Input
|
|
v-model="userTitle"
|
|
:disabled="!token"
|
|
placeholder="عنوان نظر را بنویسید..."
|
|
variant="outlined"
|
|
/>
|
|
<textarea
|
|
:disabled="!token"
|
|
class="w-full min-h-31.25 resize-none sm:min-h-50 field-sizing-content rounded-xl bg-white p-4 border border-slate-300 placeholder:text-xs lg:placeholder:text-sm placeholder:font-normal"
|
|
v-model="userComment"
|
|
placeholder="نظر خود را بنویسید..."
|
|
/>
|
|
<Button
|
|
v-if="token"
|
|
type="submit"
|
|
class="rounded-full w-full"
|
|
:loading="isCreateCommentPending || isRateProductPending"
|
|
:disabled="isCreateCommentPending || isRateProductPending || !canSubmitComment"
|
|
>
|
|
ثبت نظر
|
|
</Button>
|
|
<NuxtLink
|
|
v-else
|
|
to="/signin"
|
|
>
|
|
<Button
|
|
type="button"
|
|
class="rounded-full w-full"
|
|
>
|
|
وارد شوید
|
|
</Button>
|
|
</NuxtLink>
|
|
</form>
|
|
</div>
|
|
<div class="flex flex-col gap-8 w-full">
|
|
<Comment
|
|
v-for="comment in limitedComments"
|
|
:key="comment.id"
|
|
:title="comment.title"
|
|
:content="comment.content"
|
|
:date="comment.timestamp"
|
|
:first_name="comment.user.first_name"
|
|
:last_name="comment.user.last_name"
|
|
:rate="comment.user_rating ?? 0"
|
|
/>
|
|
|
|
<div
|
|
v-if="comments.count > 0"
|
|
class="flex items-center justify-center w-full"
|
|
>
|
|
<Pagination
|
|
v-if="showMoreComments"
|
|
:total="comments.count"
|
|
:items="comments.results.map((item, i) => ({ type: 'page', value: i }))"
|
|
:per-page="8"
|
|
/>
|
|
<Button
|
|
v-else-if="comments.count > 3"
|
|
type="button"
|
|
variant="primary"
|
|
@click="showMoreComments = !showMoreComments"
|
|
class="rounded-full px-8"
|
|
end-icon="ci:bi-plus"
|
|
>
|
|
نمایش همه
|
|
</Button>
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="h-100 lg:grow w-full border-[0.5px] flex-col-center border-slate-200 bg-white rounded-xl"
|
|
>
|
|
<NuxtImg
|
|
src="/img/heymlz/heymlz-contact-us.gif"
|
|
loading="lazy"
|
|
fetch-priority="low"
|
|
class="w-50 lg:w-75 -translate-y-6.25"
|
|
/>
|
|
<span class="text-xl text-black font-semibold -translate-y-6.25"> هیچ نظری ثبت نشده است </span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|