refactor: drop toast + over-engineered extras, add inline admin feedback
CI / quality (push) Waiting to run

Phase 1 cleanup of the personal-site revamp. Backend/architecture untouched;
changes are limited to removing unused complexity and restoring feedback.

Removals
- Toast system: delete react-hot-toast, Toaster, QueryToastBridge, lib/toast,
  the toggle/easter-egg calls, related i18n keys and the dependency.
- Contact protection: remove Turnstile + per-IP rate limiting
  (lib/contact-guard, lib/contact-protection, admin screen, form widget,
  app-config wiring, nav entry, test).
- Speculative specs: delete orders, products, downloads, project-inquiry.

Inline feedback (replaces toast, no new deps)
- Add lib/admin-feedback (withFlash/readFlash) and components/admin/admin-flash,
  rendered centrally by AdminDashboardShell.
- Emit success/error messages for media, site-settings, portfolio, smtp,
  marquee and maintenance actions; pages read them via searchParams.
- Contact form shows validation/delivery errors inline; success still
  redirects to /success.

Docs
- Fix stale paths in frontend-system-* (components/root -> components/admin,
  lib/root-navigation -> lib/admin-navigation, drop phantom src/) and remove
  contact-protection references from docs and CLAUDE.md.
- Add docs/PHASE0_DIAGNOSIS.md (diagnosis report).

