# طرح فاز ۲ — قلم ۲ (D3): سه رفتار جامانده در اپ‌ها تاریخ: ۱۴۰۵/۰۷/۲۰ (2026-10-12) ## وضعیت — تصمیم مالک گرفته شد (۲۰۲۶/۱۰/۱۲) > «الف و ب مهم پس به ترتیب برو جلو — ج اصلا اولیوت نداره جزو اخرین چیز هایی هست > که شاید درستش کنیم» | قلم | تصمیم | وضعیت | |---|---|---| | **الف** — پیام مهمان | انجام شود | ✅ **انجام شد** — گزینه ب (پیام + حفظ بازگشت به مقصد) در هر دو اپ | | **ب** — گارد `isLoaded` | انجام شود، طبق پیشنهاد پورت **نشود** | ✅ **ثبت شد** به‌عنوان «عمداً تکرار نشد — D3/ب» در `configStore.ts` هر دو اپ | | **ج** — آمار بازدید | **آخرین اولویت، شاید هرگز** | ⏸️ معوق — بخش «شکاف ۳» پایین به‌عنوان طرحِ آماده نگه داشته شد | --- --- ## چه چیزی قبلاً انجام شده (بررسی شد، ادعا نیست) D3 ده کار بود. هفت‌تایش انجام شده است: | کار | وضعیت | مدرک | |---|---|---| | ۵ صفحه `/report`، `/report/detail`، `/ChildOrder`، `/customerChoice`، `/complete_profile` | ✅ در **هر دو** اپ | `apps/user-app/src/router/index.ts` و همان فایل در `user-app-v2` — هر پنج مسیر ثبت شده با `meta.layout='FullPage'` | | گارد `sts_verify_step_two` روی تکمیل پروفایل | ✅ | `router/index.ts` گارد `requiresIncompleteProfile`؛ از سرور می‌پرسد نه از حافظه، پس پروفایلی که روی دستگاه دیگر کامل شده هم محترم است | | سقف موجودی سبد v2 (`capQty`/`maxQty`) | ✅ یکسان در هر دو اپ | `stores/cartStore.ts:80,88` در هر دو | **سه کار باقی است** و هر سه را بررسی و در مرجع تأیید کردم. --- ## شکاف ۱ — رفتار مهمان: الان به صفحه ورود پرتاب می‌شود **مرجع** (`henza_pwa/src/router/index.ts:253-261`): کاربر وارد‌نشده که یکی از پنج صفحه شخصی را باز کند، یک پیام «ابتدا وارد شوید» می‌بیند و **به خانه** می‌رود. ```ts if (to.name === 'ordersList' || ... || to.name === 'completeProfile') { showNotify('ابتدا وارد شوید'); return next({name: 'home'}) } else return next() ``` **تارگت** (`apps/user-app/src/router/index.ts:139-141`): به `/login` می‌رود با `query.redirect`، و **هیچ پیامی نشان نمی‌دهد**. ```ts if (to.meta.requiresAuth && !session.isLoggedIn) { return { name: 'login', query: { redirect: to.fullPath } }; } ``` **چرا مهم است:** کاربر مهمان که روی «سفارش‌های من» می‌زند، بی‌هیچ توضیحی در صفحه ورود می‌افتد و نمی‌داند چه شد. رفتار مرجع صریح‌تر است و D3 هم همان را خواسته. ### ⚠️ یک تضاد که باید خودت تصمیم بگیری رفتار فعلی تارگت از یک جهت **بهتر** است: `query.redirect` دارد، یعنی بعد از ورود، کاربر به همان صفحه‌ای که می‌خواست برمی‌گردد. رفتار مرجع این را ندارد — کاربر به خانه می‌رود و باید دوباره دستی به آن صفحه برود. سه گزینه: | گزینه | نتیجه | |---|---| | **الف) عیناً مرجع** | toast + خانه. `query.redirect` از بین می‌رود. تطبیق کامل با D3. | | **ب) بهترین هر دو** (پیشنهاد من) | toast «ابتدا وارد شوید» نشان داده شود، **ولی** مقصد همان صفحه ورود با `redirect` بماند. کاربر هم می‌فهمد چه شد، هم بعد از ورود سرِ جایش برمی‌گردد. در ماتریس به‌عنوان «عمداً تکرار نشد» ثبت می‌شود. | | **ج) دست نزن** | شکاف باز می‌ماند و در ماتریس ثبت می‌شود. | هزینه هر سه یکی است (چند خط). تفاوت فقط در تجربه کاربر است. --- ## شکاف ۲ — گارد `isLoaded` **مرجع** (`router/index.ts:263-266`): کاربر وارد‌شده‌ای که داده‌های اولیه‌اش (`memory.state.isLoaded`) هنوز نیامده، به هر صفحه‌ای جز `home` برود به خانه برگردانده می‌شود — مگر آن مسیر `meta.skipLoadedCheck` داشته باشد. **تارگت**: پورت نشده، و در کد **صریحاً نوشته شده چرا**: ```ts // apps/user-app/src/stores/configStore.ts:19 // Not ported (no backend): promotionList, ignoreList, priceList, isLoaded. ``` **نظر من: این یکی را نباید پورت کنیم، و پیشنهادم «نه» است.** دلیل: `isLoaded` در مرجع پرچمِ «یک درخواست بزرگ اولیه نشست» بود که تارگت ندارد — تارگت هر صفحه داده‌ی خودش را می‌گیرد. اگر این گارد را اضافه کنیم، چیزی به کاربر اضافه نمی‌شود و فقط یک پرش ناخواسته به خانه ساخته می‌شود: مثلاً کاربری که لینک محصول را از تلگرام باز می‌کند، به‌جای محصول در خانه می‌افتد. این دقیقاً مصداق «باگ مرجع را پورت نکن» (D4) است. اگر موافقی، به‌عنوان **«عمداً تکرار نشد — D4»** ثبت می‌شود و D3 بسته می‌شود. اگر مخالفی، پورت می‌کنم. --- ## شکاف ۳ — آمار بازدید صفحه‌ها (`log/create` و `log/update`) **مرجع** دو مسیر دارد و زمان ماندن کاربر در هر صفحه را اندازه می‌گیرد: | | مرجع | |---|---| | ورود به صفحه | `POST /log/create/{title}` → یک ردیف با `start_at`، `ip`، `title`، `customer_id` (اگر وارد شده باشد) می‌سازد و `id` را برمی‌گرداند | | خروج از صفحه | `GET /log/update/{id}` → `end_at` و `time_present` را پر می‌کند | | صفحه محصول | `data: {product_id}` هم می‌فرستد تا بازدید هر محصول جدا شمرده شود | | مستثنا | `login` و `userPage::hasSubRoute` | مرجع: `henza_server/app/Http/Controllers/Api/LogController.php`، `henza_pwa/src/router/customAnalytics.ts`. **تارگت**: نه مسیر، نه جدول، نه فراخوانی. کامل غایب. این بزرگ‌ترین قلم از سه‌تاست — جدول، مدل Prisma، مهاجرت، دو مسیر API، یک composable در هر اپ، و صفحه‌های گزارش ادمین (`queryTitle`، `queryAllCustomer`، `queryCustomer`، `ago`) که آمار را نشان می‌دهند. ### ⚠️ دو ایراد مرجع که عیناً پورت نمی‌کنم ۱. **مسیر بی‌محافظ است.** در مرجع `log/create` احراز هویت ندارد و هر عنوانی را می‌پذیرد، پس هر کسی از بیرون می‌تواند جدول را با ردیف‌های جعلی پر کند و آمار را بی‌معنا یا دیسک را پر کند. پیشنهاد: عنوان فقط از یک فهرست بسته (نام مسیرهای واقعی) پذیرفته شود و نرخ درخواست محدود شود. ۲. **`GET` که داده را عوض می‌کند.** `log/update` یک `GET` است که ردیف را می‌نویسد. پیشنهاد: `POST` باشد. (اپ‌های تارگت تازه نوشته می‌شوند، پس سازگاری عقب‌رو لازم نیست.) ### ⚠️ و یک پرسش که فقط تو می‌توانی جواب بدهی این قابلیت **رفتار هر مشتری را با IP ثبت می‌کند** — کدام صفحه‌ها را دید و چند ثانیه ماند. فنی‌اش ساده است، ولی این داده‌ی شخصی است و مرجع هیچ‌وقت آن را پاک نمی‌کرد (جدول بی‌نهایت رشد می‌کند). سه پرسش: | پرسش | پیشنهاد من | |---|---| | این آمار را واقعاً لازم داری، یا در سیستم قدیم هم عملاً نگاهش نمی‌کردی؟ | اگر نگاهش نمی‌کردی، نسازیم. ساده‌ترین قابلیت، قابلیتی است که وجود ندارد. | | اگر لازم است، چند وقت نگه داشته شود؟ | ۹۰ روز، و قدیمی‌ترها خودکار پاک شوند. | | IP هم ذخیره شود؟ | نه. برای «کدام صفحه چقدر دیده شد» لازم نیست و حساس‌ترین بخش داده است. | --- ## جمع‌بندی و پیشنهاد ترتیب کار | # | قلم | پیشنهاد من | هزینه | |---|---|---|---| | ۱ | toast مهمان | **گزینه ب** — هم پیام، هم بازگشت به صفحه مقصد | کم (نیم ساعت، هر دو اپ) | | ۲ | گارد `isLoaded` | **پورت نشود** — «عمداً تکرار نشد — D4» | صفر | | ۳ | آمار بازدید | **اول جواب بده لازمش داری یا نه.** اگر بله: با عنوان بسته، `POST`، بدون IP، نگه‌داری ۹۰ روزه | متوسط تا زیاد (جدول + مهاجرت + ۲ مسیر + ۲ اپ + صفحه گزارش ادمین) | اگر بگویی «قلم ۱ و ۲ را انجام بده، ۳ را فعلاً نه»، D3 با یک ردیف «عمداً تکرار نشد» برای آمار بسته می‌شود و می‌رویم سراغ قلم بعدی فاز ۲.