Merge branch 'main' of https://github.com/Byeto-Company/hossein_por_shop
This commit is contained in:
+12
-5
@@ -6,18 +6,26 @@ import { VueQueryDevtools } from "@tanstack/vue-query-devtools";
|
||||
|
||||
// state
|
||||
|
||||
useState('showLoadingOverlay', () => true);
|
||||
|
||||
const {
|
||||
$updateAvailable: updateAvailable,
|
||||
$handleUpdate: handleUpdate,
|
||||
$handleUpdateAvailable: handleUpdateAvailable,
|
||||
} = useNuxtApp();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<LoadingIndicator />
|
||||
|
||||
<NuxtRouteAnnouncer />
|
||||
<NuxtPwaManifest />
|
||||
|
||||
<UpdatePwaModal
|
||||
:isShow="updateAvailable"
|
||||
@update="handleUpdate"
|
||||
@close="handleUpdateAvailable"
|
||||
/>
|
||||
|
||||
<NuxtLayout>
|
||||
|
||||
<ToastProvider>
|
||||
<NuxtPage />
|
||||
|
||||
@@ -29,6 +37,5 @@ useState('showLoadingOverlay', () => true);
|
||||
</NuxtLayout>
|
||||
|
||||
<VueQueryDevtools dir="ltr" buttonPosition="bottom-left" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
isShow: boolean;
|
||||
};
|
||||
|
||||
type Emits = {
|
||||
update: [value: any];
|
||||
"update:isShow": [value: boolean];
|
||||
};
|
||||
|
||||
// props
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { isShow } = toRefs(props);
|
||||
|
||||
// emits
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
// computed
|
||||
|
||||
const visible = computed({
|
||||
get: () => isShow.value ?? false,
|
||||
set: (value: boolean) => emit("update:isShow", value),
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
v-model="visible"
|
||||
title="ورژن جدید"
|
||||
contectClass="!w-[90vw] lg:!w-[35vw]"
|
||||
@close="visible = false"
|
||||
>
|
||||
<template #content>
|
||||
<div class="w-full flex flex-col text-start gap-6 py-5" dir="rtl">
|
||||
<p class="leading-[0.75rem]">
|
||||
نسخه جدید و بهبود یافته اپلیکیشن با ویژگیهای جذاب منتظر
|
||||
شماست
|
||||
</p>
|
||||
<p class="leading-[0.75rem]">
|
||||
برای تجربه بهتر، لطفا نسخه فعلی را بروزرسانی کنید
|
||||
</p>
|
||||
<p class="leading-[0.75rem]">
|
||||
پس از کلیک بر روی گزینه دریافت نسخه جدید، برنامه به صورت
|
||||
خودکار بروز میشود
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="py-6 border-t border-slate-200 flex gap-3">
|
||||
<Button
|
||||
@click="emit('update', null)"
|
||||
class="rounded-full px-10"
|
||||
>
|
||||
<span>دریافت ورژن جدید</span>
|
||||
</Button>
|
||||
<DialogClose aria-label="Close">
|
||||
<Button variant="outlined" class="rounded-full px-10">
|
||||
انصراف
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
+19
-8
@@ -1,13 +1,24 @@
|
||||
FROM node:20-alpine as build-stage
|
||||
# FROM node:20-alpine as build-stage
|
||||
# WORKDIR /app
|
||||
# COPY package*.json ./
|
||||
# RUN npm install
|
||||
# COPY . .
|
||||
# RUN npm run build
|
||||
|
||||
# FROM node:20-alpine as production-stage
|
||||
# WORKDIR /app
|
||||
# COPY --from=build-stage /app /app
|
||||
# EXPOSE 3000
|
||||
# ENV NODE_ENV=production
|
||||
# CMD ["npm", "run", "start"]
|
||||
|
||||
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
RUN npm install -g npm@latest && \
|
||||
npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine as production-stage
|
||||
WORKDIR /app
|
||||
COPY --from=build-stage /app /app
|
||||
EXPOSE 3000
|
||||
ENV NODE_ENV=production
|
||||
CMD ["npm", "run", "start"]
|
||||
CMD ["npm", "start"]
|
||||
+32
-1
@@ -14,6 +14,9 @@ export default defineNuxtConfig({
|
||||
},
|
||||
|
||||
app: {
|
||||
head: {
|
||||
title: "فروشگاه هی ملز",
|
||||
},
|
||||
pageTransition: {
|
||||
enterActiveClass:
|
||||
"animate__animated animate__fadeIn animate__faster",
|
||||
@@ -69,9 +72,37 @@ export default defineNuxtConfig({
|
||||
"@nuxt/icon",
|
||||
"reka-ui/nuxt",
|
||||
"@vueuse/nuxt",
|
||||
"@formkit/auto-animate/nuxt"
|
||||
"@formkit/auto-animate/nuxt",
|
||||
"@vite-pwa/nuxt",
|
||||
],
|
||||
|
||||
pwa: {
|
||||
strategies: "injectManifest",
|
||||
srcDir: "public",
|
||||
filename: "sw.js",
|
||||
manifest: {
|
||||
name: "Heymlz",
|
||||
short_name: "Heymlz",
|
||||
theme_color: "#ffffff",
|
||||
icons: [
|
||||
{
|
||||
src: "/logo/logo-192x192.png",
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: "/logo/logo-512x512.png",
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
},
|
||||
],
|
||||
},
|
||||
workbox: {
|
||||
navigateFallback: "/",
|
||||
},
|
||||
devOptions: { enabled: true, type: "module" },
|
||||
},
|
||||
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
API_BASE_URL: "https://api.heymlz.com",
|
||||
|
||||
Generated
+13827
-11990
File diff suppressed because it is too large
Load Diff
+52
-50
@@ -1,52 +1,54 @@
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node .output/server/index.mjs",
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"dev-network": "nuxi dev --host",
|
||||
"dev-o": "nuxt dev -- -o",
|
||||
"test": "vitest",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@nuxt/icon": "^1.10.3",
|
||||
"@nuxtjs/google-fonts": "^3.2.0",
|
||||
"@tanstack/vue-query": "^5.66.9",
|
||||
"@tanstack/vue-query-devtools": "^5.66.9",
|
||||
"@vuelidate/core": "^2.0.3",
|
||||
"@vuelidate/validators": "^2.0.4",
|
||||
"@vueuse/integrations": "^12.7.0",
|
||||
"@vueuse/nuxt": "^12.7.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^1.8.1",
|
||||
"date-fns-jalali": "^4.1.0-0",
|
||||
"fast-average-color": "^9.4.0",
|
||||
"gsap": "^3.12.7",
|
||||
"isomorphic-dompurify": "^2.22.0",
|
||||
"jalali-ts": "^8.0.0",
|
||||
"masonry-layout": "^4.2.2",
|
||||
"nuxt": "^3.15.4",
|
||||
"reka-ui": "^1.0.0-alpha.6",
|
||||
"swiper": "^11.2.4",
|
||||
"universal-cookie": "^7.2.2",
|
||||
"vue": "latest",
|
||||
"vue-router": "latest",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"vue-skeletor": "^1.0.6",
|
||||
"vue3-marquee": "^4.2.2",
|
||||
"vue3-persian-datetime-picker": "^1.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.0.9",
|
||||
"@types/masonry-layout": "^4.2.8",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^4.0.9"
|
||||
}
|
||||
"name": "nuxt-app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node .output/server/index.mjs",
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"dev-network": "nuxi dev --host",
|
||||
"dev-o": "nuxt dev -- -o",
|
||||
"test": "vitest",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@nuxt/icon": "^1.10.3",
|
||||
"@nuxtjs/google-fonts": "^3.2.0",
|
||||
"@tanstack/vue-query": "^5.62.2",
|
||||
"@tanstack/vue-query-devtools": "^5.62.3",
|
||||
"@vite-pwa/nuxt": "^0.10.6",
|
||||
"@vuelidate/core": "^2.0.3",
|
||||
"@vuelidate/validators": "^2.0.4",
|
||||
"@vueuse/integrations": "^12.7.0",
|
||||
"@vueuse/nuxt": "^12.7.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^1.8.1",
|
||||
"date-fns-jalali": "^4.1.0-0",
|
||||
"fast-average-color": "^9.4.0",
|
||||
"gsap": "^3.12.7",
|
||||
"isomorphic-dompurify": "^2.22.0",
|
||||
"jalali-ts": "^8.0.0",
|
||||
"masonry-layout": "^4.2.2",
|
||||
"nuxt": "^3.15.4",
|
||||
"reka-ui": "^1.0.0-alpha.6",
|
||||
"swiper": "^11.2.4",
|
||||
"universal-cookie": "^7.2.2",
|
||||
"vue": "latest",
|
||||
"vue-router": "latest",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"vue-skeletor": "^1.0.6",
|
||||
"vue3-marquee": "^4.2.2",
|
||||
"vue3-persian-datetime-picker": "^1.2.2",
|
||||
"workbox-window": "^7.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.0.9",
|
||||
"@types/masonry-layout": "^4.2.8",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^4.0.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Workbox } from "workbox-window";
|
||||
|
||||
export default defineNuxtPlugin(() => {
|
||||
const updateAvailable = ref(false);
|
||||
|
||||
let wb = null;
|
||||
|
||||
if ("serviceWorker" in navigator) {
|
||||
wb = new Workbox("/sw.js");
|
||||
|
||||
// Detect when a new service worker is waiting
|
||||
wb.addEventListener("waiting", () => {
|
||||
updateAvailable.value = true;
|
||||
showUpdateModal();
|
||||
});
|
||||
|
||||
// Register the service worker
|
||||
wb.register();
|
||||
}
|
||||
|
||||
// Handle update confirmation
|
||||
const handleUpdate = () => {
|
||||
if (updateAvailable) {
|
||||
// Send message to service worker to skip waiting
|
||||
wb?.messageSW({ type: "SKIP_WAITING" });
|
||||
|
||||
// Wait for controller change and reload
|
||||
navigator.serviceWorker.addEventListener("controllerchange", () => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateAvailable = (state: boolean) => {
|
||||
updateAvailable.value = state;
|
||||
};
|
||||
|
||||
return {
|
||||
provide: {
|
||||
updateAvailable,
|
||||
handleUpdate,
|
||||
handleUpdateAvailable,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
function showUpdateModal() {
|
||||
// Create and show your modal UI here
|
||||
const modal = document.createElement("div");
|
||||
modal.innerHTML = `
|
||||
<div class="update-modal">
|
||||
<h2>New Version Available! 🎉</h2>
|
||||
<p>A new version of the app is available. Please update to get the latest features.</p>
|
||||
<button onclick="handleUpdate()">Update Now</button>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 210 KiB |
@@ -0,0 +1,53 @@
|
||||
import { precacheAndRoute } from "workbox-precaching";
|
||||
|
||||
// Precaching configuration for PWA assets
|
||||
precacheAndRoute(self.__WB_MANIFEST);
|
||||
|
||||
const VERSION = "1.0.0";
|
||||
|
||||
// Service Worker Installation
|
||||
self.addEventListener("install", (event) => {
|
||||
self.skipWaiting(); // Force activate new SW immediately
|
||||
console.log("Service Worker installed");
|
||||
});
|
||||
|
||||
// Service Worker Activation
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil(self.clients.claim());
|
||||
console.log("Service Worker activated");
|
||||
});
|
||||
|
||||
// Push Notification Handler for Django Web Push
|
||||
self.addEventListener("push", (event) => {
|
||||
try {
|
||||
const payload = event.data?.json() || {
|
||||
title: "New Notification",
|
||||
body: "You have a new message",
|
||||
icon: "/logo-192x192.png",
|
||||
data: { url: "/" },
|
||||
};
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.showNotification(payload.title, {
|
||||
body: payload.body,
|
||||
icon: payload.icon || "/logo-192x192.png",
|
||||
data: payload.data,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Push handling failed:", error);
|
||||
}
|
||||
});
|
||||
|
||||
// Notification Click Handler
|
||||
self.addEventListener("notificationclick", (event) => {
|
||||
event.notification.close();
|
||||
event.waitUntil(clients.openWindow(event.notification.data?.url || "/"));
|
||||
});
|
||||
|
||||
self.addEventListener("message", (event) => {
|
||||
if (event.data === "SKIP_WAITING") {
|
||||
self.skipWaiting();
|
||||
self.clients.claim();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user