{
>
-
+
+
{{ item.title }}
diff --git a/frontend/components/global/ProductsGrid.vue b/frontend/components/global/ProductsGrid.vue
index 730414a..6af65f3 100644
--- a/frontend/components/global/ProductsGrid.vue
+++ b/frontend/components/global/ProductsGrid.vue
@@ -15,25 +15,27 @@ withDefaults(defineProps(), {
-
+
-
+
{{ title }}
نمایش همه
-
+
{
await refetchProduct();
};
+const saveProductHandler = async () => {
+ await saveProduct({ product_slug: product.value!.slug });
+ await queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.product] });
+};
+
// watch
watch([selectedVariantId, product], ([selectedVariantId, product]) => {
@@ -113,12 +123,30 @@ watch(
:slides="selectedVariant!.images"
/>
-
- {{ product!.category.name }}
-
+
+
+ {{ product!.category.name }}
+
+
+
+
+
+
+
{{ product!.name }}
diff --git a/frontend/components/profile/global/ProfileSidebar.vue b/frontend/components/profile/global/ProfileSidebar.vue
index 1008aee..d4dc757 100644
--- a/frontend/components/profile/global/ProfileSidebar.vue
+++ b/frontend/components/profile/global/ProfileSidebar.vue
@@ -34,6 +34,12 @@ const profileLinks = ref([
path: { name: "profile-purchases-and-orders" },
matchPattern: /^profile-purchases-and-orders/,
},
+ {
+ icon: "bi:bookmark",
+ title: "علاقهمندی ها",
+ path: { name: "profile-saved-products" },
+ matchPattern: /^profile-saved-products/,
+ },
{
icon: "bi:ticket",
title: "تیکت ها",
@@ -95,26 +101,20 @@ const toggleSidebar = inject("toggleSidebar");
:src="account!.profile_photo"
:alt="
account?.first_name && account?.last_name
- ? `${account?.first_name.charAt(
- 0
- )} ${account?.last_name.charAt(0)}`
+ ? `${account?.first_name.charAt(0)} ${account?.last_name.charAt(0)}`
: 'بدون نام کاربری'
"
/>
-
+
{{ link.title }}
-
+
@@ -141,9 +144,7 @@ const toggleSidebar = inject("toggleSidebar");
class="**:fill-danger-500"
/>
-
- خروج از حساب
-
+
خروج از حساب
) => {
const { isLoggedIn } = useAuth();
+ const isEnabled = computed(() => {
+ return enabled.value && isLoggedIn.value;
+ });
+
// methods
const handleGetChat = async ({
@@ -26,23 +30,20 @@ const useGetChat = (productId: string | number, enabled: Ref) => {
limit: number;
offset: number;
}) => {
- const { data } = await axios.get(
- `${API_ENDPOINTS.chat.messages}/${productId}`,
- {
- params: {
- offset,
- limit,
- },
- headers: {
- Authorization: `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoyMTY3ODE2OTAwLCJpYXQiOjE3MzU4MTY5MDAsImp0aSI6ImQwN2E2Y2Y2NzgwZjRlNTE5NWIzOGQxMTAzYzU4NDQ3IiwidXNlcl9pZCI6NX0.slwd7ZSV7nUXEuDTYwwHUOo9ekCefwEEL4kVv2vSTFo`,
- },
- }
- );
+ const { data } = await axios.get(`${API_ENDPOINTS.chat.messages}/${productId}`, {
+ params: {
+ offset,
+ limit,
+ },
+ headers: {
+ Authorization: `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoyMTY3ODE2OTAwLCJpYXQiOjE3MzU4MTY5MDAsImp0aSI6ImQwN2E2Y2Y2NzgwZjRlNTE5NWIzOGQxMTAzYzU4NDQ3IiwidXNlcl9pZCI6NX0.slwd7ZSV7nUXEuDTYwwHUOo9ekCefwEEL4kVv2vSTFo`,
+ },
+ });
return data;
};
return useInfiniteQuery({
- enabled: isLoggedIn,
+ enabled: isEnabled,
queryKey: [QUERY_KEYS.chat],
initialPageParam: {
limit: 10,
diff --git a/frontend/composables/api/product/useSaveProduct.ts b/frontend/composables/api/product/useSaveProduct.ts
new file mode 100644
index 0000000..fefd298
--- /dev/null
+++ b/frontend/composables/api/product/useSaveProduct.ts
@@ -0,0 +1,29 @@
+// imports
+
+import { useMutation } from "@tanstack/vue-query";
+import { API_ENDPOINTS } from "~/constants";
+
+// types
+
+export type SaveProductRequest = {
+ product_slug: string;
+};
+
+const useSaveProduct = () => {
+ // state
+
+ const { $axios: axios } = useNuxtApp();
+
+ // methods
+
+ const handleSaveProduct = async (variables: SaveProductRequest) => {
+ const { data } = await axios.post(`${API_ENDPOINTS.product.save}`, variables);
+ return data;
+ };
+
+ return useMutation({
+ mutationFn: (variables: SaveProductRequest) => handleSaveProduct(variables),
+ });
+};
+
+export default useSaveProduct;
diff --git a/frontend/composables/api/products/useSavedProducts.ts b/frontend/composables/api/products/useSavedProducts.ts
new file mode 100644
index 0000000..2583bda
--- /dev/null
+++ b/frontend/composables/api/products/useSavedProducts.ts
@@ -0,0 +1,40 @@
+// imports
+
+import { useQuery } from "@tanstack/vue-query";
+import { useAppParams } from "~/composables/global/useAppParams";
+import { API_ENDPOINTS, QUERY_KEYS } from "~/constants";
+
+// types
+
+export type GetSavedProductsResponse = ApiPaginated;
+
+// composable
+
+const useGetSavedProducts = () => {
+ // state
+
+ const { $axios: axios } = useNuxtApp();
+
+ const { page } = useAppParams();
+
+ // methods
+
+ const handleGetSavedProducts = async ({ signal }: { signal: AbortSignal }) => {
+ const { data } = await axios.get(`${API_ENDPOINTS.products.saved}`, {
+ params: {
+ offset: Number(page.value) * 15 - 15,
+ limit: 15,
+ },
+ signal,
+ });
+
+ return data;
+ };
+
+ return useQuery({
+ queryKey: [QUERY_KEYS.saved_products, page],
+ queryFn: ({ signal }) => handleGetSavedProducts({ signal }),
+ });
+};
+
+export default useGetSavedProducts;
diff --git a/frontend/constants/index.ts b/frontend/constants/index.ts
index cfb4f66..e06ca5f 100644
--- a/frontend/constants/index.ts
+++ b/frontend/constants/index.ts
@@ -23,6 +23,7 @@ export const API_ENDPOINTS = {
comments: "/products/comments",
create_comment: "/products/comments",
get: "/products",
+ save: "/accounts/favorites/toggle",
},
auth: {
refresh: "/token/refresh",
@@ -37,6 +38,7 @@ export const API_ENDPOINTS = {
products: {
get_all: "/products",
categories: "/products/categories",
+ saved: "/accounts/favorites",
},
resellers_products: {
get_all: "/products/slider_category",
@@ -78,6 +80,7 @@ export const QUERY_KEYS = {
chat: "chat",
product: "product",
products: "products",
+ saved_products: "saved-products",
resellers_products: "resellers_products",
account: "account",
categories: "categories",
diff --git a/frontend/layouts/Profile.vue b/frontend/layouts/Profile.vue
index 594145b..8e1c3e3 100644
--- a/frontend/layouts/Profile.vue
+++ b/frontend/layouts/Profile.vue
@@ -51,9 +51,7 @@ watch(
dir="rtl"
>
-
+
diff --git a/frontend/pages/profile/purchases-and-orders/[id].vue b/frontend/pages/profile/purchases-and-orders/[id].vue
index 4c44202..01ed56a 100644
--- a/frontend/pages/profile/purchases-and-orders/[id].vue
+++ b/frontend/pages/profile/purchases-and-orders/[id].vue
@@ -2,6 +2,4 @@
-
-
-
+
\ No newline at end of file
diff --git a/frontend/pages/profile/saved-products/index.vue b/frontend/pages/profile/saved-products/index.vue
new file mode 100644
index 0000000..b48076b
--- /dev/null
+++ b/frontend/pages/profile/saved-products/index.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+ همه محصولات
+
+
+
+
+
+
diff --git a/frontend/types/global.d.ts b/frontend/types/global.d.ts
index 654f352..7ec0706 100644
--- a/frontend/types/global.d.ts
+++ b/frontend/types/global.d.ts
@@ -1,4 +1,4 @@
-export { };
+export {};
declare global {
type ApiPaginated
= {
@@ -104,6 +104,7 @@ declare global {
meta_rating: number;
category: SubCategory;
colors: string[];
+ added_to_favorites: boolean;
};
type ProductListItem = Pick;
@@ -235,7 +236,7 @@ declare global {
discount_code: DiscountCode;
items: CartItem[];
cart_total: string;
- items_discount_amount: string
+ items_discount_amount: string;
tax_amount: string;
final_price: string;
address: Address;