diff --git a/frontend/components/global/CategoryCard.vue b/frontend/components/global/CategoryCard.vue index 33be331..3e26b2c 100644 --- a/frontend/components/global/CategoryCard.vue +++ b/frontend/components/global/CategoryCard.vue @@ -1,17 +1,16 @@ \ No newline at end of file + diff --git a/frontend/components/global/Header.vue b/frontend/components/global/Header.vue index 7ff84ac..07cda45 100644 --- a/frontend/components/global/Header.vue +++ b/frontend/components/global/Header.vue @@ -1,8 +1,8 @@ @@ -45,16 +45,15 @@ const nav_links = ref([
-
+
- +
-
- KIR -
+
KIR
diff --git a/frontend/composables/api/account/useGetAccount.ts b/frontend/composables/api/account/useGetAccount.ts index a7d2bbb..7c2ed83 100644 --- a/frontend/composables/api/account/useGetAccount.ts +++ b/frontend/composables/api/account/useGetAccount.ts @@ -2,6 +2,7 @@ import { useQuery } from "@tanstack/vue-query"; import { API_ENDPOINTS, QUERY_KEYS } from "~/constants"; +import { useAuth } from "~/composables/api/auth/useAuth"; // types @@ -12,6 +13,7 @@ const useGetAccount = () => { // state const { $axios: axios } = useNuxtApp(); + const { token } = useAuth(); // methods @@ -21,6 +23,7 @@ const useGetAccount = () => { }; return useQuery({ + enabled: !!token.value, queryKey: [QUERY_KEYS.account], queryFn: () => handleGetAccount() }); diff --git a/frontend/composables/api/auth/useAuth.ts b/frontend/composables/api/auth/useAuth.ts index 831ad40..2fa8493 100644 --- a/frontend/composables/api/auth/useAuth.ts +++ b/frontend/composables/api/auth/useAuth.ts @@ -1,5 +1,3 @@ -import useGetAccount from "~/composables/api/account/useGetAccount"; - export const useAuth = () => { // state @@ -17,14 +15,6 @@ export const useAuth = () => { if (reload) window.location.reload(); }; - // watch - - watch(() => token.value, (newValue) => { - token.value = newValue; - }, { - immediate: true - }); - return { token, updateToken, logout }; }; \ No newline at end of file diff --git a/frontend/composables/api/chat/useCreateChatMessage.ts b/frontend/composables/api/chat/useCreateChatMessage.ts new file mode 100644 index 0000000..87f5da6 --- /dev/null +++ b/frontend/composables/api/chat/useCreateChatMessage.ts @@ -0,0 +1,115 @@ +// imports + +import { QueryClient, useMutation } from "@tanstack/vue-query"; +import type { InfiniteData } from "@tanstack/vue-query"; +import { API_ENDPOINTS, MUTATION_KEYS, QUERY_KEYS } from "~/constants"; + +// types + +export type CreateChatMessageRequest = { + productId: string | number; + new_message: string; +}; + +export type CreateChatMessageResponse = Chat[] + +const useCreateChatMessage = (queryClient: QueryClient) => { + + // state + + const { $axios: axios } = useNuxtApp(); + + // method + + const handleCreateChatMessage = async (variables: CreateChatMessageRequest) => { + + const { data } = await axios.post(`${API_ENDPOINTS.chat.new_message}/${variables.productId}`, variables); + return data; + }; + + return useMutation({ + mutationKey: [MUTATION_KEYS.create_chat], + mutationFn: (variables: CreateChatMessageRequest) => handleCreateChatMessage(variables), + onMutate: (newMessage) => { + const prevData = queryClient.getQueriesData({ queryKey: [QUERY_KEYS.chat] }); + + queryClient.setQueryData>>([QUERY_KEYS.chat], (oldData) => { + const lastPage = oldData!.pages[oldData!.pages.length - 1]; + + return { + pages: [ + { + count: lastPage.count, + next: lastPage.next, + previous: lastPage.previous, + results: [ + { + id: Date.now(), + content: newMessage.new_message, + sender: "user" + } + ] + }, + ...oldData!.pages + ], + pageParams: [ + ...oldData!.pageParams, + { + limit: 10, + offset: 0 + } + ] + }; + }); + + + return { prevData: prevData ? prevData[0][1] : undefined }; + }, + onSuccess: (response) => { + + queryClient.setQueryData>>([QUERY_KEYS.chat], (oldData) => { + if (oldData) { + const lastPage = oldData!.pages[oldData!.pages.length - 1]; + + return { + pages: [ + { + count: lastPage.count, + next: lastPage.next, + previous: lastPage.previous, + results: { + ...response[0], + id: Date.now() + } + }, + ...oldData!.pages + ], + pageParams: [ + ...oldData!.pageParams, + { + limit: 10, + offset: 0 + } + ] + }; + } + + return oldData; + }); + + }, + onError: (err, newMessage, context) => { + if (context) { + queryClient.setQueryData( + [QUERY_KEYS.chat], + context.prevData + ); + } + }, + onSettled: (newMessage) => { + queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.chat] }); + } + }); +}; + +export default useCreateChatMessage; diff --git a/frontend/composables/api/chat/useGetChat.ts b/frontend/composables/api/chat/useGetChat.ts new file mode 100644 index 0000000..32a6da1 --- /dev/null +++ b/frontend/composables/api/chat/useGetChat.ts @@ -0,0 +1,62 @@ +// imports + +import { useInfiniteQuery } from "@tanstack/vue-query"; +import { API_ENDPOINTS, QUERY_KEYS } from "~/constants"; + +// types + +export type GetBranchResponse = ApiPaginated; + +const useGetBranch = ( + productId: string | number, + enabled: Ref +) => { + + // state + + const { $axios: axios } = useNuxtApp(); + + // method + + const handleGetChat = async ({ productId, limit, offset }: { + productId: number | string, + limit: number, + offset: number + }) => { + + const { data } = await axios.get(`${API_ENDPOINTS.chat.messages}/${productId}`, { + params: { + offset, + limit + }, + headers: { + Authorization: `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoyMTY3ODE2OTAwLCJpYXQiOjE3MzU4MTY5MDAsImp0aSI6ImQwN2E2Y2Y2NzgwZjRlNTE5NWIzOGQxMTAzYzU4NDQ3IiwidXNlcl9pZCI6NX0.slwd7ZSV7nUXEuDTYwwHUOo9ekCefwEEL4kVv2vSTFo` + } + }); + return data; + }; + + return useInfiniteQuery({ + enabled, + queryKey: [QUERY_KEYS.chat], + initialPageParam: { + limit: 10, + offset: 0 + }, + queryFn: ({ pageParam }) => handleGetChat({ + limit: pageParam.limit, + offset: pageParam.offset, + productId: productId + }), + getNextPageParam: (lastPage, pages) => { + if (!lastPage.next) return undefined; + + return { + limit: 10, + offset: pages.length * 10 + }; + } + }); +}; + +export default useGetBranch; diff --git a/frontend/composables/api/products/useGetProducts.ts b/frontend/composables/api/products/useGetProducts.ts index e69de29..7828df4 100644 --- a/frontend/composables/api/products/useGetProducts.ts +++ b/frontend/composables/api/products/useGetProducts.ts @@ -0,0 +1,60 @@ +// imports + +import { useQuery } from "@tanstack/vue-query"; +import { API_ENDPOINTS, QUERY_KEYS } from "~/constants"; + +// types + +export type GetProductsResponse = Product[]; + +export type GetProductsFilters = { + search: string | undefined; + sort: string | undefined; + categories: string[] | undefined; + price_range: number[] | undefined; + has_discount: boolean | undefined; + in_stock: boolean | undefined; +}; + +// composable + +const useGetProducts = ( + filters: GetProductsFilters, + page: ComputedRef +) => { + // state + + const { $axios: axios } = useNuxtApp(); + + // methods + + const handleGetProducts = async ({ + filters, + page, + }: { + filters: GetProductsFilters; + page: number; + }) => { + const { data } = await axios.get( + `${API_ENDPOINTS.products.get_all}`, + { + params: { + ...filters, + page, + offest: page * 10 - 10, + limit: 10, + }, + } + ); + + return data; + }; + + return useQuery({ + staleTime: 60 * 1000, + queryKey: [QUERY_KEYS.products, filters, page], + queryFn: () => handleGetProducts({ filters, page: page.value }), + }); +}; + +export default useGetProducts; diff --git a/frontend/composables/api/global/useBaseUrl.ts b/frontend/composables/global/useBaseUrl.ts similarity index 100% rename from frontend/composables/api/global/useBaseUrl.ts rename to frontend/composables/global/useBaseUrl.ts diff --git a/frontend/composables/api/global/useImageColor.ts b/frontend/composables/global/useImageColor.ts similarity index 100% rename from frontend/composables/api/global/useImageColor.ts rename to frontend/composables/global/useImageColor.ts diff --git a/frontend/composables/api/global/useTimer.ts b/frontend/composables/global/useTimer.ts similarity index 100% rename from frontend/composables/api/global/useTimer.ts rename to frontend/composables/global/useTimer.ts diff --git a/frontend/composables/api/global/useToast.ts b/frontend/composables/global/useToast.ts similarity index 100% rename from frontend/composables/api/global/useToast.ts rename to frontend/composables/global/useToast.ts diff --git a/frontend/constants/index.ts b/frontend/constants/index.ts index f7bb85e..7a3cd9f 100644 --- a/frontend/constants/index.ts +++ b/frontend/constants/index.ts @@ -1,10 +1,10 @@ export const API_ENDPOINTS = { account: { - profile : "/accounts/profile", + profile: "/accounts/profile", send_otp: "/accounts/send_otp", }, product: { - get : "/products", + get: "/products", }, auth: { signin: "/token", @@ -14,11 +14,15 @@ export const API_ENDPOINTS = { messages: "/chat/product", new_message: "/chat/product", }, + products: { + get_all: "/products", + }, }; export const QUERY_KEYS = { chat: "chat", product: "product", + products: "products", account: "account", }; diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts index 8978648..a613368 100644 --- a/frontend/nuxt.config.ts +++ b/frontend/nuxt.config.ts @@ -1,7 +1,7 @@ // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ compatibilityDate: "2024-11-01", - ssr: false, + ssr: true, devtools: { enabled: false }, css: ["~/assets/css/tailwind.css", "swiper/css"], diff --git a/frontend/pages/products.vue b/frontend/pages/products.vue index 80985b8..443a4c2 100644 --- a/frontend/pages/products.vue +++ b/frontend/pages/products.vue @@ -1,11 +1,28 @@