Compare commits
3 Commits
b867030c8d
...
5de09aa13b
Author | SHA1 | Date | |
---|---|---|---|
5de09aa13b | |||
7cc292296b | |||
a000f966eb |
@ -1,6 +1,6 @@
|
|||||||
module pocketbase
|
module pocketbase
|
||||||
|
|
||||||
go 1.24.0
|
go 1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/chai2010/webp v1.4.0
|
github.com/chai2010/webp v1.4.0
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<p class="text-lg xl:text-2xl font-extrabold text-black dark:text-white">
|
<p class="text-lg xl:text-2xl font-extrabold text-black dark:text-white">
|
||||||
{{ mainCategoryName }}
|
{{ mainCategoryName }}
|
||||||
</p>
|
</p>
|
||||||
<span :class="['flex items-center justify-center',isOpen && 'rotate-180', 'transition-all']"><i
|
<span :class="['flex items-center justify-center', isOpen && 'rotate-180', 'transition-all']"><i
|
||||||
class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto"></i></span>
|
class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto"></i></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -40,7 +40,7 @@ const toggle = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Computed properties
|
// Computed properties
|
||||||
const hasMainCategory = computed(() => props.data.length > 0 && props.data[0].langs && props.data[0].langs.length > 0);
|
const hasMainCategory = computed(() => props.data && props.data.length > 0 && props.data[0].langs && props.data[0].langs.length > 0);
|
||||||
const mainCategoryName = computed(() => hasMainCategory.value ? props.data[0].langs[0].Name : '');
|
const mainCategoryName = computed(() => hasMainCategory.value ? props.data[0].langs[0].Name : '');
|
||||||
const subcategories = computed(() => hasMainCategory.value && props.data[0].children ? props.data[0].children : []);
|
const subcategories = computed(() => hasMainCategory.value && props.data[0].children ? props.data[0].children : []);
|
||||||
</script>
|
</script>
|
@ -78,9 +78,7 @@ type Component = {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
component: Component;
|
component: Component;
|
||||||
data: Object;
|
|
||||||
infoData: Object;
|
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</h1>
|
</h1>
|
||||||
<div class="flex flex-col gap-[25px]">
|
<div class="flex flex-col gap-[25px]">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="categoriesList.length < 1" class="animate-pulse">
|
<div v-if="categoriesList && categoriesList.length < 1" class="animate-pulse">
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between mt-4 text-white rounded-lg cursor-pointer xl:pr-24">
|
class="flex items-center justify-between mt-4 text-white rounded-lg cursor-pointer xl:pr-24">
|
||||||
<div class="w-32 h-4 bg-gray-200 rounded"></div>
|
<div class="w-32 h-4 bg-gray-200 rounded"></div>
|
||||||
@ -34,7 +34,8 @@
|
|||||||
|
|
||||||
<div v-for="(item, itemIndex) in filters" :key="itemIndex"
|
<div v-for="(item, itemIndex) in filters" :key="itemIndex"
|
||||||
:class="['mb-[30px]', visibleFeatures[item.feature] && 'border-b border-block pb-2']">
|
:class="['mb-[30px]', visibleFeatures[item.feature] && 'border-b border-block pb-2']">
|
||||||
<span class="flex justify-between items-center font-bold cursor-pointer mb-[25px] text-base"
|
<span
|
||||||
|
class="flex justify-between items-center font-bold cursor-pointer mb-[25px] text-base"
|
||||||
@click="toggleFeature(item.feature)">
|
@click="toggleFeature(item.feature)">
|
||||||
{{ item.feature }}
|
{{ item.feature }}
|
||||||
<span :class="[visibleFeatures[item.feature] && 'rotate-180', 'transition-all']"><i
|
<span :class="[visibleFeatures[item.feature] && 'rotate-180', 'transition-all']"><i
|
||||||
@ -69,7 +70,7 @@
|
|||||||
<div class="flex flex-col gap-12">
|
<div class="flex flex-col gap-12">
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<div v-if="categoriesList.length < 1" class="animate-pulse">
|
<div v-if="categoriesList && categoriesList.length < 1" class="animate-pulse">
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between mt-4 text-white rounded-lg cursor-pointer xl:pr-24">
|
class="flex items-center justify-between mt-4 text-white rounded-lg cursor-pointer xl:pr-24">
|
||||||
<div class="w-32 h-4 bg-gray-200 rounded"></div>
|
<div class="w-32 h-4 bg-gray-200 rounded"></div>
|
||||||
@ -167,7 +168,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import Product from "./Product.vue";
|
import Product from "./Product.vue";
|
||||||
import type { Feature, ProductType } from "~/types";
|
import type { Feature, GenericResponse, GenericResponseChildren, GenericResponseItems, ProductType } from "~/types";
|
||||||
import CategoryTree from "./CategoryTree.vue";
|
import CategoryTree from "./CategoryTree.vue";
|
||||||
|
|
||||||
defineProps<{ component: Component }>();
|
defineProps<{ component: Component }>();
|
||||||
@ -199,19 +200,21 @@ const maxElements = ref(0);
|
|||||||
const products = ref([] as ProductType[]);
|
const products = ref([] as ProductType[]);
|
||||||
async function getProducts() {
|
async function getProducts() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>(
|
||||||
`http://127.0.0.1:4000/api/public/products/category/${categoryId.value}?p=${page.value}&elems=${elems.value}`,
|
`/api/public/products/category/${categoryId.value}?p=${page.value}&elems=${elems.value}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: (_, status) => {
|
||||||
|
throw new Error(`HTTP error: ${status}`);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
products.value = data.data.items;
|
products.value = data.items;
|
||||||
maxElements.value = data.data.items_count + 1;
|
maxElements.value = data.items_count + 1;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("getProducts error:", error);
|
console.error("getProducts error:", error);
|
||||||
@ -221,18 +224,20 @@ async function getProducts() {
|
|||||||
const filters = ref([] as Feature[]);
|
const filters = ref([] as Feature[]);
|
||||||
async function getCategory() {
|
async function getCategory() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const { data } = await useMyFetch<GenericResponse<object>>(
|
||||||
`http://127.0.0.1:4000/api/public/products/category/1/classification`,
|
`/api/public/products/category/1/classification`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: (_, status) => {
|
||||||
|
throw new Error(`HTTP error: ${status}`);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
filters.value = data.data as Feature[];
|
filters.value = data as Feature[];
|
||||||
filters.value.forEach((el) => {
|
filters.value.forEach((el) => {
|
||||||
const parentId = el.feature_id;
|
const parentId = el.feature_id;
|
||||||
el.feature_values.forEach((el) => {
|
el.feature_values.forEach((el) => {
|
||||||
@ -245,20 +250,22 @@ async function getCategory() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const categoriesList = ref([]);
|
const categoriesList = ref();
|
||||||
async function getCategoryTree() {
|
async function getCategoryTree() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const { data } = await useMyFetch<GenericResponseChildren<ProductType[]>>(
|
||||||
`http://127.0.0.1:4000/api/public/categories/tree`,
|
`/api/public/categories/tree`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: (_, status) => {
|
||||||
|
throw new Error(`HTTP error: ${status}`);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = await res.json();
|
categoriesList.value = data.children;
|
||||||
categoriesList.value = data.data.children;
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("getCategory error:", error);
|
console.error("getCategory error:", error);
|
||||||
@ -325,20 +332,22 @@ async function loadMoreProducts() {
|
|||||||
qParams.append("features", selectedFilters.value.length > 0 ? selectedFilters.value : null);
|
qParams.append("features", selectedFilters.value.length > 0 ? selectedFilters.value : null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>(
|
||||||
`http://127.0.0.1:4000/api/public/products/category/${categoryId.value}?${qParams.toString()}`,
|
`/api/public/products/category/${categoryId.value}?${qParams.toString()}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: (_, status) => {
|
||||||
|
throw new Error(`HTTP error: ${status}`);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = await res.json();
|
maxElements.value = data.items_count;
|
||||||
maxElements.value = data.data.items_count;
|
|
||||||
|
|
||||||
if (data.data.items) {
|
if (data.items) {
|
||||||
products.value.push(...(data.data.items as ProductType[]));
|
products.value.push(...(data.items as ProductType[]));
|
||||||
} else {
|
} else {
|
||||||
reachedEnd.value = true;
|
reachedEnd.value = true;
|
||||||
}
|
}
|
||||||
@ -369,18 +378,20 @@ watch(selectedFilters, async (newQuestion) => {
|
|||||||
qParams.append("features", selectedFilters.value.length > 0 ? selectedFilters.value : null);
|
qParams.append("features", selectedFilters.value.length > 0 ? selectedFilters.value : null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>(
|
||||||
`http://127.0.0.1:4000/api/public/products/category/1?${qParams.toString()}`,
|
`/api/public/products/category/1?${qParams.toString()}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: (_, status) => {
|
||||||
|
throw new Error(`HTTP error: ${status}`);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = await res.json();
|
products.value = data.items;
|
||||||
products.value = data.data.items;
|
maxElements.value = data.items_count;
|
||||||
maxElements.value = data.data.items_count;
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("selectedFilters error:", error);
|
console.error("selectedFilters error:", error);
|
||||||
@ -401,18 +412,20 @@ watch(categoryId, async (newQuestion) => {
|
|||||||
qParams.append("features", selectedFilters.value.length > 0 ? selectedFilters.value : null);
|
qParams.append("features", selectedFilters.value.length > 0 ? selectedFilters.value : null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>(
|
||||||
`http://127.0.0.1:4000/api/public/products/category/${categoryId.value}?${qParams.toString()}`,
|
`api/public/products/category/${categoryId.value}?${qParams.toString()}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: (_, status) => {
|
||||||
|
throw new Error(`HTTP error: ${status}`);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = await res.json();
|
products.value = data.items;
|
||||||
products.value = data.data.items;
|
maxElements.value = data.items_count;
|
||||||
maxElements.value = data.data.items_count;
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("getCategory error:", error);
|
console.error("getCategory error:", error);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ofetch } from "ofetch";
|
import { ofetch } from "ofetch";
|
||||||
|
|
||||||
export interface RequestOptions<T> extends RequestInit {
|
export interface RequestOptions<T> extends RequestInit {
|
||||||
onError?: (error: Error, statusCode: number) => void;
|
onErrorOccured?: (error: Error, statusCode: number) => Promise<void>;
|
||||||
onSuccess?: (data: T, statusCode: number) => void;
|
onSuccess?: (data: T, statusCode: number) => void;
|
||||||
onStart?: () => void;
|
onStart?: () => void;
|
||||||
}
|
}
|
||||||
@ -24,7 +24,7 @@ export interface RequestOptions<T> extends RequestInit {
|
|||||||
export const useMyFetch = async <T>(
|
export const useMyFetch = async <T>(
|
||||||
url: string,
|
url: string,
|
||||||
options?: RequestOptions<T>
|
options?: RequestOptions<T>
|
||||||
): Promise<T | undefined> => {
|
): Promise<T> => {
|
||||||
if (options?.onStart) options.onStart();
|
if (options?.onStart) options.onStart();
|
||||||
let response = null;
|
let response = null;
|
||||||
try {
|
try {
|
||||||
@ -50,23 +50,23 @@ export const useMyFetch = async <T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handle errors if any
|
// handle errors if any
|
||||||
if (!response.ok && typeof options.onError == "function") {
|
if (!response.ok && typeof options.onErrorOccured == "function") {
|
||||||
options.onError(new Error(response.statusText), response.status);
|
options.onErrorOccured(new Error(response.statusText), response.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle success to be able clearly marked that request has finished
|
// handle success to be able clearly marked that request has finished
|
||||||
if (response.ok && typeof options.onSuccess == "function") {
|
if (response.ok && typeof options.onSuccess == "function") {
|
||||||
options.onSuccess(response._data.data, response.status);
|
options.onSuccess(response._data, response.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response._data as T;
|
return response._data as T;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// handle errors if any
|
// handle errors if any
|
||||||
if (typeof options?.onError == "function") {
|
if (typeof options?.onErrorOccured == "function") {
|
||||||
options.onError(e as Error, response?.status || 500);
|
options.onErrorOccured(e as Error, response?.status || 500);
|
||||||
} else {
|
} else {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
return undefined;
|
return {} as T;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
11
error.vue
Normal file
11
error.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-10 text-center">
|
||||||
|
<h1 class="text-3xl font-bold">Error {{ error?.statusCode }}</h1>
|
||||||
|
<p class="mt-4 text-gray-600">{{ error?.statusMessage }}</p>
|
||||||
|
<NuxtLink to="/" class="mt-6 text-blue-500 underline">Go back home</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps({ error: Object })
|
||||||
|
</script>
|
@ -11,6 +11,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
// import FooterBlock from "~/components/section/FooterBlock.vue";
|
||||||
useHead({
|
useHead({
|
||||||
link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.png" }],
|
link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.png" }],
|
||||||
});
|
});
|
||||||
|
@ -42,6 +42,9 @@ export default defineNuxtConfig({
|
|||||||
watch: {
|
watch: {
|
||||||
ignored: ["**/backend/pb_data/**"],
|
ignored: ["**/backend/pb_data/**"],
|
||||||
},
|
},
|
||||||
|
hmr: {
|
||||||
|
clientPort: 3000, // useful if proxying
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
typescript: {
|
typescript: {
|
||||||
|
13372
pnpm-lock.yaml
generated
13372
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,11 @@
|
|||||||
import { usePB } from "~/composables/usePB";
|
import { usePB } from "~/composables/usePB";
|
||||||
import type {
|
import type {
|
||||||
CountryList,
|
Country,
|
||||||
Currencies,
|
Currencies,
|
||||||
|
Currency,
|
||||||
FooterListResponse,
|
FooterListResponse,
|
||||||
|
GenericResponse,
|
||||||
|
GenericResponseItems,
|
||||||
MenuListResponse,
|
MenuListResponse,
|
||||||
PBFooterItem,
|
PBFooterItem,
|
||||||
PBMenuItem,
|
PBMenuItem,
|
||||||
@ -10,6 +13,7 @@ import type {
|
|||||||
} from "~/types";
|
} from "~/types";
|
||||||
import { useStore } from "./store";
|
import { useStore } from "./store";
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
|
import { useMyFetch } from "#imports";
|
||||||
|
|
||||||
function buildTreeRecursive(
|
function buildTreeRecursive(
|
||||||
data: (PBMenuItem | UIMenuItem)[],
|
data: (PBMenuItem | UIMenuItem)[],
|
||||||
@ -41,8 +45,8 @@ export const useMenuStore = defineStore("menuStore", () => {
|
|||||||
const menuItems = ref<MenuListResponse>();
|
const menuItems = ref<MenuListResponse>();
|
||||||
|
|
||||||
const footerItems = ref<FooterListResponse>();
|
const footerItems = ref<FooterListResponse>();
|
||||||
const countryList = ref<CountryList[]>();
|
const countryList = ref<Country[]>();
|
||||||
const currencies = ref<Currencies[]>();
|
const currencies = ref<Currency[]>();
|
||||||
|
|
||||||
// curr/country
|
// curr/country
|
||||||
const selectedCountry = ref();
|
const selectedCountry = ref();
|
||||||
@ -87,8 +91,8 @@ export const useMenuStore = defineStore("menuStore", () => {
|
|||||||
|
|
||||||
const getCountryList = async () => {
|
const getCountryList = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const {data} = await useMyFetch<GenericResponse<Country[]>>(
|
||||||
`http://127.0.0.1:4000/api/public/country/list`,
|
`/api/public/country/list`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@ -96,12 +100,12 @@ export const useMenuStore = defineStore("menuStore", () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!res.ok) {
|
// if (!res.ok) {
|
||||||
throw new Error(`HTTP error: ${res.status}`);
|
// throw new Error(`HTTP error: ${res.status}`);
|
||||||
}
|
// }
|
||||||
|
|
||||||
const data = await res.json();
|
// const data = await res.json();
|
||||||
countryList.value = data.data
|
countryList.value = data
|
||||||
if (countryList.value)
|
if (countryList.value)
|
||||||
selectedPhoneCountry.value = countryList.value[0]
|
selectedPhoneCountry.value = countryList.value[0]
|
||||||
|
|
||||||
@ -112,21 +116,25 @@ export const useMenuStore = defineStore("menuStore", () => {
|
|||||||
|
|
||||||
const getCurrencies = async () => {
|
const getCurrencies = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const {data} = await useMyFetch<GenericResponseItems<Currency[]>>(
|
||||||
`http://127.0.0.1:4000/api/public/currencies`,
|
`/api/public/currencies`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: (_, status) => { throw new Error(`HTTP error: ${status}`) },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!res.ok) {
|
// if (!res.ok) {
|
||||||
throw new Error(`HTTP error: ${res.status}`);
|
// throw new Error(`HTTP error: ${res.status}`);
|
||||||
}
|
// }
|
||||||
|
|
||||||
const data = await res.json();
|
// const data = await res.json();
|
||||||
currencies.value = data.data.items
|
currencies.value = data.items
|
||||||
|
|
||||||
|
// console.log(data.items, "data");
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("getList error:", error);
|
console.error("getList error:", error);
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,31 @@
|
|||||||
|
import { NuxtErrorBoundary } from "#components";
|
||||||
|
import { useMyFetch } from "#imports";
|
||||||
|
import type { GenericResponse, GenericResponseItems, UserCart } from "~/types";
|
||||||
|
import type { Product } from "~/types/product";
|
||||||
|
|
||||||
export const useProductStore = defineStore("productStore", () => {
|
export const useProductStore = defineStore("productStore", () => {
|
||||||
const productList = ref();
|
const productList = ref<Product>();
|
||||||
const modules = ref();
|
const modules = ref();
|
||||||
|
|
||||||
async function getList(count: number, categoryId = 1) {
|
async function getList(count: number, categoryId = 1) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const { data } = await useMyFetch<GenericResponseItems<Product>>(
|
||||||
`http://127.0.0.1:4000/api/public/products/category/${categoryId}?p=1&elems=${count}`,
|
`/api/public/products/category/${categoryId}?p=1&elems=${count}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: async (_, status) => {
|
||||||
|
// await navigateTo("/error", { replace: true });
|
||||||
|
throw createError({
|
||||||
|
statusCode: status,
|
||||||
|
statusMessage: `HTTP error: ${status}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!res.ok) {
|
productList.value = data.items;
|
||||||
throw new Error(`HTTP error: ${res.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await res.json();
|
|
||||||
productList.value = data.data.items;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("getList error:", error);
|
console.error("getList error:", error);
|
||||||
}
|
}
|
||||||
@ -49,7 +56,7 @@ export const useProductStore = defineStore("productStore", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addToCart(product) {
|
async function addToCart(product: Product) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`http://127.0.0.1:4000/api/public/user/cart/item/add/${product.id}/1`,
|
`http://127.0.0.1:4000/api/public/user/cart/item/add/${product.id}/1`,
|
||||||
@ -72,27 +79,30 @@ export const useProductStore = defineStore("productStore", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cart = ref();
|
const cart = ref({} as UserCart);
|
||||||
// async function getCart() {
|
async function getCart() {
|
||||||
// try {
|
try {
|
||||||
// const res = await fetch(`http://127.0.0.1:4000/api/public/user/cart`, {
|
const { data } = await useMyFetch<GenericResponse<UserCart>>(
|
||||||
// headers: {
|
`/api/public/user/cart`,
|
||||||
// "Content-Type": "application/json",
|
{
|
||||||
// },
|
headers: {
|
||||||
// });
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
onErrorOccured: (_, status) => {
|
||||||
|
throw new Error(`HTTP error: ${status}`);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// if (!res.ok) {
|
// if (!res.ok) {
|
||||||
// throw new Error(`HTTP error: ${res.status}`);
|
// throw new Error(`HTTP error: ${res.status}`);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// const data = await res.json();
|
cart.value = data;
|
||||||
// console.log(data);
|
} catch (error) {
|
||||||
|
console.error("getList error:", error);
|
||||||
// cart.value = data;
|
}
|
||||||
// } catch (error) {
|
}
|
||||||
// console.error("getList error:", error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
productList,
|
productList,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import { useMyFetch } from "#imports";
|
||||||
import { usePB } from "~/composables/usePB";
|
import { usePB } from "~/composables/usePB";
|
||||||
import type { componentsListType, PBPageItem } from "~/types";
|
import type { componentsListType, GenericResponse, PBPageItem, PlanPrediction } from "~/types";
|
||||||
// import { useI18n } from "vue-i18n";
|
// import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
export const useStore = defineStore("store", () => {
|
export const useStore = defineStore("store", () => {
|
||||||
@ -10,7 +11,7 @@ export const useStore = defineStore("store", () => {
|
|||||||
// calculator
|
// calculator
|
||||||
const monthlySavings = ref(137);
|
const monthlySavings = ref(137);
|
||||||
const storagePeriod = ref(10);
|
const storagePeriod = ref(10);
|
||||||
const totalInvestment = ref()
|
const totalInvestment:Ref<number> = ref(0)
|
||||||
const minValue = ref()
|
const minValue = ref()
|
||||||
|
|
||||||
// login
|
// login
|
||||||
@ -71,21 +72,17 @@ export const useStore = defineStore("store", () => {
|
|||||||
|
|
||||||
async function getCalculator() {
|
async function getCalculator() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const {data} = await useMyFetch<GenericResponse<PlanPrediction>>(
|
||||||
`http://127.0.0.1:4000/api/public/plan-prediction/easy/calculate?monthly_deposit=${monthlySavings.value}&years=${storagePeriod.value}`,
|
`/api/public/plan-prediction/easy/calculate?monthly_deposit=${monthlySavings.value}&years=${storagePeriod.value}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: (_, status) => { throw new Error(`HTTP error: ${status}`) },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!res.ok) {
|
totalInvestment.value = data.total_investement_value
|
||||||
throw new Error(`HTTP error: ${res.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await res.json();
|
|
||||||
totalInvestment.value = data.data.total_investement_value
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("getList error:", error);
|
console.error("getList error:", error);
|
||||||
@ -94,21 +91,22 @@ export const useStore = defineStore("store", () => {
|
|||||||
|
|
||||||
async function getMinValue() {
|
async function getMinValue() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const {data} = await useMyFetch<GenericResponse<number>>(
|
||||||
'http://127.0.0.1:4000/api/public/plan-prediction/free/minimum',
|
'/api/public/plan-prediction/free/minimum',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
onErrorOccured: (_, status) => { throw new Error(`HTTP error: ${status}`) },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!res.ok) {
|
// if (!res.ok) {
|
||||||
throw new Error(`HTTP error: ${res.status}`);
|
// throw new Error(`HTTP error: ${res.status}`);
|
||||||
}
|
// }
|
||||||
|
|
||||||
const data = await res.json();
|
// const data = await res.json();
|
||||||
minValue.value = data.data
|
minValue.value = data
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("getList error:", error);
|
console.error("getList error:", error);
|
||||||
|
10
taskfile.yml
10
taskfile.yml
@ -69,29 +69,29 @@ tasks:
|
|||||||
desc: "build and watch frontend in dev mode"
|
desc: "build and watch frontend in dev mode"
|
||||||
cmds:
|
cmds:
|
||||||
- |
|
- |
|
||||||
pnpm run dev
|
bun run dev
|
||||||
|
|
||||||
preview_front:
|
preview_front:
|
||||||
aliases: [pf]
|
aliases: [pf]
|
||||||
desc: "build and preview frontend"
|
desc: "build and preview frontend"
|
||||||
cmds:
|
cmds:
|
||||||
- |
|
- |
|
||||||
pnpm run build && pnpm run preview
|
bun run build && bun run preview
|
||||||
|
|
||||||
rebuild_front:
|
rebuild_front:
|
||||||
aliases: [rf]
|
aliases: [rf]
|
||||||
desc: "remove all and install all packages"
|
desc: "remove all and install all packages"
|
||||||
cmds:
|
cmds:
|
||||||
- |
|
- |
|
||||||
rm -rf ./node_modules ./pnpm-lock.yaml ./.nuxt ./.output
|
rm -rf ./node_modules ./bun-lock ./.nuxt ./.output
|
||||||
pnpm install
|
bun install
|
||||||
|
|
||||||
# build_docker_image:
|
# build_docker_image:
|
||||||
# aliases: [bdi]
|
# aliases: [bdi]
|
||||||
# desc: "build docker image"
|
# desc: "build docker image"
|
||||||
# cmds:
|
# cmds:
|
||||||
# - |
|
# - |
|
||||||
# pnpm run build
|
# bun run build
|
||||||
# task compile_gnu
|
# task compile_gnu
|
||||||
# cat <<EOF > temp.Dockerfile
|
# cat <<EOF > temp.Dockerfile
|
||||||
# FROM node:slim
|
# FROM node:slim
|
||||||
|
125
types/index.ts
125
types/index.ts
@ -1,3 +1,5 @@
|
|||||||
|
import type { DefineComponent } from "vue";
|
||||||
|
|
||||||
export interface ListResponse {
|
export interface ListResponse {
|
||||||
page: number;
|
page: number;
|
||||||
perPage: number;
|
perPage: number;
|
||||||
@ -42,7 +44,7 @@ export interface PBFooterItem {
|
|||||||
}>;
|
}>;
|
||||||
data: Array<{
|
data: Array<{
|
||||||
title: string;
|
title: string;
|
||||||
items: Array<string>
|
items: Array<string>;
|
||||||
}>;
|
}>;
|
||||||
company_info: Array<{
|
company_info: Array<{
|
||||||
data: string;
|
data: string;
|
||||||
@ -69,7 +71,6 @@ export interface PBPageItem {
|
|||||||
section_name: string;
|
section_name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
import type { DefineComponent } from "vue";
|
|
||||||
export interface SectionLangData {
|
export interface SectionLangData {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
@ -97,25 +98,25 @@ export type componentsListType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// menuStore
|
// menuStore
|
||||||
export type CountryList = {
|
// export type CountryList = {
|
||||||
call_prefix: string;
|
// call_prefix: string;
|
||||||
currency_iso_code: string;
|
// currency_iso_code: string;
|
||||||
iso_code: string;
|
// iso_code: string;
|
||||||
name: string;
|
// name: string;
|
||||||
}
|
// }
|
||||||
|
|
||||||
export type Countries = {
|
export type Countries = {
|
||||||
call_prefix: string;
|
call_prefix: string;
|
||||||
currency_iso_code: string;
|
currency_iso_code: string;
|
||||||
iso_code: string;
|
iso_code: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type PartnersList = {
|
export type PartnersList = {
|
||||||
country_iso: string;
|
country_iso: string;
|
||||||
country_name: string;
|
country_name: string;
|
||||||
total: number;
|
total: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Currencies = {
|
export type Currencies = {
|
||||||
iso_code: string;
|
iso_code: string;
|
||||||
@ -126,34 +127,88 @@ export type Currencies = {
|
|||||||
sign: string;
|
sign: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
suffix: boolean;
|
suffix: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type FeatureValue = {
|
export type FeatureValue = {
|
||||||
parent: number,
|
parent: number;
|
||||||
products_with_value: number,
|
products_with_value: number;
|
||||||
value: string,
|
value: string;
|
||||||
value_id: number,
|
value_id: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Feature = {
|
export type Feature = {
|
||||||
feature: string,
|
feature: string;
|
||||||
feature_id: number,
|
feature_id: number;
|
||||||
feature_values: FeatureValue[],
|
feature_values: FeatureValue[];
|
||||||
products_with_feature: number,
|
products_with_feature: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type ProductType = {
|
export type ProductType = {
|
||||||
applied_tax_rate: number,
|
applied_tax_rate: number;
|
||||||
cover_picture_uuid: string,
|
cover_picture_uuid: string;
|
||||||
description: string,
|
description: string;
|
||||||
formatted_price: string,
|
formatted_price: string;
|
||||||
id: number,
|
id: number;
|
||||||
in_stock: number,
|
in_stock: number;
|
||||||
is_sale_active: boolean,
|
is_sale_active: boolean;
|
||||||
link_rewrite: string,
|
link_rewrite: string;
|
||||||
name: string,
|
name: string;
|
||||||
price: number,
|
price: number;
|
||||||
tax_name: string,
|
tax_name: string;
|
||||||
cart_item_id?: number
|
cart_item_id?: number;
|
||||||
product_id?: number
|
product_id?: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export interface Country {
|
||||||
|
iso_code: string;
|
||||||
|
currency_iso_code: string;
|
||||||
|
call_prefix: string;
|
||||||
|
need_postcode: boolean;
|
||||||
|
postcode_format: string;
|
||||||
|
is_default: boolean;
|
||||||
|
active: boolean;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Currency {
|
||||||
|
iso_code: string;
|
||||||
|
name: string;
|
||||||
|
UpdatedAt: string;
|
||||||
|
iso_code_num: number;
|
||||||
|
precision: number;
|
||||||
|
sign: string;
|
||||||
|
active: boolean;
|
||||||
|
suffix: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserCart {
|
||||||
|
id: number;
|
||||||
|
checkout_in_progress: boolean;
|
||||||
|
total_value: number;
|
||||||
|
currency_iso: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenericResponse<Data> {
|
||||||
|
data: Data;
|
||||||
|
message?: string;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenericResponseItems<Data> {
|
||||||
|
data: { items: Data; items_count: number };
|
||||||
|
message?: string;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenericResponseChildren<Data> {
|
||||||
|
data: { children: Data; items_count: number };
|
||||||
|
message?: string;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type {
|
||||||
|
InvestmentPiece,
|
||||||
|
PlanPrediction,
|
||||||
|
PeriodToFirstPiece,
|
||||||
|
} from "./planPrediction";
|
||||||
|
export type { Product } from "./product";
|
||||||
|
40
types/planPrediction.ts
Normal file
40
types/planPrediction.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
export interface PlanPrediction {
|
||||||
|
total_investement_value: number
|
||||||
|
total_input_price_increase: number
|
||||||
|
total_input_price_increase_covered_pieces: number
|
||||||
|
months_spent_on_plan: number
|
||||||
|
money_spent: number
|
||||||
|
bought_pieces: number
|
||||||
|
annual_investment_return: number
|
||||||
|
investment_piece: InvestmentPiece
|
||||||
|
max_pieces_in_package: number
|
||||||
|
period_to_first_piece: PeriodToFirstPiece
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InvestmentPiece {
|
||||||
|
referenced_product_id: number
|
||||||
|
name: string
|
||||||
|
cover_picture_uuid: string
|
||||||
|
weight: string
|
||||||
|
active: boolean
|
||||||
|
price: number
|
||||||
|
price_per_gram: number
|
||||||
|
currency_iso: string
|
||||||
|
currency_conversion_rate: string
|
||||||
|
max_comission_value: number
|
||||||
|
average_input_price_increase: number
|
||||||
|
features: Feature[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Feature {
|
||||||
|
feature_id: number
|
||||||
|
feature: string
|
||||||
|
value_id: number
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeriodToFirstPiece {
|
||||||
|
years: number
|
||||||
|
months: number
|
||||||
|
days: number
|
||||||
|
}
|
13
types/product.ts
Normal file
13
types/product.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export interface Product {
|
||||||
|
id: number;
|
||||||
|
link_rewrite: string;
|
||||||
|
name: string;
|
||||||
|
cover_picture_uuid: string;
|
||||||
|
description: string;
|
||||||
|
price: number;
|
||||||
|
formatted_price: string;
|
||||||
|
in_stock: number;
|
||||||
|
is_sale_active: boolean;
|
||||||
|
applied_tax_rate: number;
|
||||||
|
tax_name: string;
|
||||||
|
}
|
Reference in New Issue
Block a user