Note: proxy.ts self-fetch kept intentionally; it also drives maintenance mode.
This commit is contained in:
MohFarawati
2026-07-14 21:03:51 +02:00
parent 1a7b3397f6
commit d48497b992
62 changed files with 443 additions and 1453 deletions
+1 -8
View File
@@ -73,9 +73,6 @@ Handles media library queries and media bindings.
lib/mail.ts
Handles SMTP delivery via nodemailer.
lib/contact-guard.ts
Handles Turnstile verification and rate limiting for contact submissions.
These modules act as the primary server-side application layer.
---
@@ -149,11 +146,7 @@ Database queries should not be implemented directly inside route-level UI files.
app/[locale]/(site)/contact/page.tsx
2. Server action validates the input using Zod.
3. Turnstile verification and rate limiting run via:
lib/contact-guard.ts
4. Email is sent through:
3. Email is sent through:
lib/mail.ts
+1 -3
View File
@@ -49,9 +49,7 @@
### Current implementation
- Contact submission requires valid name, email, and message
- Turnstile is optional and controlled by settings
- Rate limiting is optional and keyed by hashed client IP plus time window
- Contact submission requires valid name, email, and message (validated with Zod)
- Successful submission sends email only
- No submission record is stored in the database
-3
View File
@@ -13,8 +13,6 @@
- `CASE_STUDY`
- Contact form with:
- validation
- optional Turnstile
- rate limiting
- email delivery
- Success page after contact submission
- Maintenance redirect flow
@@ -29,7 +27,6 @@
- Media library with usage bindings
- Site settings management
- SMTP settings and test email
- Contact protection settings
- Marquee settings
- Maintenance toggle
- UI Kit preview page
+148
View File
@@ -0,0 +1,148 @@
# تقرير المرحلة 0 — الجرد والتشخيص
> تقرير فقط، بدون أي تعديل على الكود. لا يبدأ أي تنفيذ قبل موافقتك.
> التاريخ: 2026-07-14
---
## خلاصة سريعة
البنية التقنية والـ backend سليمة فعلاً كما ذكرت — لا تحتاج إعادة كتابة. المشاكل الحقيقية في طبقتين:
1. **طبقة العرض/التجربة (UI/UX):** الصفحة الرئيسية والـ views الثلاثة تشترك في مظهر واحد رتيب (كروت ناعمة على سطح رمادي)، وأهم عيب أن **البورتفوليو بلا صور** في الصفحة الرئيسية وصفحة قائمة الأعمال — وهذا قاتل لموقع مصمم.
2. **الوثائق:** الملفات الأساسية دقيقة، لكن ملفات `frontend-system-*.md` الثلاثة قديمة (تشير إلى مسارات محذوفة).
كما رصدت تعقيدات على طراز الـ SaaS زائدة عن حاجة موقع شخصي، أهمها: نظام الـ Toast، بنية الأدمن الثلاثية، حماية نموذج التواصل (rate-limit + Turnstile)، وطبقتا مصادقة للأدمن.
---
## 0.2 — حالة الوثائق (`docs/` و `specs/`) مقابل الكود
### دقيقة ومطابقة للكود ✅
| الملف | الحالة |
|---|---|
| `docs/ARCHITECTURE.md` | دقيق (ملاحظة: يسمّي الملف `middleware` لكنه فعلياً `proxy.ts` بعد ترقية Next.js 16) |
| `docs/DOMAIN_RULES.md` | دقيق — قواعد البورتفوليو والميديا والتواصل كلها مطابقة للـ schema |
| `docs/PROJECT_OVERVIEW.md` | دقيق (ينقصه ذكر بعض المسارات) |
| `docs/FEATURES.md` | دقيق لكنه ناقص (لا يذكر الاختبارات الموجودة فعلاً ولا نظام الصوت) |
| كل ملفات `specs/*.md` السبعة | دقيقة وصادقة في التمييز بين المنفَّذ والمقترح |
### قديمة أو خاطئة ❌ (تحتاج تصحيح في المرحلة 1)
الملفات الثلاثة `docs/frontend-system-audit.md`، `frontend-system-current-state.md`، `frontend-system-refactor-summary.md` كُتبت قبل إعادة تسمية مجلد، فصارت تشير إلى مسارات غير موجودة:
- `components/root/*` ← الصحيح `components/admin/*`
- `lib/root-navigation.ts` ← الصحيح `lib/admin-navigation.ts`
- تدّعي وجود مجلد `src/` — وهو غير موجود أصلاً
المحتوى المفاهيمي فيها (التوكنز، الحركة، الأيقونات) ما زال صحيحاً؛ المشكلة في المسارات فقط.
### ميزات موجودة في الكود لكنها غير موثّقة
- نظام الصوت: `components/sound-provider.tsx` + `sound-toggle.tsx`
- مسارات API: `app/api/health` و `app/api/site/default-locale`
- خط رفع/تقديم الميديا المحلي: `app/uploads/media/[...segments]/route.ts`
- صفحة `portfolio/category` الفهرسية، وصفحات إعدادات الموقع الفرعية (brand / localization)
---
## 0.3 و 0.4 — الـ Views الثلاثة والصفحة الرئيسية
المقصود بـ «3 views» على الأرجح قوالب الصفحات الرئيسية الثلاثة:
### View 1 — الصفحة الرئيسية (`app/[locale]/(site)/page.tsx`)
**البنية:** hero بطول الشاشة (نص + تدرّج لوني فقط) ← ثم 6 أقسام متتالية (Bento، Marquee، Projects، Capabilities، Process، Contact CTA) كلها مبنية على نفس نمط `SectionHeading` + شبكة كروت `AppCard` متطابقة.
**لماذا ضعيفة:**
- **لا مفهوم ولا صور إطلاقاً** — الـ hero نص وتدرّج فقط، ولا توجد أي صورة عمل في الصفحة كلها. حتى كروت المشاريع المميزة نصية بلا صور مصغّرة.
- **رتابة وتكرار** — خمسة من ستة أقسام نفس النمط البصري تماماً، لا إيقاع ولا تمييز بينها.
- **تسلسل هرمي ضعيف** — بعد hero طويل يصطدم النظر بجدار كروت متساوية الوزن، ولا شيء يقول «هذا الأهم». الـ CTA الوحيد مدفون في الأسفل.
- **محتوى عام** («Core stack»، «Current focus») يشبه صفحة «about» أكثر من واجهة مصمم.
### View 2 — قائمة الأعمال (`portfolio/page.tsx`)
hero مضغوط + فلتر تصنيفات + شبكة `PortfolioProjectGrid` بعمودين. صفحة التصنيف `category/[slug]` مطابقة حرفياً لها.
**لماذا ضعيفة:**
- **بلا صور بتاتاً** — الكروت 100% نص رغم أن البيانات تحتوي `coverImagePath`. هذه أكبر مشكلة: القائمة التي يُفترض أن تبيع العمل لا تعرض منه شيئاً. يشبه فهرس مدوّنة لا بورتفوليو.
- تخطيط مسطّح بعمودين، كروت متساوية، بلا تمييز أو معاينة عند المرور.
### View 3 — تفاصيل المشروع (`portfolio/[slug]/page.tsx`)
`PageHero` **فارغ** (بلا عنوان) ثم `PortfolioProjectDetail` الذي يتفرّع لثلاثة قوالب: `GRID` / `STORY` / `CASE_STUDY`.
**لماذا ضعيفة:**
- **hero فارغ** يهدر أعلى الصفحة بمساحة تدرّج فارغة؛ العنوان الحقيقي يظهر لاحقاً داخل كرت.
- **كل شيء محبوس داخل كروت** — الصور والنصوص والمعرض كلها في كروت مدوّرة على سطح، بلا صورة غلاف ممتدة (full-bleed) ولا عرض غامر. الصور مقصوصة بارتفاعات ثابتة.
- **ثلاثة قوالب بمظهر واحد** — رغم تعقيد الكود، النتيجة البصرية متشابهة.
### حالة المعرض (Gallery)
يوجد معرض فعلاً لكن **داخل صفحة التفاصيل فقط** (`AssetGallery`). لا يوجد أي معرض/صور في الصفحة الرئيسية ولا في قائمة الأعمال — وهذا هو أساس ملاحظتك «لا يوجد gallery».
### النظام البصري الحالي
- ألوان: متغيرات HSL بثيم فاتح/داكن، لون العلامة أحمر-برتقالي `9 73° 50%`.
- زوايا: مدوّرة وناعمة جداً (`radius-surface: 24px`) — تعزّز مظهر الكروت الموحّد.
- خطوط: `Museo Sans Rounded` (لاتيني) + `Dubai` (عربي).
- **العيب على مستوى النظام:** كفؤ تقنياً لكنه يُنتج نسيجاً واحداً في كل مكان (كرت ناعم على سطح رمادي + لمسة برتقالية) — يشبه ثيم لوحة تحكم SaaS طُبِّق على بورتفوليو، لا هوية بصرية مبنية للبورتفوليو أولاً.
---
## 0.5 — نظام الـ Toast/الإشعارات (جاهز للإزالة)
المكتبة: `react-hot-toast` (لا Radix toast ولا Sonner).
**البنية الأساسية (مرشحة للحذف):**
- `lib/toast.tsx` — الغلاف حول react-hot-toast
- `components/ui/toaster.tsx` — مضيف العرض الوحيد
- `components/admin/query-toast-bridge.tsx` — يقرأ `?success=`/`?error=` و sessionStorage ويطلق الـ toast ثم ينظّف الرابط
- نقطة التركيب: `app/layout.tsx` (سطور 6، 9، 99-100)
- التبعية: `react-hot-toast` في `package.json`
**مواضع الاستدعاء:** toggle الثيم والصوت، تبديل اللغة، easter egg للنقر الثلاثي على الشعار، وكل أوامر الأدمن (media, site-settings, portfolio, smtp, marquee, maintenance) عبر `?success=`/`?error=`، ونموذج التواصل.
**⚠️ تحذير مهم — الـ toast هو قناة التغذية الراجعة الوحيدة في:**
1. **كل عمليات الأدمن (CRUD)** — الحفظ/الحذف/الخطأ تظهر فقط عبر الـ toast. إزالته دون بديل تترك كل عملية بلا أي تأكيد أو رسالة خطأ.
2. **أخطاء دخول الأدمن** (`locked`/`invalid`) — تصبح صامتة.
3. **أخطاء نموذج التواصل** (تحقق، rate-limit، Turnstile) — تعتمد كلياً على الـ toast.
**الخلاصة:** إزالة الـ toast من العناصر التجميلية (toggles، easter egg) آمنة تماماً. أما أوامر الأدمن ونموذج التواصل فتحتاج **بديلاً بسيطاً للتغذية الراجعة** (رسالة inline داخل الصفحة) قبل الإزالة، وإلا ستصبح العمليات صامتة. النجاح في نموذج التواصل محمي لأنه يحوّل لصفحة `/success` مستقلة.
---
## 0.6 — تعقيدات SaaS زائدة (مقترحة للإزالة، بالأولوية)
اسم المشروع نفسه `sass-mohfarawati` يكشف الأصل: بُني كأنه منتج SaaS متعدد المستخدمين، بينما هو موقع شخصي بمالك واحد.
| # | البند | لماذا زائد | خطر الإزالة |
|---|---|---|---|
| P1 | **بنية الأدمن الثلاثية** (`_admin` + `admin-internal` + `/root`) — نحو 19 ملف re-export + منطق توجيه في `lib/admin-routing.ts` و`proxy.ts` | موقع بمالك واحد يحتاج مسار أدمن واحد خلف كلمة مرور، لا توجيه بمستوى white-label | متوسط |
| P2 | **الإعدادات في قاعدة البيانات** (`AppConfig` + `lib/app-config.ts` 310 سطر) تخزّن كل شيء: إعدادات، SMTP، maintenance، عدّادات rate-limit، قفل الدخول | معظمها ثابت ويصلح كمتغيرات بيئة/ملف إعداد بدل لوحة تحكم | متوسط |
| P3 | **حماية نموذج التواصل** (`contact-guard.ts` + `contact-protection.ts` + شاشة أدمن): rate-limit لكل IP + Turnstile | دفاع ضد الإساءة لنموذج عالي الحركة؛ بورتفوليو شخصي يكفيه `zod` + honeypot | منخفض |
| P4 | **قفل دخول الأدمن ضد التخمين** (`admin-auth.ts` 309 سطر) + طبقة Basic Auth ثانية في `proxy.ts` | طبقتا مصادقة لمستخدم واحد؛ كوكي موقّع واحد يكفي | متوسط |
| P5 | **مواصفات ميزات SaaS ميتة**: `specs/orders.md`, `products.md`, `downloads.md`, `project-inquiry.md` | مجرد وثائق لميزات تجارية غير مبنية أصلاً | منخفض (حذف وثائق) |
| P6 | **مكتبة ميديا مع تتبّع الاستخدام** (`MediaAsset` + `MediaUsage` polymorphic + 4 وحدات lib) | ميزة CMS للفرق؛ المالك الواحد يربط الصور مباشرة بالمشاريع | **مرتفع** (مربوطة بالبورتفوليو والإعدادات) |
| P7 | **ثيم/ماركي قابلان للتعديل وقت التشغيل** (`site-theme.ts`, `marquee-settings.ts` + شاشات أدمن) | تخصيص للعميل؛ المالك يثبّت لونه ونصوصه في الكود | متوسط |
| P8 | **Docker/Postgres + طلب middleware ذاتي** (`proxy.ts` يستدعي `/api/site/default-locale` على نفسه في كل طلب) | ثقيل لبورتفوليو؛ الـ self-fetch نمط مضاد نتج عن P2 | متوسط-مرتفع |
**نقطة إيجابية:** لا توجد جداول Order/Product/Download/Analytics/AuditLog/User-roles في قاعدة البيانات — هذه بقيت مواصفات فقط (P5). النماذج الأساسية (`Category`, `PortfolioProject`, `PortfolioSection`, `PortfolioAsset`) شرعية وسليمة.
**ترتيب مقترح للإزالة (من الأسهل):** P5 (حذف وثائق) → P3 → Toast → P1 → P4 → P2/P8 → لاحقاً الأعلى خطراً P6/P7.
---
## القرارات المطلوبة منك قبل بدء المرحلة 1
المرحلة 0 اكتملت. لن أعدّل أي شيء قبل موافقتك. أحتاج قرارك في:
1. **نظام Toast:** الإزالة متفق عليها. هل تريدني أستبدله برسائل inline بسيطة داخل صفحات الأدمن والتواصل (موصى به لتفادي عمليات صامتة)، أم إزالة تامة بلا بديل؟
2. **تعقيدات SaaS (P1P8):** أيها توافق على إزالته الآن؟ اقتراحي أن نبدأ بالآمن (P5, P3, P8-self-fetch) ونؤجل الأعلى خطراً (P6 مكتبة الميديا، P1 بنية الأدمن) لجلسة منفصلة.
3. **تصحيح الوثائق القديمة** (`frontend-system-*.md`): موافقة على تحديث المسارات؟
-1
View File
@@ -92,7 +92,6 @@ navigation item marked as
- Site Settings
- Marquee Settings
- SMTP Settings
- Contact Protection
- Portfolio management
### Data model
+20 -20
View File
@@ -140,7 +140,7 @@ Patterns:
Repeated in:
- `components/dashboard/dashboard-layout.tsx`
- `components/root/root-dashboard-shell.tsx`
- `components/admin/root-dashboard-shell.tsx`
- many `app/root/*/page.tsx`
Patterns:
@@ -160,8 +160,8 @@ Patterns:
Repeated in:
- `components/root/site-settings-form.tsx`
- `components/root/portfolio-project-form.tsx`
- `components/admin/site-settings-form.tsx`
- `components/admin/portfolio-project-form.tsx`
- `app/root/page.tsx`
Patterns:
@@ -204,7 +204,7 @@ Most repeated token usage:
- `components/ui/textarea.tsx`
- `components/ui/select.tsx`
- `components/dashboard/sidebar.tsx`
- `components/root/contact-protection-form.tsx`
- `components/admin/contact-protection-form.tsx`
- `rounded-[var(--radius-pill)]`
- `components/layout/site-header.tsx`
- `components/layout/floating-preferences.tsx`
@@ -220,13 +220,13 @@ Most repeated token usage:
- several content blocks and previews
- examples:
- `app/root/page.tsx`
- `components/root/site-settings-form.tsx`
- `components/root/media-field-picker.tsx`
- `components/admin/site-settings-form.tsx`
- `components/admin/media-field-picker.tsx`
- `rounded-lg`
- examples:
- `components/ui/dialog.tsx`
- `components/dashboard/dashboard-layout.tsx`
- `components/root/portfolio-project-form.tsx`
- `components/admin/portfolio-project-form.tsx`
## 6. Repeated spacing patterns found in code
@@ -321,14 +321,14 @@ Very common combinations:
- `border border-input bg-background`
- `components/ui/input.tsx`
- `components/ui/select.tsx`
- `components/root/portfolio-categories-manager.tsx`
- `components/root/portfolio-projects-overview.tsx`
- `components/admin/portfolio-categories-manager.tsx`
- `components/admin/portfolio-projects-overview.tsx`
- `bg-primary text-primary-foreground`
- active nav and badges/buttons:
- `components/ui/button.tsx`
- `components/ui/badge.tsx`
- `components/dashboard/sidebar.tsx`
- `components/root/portfolio-subnav.tsx`
- `components/admin/portfolio-subnav.tsx`
- `app/[locale]/(site)/portfolio/page.tsx`
- `text-muted-foreground`
- repeated throughout forms, cards, table headers, descriptions
@@ -406,7 +406,7 @@ Evidence:
- dependency in `package.json`
- broad usage across app and components
- examples:
- `components/root/root-dashboard-shell.tsx`
- `components/admin/root-dashboard-shell.tsx`
- `components/layout/site-header.tsx`
- `components/theme-toggle.tsx`
- `app/root/page.tsx`
@@ -422,8 +422,8 @@ No second React icon library is present in `package.json`.
- public site routes live under:
- `app/[locale]/(site)/*`
- admin routes live under:
- `app/root/*`
- `src` exists but is empty in the current codebase.
- `app/_admin/*` (canonical source), mirrored to `app/admin-internal/*` (rewrite target) and `app/root/*` (dev alias)
- there is no `src/` directory in the codebase.
### Component folders
@@ -431,7 +431,7 @@ No second React icon library is present in `package.json`.
- primitive and near-primitive reusable controls
- `components/layout`
- site shell, hero, header, footer, container, backdrops
- `components/root`
- `components/admin`
- admin feature components and forms
- `components/dashboard`
- admin navigation and dashboard layout shell
@@ -442,7 +442,7 @@ No second React icon library is present in `package.json`.
- `lib/utils.ts`
- `cn()`
- `lib/root-navigation.ts`
- `lib/admin-navigation.ts`
- navigation config for admin shell
- other `lib/*`
- app settings, metadata, locale, media, portfolio, auth
@@ -457,7 +457,7 @@ Confirmed custom wrappers around local primitive layer:
- wraps `Button`
- `components/layout/locale-toggle.tsx`
- uses `Button` and `DropdownMenu`
- `components/root/form-save-button.tsx`
- `components/admin/form-save-button.tsx`
- uses `Button`
## 11. Violations or inconsistencies found in the current codebase
@@ -498,8 +498,8 @@ Examples:
- `components/ui/dialog.tsx`
- `components/dashboard/dashboard-layout.tsx`
- `components/root/site-settings-form.tsx`
- `components/root/portfolio-project-form.tsx`
- `components/admin/site-settings-form.tsx`
- `components/admin/portfolio-project-form.tsx`
### Some primitives follow the local system, some keep stock shadcn-style values
@@ -529,7 +529,7 @@ So the file defines multiple levels, but two of them are currently identical.
Most interactive primitives use Radix wrappers, but `components/ui/tabs.tsx` is a custom React context implementation instead.
### Tailwind scans `src`, but `src` is empty
### Tailwind scans `src`, but `src` does not exist
`tailwind.config.ts`
includes:
@@ -538,7 +538,7 @@ includes:
Current project state:
- `src` contains no files.
- there is no `src/` directory; this glob matches nothing.
## 12. Codex rules derived from the existing system
+7 -7
View File
@@ -215,11 +215,11 @@ Current feature code now reuses these levels in shared admin surfaces.
Examples:
- `components/root/media-field-picker.tsx`
- `components/root/portfolio-projects-overview.tsx`
- `components/root/portfolio-project-form.tsx`
- `components/root/portfolio-categories-manager.tsx`
- `components/root/site-settings-form.tsx`
- `components/admin/media-field-picker.tsx`
- `components/admin/portfolio-projects-overview.tsx`
- `components/admin/portfolio-project-form.tsx`
- `components/admin/portfolio-categories-manager.tsx`
- `components/admin/site-settings-form.tsx`
## 6. Current input/button/select/dialog/sheet rules in use
@@ -485,7 +485,7 @@ Examples:
- `components/ui/select.tsx`
- `components/layout/site-header.tsx`
- `components/theme-toggle.tsx`
- `components/root/portfolio-project-form.tsx`
- `components/admin/portfolio-project-form.tsx`
No second React icon library is present in:
@@ -508,7 +508,7 @@ Remaining transitional or non-shared drift still present in the codebase:
- `app/[locale]/(site)/portfolio/page.tsx`
- `app/[locale]/(site)/portfolio/[slug]/page.tsx`
- one custom dark preview surface remains in current admin code
- `components/root/site-settings-form.tsx`
- `components/admin/site-settings-form.tsx`
- `bg-slate-950`
- `text-white/55`
- `text-white/60`
+12 -12
View File
@@ -77,11 +77,11 @@ Repeated nested panels and stat blocks in feature code were moved toward shared
Refactored areas include:
- `components/root/media-field-picker.tsx`
- `components/root/portfolio-projects-overview.tsx`
- `components/root/portfolio-project-form.tsx`
- `components/root/portfolio-categories-manager.tsx`
- `components/root/site-settings-form.tsx`
- `components/admin/media-field-picker.tsx`
- `components/admin/portfolio-projects-overview.tsx`
- `components/admin/portfolio-project-form.tsx`
- `components/admin/portfolio-categories-manager.tsx`
- `components/admin/site-settings-form.tsx`
### 5. False abstraction fixed
@@ -113,13 +113,13 @@ Current `AppCard` levels are now meaningfully distinct:
- `components/dashboard/dashboard-layout.tsx`
- `components/layout/floating-preferences.tsx`
- `components/layout/site-header.tsx`
- `components/root/flash-message.tsx`
- `components/root/media-field-picker.tsx`
- `components/root/portfolio-categories-manager.tsx`
- `components/root/portfolio-project-form.tsx`
- `components/root/portfolio-projects-overview.tsx`
- `components/root/portfolio-subnav.tsx`
- `components/root/site-settings-form.tsx`
- `components/admin/flash-message.tsx`
- `components/admin/media-field-picker.tsx`
- `components/admin/portfolio-categories-manager.tsx`
- `components/admin/portfolio-project-form.tsx`
- `components/admin/portfolio-projects-overview.tsx`
- `components/admin/portfolio-subnav.tsx`
- `components/admin/site-settings-form.tsx`
- `components/ui/app-card.tsx`
- `components/ui/dialog.tsx`
- `components/ui/sheet.tsx`