نظام الأوضاع
الفهرس
نظرة عامة
النظام بيخدم نوعين من الجهات — خاص وعام — بفورمات وحقول مختلفة. الـ mode بيتحدد runtime بعد تسجيل الدخول، وكل فرق حقيقي بين الوضعين بيتعزل في ملف "variant" نظيف بدل شروط متناثرة في الكود.
حل الـ Mode — useMode
| المصدر | القيمة | النتيجة |
|---|---|---|
| privateCompanyMode | true | private |
| privateCompanyMode | false | public |
| mode | 1 | private |
| mode | 2 | public |
| شكل غير متعارف عليه | — | public (مع تحذير) |
export const useMode = () => {
const { data: session } = useAuth()
const mode = computed(() => resolveMode(session.value))
const isMode = (...modes) => modes.includes(mode.value)
return { mode, isMode }
} لو شكل بيانات الـ session جه بصيغة مش متعارف عليها، النظام بيرجع لـ public كـ fallback آمن بدل ما يفشل، مع تحذير واضح في الكونسول يساعد في التشخيص السريع.
آلية التبديل — Keyed Remount
<script setup>
const { mode } = useMode()
const route = useRoute()
watch(mode, async () => {
clearNuxtData()
const allowed = route.meta.modes
if (allowed && !allowed.includes(mode.value)) {
await navigateTo('/')
}
})
</script>
<template>
<NuxtLayout :key="mode">
<NuxtPage />
</NuxtLayout>
</template>اقرأ الـ mode في أي وقت، لكن لا تخزّن نسخة منه (أو من أي قيمة محسوبة منه) في مكان بيعيش برّه شجرة المكونات — أي نسخة زي كده بتنجو من عملية الهدم وبتفضل شايلة قيمة الوضع القديم بصمت.
الـ Helper الرسمي — useModeVariant
export const useModeVariant = (variants) => {
const { mode } = useMode()
return variants[mode.value]?.() ?? {}
}export const useEmployeeEmploymentSchema = () => {
const modeSpecific = useModeVariant({
private: useEmployeeEmploymentSchemaPrivate,
public: useEmployeeEmploymentSchemaPublic
})
const baseSchema = z.object({ /* الحقول المشتركة */ })
const schema = computed(() =>
modeSpecific.extension
? baseSchema.extend(modeSpecific.extension.shape)
: baseSchema
)
return { schema }
}export const useQualificationsSchema = () => {
const schema = z.object({ /* ... */ })
return { schema }
}فلسفة الدمج — مش قاعدة واحدة شاملة
| الـ Artifact | الفلسفة | الشكل |
|---|---|---|
| Schema / Form State | base + extension | ملف الـ variant يصدّر الفرق (delta) بس — المشترك مُعرَّف مرة واحدة |
| Columns | تعريف كامل لكل وضع | ترتيب الأعمدة عقد مرئي — كل variant بيعرّف الأعمدة كاملة |
| Sidebar | تعريف كامل عند ظهور فرق | نفس منطق الـ Columns — الترتيب والتجميع مرئيين |
| Components | فصل كامل بالملفات | هيكل مختلف يستحق ملفات مستقلة عبر componentsMap |
المكوّن الرسمي للفروقات الصغيرة — BaseModeShow
<!-- إظهار للوضع الخاص فقط -->
<BaseModeShow :modes="['private']">
<BaseInput v-model="state.someField" label="حقل خاص بالوضع الخاص" />
</BaseModeShow>| حجم الفرق | الأداة |
|---|---|
| نص فقط (عنوان، label) | مفتاح ترجمة بلاحقة الوضع |
| حقل أو عنصر واحد | BaseModeShow |
| هيكل مختلف بالكامل | ملف variant مستقل عبر componentsMap |
حماية الصفحات — Middleware
export default defineNuxtRouteMiddleware((to) => {
const allowedModes = to.meta?.modes
if (!allowedModes || !Array.isArray(allowedModes) || allowedModes.length === 0) return
const { isMode } = useMode()
if (!isMode(...allowedModes)) return navigateTo('/')
})// صفحة متاحة لكل الأوضاع
definePageMeta({ title: 'pages.qualifications' })
// صفحة خاصة بالوضع الخاص فقط
definePageMeta({ title: 'pages.employment', modes: ['private'] })الـ mode في الواجهة أداة عرض (UX) بس — الحماية دي بتنظّم التنقل، مش بديل عن أي تحقق أمني حقيقي على مستوى الـ backend.
نمط الموديول — مثال حقيقي (Employment)
composables/modules/administration/employees/_employment/
├── schema/
│ ├── index.js ← الأوركسترا (useModeVariant)
│ ├── schema.private.js ← extension الوضع الخاص
│ └── schema.public.js ← extension الوضع العام
├── form/
│ ├── index.js
│ ├── form.private.js
│ └── form.public.js
└── lists/
└── index.js ← مشترك، بدون فروقاتموديولات تانية بلا فروقات حقيقية (زي المؤهلات) — ملفات عادية بلا أي variant، وكأن نظام الـ mode مش موجود خالص.
القرارات التصميمية
| البديل | المشكلة |
|---|---|
| Config-Driven (حقول كـ array) | يفقد مرونة الـ template — لا slots مخصصة، لا layouts معقدة، كل نوع حقل جديد يحتاج case جديد في الـ renderer |
| Nuxt Layers (طبقة منفصلة لكل وضع) | الـ mode بييجي runtime من بيانات الـ session — Layers بتشتغل build-time بس، مش مناسبة للسيناريو ده |
ما تغيّر أثناء البناء
| التغيير | كان مصمَّم | اتضح في المراجعة | التعديل وليه |
|---|---|---|---|
| من 3 أوضاع لوضعين | نظام بتلات أوضاع منفصلة — خاص، حكومي، ومختلط — كل واحد بملفات ومنطق مستقل خاص بيه. | الوضع "المختلط" ما كانش له تمايز فعلي كافي يستاهل مسار كامل مستقل، والفرق الحقيقي بين "الحكومي" وباقي الحالات كان بيتلخص عملياً في نفس منطق الوضع "العام". | تقليص النظام لوضعين بس. تبسيط حقيقي في عدد المسارات المطلوب صيانتها، مش فقدان قدرة فعلية كانت مستخدَمة. |
| حذف ModeSwitch وfilterByMode | مكوّن ModeSwitch (Dynamic component يختار الشكل المناسب حسب الوضع تلقائياً) ودالة filterByMode (تصفية عناصر قائمة حسب الوضع) — كأدوات مساعدة عامة. | ModeSwitch كان بيكرر بالظبط نفس وظيفة نمط componentsMap المستخدَم أصلاً في كل أوركسترا. filterByMode طلعت dead code فعلياً — صفر استخدامات حقيقية في الكود وقت المراجعة. | الاتنين اتشالوا بالكامل. ModeSwitch استُبدل بـ componentsMap مباشرة، وfilterByMode اتشالت من غير أي بديل — مكنش ليها استخدام حقيقي أصلاً. |