Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
Clubphp
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Clubphp
Commits
a03e3d0d
Commit
a03e3d0d
authored
Apr 08, 2026
by
Mahmoud Aglan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
kkookkokoko
parent
01cb0797
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
678 additions
and
600 deletions
+678
-600
alerts.php
app/Shared/Components/alerts.php
+43
-19
sidebar.php
app/Shared/Components/sidebar.php
+168
-58
auth.php
app/Shared/Layout/auth.php
+60
-38
main.php
app/Shared/Layout/main.php
+67
-102
main.css
public/assets/css/main.css
+340
-383
No files found.
app/Shared/Components/alerts.php
View file @
a03e3d0d
<?php
/**
* Alerts component — reads flash alerts from session and displays them.
* Bulletproof: will never crash, never kill the page.
* Flash alert messages component.
*/
$__alertList
=
[];
try
{
if
(
isset
(
$_SESSION
[
'_flash'
][
'_alerts'
])
&&
is_array
(
$_SESSION
[
'_flash'
][
'_alerts'
]))
{
$__alertList
=
$_SESSION
[
'_flash'
][
'_alerts'
];
unset
(
$_SESSION
[
'_flash'
][
'_alerts'
]);
}
elseif
(
isset
(
$_SESSION
[
'_alerts'
])
&&
is_array
(
$_SESSION
[
'_alerts'
]))
{
$__alertList
=
$_SESSION
[
'_alerts'
];
unset
(
$_SESSION
[
'_alerts'
]);
}
}
catch
(
\Throwable
$__e
)
{
$__alertList
=
[];
}
$session
=
\App\Core\App
::
getInstance
()
->
session
();
$alerts
=
$session
->
getAlerts
();
foreach
(
$__alertList
as
$__a
)
:
$__type
=
$__a
[
'type'
]
??
'info'
;
$__msg
=
$__a
[
'message'
]
??
''
;
if
(
$__msg
===
''
)
continue
;
if
(
empty
(
$alerts
))
{
return
;
}
?>
<div
class=
"alert alert-
<?=
e
(
$__type
)
?>
"
>
<?=
e
(
$__msg
)
?>
</div>
<?php
foreach
(
$alerts
as
$alert
)
:
?>
<?php
$type
=
$alert
[
'type'
]
??
'info'
;
$message
=
$alert
[
'message'
]
??
''
;
$bgColor
=
match
(
$type
)
{
'success'
=>
'#F0FDF4'
,
'error'
=>
'#FEF2F2'
,
'warning'
=>
'#FFF7ED'
,
'info'
=>
'#EFF6FF'
,
default
=>
'#F9FAFB'
,
};
$textColor
=
match
(
$type
)
{
'success'
=>
'#059669'
,
'error'
=>
'#DC2626'
,
'warning'
=>
'#D97706'
,
'info'
=>
'#0284C7'
,
default
=>
'#6B7280'
,
};
$borderColor
=
match
(
$type
)
{
'success'
=>
'#BBF7D0'
,
'error'
=>
'#FECACA'
,
'warning'
=>
'#FED7AA'
,
'info'
=>
'#BFDBFE'
,
default
=>
'#E5E7EB'
,
};
$icon
=
match
(
$type
)
{
'success'
=>
'✓'
,
'error'
=>
'✗'
,
'warning'
=>
'⚠'
,
'info'
=>
'ℹ'
,
default
=>
''
,
};
?>
<div
class=
"alert alert-
<?=
$type
?>
"
style=
"background:
<?=
$bgColor
?>
;color:
<?=
$textColor
?>
;border:1px solid
<?=
$borderColor
?>
;padding:12px 20px;border-radius:8px;margin:0 25px 15px;font-size:14px;display:flex;justify-content:space-between;align-items:center;"
>
<span>
<?=
$icon
?>
<?=
e
(
$message
)
?>
</span>
<button
onclick=
"this.parentElement.remove()"
style=
"background:none;border:none;cursor:pointer;font-size:18px;color:
<?=
$textColor
?>
;opacity:0.6;padding:0 5px;"
>
×
</button>
</div>
<?php
endforeach
;
?>
\ No newline at end of file
app/Shared/Components/sidebar.php
View file @
a03e3d0d
<?php
$currentPath
=
parse_url
(
$_SERVER
[
'REQUEST_URI'
]
??
'/'
,
PHP_URL_PATH
);
$currentPath
=
rtrim
(
$currentPath
,
'/'
)
?:
'/'
;
/**
* Sidebar component — reads menu items from MenuRegistry.
*/
function
sidebarActive
(
string
$path
,
string
$current
)
:
string
{
if
(
$path
===
'/'
&&
$current
===
'/'
)
return
'active'
;
if
(
$path
!==
'/'
&&
str_starts_with
(
$current
,
$path
))
return
'active'
;
return
''
;
}
?>
<aside
class=
"sidebar"
id=
"mainSidebar"
>
<div
class=
"sidebar-logo"
>
<h1>
🏛️ THE CLUB
</h1>
<div
class=
"subtitle"
>
نادي النادي شيراتون — Sports City
</div>
</div>
use
App\Core\App
;
use
App\Core\Registries\MenuRegistry
;
$currentPath
=
parse_url
(
$_SERVER
[
'REQUEST_URI'
]
??
'/'
,
PHP_URL_PATH
);
$employee
=
App
::
getInstance
()
->
currentEmployee
();
<nav
class=
"sidebar-nav"
>
<div
class=
"sidebar-section"
>
الرئيسية
</div>
<a
href=
"/dashboard"
class=
"sidebar-link
<?=
sidebarActive
(
'/dashboard'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📊
</span>
لوحة التحكم
</a>
// Icon name → emoji mapping
$iconMap
=
[
'dashboard'
=>
'📊'
,
'tachometer-alt'
=>
'📊'
,
'home'
=>
'🏠'
,
'users'
=>
'👥'
,
'user'
=>
'👤'
,
'user-plus'
=>
'👤'
,
'user-clock'
=>
'⏰'
,
'user-tie'
=>
'👔'
,
'user-shield'
=>
'🛡️'
,
'user-friends'
=>
'👥'
,
'clipboard'
=>
'📋'
,
'clipboard-list'
=>
'📋'
,
'file-alt'
=>
'📄'
,
'calendar'
=>
'📅'
,
'calendar-alt'
=>
'📅'
,
'calendar-check'
=>
'✅'
,
'money-bill'
=>
'💰'
,
'wallet'
=>
'💳'
,
'cash-register'
=>
'💰'
,
'credit-card'
=>
'💳'
,
'receipt'
=>
'🧾'
,
'coins'
=>
'🪙'
,
'file-invoice-dollar'
=>
'💲'
,
'hand-holding-usd'
=>
'💰'
,
'exchange-alt'
=>
'🔀'
,
'random'
=>
'🔀'
,
'transfer'
=>
'🔀'
,
'gavel'
=>
'⚖️'
,
'balance-scale'
=>
'⚖️'
,
'exclamation-triangle'
=>
'⚠️'
,
'alert'
=>
'⚠️'
,
'warning'
=>
'⚠️'
,
'ban'
=>
'🚫'
,
'trophy'
=>
'🏆'
,
'medal'
=>
'🏅'
,
'award'
=>
'🎖️'
,
'star'
=>
'⭐'
,
'globe'
=>
'🌍'
,
'globe-americas'
=>
'🌎'
,
'flag'
=>
'🏳️'
,
'id-card'
=>
'🪪'
,
'address-card'
=>
'🪪'
,
'qrcode'
=>
'📱'
,
'file'
=>
'📁'
,
'folder'
=>
'📁'
,
'folder-open'
=>
'📂'
,
'sms'
=>
'📱'
,
'envelope'
=>
'✉️'
,
'bell'
=>
'🔔'
,
'comment'
=>
'💬'
,
'chart-bar'
=>
'📈'
,
'chart-line'
=>
'📈'
,
'chart-pie'
=>
'📊'
,
'cog'
=>
'⚙️'
,
'cogs'
=>
'⚙️'
,
'wrench'
=>
'🔧'
,
'tools'
=>
'🛠️'
,
'sliders-h'
=>
'⚙️'
,
'settings'
=>
'⚙️'
,
'shield-alt'
=>
'🔐'
,
'lock'
=>
'🔒'
,
'key'
=>
'🔑'
,
'building'
=>
'🏢'
,
'city'
=>
'🏙️'
,
'store'
=>
'🏪'
,
'book'
=>
'📖'
,
'history'
=>
'📜'
,
'archive'
=>
'🗄️'
,
'sitemap'
=>
'🔄'
,
'project-diagram'
=>
'🔄'
,
'workflow'
=>
'🔄'
,
'heart'
=>
'❤️'
,
'heartbeat'
=>
'💓'
,
'cross'
=>
'✝️'
,
'ring'
=>
'💍'
,
'baby'
=>
'👶'
,
'child'
=>
'👦'
,
'children'
=>
'👨👩👧👦'
,
'repeat'
=>
'🔄'
,
'sync'
=>
'🔄'
,
'redo'
=>
'🔄'
,
'print'
=>
'🖨️'
,
'search'
=>
'🔍'
,
'plus'
=>
'➕'
,
'edit'
=>
'✏️'
,
'trash'
=>
'🗑️'
,
'times'
=>
'❌'
,
'check'
=>
'✅'
,
'dollar-sign'
=>
'💲'
,
'percentage'
=>
'💹'
,
'swimming-pool'
=>
'🏊'
,
'running'
=>
'🏃'
,
'futbol'
=>
'⚽'
,
'sun'
=>
'☀️'
,
'umbrella-beach'
=>
'🏖️'
,
];
<div
class=
"sidebar-section"
>
الأعضاء
</div>
<a
href=
"/members"
class=
"sidebar-link
<?=
sidebarActive
(
'/members'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
👥
</span>
إدارة الأعضاء
</a>
<a
href=
"/members/search"
class=
"sidebar-link
<?=
sidebarActive
(
'/members/search'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🔍
</span>
بحث الأعضاء
</a>
<a
href=
"/interviews"
class=
"sidebar-link
<?=
sidebarActive
(
'/interviews'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🗓️
</span>
المقابلات
</a>
<a
href=
"/carnets"
class=
"sidebar-link
<?=
sidebarActive
(
'/carnets'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🪪
</span>
الكارنيهات
</a>
$getIcon
=
function
(
?
string
$icon
)
use
(
$iconMap
)
:
string
{
if
(
$icon
===
null
||
$icon
===
''
)
return
'📌'
;
// Already an emoji
if
(
mb_strlen
(
$icon
)
<=
2
&&
!
ctype_alpha
(
$icon
))
return
$icon
;
// Check map
return
$iconMap
[
$icon
]
??
$iconMap
[
strtolower
(
$icon
)]
??
'📌'
;
};
<div
class=
"sidebar-section"
>
مالية
</div>
<a
href=
"/payments"
class=
"sidebar-link
<?=
sidebarActive
(
'/payments'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
💰
</span>
المدفوعات
</a>
<a
href=
"/receipts"
class=
"sidebar-link
<?=
sidebarActive
(
'/receipts'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🧾
</span>
الإيصالات
</a>
<a
href=
"/installments"
class=
"sidebar-link
<?=
sidebarActive
(
'/installments'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📅
</span>
الأقساط
</a>
<a
href=
"/subscriptions"
class=
"sidebar-link
<?=
sidebarActive
(
'/subscriptions'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📋
</span>
الاشتراكات
</a>
// Get all permissions for current employee
$employeePermissions
=
[];
if
(
$employee
&&
method_exists
(
$employee
,
'getAllPermissions'
))
{
$employeePermissions
=
$employee
->
getAllPermissions
();
}
elseif
(
$employee
)
{
try
{
$db
=
App
::
getInstance
()
->
db
();
$empId
=
$employee
->
id
??
0
;
$perms
=
$db
->
select
(
"SELECT DISTINCT rp.permission_key
FROM employee_roles er
JOIN role_permissions rp ON rp.role_id = er.role_id
WHERE er.employee_id = ? AND er.is_active = 1"
,
[(
int
)
$empId
]
);
$employeePermissions
=
array_column
(
$perms
,
'permission_key'
);
$isSuperAdmin
=
$db
->
selectOne
(
"SELECT 1 FROM employee_roles er
JOIN roles r ON r.id = er.role_id
WHERE er.employee_id = ? AND r.role_code = 'super_admin' AND er.is_active = 1
LIMIT 1"
,
[(
int
)
$empId
]
);
if
(
$isSuperAdmin
)
{
$employeePermissions
=
[
'*'
];
}
}
catch
(
\Throwable
$e
)
{
$employeePermissions
=
[];
}
}
<div
class=
"sidebar-section"
>
المخالفات
</div>
<a
href=
"/violations"
class=
"sidebar-link
<?=
sidebarActive
(
'/violations'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
⚠️
</span>
المخالفات
</a>
<a
href=
"/fines"
class=
"sidebar-link
<?=
sidebarActive
(
'/fines'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
💸
</span>
الغرامات
</a>
$hasPerm
=
function
(
string
$perm
)
use
(
$employeePermissions
)
:
bool
{
if
(
empty
(
$perm
))
return
true
;
if
(
in_array
(
'*'
,
$employeePermissions
))
return
true
;
return
in_array
(
$perm
,
$employeePermissions
);
};
<div
class=
"sidebar-section"
>
عمليات
</div>
<a
href=
"/transfers"
class=
"sidebar-link
<?=
sidebarActive
(
'/transfers'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🔄
</span>
التحويلات
</a>
<a
href=
"/divorce"
class=
"sidebar-link
<?=
sidebarActive
(
'/divorce'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
💔
</span>
الطلاق
</a>
<a
href=
"/death"
class=
"sidebar-link
<?=
sidebarActive
(
'/death'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🕊️
</span>
الوفاة
</a>
<a
href=
"/waiver"
class=
"sidebar-link
<?=
sidebarActive
(
'/waiver'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📝
</span>
التنازل
</a>
$isActive
=
function
(
string
$route
)
use
(
$currentPath
)
:
bool
{
if
(
$route
===
'/'
)
return
$currentPath
===
'/'
;
return
str_starts_with
(
$currentPath
,
$route
);
};
<div
class=
"sidebar-section"
>
أنواع العضوية
</div>
<a
href=
"/temporary"
class=
"sidebar-link
<?=
sidebarActive
(
'/temporary'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
⏱️
</span>
المؤقتون
</a>
<a
href=
"/seasonal"
class=
"sidebar-link
<?=
sidebarActive
(
'/seasonal'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🌊
</span>
الموسمية
</a>
<a
href=
"/sports"
class=
"sidebar-link
<?=
sidebarActive
(
'/sports'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🏅
</span>
الرياضية
</a>
<a
href=
"/honorary"
class=
"sidebar-link
<?=
sidebarActive
(
'/honorary'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🎖️
</span>
الشرفية
</a>
<a
href=
"/foreign"
class=
"sidebar-link
<?=
sidebarActive
(
'/foreign'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🌍
</span>
الأجانب
</a>
// Get menu items from registry
$menuItems
=
MenuRegistry
::
getAll
();
usort
(
$menuItems
,
fn
(
$a
,
$b
)
=>
(
$a
[
'order'
]
??
999
)
<=>
(
$b
[
'order'
]
??
999
));
<div
class=
"sidebar-section"
>
نماذج و مستندات
</div>
<a
href=
"/forms/submissions"
class=
"sidebar-link
<?=
sidebarActive
(
'/forms'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📄
</span>
النماذج
</a>
<a
href=
"/documents"
class=
"sidebar-link
<?=
sidebarActive
(
'/documents'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📁
</span>
المستندات
</a>
// Fallback if registry empty
if
(
empty
(
$menuItems
))
{
$menuItems
=
[
[
'key'
=>
'dashboard'
,
'label_ar'
=>
'لوحة التحكم'
,
'icon'
=>
'📊'
,
'route'
=>
'/dashboard'
,
'permission'
=>
''
,
'order'
=>
10
,
'children'
=>
[]],
[
'key'
=>
'members'
,
'label_ar'
=>
'إدارة الأعضاء'
,
'icon'
=>
'👥'
,
'route'
=>
'/members'
,
'permission'
=>
'member.view'
,
'order'
=>
100
,
'children'
=>
[
[
'label_ar'
=>
'كل الأعضاء'
,
'route'
=>
'/members'
,
'permission'
=>
'member.view'
],
[
'label_ar'
=>
'عضو جديد'
,
'route'
=>
'/members/create'
,
'permission'
=>
'member.create'
],
]],
[
'key'
=>
'users'
,
'label_ar'
=>
'الموظفون'
,
'icon'
=>
'👤'
,
'route'
=>
'/users'
,
'permission'
=>
'user.view'
,
'order'
=>
910
,
'children'
=>
[]],
[
'key'
=>
'roles'
,
'label_ar'
=>
'الأدوار'
,
'icon'
=>
'🔐'
,
'route'
=>
'/roles'
,
'permission'
=>
'role.view'
,
'order'
=>
920
,
'children'
=>
[]],
[
'key'
=>
'branches'
,
'label_ar'
=>
'الفروع'
,
'icon'
=>
'🏢'
,
'route'
=>
'/branches'
,
'permission'
=>
'branch.view'
,
'order'
=>
930
,
'children'
=>
[]],
[
'key'
=>
'settings'
,
'label_ar'
=>
'الإعدادات'
,
'icon'
=>
'⚙️'
,
'route'
=>
'/settings'
,
'permission'
=>
'settings.view'
,
'order'
=>
960
,
'children'
=>
[]],
];
}
?>
<div
class=
"sidebar-section"
>
تقارير و إشعارات
</div
>
<a
href=
"/reports"
class=
"sidebar-link
<?=
sidebarActive
(
'/reports'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📈
</span>
التقارير
</a
>
<a
href=
"/notifications/templates"
class=
"sidebar-link
<?=
sidebarActive
(
'/notifications'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📱
</span>
الإشعارات
</a
>
<div
class=
"sidebar-header"
>
<div
class=
"sidebar-brand"
>
THE CLUB
</div
>
</div
>
<div
class=
"sidebar-section"
>
النظام
</div>
<a
href=
"/users"
class=
"sidebar-link
<?=
sidebarActive
(
'/users'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
👤
</span>
المستخدمون
</a>
<a
href=
"/roles"
class=
"sidebar-link
<?=
sidebarActive
(
'/roles'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🔐
</span>
الصلاحيات
</a>
<a
href=
"/branches"
class=
"sidebar-link
<?=
sidebarActive
(
'/branches'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🏢
</span>
الفروع
</a>
<a
href=
"/rules"
class=
"sidebar-link
<?=
sidebarActive
(
'/rules'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
⚙️
</span>
القواعد
</a>
<a
href=
"/pricing"
class=
"sidebar-link
<?=
sidebarActive
(
'/pricing'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
💲
</span>
التسعير
</a>
<a
href=
"/settings"
class=
"sidebar-link
<?=
sidebarActive
(
'/settings'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🛠️
</span>
الإعدادات
</a>
<a
href=
"/workflow"
class=
"sidebar-link
<?=
sidebarActive
(
'/workflow'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🔀
</span>
سير العمل
</a>
<a
href=
"/audit"
class=
"sidebar-link
<?=
sidebarActive
(
'/audit'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📜
</span>
سجل المراجعة
</a>
<a
href=
"/archive"
class=
"sidebar-link
<?=
sidebarActive
(
'/archive'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🗄️
</span>
الأرشيف
</a>
</nav>
</aside>
\ No newline at end of file
<nav
class=
"sidebar-nav"
>
<ul
class=
"sidebar-menu"
>
<?php
foreach
(
$menuItems
as
$item
)
:
?>
<?php
if
(
!
empty
(
$item
[
'permission'
])
&&
!
$hasPerm
(
$item
[
'permission'
]))
continue
;
if
(
!
empty
(
$item
[
'is_separator'
]))
{
echo
'<li style="padding:15px 20px 5px;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:1px;">'
.
e
(
$item
[
'label_ar'
])
.
'</li>'
;
continue
;
}
$children
=
$item
[
'children'
]
??
[];
$visibleChildren
=
[];
foreach
(
$children
as
$child
)
{
if
(
empty
(
$child
[
'permission'
])
||
$hasPerm
(
$child
[
'permission'
]))
{
$visibleChildren
[]
=
$child
;
}
}
$hasChildren
=
!
empty
(
$visibleChildren
);
$itemRoute
=
$item
[
'route'
]
??
'#'
;
$itemActive
=
$isActive
(
$itemRoute
);
$childActive
=
false
;
foreach
(
$visibleChildren
as
$child
)
{
if
(
$isActive
(
$child
[
'route'
]
??
''
))
{
$childActive
=
true
;
break
;
}
}
$isOpen
=
$itemActive
||
$childActive
;
$icon
=
$getIcon
(
$item
[
'icon'
]
??
''
);
?>
<li
class=
"sidebar-item
<?=
$isOpen
&&
$hasChildren
?
' open'
:
''
?>
"
>
<?php
if
(
$hasChildren
)
:
?>
<a
href=
"javascript:void(0)"
class=
"sidebar-link
<?=
$itemActive
?
' active'
:
''
?>
"
onclick=
"toggleSubmenu(this)"
>
<span
class=
"sidebar-icon"
>
<?=
$icon
?>
</span>
<span
class=
"sidebar-text"
>
<?=
e
(
$item
[
'label_ar'
])
?>
</span>
<span
class=
"sidebar-arrow"
>
◀
</span>
</a>
<ul
class=
"sidebar-submenu"
style=
"display:
<?=
$isOpen
?
'block'
:
'none'
?>
;"
>
<?php
foreach
(
$visibleChildren
as
$child
)
:
?>
<?php
$childRoute
=
$child
[
'route'
]
??
'#'
;
?>
<li>
<a
href=
"
<?=
e
(
$childRoute
)
?>
"
class=
"sidebar-sublink
<?=
$isActive
(
$childRoute
)
?
' active'
:
''
?>
"
>
<?=
e
(
$child
[
'label_ar'
])
?>
</a>
</li>
<?php
endforeach
;
?>
</ul>
<?php
else
:
?>
<a
href=
"
<?=
e
(
$itemRoute
)
?>
"
class=
"sidebar-link
<?=
$itemActive
?
' active'
:
''
?>
"
>
<span
class=
"sidebar-icon"
>
<?=
$icon
?>
</span>
<span
class=
"sidebar-text"
>
<?=
e
(
$item
[
'label_ar'
])
?>
</span>
</a>
<?php
endif
;
?>
</li>
<?php
endforeach
;
?>
</ul>
</nav>
\ No newline at end of file
app/Shared/Layout/auth.php
View file @
a03e3d0d
<?php
use
App\Core\CSRF
;
?>
<!DOCTYPE html>
<html
lang=
"ar"
dir=
"rtl"
>
<head>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
>
<title>
<?php
$__template
->
yield
(
'title'
,
'تسجيل الدخول'
);
?>
— نادي النادي
</title>
<link
rel=
"preconnect"
href=
"https://fonts.googleapis.com"
>
<link
href=
"https://fonts.googleapis.com/css2?family=Cairo:wght@400;500;600;700;800&display=swap"
rel=
"stylesheet"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"csrf-token"
content=
"
<?=
e
(
CSRF
::
token
())
?>
"
>
<title>
<?=
$__template
->
yield
(
'title'
,
'تسجيل الدخول'
)
?>
— نادي النادي شيراتون
</title>
<link
rel=
"stylesheet"
href=
"
<?=
url
(
'assets/css/main.css'
)
?>
"
>
<style>
body
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
min-height
:
100vh
;
background
:
linear-gradient
(
135deg
,
#0D7377
0%
,
#1A1A2E
100%
);
padding
:
20px
;
}
.auth-card
{
background
:
#fff
;
border-radius
:
12px
;
padding
:
40px
;
width
:
100%
;
max-width
:
420px
;
box-shadow
:
0
20px
60px
rgba
(
0
,
0
,
0
,
0.3
);
}
.auth-logo
{
text-align
:
center
;
margin-bottom
:
30px
;
}
.auth-logo
h1
{
font-size
:
24px
;
color
:
#0D7377
;
margin
:
0
;
}
.auth-logo
p
{
color
:
#6B7280
;
font-size
:
13px
;
margin-top
:
5px
;
}
@media
(
max-width
:
480px
)
{
.auth-card
{
padding
:
25px
20px
;
}
.auth-logo
h1
{
font-size
:
20px
;
}
body
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
min-height
:
100vh
;
margin
:
0
;
background
:
linear-gradient
(
135deg
,
#1A1A2E
0%
,
#0D7377
100%
);
font-family
:
'Cairo'
,
'Segoe UI'
,
Tahoma
,
Arial
,
sans-serif
;
direction
:
rtl
;
}
.auth-card
{
background
:
#fff
;
border-radius
:
12px
;
box-shadow
:
0
20px
60px
rgba
(
0
,
0
,
0
,
0.3
);
padding
:
40px
;
width
:
100%
;
max-width
:
420px
;
}
.auth-header
{
text-align
:
center
;
margin-bottom
:
30px
;
}
.auth-header
h1
{
color
:
#0D7377
;
font-size
:
24px
;
margin
:
0
0
5px
;
}
.auth-header
p
{
color
:
#6B7280
;
font-size
:
14px
;
margin
:
0
;
}
</style>
</head>
<body>
<div
class=
"auth-card"
>
<div
class=
"auth-logo
"
>
<h1>
🏛️ THE CLUB
</h1>
<p>
نادي النادي شيراتون — Sports City
</p>
</div>
<div
class=
"auth-card"
>
<div
class=
"auth-header
"
>
<h1>
نادي النادي شيراتون
</h1>
<p>
THE CLUB Sheraton
</p>
</div>
<?php
// Inline alert reading — no includes, no method calls that can crash
$__alertList
=
[];
try
{
if
(
isset
(
$_SESSION
[
'_flash'
][
'_alerts'
])
&&
is_array
(
$_SESSION
[
'_flash'
][
'_alerts'
]))
{
$__alertList
=
$_SESSION
[
'_flash'
][
'_alerts'
];
unset
(
$_SESSION
[
'_flash'
][
'_alerts'
]);
}
elseif
(
isset
(
$_SESSION
[
'_alerts'
])
&&
is_array
(
$_SESSION
[
'_alerts'
]))
{
$__alertList
=
$_SESSION
[
'_alerts'
];
unset
(
$_SESSION
[
'_alerts'
]);
}
}
catch
(
\Throwable
$__e
)
{}
foreach
(
$__alertList
as
$__a
)
:
$__type
=
$__a
[
'type'
]
??
'info'
;
$__msg
=
$__a
[
'message'
]
??
''
;
if
(
$__msg
===
''
)
continue
;
?>
<div
class=
"alert alert-
<?=
e
(
$__type
)
?>
"
style=
"margin-bottom:15px;"
>
<?=
e
(
$__msg
)
?>
</div>
<?php
endforeach
;
?>
<?php
$session
=
\App\Core\App
::
getInstance
()
->
session
();
$alerts
=
$session
->
getAlerts
();
if
(
!
empty
(
$alerts
))
:
foreach
(
$alerts
as
$alert
)
:
?>
<div
style=
"padding:10px 15px;border-radius:6px;margin-bottom:15px;font-size:13px;
background:
<?=
(
$alert
[
'type'
]
??
''
)
===
'error'
?
'#FEF2F2'
:
'#F0FDF4'
?>
;
color:
<?=
(
$alert
[
'type'
]
??
''
)
===
'error'
?
'#DC2626'
:
'#059669'
?>
;
border:1px solid
<?=
(
$alert
[
'type'
]
??
''
)
===
'error'
?
'#FECACA'
:
'#BBF7D0'
?>
;"
>
<?=
e
(
$alert
[
'message'
]
??
''
)
?>
</div>
<?php
endforeach
;
endif
;
?>
<?php
$__template
->
yield
(
'content'
);
?>
</div>
<?=
$__template
->
yield
(
'content'
,
''
)
?>
</div>
</body>
</html>
\ No newline at end of file
app/Shared/Layout/main.php
View file @
a03e3d0d
<?php
/**
* Main application layout — RTL Arabic-first.
* All authenticated pages use this layout.
*/
use
App\Core\App
;
use
App\Core\CSRF
;
$app
=
App
::
getInstance
();
$employee
=
$app
->
currentEmployee
();
$employeeName
=
$employee
?
(
$employee
->
full_name_ar
??
'مستخدم'
)
:
'زائر'
;
$currentPath
=
parse_url
(
$_SERVER
[
'REQUEST_URI'
]
??
'/'
,
PHP_URL_PATH
);
?>
<!DOCTYPE html>
<html
lang=
"ar"
dir=
"rtl"
>
<head>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0
, maximum-scale=1.0, user-scalable=no
"
>
<
title>
<?php
$__template
->
yield
(
'title'
,
'النظام'
);
?>
— نادي النادي
</title
>
<
link
rel=
"preconnect"
href=
"https://fonts.googleapis.com"
>
<link
href=
"https://fonts.googleapis.com/css2?family=Cairo:wght@400;500;600;700;800&display=swap"
rel=
"stylesheet
"
>
<
link
rel=
"stylesheet"
href=
"
<?=
url
(
'assets/css/main.css'
)
?>
?v=
<?=
@
filemtime
(
App\Core\App
::
getInstance
()
->
basePath
()
.
'/public/assets/css/main.css'
)
?>
"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<
meta
name=
"csrf-token"
content=
"
<?=
e
(
CSRF
::
token
())
?>
"
>
<
title>
<?=
$__template
->
yield
(
'title'
,
'لوحة التحكم'
)
?>
— نادي النادي شيراتون
</title
>
<link
rel=
"stylesheet"
href=
"
<?=
url
(
'assets/css/main.css'
)
?>
"
>
<
?=
$__template
->
yield
(
'styles'
,
''
)
?
>
</head>
<body>
<div
class=
"app-layout"
>
<div
class=
"sidebar-overlay"
id=
"sidebarOverlay"
></div>
<!-- Sidebar -->
<aside
class=
"sidebar"
id=
"sidebar"
>
<?php
$__template
->
include
(
'Shared.Components.sidebar'
);
?>
</aside>
<!-- Main Wrapper -->
<div
class=
"main-wrapper"
id=
"main-wrapper"
>
<div
class=
"main-wrapper"
>
<header
class=
"main-header"
>
<div
style=
"display:flex;align-items:center;gap:12px;"
>
<button
class=
"hamburger-btn"
id=
"hamburgerBtn"
aria-label=
"القائمة"
>
☰
</button>
<h1
class=
"page-title"
>
<?php
$__template
->
yield
(
'title'
,
'لوحة التحكم'
);
?>
</h1>
<!-- Top Header -->
<header
class=
"top-header"
>
<div
class=
"header-right"
>
<button
class=
"sidebar-toggle-btn"
onclick=
"toggleSidebar()"
>
☰
</button>
<div
class=
"header-title"
>
<h1>
<?=
$__template
->
yield
(
'title'
,
'لوحة التحكم'
)
?>
</h1>
</div>
<div
class=
"page-actions"
>
<?php
$__template
->
yield
(
'page_actions'
,
''
);
?>
<div
class=
"user-info"
>
<?php
$__employee
=
\App\Core\App
::
getInstance
()
->
currentEmployee
();
if
(
$__employee
)
:
?>
<span
class=
"user-name-text"
>
<?=
e
(
$__employee
->
full_name_ar
??
''
)
?>
</span>
<a
href=
"/logout"
class=
"btn btn-sm btn-outline"
style=
"color:var(--red);border-color:var(--red);"
>
خروج
</a>
<?php
endif
;
?>
</div>
</div>
<div
class=
"header-left"
>
<?=
$__template
->
yield
(
'page_actions'
,
''
)
?>
<div
class=
"header-user"
>
<span
class=
"header-user-name"
>
<?=
e
(
$employeeName
)
?>
</span>
<a
href=
"/logout"
class=
"header-logout"
title=
"تسجيل الخروج"
>
🚪
</a>
</div>
</header>
<?php
// Inline alert reading — direct $_SESSION access, zero method calls
$__alertList
=
[];
try
{
if
(
isset
(
$_SESSION
[
'_flash'
][
'_alerts'
])
&&
is_array
(
$_SESSION
[
'_flash'
][
'_alerts'
]))
{
$__alertList
=
$_SESSION
[
'_flash'
][
'_alerts'
];
unset
(
$_SESSION
[
'_flash'
][
'_alerts'
]);
}
elseif
(
isset
(
$_SESSION
[
'_alerts'
])
&&
is_array
(
$_SESSION
[
'_alerts'
]))
{
$__alertList
=
$_SESSION
[
'_alerts'
];
unset
(
$_SESSION
[
'_alerts'
]);
}
}
catch
(
\Throwable
$__e
)
{}
if
(
!
empty
(
$__alertList
))
:
?>
<div
style=
"padding:15px 25px 0;"
>
<?php
foreach
(
$__alertList
as
$__a
)
:
$__type
=
$__a
[
'type'
]
??
'info'
;
$__msg
=
$__a
[
'message'
]
??
''
;
if
(
$__msg
===
''
)
continue
;
?>
<div
class=
"alert alert-
<?=
e
(
$__type
)
?>
"
>
<?=
e
(
$__msg
)
?>
</div>
<?php
endforeach
;
?>
</div>
<?php
endif
;
?>
</header>
<!-- Alerts -->
<?php
$__template
->
include
(
'Shared.Components.alerts'
);
?>
<!-- Page Content -->
<main
class=
"page-content"
>
<?=
$__template
->
yield
(
'content'
,
''
)
?>
</main>
<main
class=
"main-content"
>
<?php
$__template
->
yield
(
'content'
);
?>
</main>
</div>
<!-- Footer -->
<footer
class=
"page-footer"
>
<span>
نادي النادي شيراتون
©
<?=
date
(
'Y'
)
?>
</span>
<span>
الإصدار 1.0.0
</span>
<span>
<?=
arabic_date
(
date
(
'Y-m-d'
))
?>
</span>
</footer>
</div>
<script
src=
"
<?=
url
(
'assets/js/app.js'
)
?>
"
></script>
<script>
(
function
()
{
var
sidebar
=
document
.
querySelector
(
'.sidebar'
);
var
overlay
=
document
.
getElementById
(
'sidebarOverlay'
);
var
hamburger
=
document
.
getElementById
(
'hamburgerBtn'
);
function
openSidebar
()
{
sidebar
.
classList
.
add
(
'open'
);
overlay
.
classList
.
add
(
'active'
);
document
.
body
.
style
.
overflow
=
'hidden'
;
}
function
closeSidebar
()
{
sidebar
.
classList
.
remove
(
'open'
);
overlay
.
classList
.
remove
(
'active'
);
document
.
body
.
style
.
overflow
=
''
;
}
if
(
hamburger
)
{
hamburger
.
addEventListener
(
'click'
,
function
(
e
)
{
e
.
stopPropagation
();
if
(
sidebar
.
classList
.
contains
(
'open'
))
{
closeSidebar
();
}
else
{
openSidebar
();
}
});
function
toggleSidebar
()
{
var
sb
=
document
.
getElementById
(
'sidebar'
);
var
mw
=
document
.
getElementById
(
'main-wrapper'
);
sb
.
classList
.
toggle
(
'collapsed'
);
mw
.
classList
.
toggle
(
'sidebar-collapsed'
);
}
function
toggleSubmenu
(
el
)
{
var
parent
=
el
.
parentElement
;
var
submenu
=
parent
.
querySelector
(
'.sidebar-submenu'
);
if
(
submenu
)
{
var
isOpen
=
submenu
.
style
.
display
===
'block'
;
submenu
.
style
.
display
=
isOpen
?
'none'
:
'block'
;
parent
.
classList
.
toggle
(
'open'
,
!
isOpen
);
}
if
(
overlay
)
{
overlay
.
addEventListener
(
'click'
,
closeSidebar
);
}
var
sidebarLinks
=
sidebar
?
sidebar
.
querySelectorAll
(
'.sidebar-link'
)
:
[];
sidebarLinks
.
forEach
(
function
(
link
)
{
link
.
addEventListener
(
'click'
,
function
()
{
if
(
window
.
innerWidth
<=
768
)
{
closeSidebar
();
}
});
});
document
.
addEventListener
(
'keydown'
,
function
(
e
)
{
if
(
e
.
key
===
'Escape'
)
closeSidebar
();
});
window
.
addEventListener
(
'resize'
,
function
()
{
if
(
window
.
innerWidth
>
768
)
{
closeSidebar
();
}
});
})();
}
</script>
<?
php
$__template
->
yield
(
'scripts'
,
''
);
?>
<?
=
$__template
->
yield
(
'scripts'
,
''
)
?>
</body>
</html>
\ No newline at end of file
public/assets/css/main.css
View file @
a03e3d0d
/* ═══════════════════════════════════════════════════
THE CLUB ERP — MAIN STYLESHEET
RTL-first · Mobile-responsive · Production-ready
═══════════════════════════════════════════════════ */
/* ── Reset & Base ── */
*,
*
::before
,
*
::after
{
box-sizing
:
border-box
;
margin
:
0
;
padding
:
0
;
}
:root
{
--primary
:
#0D7377
;
--primary-dark
:
#0A5C5F
;
--primary-light
:
#E6F4F4
;
--dark
:
#1A1A2E
;
--gray-50
:
#F9FAFB
;
--gray-100
:
#F3F4F6
;
--gray-200
:
#E5E7EB
;
--gray-300
:
#D1D5DB
;
--gray-400
:
#9CA3AF
;
--gray-500
:
#6B7280
;
--gray-600
:
#4B5563
;
--gray-700
:
#374151
;
--red
:
#DC2626
;
--green
:
#059669
;
--amber
:
#D97706
;
--blue
:
#0284C7
;
--purple
:
#7C3AED
;
--sidebar-width
:
260px
;
--header-height
:
60px
;
}
html
{
font-size
:
14px
;
}
body
{
font-family
:
'Cairo'
,
'Segoe UI'
,
Tahoma
,
Arial
,
sans-serif
;
background
:
var
(
--gray-50
);
color
:
var
(
--dark
);
direction
:
rtl
;
line-height
:
1.6
;
min-height
:
100vh
;
-webkit-font-smoothing
:
antialiased
;
}
a
{
color
:
var
(
--primary
);
text-decoration
:
none
;
}
a
:hover
{
text-decoration
:
underline
;
}
/* ── Layout Shell ── */
.app-layout
{
display
:
flex
;
min-height
:
100vh
;
}
/* ═══════════════════════════════════════════
ADD THESE TO THE END OF YOUR main.css
IF SIDEBAR STYLES ARE MISSING
═══════════════════════════════════════════ */
/* ── Sidebar ── */
.sidebar
{
width
:
var
(
--sidebar-width
);
background
:
var
(
--dark
);
color
:
#fff
;
position
:
fixed
;
top
:
0
;
right
:
0
;
bottom
:
0
;
z-index
:
1000
;
width
:
260px
;
height
:
100vh
;
background
:
#1A1A2E
;
color
:
#E5E7EB
;
overflow-y
:
auto
;
overflow-x
:
hidden
;
transition
:
transform
0.3s
ease
;
z-index
:
1000
;
transition
:
width
0.3s
ease
,
transform
0.3s
ease
;
display
:
flex
;
flex-direction
:
column
;
}
.sidebar-logo
{
padding
:
20px
;
text-align
:
center
;
.sidebar.collapsed
{
width
:
0
;
transform
:
translateX
(
260px
);
}
.sidebar-header
{
padding
:
20px
15px
;
border-bottom
:
1px
solid
rgba
(
255
,
255
,
255
,
0.1
);
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
flex-shrink
:
0
;
}
.sidebar-logo
h1
{
.sidebar-brand
{
font-size
:
18px
;
font-weight
:
700
;
color
:
#14b8a6
;
letter-spacing
:
1px
;
}
.sidebar-toggle
{
background
:
none
;
border
:
none
;
color
:
#9CA3AF
;
font-size
:
18px
;
cursor
:
pointer
;
padding
:
5px
;
}
.sidebar-toggle
:hover
{
color
:
#fff
;
margin
:
0
;
}
.sidebar-logo
.subtitle
{
font-size
:
11px
;
color
:
var
(
--gray-400
);
margin-top
:
2px
;
.sidebar-nav
{
flex
:
1
;
overflow-y
:
auto
;
padding
:
10px
0
;
}
.sidebar-nav
{
padding
:
10px
0
;
}
.sidebar-menu
{
list-style
:
none
;
padding
:
0
;
margin
:
0
;
}
.sidebar-section
{
padding
:
8px
20px
4px
;
font-size
:
10px
;
text-transform
:
uppercase
;
letter-spacing
:
1px
;
color
:
var
(
--gray-400
);
font-weight
:
700
;
.sidebar-item
{
margin
:
2px
0
;
}
.sidebar-link
{
...
...
@@ -96,90 +75,212 @@ a:hover { text-decoration: underline; }
align-items
:
center
;
gap
:
10px
;
padding
:
10px
20px
;
color
:
var
(
--gray-300
);
font-size
:
13px
;
font-weight
:
500
;
transition
:
all
0.15s
;
border-right
:
3px
solid
transparent
;
color
:
#D1D5DB
;
text-decoration
:
none
;
font-size
:
14px
;
font-weight
:
500
;
transition
:
all
0.2s
ease
;
border-radius
:
0
;
position
:
relative
;
}
.sidebar-link
:hover
{
background
:
rgba
(
255
,
255
,
255
,
0.08
);
color
:
#fff
;
text-decoration
:
none
;
}
.sidebar-link.active
{
background
:
rgba
(
13
,
115
,
119
,
0.3
);
background
:
rgba
(
13
,
115
,
119
,
0.3
);
color
:
#14b8a6
;
border-left
:
3px
solid
#14b8a6
;
}
.sidebar-icon
{
font-size
:
16px
;
width
:
24px
;
text-align
:
center
;
flex-shrink
:
0
;
}
.sidebar-text
{
flex
:
1
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
.sidebar-arrow
{
font-size
:
10px
;
transition
:
transform
0.2s
ease
;
color
:
#6B7280
;
}
.sidebar-item.open
>
.sidebar-link
.sidebar-arrow
{
transform
:
rotate
(
-90deg
);
}
.sidebar-submenu
{
list-style
:
none
;
padding
:
0
;
margin
:
0
;
background
:
rgba
(
0
,
0
,
0
,
0.15
);
}
.sidebar-sublink
{
display
:
block
;
padding
:
8px
20px
8px
45px
;
color
:
#9CA3AF
;
text-decoration
:
none
;
font-size
:
13px
;
transition
:
all
0.2s
ease
;
}
.sidebar-sublink
:hover
{
color
:
#fff
;
border-right-color
:
var
(
--primary
);
font-weight
:
700
;
background
:
rgba
(
255
,
255
,
255
,
0.05
);
}
.sidebar-link
.icon
{
font-size
:
16px
;
width
:
22px
;
text-align
:
center
;
flex-shrink
:
0
;
}
.sidebar-sublink.active
{
color
:
#14b8a6
;
font-weight
:
600
;
}
/* ── Main Wrapper ── */
.main-wrapper
{
flex
:
1
;
margin-right
:
var
(
--sidebar-width
);
margin-right
:
260px
;
min-height
:
100vh
;
display
:
flex
;
flex-direction
:
column
;
transition
:
margin-right
0.3s
ease
;
background
:
#F3F4F6
;
}
/* ── Header ── */
.main-header
{
height
:
var
(
--header-height
);
.main-wrapper.sidebar-collapsed
{
margin-right
:
0
;
}
/* ── Top Header ── */
.top-header
{
background
:
#fff
;
border-bottom
:
1px
solid
var
(
--gray-200
);
border-bottom
:
1px
solid
#E5E7EB
;
padding
:
12px
25px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
padding
:
0
25px
;
align-items
:
center
;
position
:
sticky
;
top
:
0
;
z-index
:
900
;
z-index
:
100
;
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.05
);
}
.header-right
{
display
:
flex
;
align-items
:
center
;
gap
:
15px
;
}
.main-header
.page-title
{
.header-left
{
display
:
flex
;
align-items
:
center
;
gap
:
15px
;
}
.header-title
h1
{
margin
:
0
;
font-size
:
18px
;
font-weight
:
700
;
color
:
var
(
--dark
)
;
color
:
#1A1A2E
;
}
.main-header
.page-actions
{
.header-user
{
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
font-size
:
14px
;
color
:
#4B5563
;
}
.main-header
.user-info
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
font-size
:
13px
;
color
:
var
(
--gray-500
);
.header-user-name
{
font-weight
:
600
;
}
.hamburger-btn
{
display
:
none
;
.header-logout
{
text-decoration
:
none
;
font-size
:
18px
;
padding
:
4px
;
}
.sidebar-toggle-btn
{
background
:
none
;
border
:
none
;
font-size
:
24px
;
border
:
1px
solid
#E5E7EB
;
border-radius
:
6px
;
padding
:
6px
10px
;
cursor
:
pointer
;
padding
:
5
px
;
color
:
var
(
--dark
)
;
line-height
:
1
;
font-size
:
16
px
;
color
:
#4B5563
;
display
:
none
;
}
/* ──
Main
Content ── */
.
main
-content
{
/* ──
Page
Content ── */
.
page
-content
{
flex
:
1
;
padding
:
25px
;
}
/* ── Footer ── */
.page-footer
{
padding
:
15px
25px
;
background
:
#fff
;
border-top
:
1px
solid
#E5E7EB
;
display
:
flex
;
justify-content
:
space-between
;
font-size
:
12px
;
color
:
#9CA3AF
;
}
/* ── Cards ── */
.card
{
background
:
#fff
;
border-radius
:
8px
;
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.06
);
border
:
1px
solid
var
(
--gray-200
);
border
:
1px
solid
#E5E7EB
;
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.04
);
margin-bottom
:
0
;
overflow
:
hidden
;
}
/* ── Tables ── */
.table-responsive
{
overflow-x
:
auto
;
}
.data-table
{
width
:
100%
;
border-collapse
:
collapse
;
font-size
:
14px
;
}
.data-table
thead
{
background
:
#0D7377
;
color
:
#fff
;
}
.data-table
th
{
padding
:
12px
15px
;
text-align
:
right
;
font-weight
:
600
;
white-space
:
nowrap
;
}
.data-table
td
{
padding
:
10px
15px
;
border-bottom
:
1px
solid
#F3F4F6
;
text-align
:
right
;
}
.data-table
tbody
tr
:hover
{
background
:
#F9FAFB
;
}
/* ── Buttons ── */
.btn
{
display
:
inline-flex
;
...
...
@@ -187,337 +288,193 @@ a:hover { text-decoration: underline; }
justify-content
:
center
;
gap
:
6px
;
padding
:
8px
16px
;
font-size
:
13px
;
font-weight
:
600
;
border-radius
:
6px
;
border
:
1px
solid
transparent
;
cursor
:
pointer
;
transition
:
all
0.15s
;
font-size
:
14px
;
font-weight
:
600
;
text-decoration
:
none
;
cursor
:
pointer
;
border
:
1px
solid
transparent
;
transition
:
all
0.2s
ease
;
white-space
:
nowrap
;
font-family
:
inherit
;
line-height
:
1.4
;
}
.btn
:hover
{
text-decoration
:
none
;
opacity
:
0.9
;
}
.btn
:active
{
transform
:
scale
(
0.98
);
}
.btn-primary
{
background
:
var
(
--primary
);
color
:
#fff
;
border-color
:
var
(
--primary
);
}
.btn-primary
:hover
{
background
:
var
(
--primary-dark
);
}
.btn-primary
{
background
:
#0D7377
;
color
:
#fff
;
border-color
:
#0D7377
;
}
.btn-primary
:hover
{
background
:
#0a5c5f
;
}
.btn-outline
{
background
:
transparent
;
color
:
#0D7377
;
border-color
:
#D1D5DB
;
}
.btn-outline
{
background
:
#fff
;
color
:
var
(
--dark
);
border-color
:
var
(
--gray-300
);
}
.btn-outline
:hover
{
background
:
var
(
--gray-50
);
border-color
:
var
(
--gray-400
);
}
.btn-outline
:hover
{
background
:
#F3F4F6
;
border-color
:
#0D7377
;
}
.btn-sm
{
padding
:
4px
10px
;
font-size
:
12px
;
}
.btn-xs
{
padding
:
2px
6px
;
font-size
:
11px
;
}
.btn-sm
{
padding
:
4px
10px
;
font-size
:
12px
;
}
/* ── Forms ── */
.form-group
{
margin-bottom
:
0
;
}
.form-group
{
margin-bottom
:
0
;
}
.form-label
{
display
:
block
;
margin-bottom
:
5px
;
font-size
:
13px
;
font-weight
:
600
;
color
:
var
(
--gray-700
);
margin-bottom
:
4px
;
color
:
#374151
;
}
.form-input
,
.form-select
,
.form-textarea
{
.form-input
,
.form-select
,
.form-textarea
{
width
:
100%
;
padding
:
8px
12px
;
border
:
1px
solid
#D1D5DB
;
border-radius
:
6px
;
font-size
:
14px
;
font-family
:
inherit
;
border
:
1px
solid
var
(
--gray-300
);
border-radius
:
6px
;
background
:
#fff
;
color
:
var
(
--dark
);
transition
:
border-color
0.15s
;
direction
:
rtl
;
}
.form-input
:focus
,
.form-select
:focus
,
.form-textarea
:focus
{
outline
:
none
;
border-color
:
var
(
--primary
);
box-shadow
:
0
0
0
3px
rgba
(
13
,
115
,
119
,
0.1
);
color
:
#1A1A2E
;
transition
:
border-color
0.2s
;
box-sizing
:
border-box
;
}
.form-textarea
{
resize
:
vertical
;
min-height
:
60px
;
}
.form-select
{
appearance
:
auto
;
}
/* ── Tables ── */
.table-responsive
{
overflow-x
:
auto
;
-webkit-overflow-scrolling
:
touch
;
.form-input
:focus
,
.form-select
:focus
,
.form-textarea
:focus
{
outline
:
none
;
border-color
:
#0D7377
;
box-shadow
:
0
0
0
3px
rgba
(
13
,
115
,
119
,
0.1
);
}
.data-table
{
width
:
100%
;
border-collapse
:
collapse
;
font-size
:
13px
;
}
.data-table
thead
{
background
:
var
(
--gray-50
);
}
.data-table
th
{
padding
:
10px
14px
;
text-align
:
right
;
font-weight
:
700
;
color
:
var
(
--gray-600
);
border-bottom
:
2px
solid
var
(
--gray-200
);
white-space
:
nowrap
;
font-size
:
12px
;
}
.data-table
td
{
padding
:
10px
14px
;
border-bottom
:
1px
solid
var
(
--gray-100
);
vertical-align
:
middle
;
.form-textarea
{
resize
:
vertical
;
min-height
:
80px
;
}
.data-table
tbody
tr
:hover
{
background
:
var
(
--gray-50
);
}
/* ── Alerts ── */
.alert
{
padding
:
12px
16
px
;
border-radius
:
6
px
;
margin
-bottom
:
15px
;
padding
:
12px
20
px
;
border-radius
:
8
px
;
margin
:
0
25px
15px
;
font-size
:
14px
;
font-weight
:
500
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
gap
:
8px
;
}
.alert-success
{
background
:
#F0FDF4
;
border
:
1px
solid
#BBF7D0
;
color
:
var
(
--green
);
}
.alert-error
{
background
:
#FEF2F2
;
border
:
1px
solid
#FECACA
;
color
:
var
(
--red
);
}
.alert-warning
{
background
:
#FFF7ED
;
border
:
1px
solid
#FED7AA
;
color
:
var
(
--amber
);
}
.alert-info
{
background
:
#EFF6FF
;
border
:
1px
solid
#BFDBFE
;
color
:
var
(
--blue
);
}
/* ── Pagination ── */
.pagination-wrapper
{
display
:
flex
;
justify-content
:
center
;
}
.pagination
{
display
:
flex
;
gap
:
5px
;
list-style
:
none
;
padding
:
0
;
margin
:
0
;
flex-wrap
:
wrap
;
justify-content
:
center
;
.alert-success
{
background
:
#F0FDF4
;
color
:
#059669
;
border
:
1px
solid
#BBF7D0
;
}
/* ── Sidebar Overlay (mobile) ── */
.sidebar-overlay
{
display
:
none
;
position
:
fixed
;
inset
:
0
;
background
:
rgba
(
0
,
0
,
0
,
0.5
);
z-index
:
999
;
}
.sidebar-overlay.active
{
display
:
block
;
}
/* ── Scrollbar ── */
.sidebar
::-webkit-scrollbar
{
width
:
4px
;
}
.sidebar
::-webkit-scrollbar-track
{
background
:
transparent
;
}
.sidebar
::-webkit-scrollbar-thumb
{
background
:
rgba
(
255
,
255
,
255
,
0.2
);
border-radius
:
4px
;
}
/* ── Utility ── */
.text-center
{
text-align
:
center
;
}
.text-left
{
text-align
:
left
;
}
.text-right
{
text-align
:
right
;
}
.font-bold
{
font-weight
:
700
;
}
.hidden
{
display
:
none
!important
;
}
/* ══════════════════════════════════════════
RESPONSIVE — TABLET (≤1024px)
══════════════════════════════════════════ */
@media
(
max-width
:
1024px
)
{
:root
{
--sidebar-width
:
220px
;
}
.alert-error
{
background
:
#FEF2F2
;
color
:
#DC2626
;
border
:
1px
solid
#FECACA
;
}
.sidebar-link
{
padding
:
9px
15px
;
font-size
:
12px
;
}
.sidebar-section
{
padding
:
8px
15px
4px
;
}
.main-content
{
padding
:
20px
;
}
.main-header
{
padding
:
0
20px
;
}
.alert-warning
{
background
:
#FFF7ED
;
color
:
#D97706
;
border
:
1px
solid
#FED7AA
;
}
/* ══════════════════════════════════════════
RESPONSIVE — MOBILE (≤768px)
══════════════════════════════════════════ */
@media
(
max-width
:
768px
)
{
:root
{
--sidebar-width
:
270px
;
--header-height
:
54px
;
}
.alert-info
{
background
:
#EFF6FF
;
color
:
#0284C7
;
border
:
1px
solid
#BFDBFE
;
}
.alert-close
{
background
:
none
;
border
:
none
;
cursor
:
pointer
;
font-size
:
18px
;
color
:
inherit
;
opacity
:
0.6
;
padding
:
0
5px
;
}
.alert-close
:hover
{
opacity
:
1
;
}
/* Sidebar: off-canvas */
/* ── Responsive ── */
@media
(
max-width
:
1024px
)
{
.sidebar
{
transform
:
translateX
(
100%
);
transform
:
translateX
(
260px
);
width
:
260px
;
}
.sidebar.
open
{
.sidebar.
show
{
transform
:
translateX
(
0
);
}
/* Main wrapper: full width */
.main-wrapper
{
margin-right
:
0
;
}
/* Show hamburger */
.hamburger-btn
{
.sidebar-toggle-btn
{
display
:
block
;
}
}
/* Header */
.main-header
{
padding
:
0
15px
;
gap
:
10px
;
}
.main-header
.page-title
{
font-size
:
15px
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
max-width
:
50vw
;
}
.main-header
.page-actions
{
gap
:
6px
;
flex-wrap
:
wrap
;
}
.main-header
.page-actions
.btn
{
padding
:
5px
10px
;
font-size
:
11px
;
}
.main-header
.user-info
.user-name-text
{
display
:
none
;
}
/* Content */
.main-content
{
padding
:
15px
;
}
/* ── Grid overrides: stack everything ── */
[
style
*=
"grid-template-columns: 1fr 1fr"
],
[
style
*=
"grid-template-columns:1fr 1fr"
],
[
style
*=
"grid-template-columns: 2fr 1fr"
],
[
style
*=
"grid-template-columns:2fr 1fr"
],
[
style
*=
"grid-template-columns: 1fr 300px"
],
[
style
*=
"grid-template-columns:1fr 300px"
],
[
style
*=
"grid-template-columns: 250px 1fr"
],
[
style
*=
"grid-template-columns:250px 1fr"
]
{
grid-template-columns
:
1
fr
!important
;
}
/* Stats cards row */
[
style
*=
"grid-template-columns: repeat(auto-fit"
],
[
style
*=
"grid-template-columns:repeat(auto-fit"
]
{
grid-template-columns
:
1
fr
1
fr
!important
;
}
/* ── Filter forms: stack vertically ── */
.card
form
[
style
*=
"display:flex"
],
.card
form
[
style
*=
"display: flex"
]
{
flex-direction
:
column
!important
;
align-items
:
stretch
!important
;
}
.card
form
[
style
*=
"display:flex"
]
>
div
,
.card
form
[
style
*=
"display: flex"
]
>
div
{
min-width
:
0
!important
;
width
:
100%
!important
;
}
.card
form
[
style
*=
"display:flex"
]
.form-input
,
.card
form
[
style
*=
"display:flex"
]
.form-select
,
.card
form
[
style
*=
"display: flex"
]
.form-input
,
.card
form
[
style
*=
"display: flex"
]
.form-select
{
min-width
:
0
!important
;
width
:
100%
!important
;
}
.card
form
[
style
*=
"display:flex"
]
.btn
,
.card
form
[
style
*=
"display: flex"
]
.btn
{
width
:
100%
;
}
/* ── Tables: tighter ── */
.data-table
th
,
.data-table
td
{
padding
:
8px
8px
;
font-size
:
12px
;
}
.data-table
th
{
font-size
:
11px
;
}
/* ── Cards: less padding ── */
.card
>
div
[
style
*=
"padding:20px"
],
.card
>
div
[
style
*=
"padding: 20px"
]
{
padding
:
15px
!important
;
}
.card
>
div
[
style
*=
"padding:15px 20px"
],
.card
>
div
[
style
*=
"padding: 15px 20px"
]
{
padding
:
12px
15px
!important
;
}
/* ── Buttons in table cells: stack ── */
td
[
style
*=
"display:flex"
],
td
[
style
*=
"display: flex"
]
{
flex-wrap
:
wrap
!important
;
}
/* ── Member show page: action buttons wrap ── */
.member-actions
{
flex-wrap
:
wrap
!important
;
}
/* ── Inline forms (pay buttons etc) ── */
td
form
[
style
*=
"display:flex"
],
td
form
[
style
*=
"display: flex"
]
{
flex-wrap
:
wrap
!important
;
/* ── Print ── */
@media
print
{
.sidebar
,
.top-header
,
.page-footer
,
.btn
,
.sidebar-toggle-btn
{
display
:
none
!important
;
}
/* ── Full-width grid children on mobile ── */
[
style
*=
"grid-column:1/-1"
],
[
style
*=
"grid-column: 1/-1"
]
{
grid-column
:
1
/
-1
!important
;
.main-wrapper
{
margin-right
:
0
!important
;
}
/* ── Print button row ── */
[
style
*=
"display:flex;gap:10px;"
],
[
style
*=
"display: flex; gap: 10px;"
]
{
flex-wrap
:
wrap
!important
;
.page-content
{
padding
:
0
!important
;
}
}
/* ══════════════════════════════════════════
RESPONSIVE — SMALL MOBILE (≤480px)
══════════════════════════════════════════ */
@media
(
max-width
:
480px
)
{
html
{
font-size
:
13px
;
}
.main-content
{
padding
:
10px
;
}
.main-header
.page-title
{
font-size
:
14px
;
max-width
:
40vw
;
}
/* Stats cards: single column */
[
style
*=
"grid-template-columns: repeat(auto-fit"
],
[
style
*=
"grid-template-columns:repeat(auto-fit"
]
{
grid-template-columns
:
1
fr
!important
;
}
/* Even tighter table cells */
.data-table
th
,
.data-table
td
{
padding
:
6px
6px
;
font-size
:
11px
;
}
/* ── Body Reset ── */
*
{
box-sizing
:
border-box
;
}
.btn
{
padding
:
6px
12px
;
font-size
:
12px
;
}
.btn-sm
{
padding
:
3px
8px
;
font-size
:
11px
;
}
body
{
margin
:
0
;
padding
:
0
;
font-family
:
'Cairo'
,
'Segoe UI'
,
Tahoma
,
Arial
,
sans-serif
;
font-size
:
14px
;
color
:
#1A1A2E
;
background
:
#F3F4F6
;
direction
:
rtl
;
line-height
:
1.6
;
}
/* Card section headers */
.card
h3
{
font-size
:
15px
!important
;
}
.card
h4
{
font-size
:
14px
!important
;
}
a
{
color
:
#0D7377
;
text-decoration
:
none
;
}
/* Large money displays */
[
style
*=
"font-size:28px"
]
{
font-size
:
22px
!important
;
}
[
style
*=
"font-size: 28px"
]
{
font-size
:
22px
!important
;
}
[
style
*=
"font-size:20px"
]
{
font-size
:
16px
!important
;
}
[
style
*=
"font-size: 20px"
]
{
font-size
:
16px
!important
;
}
a
:hover
{
text-decoration
:
underline
;
}
/* ══════════════════════════════════════════
PRINT
══════════════════════════════════════════ */
@media
print
{
.sidebar
,
.main-header
,
.hamburger-btn
,
.sidebar-overlay
{
display
:
none
!important
;
}
.main-wrapper
{
margin-right
:
0
!important
;
}
.main-content
{
padding
:
0
!important
;
}
.card
{
box-shadow
:
none
;
border
:
1px
solid
#ddd
;
}
.btn
{
display
:
none
!important
;
}
code
{
background
:
#F3F4F6
;
padding
:
2px
6px
;
border-radius
:
4px
;
font-size
:
12px
;
font-family
:
'Courier New'
,
monospace
;
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment