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
Show 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
<?php
/**
/**
* Alerts component — reads flash alerts from session and displays them.
* Flash alert messages component.
* Bulletproof: will never crash, never kill the page.
*/
*/
$__alertList
=
[];
$session
=
\App\Core\App
::
getInstance
()
->
session
();
try
{
$alerts
=
$session
->
getAlerts
();
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
=
[];
}
foreach
(
$__alertList
as
$__a
)
:
if
(
empty
(
$alerts
))
{
$__type
=
$__a
[
'type'
]
??
'info'
;
return
;
$__msg
=
$__a
[
'message'
]
??
''
;
}
if
(
$__msg
===
''
)
continue
;
?>
?>
<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
;
?>
<?php
endforeach
;
?>
\ No newline at end of file
app/Shared/Components/sidebar.php
View file @
a03e3d0d
<?php
<?php
/**
* Sidebar component — reads menu items from MenuRegistry.
*/
use
App\Core\App
;
use
App\Core\Registries\MenuRegistry
;
$currentPath
=
parse_url
(
$_SERVER
[
'REQUEST_URI'
]
??
'/'
,
PHP_URL_PATH
);
$currentPath
=
parse_url
(
$_SERVER
[
'REQUEST_URI'
]
??
'/'
,
PHP_URL_PATH
);
$currentPath
=
rtrim
(
$currentPath
,
'/'
)
?:
'/'
;
$employee
=
App
::
getInstance
()
->
currentEmployee
();
// 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'
=>
'🏖️'
,
];
$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
)]
??
'📌'
;
};
// 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'
);
function
sidebarActive
(
string
$path
,
string
$current
)
:
string
{
$isSuperAdmin
=
$db
->
selectOne
(
if
(
$path
===
'/'
&&
$current
===
'/'
)
return
'active'
;
"SELECT 1 FROM employee_roles er
if
(
$path
!==
'/'
&&
str_starts_with
(
$current
,
$path
))
return
'active'
;
JOIN roles r ON r.id = er.role_id
return
''
;
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
=
[];
}
}
$hasPerm
=
function
(
string
$perm
)
use
(
$employeePermissions
)
:
bool
{
if
(
empty
(
$perm
))
return
true
;
if
(
in_array
(
'*'
,
$employeePermissions
))
return
true
;
return
in_array
(
$perm
,
$employeePermissions
);
};
$isActive
=
function
(
string
$route
)
use
(
$currentPath
)
:
bool
{
if
(
$route
===
'/'
)
return
$currentPath
===
'/'
;
return
str_starts_with
(
$currentPath
,
$route
);
};
// Get menu items from registry
$menuItems
=
MenuRegistry
::
getAll
();
usort
(
$menuItems
,
fn
(
$a
,
$b
)
=>
(
$a
[
'order'
]
??
999
)
<=>
(
$b
[
'order'
]
??
999
));
// 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'
=>
[]],
];
}
}
?>
?>
<aside
class=
"sidebar"
id=
"mainSidebar"
>
<div
class=
"sidebar-logo"
>
<div
class=
"sidebar-header"
>
<h1>
🏛️ THE CLUB
</h1>
<div
class=
"sidebar-brand"
>
THE CLUB
</div>
<div
class=
"subtitle"
>
نادي النادي شيراتون — Sports City
</div>
</div>
</div>
<nav
class=
"sidebar-nav"
>
<nav
class=
"sidebar-nav"
>
<ul
class=
"sidebar-menu"
>
<div
class=
"sidebar-section"
>
الرئيسية
</div>
<?php
foreach
(
$menuItems
as
$item
)
:
?>
<a
href=
"/dashboard"
class=
"sidebar-link
<?=
sidebarActive
(
'/dashboard'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📊
</span>
لوحة التحكم
</a>
<?php
if
(
!
empty
(
$item
[
'permission'
])
&&
!
$hasPerm
(
$item
[
'permission'
]))
continue
;
<div
class=
"sidebar-section"
>
الأعضاء
</div>
<a
href=
"/members"
class=
"sidebar-link
<?=
sidebarActive
(
'/members'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
👥
</span>
إدارة الأعضاء
</a>
if
(
!
empty
(
$item
[
'is_separator'
]))
{
<a
href=
"/members/search"
class=
"sidebar-link
<?=
sidebarActive
(
'/members/search'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🔍
</span>
بحث الأعضاء
</a>
echo
'<li style="padding:15px 20px 5px;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:1px;">'
.
e
(
$item
[
'label_ar'
])
.
'</li>'
;
<a
href=
"/interviews"
class=
"sidebar-link
<?=
sidebarActive
(
'/interviews'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🗓️
</span>
المقابلات
</a>
continue
;
<a
href=
"/carnets"
class=
"sidebar-link
<?=
sidebarActive
(
'/carnets'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🪪
</span>
الكارنيهات
</a>
}
<div
class=
"sidebar-section"
>
مالية
</div>
$children
=
$item
[
'children'
]
??
[];
<a
href=
"/payments"
class=
"sidebar-link
<?=
sidebarActive
(
'/payments'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
💰
</span>
المدفوعات
</a>
$visibleChildren
=
[];
<a
href=
"/receipts"
class=
"sidebar-link
<?=
sidebarActive
(
'/receipts'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🧾
</span>
الإيصالات
</a>
foreach
(
$children
as
$child
)
{
<a
href=
"/installments"
class=
"sidebar-link
<?=
sidebarActive
(
'/installments'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📅
</span>
الأقساط
</a>
if
(
empty
(
$child
[
'permission'
])
||
$hasPerm
(
$child
[
'permission'
]))
{
<a
href=
"/subscriptions"
class=
"sidebar-link
<?=
sidebarActive
(
'/subscriptions'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📋
</span>
الاشتراكات
</a>
$visibleChildren
[]
=
$child
;
}
<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>
$hasChildren
=
!
empty
(
$visibleChildren
);
$itemRoute
=
$item
[
'route'
]
??
'#'
;
<div
class=
"sidebar-section"
>
عمليات
</div>
$itemActive
=
$isActive
(
$itemRoute
);
<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>
$childActive
=
false
;
<a
href=
"/death"
class=
"sidebar-link
<?=
sidebarActive
(
'/death'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🕊️
</span>
الوفاة
</a>
foreach
(
$visibleChildren
as
$child
)
{
<a
href=
"/waiver"
class=
"sidebar-link
<?=
sidebarActive
(
'/waiver'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📝
</span>
التنازل
</a>
if
(
$isActive
(
$child
[
'route'
]
??
''
))
{
$childActive
=
true
;
break
;
}
}
<div
class=
"sidebar-section"
>
أنواع العضوية
</div>
<a
href=
"/temporary"
class=
"sidebar-link
<?=
sidebarActive
(
'/temporary'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
⏱️
</span>
المؤقتون
</a>
$isOpen
=
$itemActive
||
$childActive
;
<a
href=
"/seasonal"
class=
"sidebar-link
<?=
sidebarActive
(
'/seasonal'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🌊
</span>
الموسمية
</a>
$icon
=
$getIcon
(
$item
[
'icon'
]
??
''
);
<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>
<li
class=
"sidebar-item
<?=
$isOpen
&&
$hasChildren
?
' open'
:
''
?>
"
>
<a
href=
"/foreign"
class=
"sidebar-link
<?=
sidebarActive
(
'/foreign'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🌍
</span>
الأجانب
</a>
<?php
if
(
$hasChildren
)
:
?>
<a
href=
"javascript:void(0)"
class=
"sidebar-link
<?=
$itemActive
?
' active'
:
''
?>
"
onclick=
"toggleSubmenu(this)"
>
<div
class=
"sidebar-section"
>
نماذج و مستندات
</div>
<span
class=
"sidebar-icon"
>
<?=
$icon
?>
</span>
<a
href=
"/forms/submissions"
class=
"sidebar-link
<?=
sidebarActive
(
'/forms'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📄
</span>
النماذج
</a>
<span
class=
"sidebar-text"
>
<?=
e
(
$item
[
'label_ar'
])
?>
</span>
<a
href=
"/documents"
class=
"sidebar-link
<?=
sidebarActive
(
'/documents'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📁
</span>
المستندات
</a>
<span
class=
"sidebar-arrow"
>
◀
</span>
</a>
<div
class=
"sidebar-section"
>
تقارير و إشعارات
</div>
<ul
class=
"sidebar-submenu"
style=
"display:
<?=
$isOpen
?
'block'
:
'none'
?>
;"
>
<a
href=
"/reports"
class=
"sidebar-link
<?=
sidebarActive
(
'/reports'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📈
</span>
التقارير
</a>
<?php
foreach
(
$visibleChildren
as
$child
)
:
?>
<a
href=
"/notifications/templates"
class=
"sidebar-link
<?=
sidebarActive
(
'/notifications'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📱
</span>
الإشعارات
</a>
<?php
$childRoute
=
$child
[
'route'
]
??
'#'
;
?>
<li>
<div
class=
"sidebar-section"
>
النظام
</div>
<a
href=
"
<?=
e
(
$childRoute
)
?>
"
class=
"sidebar-sublink
<?=
$isActive
(
$childRoute
)
?
' active'
:
''
?>
"
>
<a
href=
"/users"
class=
"sidebar-link
<?=
sidebarActive
(
'/users'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
👤
</span>
المستخدمون
</a>
<?=
e
(
$child
[
'label_ar'
])
?>
<a
href=
"/roles"
class=
"sidebar-link
<?=
sidebarActive
(
'/roles'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🔐
</span>
الصلاحيات
</a>
</a>
<a
href=
"/branches"
class=
"sidebar-link
<?=
sidebarActive
(
'/branches'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🏢
</span>
الفروع
</a>
</li>
<a
href=
"/rules"
class=
"sidebar-link
<?=
sidebarActive
(
'/rules'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
⚙️
</span>
القواعد
</a>
<?php
endforeach
;
?>
<a
href=
"/pricing"
class=
"sidebar-link
<?=
sidebarActive
(
'/pricing'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
💲
</span>
التسعير
</a>
</ul>
<a
href=
"/settings"
class=
"sidebar-link
<?=
sidebarActive
(
'/settings'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🛠️
</span>
الإعدادات
</a>
<?php
else
:
?>
<a
href=
"/workflow"
class=
"sidebar-link
<?=
sidebarActive
(
'/workflow'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🔀
</span>
سير العمل
</a>
<a
href=
"
<?=
e
(
$itemRoute
)
?>
"
class=
"sidebar-link
<?=
$itemActive
?
' active'
:
''
?>
"
>
<a
href=
"/audit"
class=
"sidebar-link
<?=
sidebarActive
(
'/audit'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
📜
</span>
سجل المراجعة
</a>
<span
class=
"sidebar-icon"
>
<?=
$icon
?>
</span>
<a
href=
"/archive"
class=
"sidebar-link
<?=
sidebarActive
(
'/archive'
,
$currentPath
)
?>
"
><span
class=
"icon"
>
🗄️
</span>
الأرشيف
</a>
<span
class=
"sidebar-text"
>
<?=
e
(
$item
[
'label_ar'
])
?>
</span>
</nav>
</a>
</aside>
<?php
endif
;
?>
\ No newline at end of file
</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>
<!DOCTYPE html>
<html
lang=
"ar"
dir=
"rtl"
>
<html
lang=
"ar"
dir=
"rtl"
>
<head>
<head>
<meta
charset=
"UTF-8"
>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
<?php
$__template
->
yield
(
'title'
,
'تسجيل الدخول'
);
?>
— نادي النادي
</title>
<meta
name=
"csrf-token"
content=
"
<?=
e
(
CSRF
::
token
())
?>
"
>
<link
rel=
"preconnect"
href=
"https://fonts.googleapis.com"
>
<title>
<?=
$__template
->
yield
(
'title'
,
'تسجيل الدخول'
)
?>
— نادي النادي شيراتون
</title>
<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'
)
?>
"
>
<link
rel=
"stylesheet"
href=
"
<?=
url
(
'assets/css/main.css'
)
?>
"
>
<style>
<style>
body
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
min-height
:
100vh
;
background
:
linear-gradient
(
135deg
,
#0D7377
0%
,
#1A1A2E
100%
);
padding
:
20px
;
}
body
{
.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
);
}
display
:
flex
;
.auth-logo
{
text-align
:
center
;
margin-bottom
:
30px
;
}
justify-content
:
center
;
.auth-logo
h1
{
font-size
:
24px
;
color
:
#0D7377
;
margin
:
0
;
}
align-items
:
center
;
.auth-logo
p
{
color
:
#6B7280
;
font-size
:
13px
;
margin-top
:
5px
;
}
min-height
:
100vh
;
@media
(
max-width
:
480px
)
{
margin
:
0
;
.auth-card
{
padding
:
25px
20px
;
}
background
:
linear-gradient
(
135deg
,
#1A1A2E
0%
,
#0D7377
100%
);
.auth-logo
h1
{
font-size
:
20px
;
}
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>
</style>
</head>
</head>
<body>
<body>
<div
class=
"auth-card"
>
<div
class=
"auth-card"
>
<div
class=
"auth-logo
"
>
<div
class=
"auth-header
"
>
<h1>
🏛️ THE CLUB
</h1>
<h1>
نادي النادي شيراتون
</h1>
<p>
نادي النادي شيراتون — Sports City
</p>
<p>
THE CLUB Sheraton
</p>
</div>
</div>
<?php
<?php
// Inline alert reading — no includes, no method calls that can crash
$session
=
\App\Core\App
::
getInstance
()
->
session
();
$__alertList
=
[];
$alerts
=
$session
->
getAlerts
();
try
{
if
(
!
empty
(
$alerts
))
:
if
(
isset
(
$_SESSION
[
'_flash'
][
'_alerts'
])
&&
is_array
(
$_SESSION
[
'_flash'
][
'_alerts'
]))
{
foreach
(
$alerts
as
$alert
)
:
$__alertList
=
$_SESSION
[
'_flash'
][
'_alerts'
];
?>
unset
(
$_SESSION
[
'_flash'
][
'_alerts'
]);
<div
style=
"padding:10px 15px;border-radius:6px;margin-bottom:15px;font-size:13px;
}
elseif
(
isset
(
$_SESSION
[
'_alerts'
])
&&
is_array
(
$_SESSION
[
'_alerts'
]))
{
background:
<?=
(
$alert
[
'type'
]
??
''
)
===
'error'
?
'#FEF2F2'
:
'#F0FDF4'
?>
;
$__alertList
=
$_SESSION
[
'_alerts'
];
color:
<?=
(
$alert
[
'type'
]
??
''
)
===
'error'
?
'#DC2626'
:
'#059669'
?>
;
unset
(
$_SESSION
[
'_alerts'
]);
border:1px solid
<?=
(
$alert
[
'type'
]
??
''
)
===
'error'
?
'#FECACA'
:
'#BBF7D0'
?>
;"
>
}
<?=
e
(
$alert
[
'message'
]
??
''
)
?>
}
catch
(
\Throwable
$__e
)
{}
</div>
foreach
(
$__alertList
as
$__a
)
:
<?php
$__type
=
$__a
[
'type'
]
??
'info'
;
endforeach
;
$__msg
=
$__a
[
'message'
]
??
''
;
endif
;
if
(
$__msg
===
''
)
continue
;
?>
?>
<div
class=
"alert alert-
<?=
e
(
$__type
)
?>
"
style=
"margin-bottom:15px;"
>
<?=
e
(
$__msg
)
?>
</div>
<?php
endforeach
;
?>
<?php
$__template
->
yield
(
'content'
);
?>
<?=
$__template
->
yield
(
'content'
,
''
)
?>
</div>
</div>
</body>
</body>
</html>
</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>
<!DOCTYPE html>
<html
lang=
"ar"
dir=
"rtl"
>
<html
lang=
"ar"
dir=
"rtl"
>
<head>
<head>
<meta
charset=
"UTF-8"
>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0
, maximum-scale=1.0, user-scalable=no
"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<
title>
<?php
$__template
->
yield
(
'title'
,
'النظام'
);
?>
— نادي النادي
</title
>
<
meta
name=
"csrf-token"
content=
"
<?=
e
(
CSRF
::
token
())
?>
"
>
<
link
rel=
"preconnect"
href=
"https://fonts.googleapis.com"
>
<
title>
<?=
$__template
->
yield
(
'title'
,
'لوحة التحكم'
)
?>
— نادي النادي شيراتون
</title
>
<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'
)
?>
"
>
<
link
rel=
"stylesheet"
href=
"
<?=
url
(
'assets/css/main.css'
)
?>
?v=
<?=
@
filemtime
(
App\Core\App
::
getInstance
()
->
basePath
()
.
'/public/assets/css/main.css'
)
?>
"
>
<
?=
$__template
->
yield
(
'styles'
,
''
)
?
>
</head>
</head>
<body>
<body>
<div
class=
"app-layout"
>
<!-- Sidebar -->
<div
class=
"sidebar-overlay"
id=
"sidebarOverlay"
></div>
<aside
class=
"sidebar"
id=
"sidebar"
>
<?php
$__template
->
include
(
'Shared.Components.sidebar'
);
?>
<?php
$__template
->
include
(
'Shared.Components.sidebar'
);
?>
</aside>
<div
class=
"main-wrapper"
>
<!-- Main Wrapper -->
<header
class=
"main-header"
>
<div
class=
"main-wrapper"
id=
"main-wrapper"
>
<div
style=
"display:flex;align-items:center;gap:12px;"
>
<button
class=
"hamburger-btn"
id=
"hamburgerBtn"
aria-label=
"القائمة"
>
☰
</button>
<!-- Top Header -->
<h1
class=
"page-title"
>
<?php
$__template
->
yield
(
'title'
,
'لوحة التحكم'
);
?>
</h1>
<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>
</div>
<div
class=
"page-actions"
>
<div
class=
"header-left"
>
<?php
$__template
->
yield
(
'page_actions'
,
''
);
?>
<?=
$__template
->
yield
(
'page_actions'
,
''
)
?>
<div
class=
"user-info"
>
<div
class=
"header-user"
>
<?php
<span
class=
"header-user-name"
>
<?=
e
(
$employeeName
)
?>
</span>
$__employee
=
\App\Core\App
::
getInstance
()
->
currentEmployee
();
<a
href=
"/logout"
class=
"header-logout"
title=
"تسجيل الخروج"
>
🚪
</a>
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>
</div>
</header>
</header>
<?php
<!-- Alerts -->
// Inline alert reading — direct $_SESSION access, zero method calls
<?php
$__template
->
include
(
'Shared.Components.alerts'
);
?>
$__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
;
?>
<main
class=
"main-content"
>
<!-- Page Content -->
<?php
$__template
->
yield
(
'content'
);
?>
<main
class=
"page-content"
>
<?=
$__template
->
yield
(
'content'
,
''
)
?>
</main>
</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>
</div>
<script
src=
"
<?=
url
(
'assets/js/app.js'
)
?>
"
></script>
<script
src=
"
<?=
url
(
'assets/js/app.js'
)
?>
"
></script>
<script>
<script>
(
function
()
{
function
toggleSidebar
()
{
var
sidebar
=
document
.
querySelector
(
'.sidebar'
);
var
sb
=
document
.
getElementById
(
'sidebar'
);
var
overlay
=
document
.
getElementById
(
'sidebarOverlay'
);
var
mw
=
document
.
getElementById
(
'main-wrapper'
);
var
hamburger
=
document
.
getElementById
(
'hamburgerBtn'
);
sb
.
classList
.
toggle
(
'collapsed'
);
mw
.
classList
.
toggle
(
'sidebar-collapsed'
);
function
openSidebar
()
{
}
sidebar
.
classList
.
add
(
'open'
);
function
toggleSubmenu
(
el
)
{
overlay
.
classList
.
add
(
'active'
);
var
parent
=
el
.
parentElement
;
document
.
body
.
style
.
overflow
=
'hidden'
;
var
submenu
=
parent
.
querySelector
(
'.sidebar-submenu'
);
}
if
(
submenu
)
{
function
closeSidebar
()
{
var
isOpen
=
submenu
.
style
.
display
===
'block'
;
sidebar
.
classList
.
remove
(
'open'
);
submenu
.
style
.
display
=
isOpen
?
'none'
:
'block'
;
overlay
.
classList
.
remove
(
'active'
);
parent
.
classList
.
toggle
(
'open'
,
!
isOpen
);
document
.
body
.
style
.
overflow
=
''
;
}
if
(
hamburger
)
{
hamburger
.
addEventListener
(
'click'
,
function
(
e
)
{
e
.
stopPropagation
();
if
(
sidebar
.
classList
.
contains
(
'open'
))
{
closeSidebar
();
}
else
{
openSidebar
();
}
});
}
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>
</script>
<?
php
$__template
->
yield
(
'scripts'
,
''
);
?>
<?
=
$__template
->
yield
(
'scripts'
,
''
)
?>
</body>
</body>
</html>
</html>
\ No newline at end of file
public/assets/css/main.css
View file @
a03e3d0d
/* ═══════════════════════════════════════════════════
/* ═══════════════════════════════════════════
THE CLUB ERP — MAIN STYLESHEET
ADD THESE TO THE END OF YOUR main.css
RTL-first · Mobile-responsive · Production-ready
IF SIDEBAR STYLES ARE MISSING
═══════════════════════════════════════════════════ */
═══════════════════════════════════════════ */
/* ── 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
;
}
/* ── Sidebar ── */
.sidebar
{
.sidebar
{
width
:
var
(
--sidebar-width
);
background
:
var
(
--dark
);
color
:
#fff
;
position
:
fixed
;
position
:
fixed
;
top
:
0
;
top
:
0
;
right
:
0
;
right
:
0
;
bottom
:
0
;
width
:
260px
;
z-index
:
1000
;
height
:
100vh
;
background
:
#1A1A2E
;
color
:
#E5E7EB
;
overflow-y
:
auto
;
overflow-y
:
auto
;
overflow-x
:
hidden
;
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
{
.sidebar.collapsed
{
padding
:
20px
;
width
:
0
;
text-align
:
center
;
transform
:
translateX
(
260px
);
}
.sidebar-header
{
padding
:
20px
15px
;
border-bottom
:
1px
solid
rgba
(
255
,
255
,
255
,
0.1
);
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-size
:
18px
;
font-weight
:
700
;
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
;
color
:
#fff
;
margin
:
0
;
}
}
.sidebar-logo
.subtitle
{
font-size
:
11px
;
.sidebar-nav
{
color
:
var
(
--gray-400
);
flex
:
1
;
margin-top
:
2px
;
overflow-y
:
auto
;
padding
:
10px
0
;
}
}
.sidebar-nav
{
padding
:
10px
0
;
}
.sidebar-menu
{
list-style
:
none
;
padding
:
0
;
margin
:
0
;
}
.sidebar-section
{
.sidebar-item
{
padding
:
8px
20px
4px
;
margin
:
2px
0
;
font-size
:
10px
;
text-transform
:
uppercase
;
letter-spacing
:
1px
;
color
:
var
(
--gray-400
);
font-weight
:
700
;
}
}
.sidebar-link
{
.sidebar-link
{
...
@@ -96,90 +75,212 @@ a:hover { text-decoration: underline; }
...
@@ -96,90 +75,212 @@ a:hover { text-decoration: underline; }
align-items
:
center
;
align-items
:
center
;
gap
:
10px
;
gap
:
10px
;
padding
:
10px
20px
;
padding
:
10px
20px
;
color
:
var
(
--gray-300
);
color
:
#D1D5DB
;
font-size
:
13px
;
font-weight
:
500
;
transition
:
all
0.15s
;
border-right
:
3px
solid
transparent
;
text-decoration
:
none
;
text-decoration
:
none
;
font-size
:
14px
;
font-weight
:
500
;
transition
:
all
0.2s
ease
;
border-radius
:
0
;
position
:
relative
;
}
}
.sidebar-link
:hover
{
.sidebar-link
:hover
{
background
:
rgba
(
255
,
255
,
255
,
0.08
);
background
:
rgba
(
255
,
255
,
255
,
0.08
);
color
:
#fff
;
color
:
#fff
;
text-decoration
:
none
;
}
}
.sidebar-link.active
{
.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
;
color
:
#fff
;
border-right-color
:
var
(
--primary
);
background
:
rgba
(
255
,
255
,
255
,
0.05
);
font-weight
:
700
;
}
.sidebar-sublink.active
{
color
:
#14b8a6
;
font-weight
:
600
;
}
}
.sidebar-link
.icon
{
font-size
:
16px
;
width
:
22px
;
text-align
:
center
;
flex-shrink
:
0
;
}
/* ── Main Wrapper ── */
.main-wrapper
{
.main-wrapper
{
flex
:
1
;
margin-right
:
260px
;
margin-right
:
var
(
--sidebar-width
);
min-height
:
100vh
;
min-height
:
100vh
;
display
:
flex
;
display
:
flex
;
flex-direction
:
column
;
flex-direction
:
column
;
transition
:
margin-right
0.3s
ease
;
background
:
#F3F4F6
;
}
.main-wrapper.sidebar-collapsed
{
margin-right
:
0
;
}
}
/* ── Header ── */
/* ── Top Header ── */
.main-header
{
.top-header
{
height
:
var
(
--header-height
);
background
:
#fff
;
background
:
#fff
;
border-bottom
:
1px
solid
var
(
--gray-200
);
border-bottom
:
1px
solid
#E5E7EB
;
padding
:
12px
25px
;
display
:
flex
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
justify-content
:
space-between
;
padding
:
0
25px
;
align-items
:
center
;
position
:
sticky
;
position
:
sticky
;
top
:
0
;
top
:
0
;
z-index
:
900
;
z-index
:
100
;
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.05
);
}
}
.main-header
.page-title
{
.header-right
{
display
:
flex
;
align-items
:
center
;
gap
:
15px
;
}
.header-left
{
display
:
flex
;
align-items
:
center
;
gap
:
15px
;
}
.header-title
h1
{
margin
:
0
;
font-size
:
18px
;
font-size
:
18px
;
font-weight
:
700
;
font-weight
:
700
;
color
:
var
(
--dark
)
;
color
:
#1A1A2E
;
}
}
.main-header
.page-actions
{
.header-user
{
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
center
;
gap
:
10px
;
gap
:
10px
;
font-size
:
14px
;
color
:
#4B5563
;
}
}
.main-header
.user-info
{
display
:
flex
;
.header-user-name
{
align-items
:
center
;
font-weight
:
600
;
gap
:
8px
;
font-size
:
13px
;
color
:
var
(
--gray-500
);
}
}
.hamburger-btn
{
display
:
none
;
.header-logout
{
text-decoration
:
none
;
font-size
:
18px
;
padding
:
4px
;
}
.sidebar-toggle-btn
{
background
:
none
;
background
:
none
;
border
:
none
;
border
:
1px
solid
#E5E7EB
;
font-size
:
24px
;
border-radius
:
6px
;
padding
:
6px
10px
;
cursor
:
pointer
;
cursor
:
pointer
;
padding
:
5
px
;
font-size
:
16
px
;
color
:
var
(
--dark
)
;
color
:
#4B5563
;
line-height
:
1
;
display
:
none
;
}
}
/* ──
Main
Content ── */
/* ──
Page
Content ── */
.
main
-content
{
.
page
-content
{
flex
:
1
;
flex
:
1
;
padding
:
25px
;
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 ── */
/* ── Cards ── */
.card
{
.card
{
background
:
#fff
;
background
:
#fff
;
border-radius
:
8px
;
border-radius
:
8px
;
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.06
);
border
:
1px
solid
#E5E7EB
;
border
:
1px
solid
var
(
--gray-200
);
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.04
);
margin-bottom
:
0
;
overflow
:
hidden
;
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 ── */
/* ── Buttons ── */
.btn
{
.btn
{
display
:
inline-flex
;
display
:
inline-flex
;
...
@@ -187,337 +288,193 @@ a:hover { text-decoration: underline; }
...
@@ -187,337 +288,193 @@ a:hover { text-decoration: underline; }
justify-content
:
center
;
justify-content
:
center
;
gap
:
6px
;
gap
:
6px
;
padding
:
8px
16px
;
padding
:
8px
16px
;
font-size
:
13px
;
font-weight
:
600
;
border-radius
:
6px
;
border-radius
:
6px
;
border
:
1px
solid
transparent
;
font-size
:
14px
;
cursor
:
pointer
;
font-weight
:
600
;
transition
:
all
0.15s
;
text-decoration
:
none
;
text-decoration
:
none
;
cursor
:
pointer
;
border
:
1px
solid
transparent
;
transition
:
all
0.2s
ease
;
white-space
:
nowrap
;
white-space
:
nowrap
;
font-family
:
inherit
;
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
{
.btn-primary
:hover
{
background
:
var
(
--primary-dark
);
}
background
:
#0D7377
;
color
:
#fff
;
border-color
:
#0D7377
;
}
.btn-outline
{
background
:
#fff
;
color
:
var
(
--dark
);
border-color
:
var
(
--gray-300
);
}
.btn-primary
:hover
{
.btn-outline
:hover
{
background
:
var
(
--gray-50
);
border-color
:
var
(
--gray-400
);
}
background
:
#0a5c5f
;
}
.btn-outline
{
background
:
transparent
;
color
:
#0D7377
;
border-color
:
#D1D5DB
;
}
.btn-sm
{
padding
:
4px
10px
;
font-size
:
12px
;
}
.btn-outline
:hover
{
.btn-xs
{
padding
:
2px
6px
;
font-size
:
11px
;
}
background
:
#F3F4F6
;
border-color
:
#0D7377
;
}
.btn-sm
{
padding
:
4px
10px
;
font-size
:
12px
;
}
/* ── Forms ── */
/* ── Forms ── */
.form-group
{
margin-bottom
:
0
;
}
.form-group
{
margin-bottom
:
0
;
}
.form-label
{
.form-label
{
display
:
block
;
display
:
block
;
margin-bottom
:
5px
;
font-size
:
13px
;
font-size
:
13px
;
font-weight
:
600
;
font-weight
:
600
;
color
:
var
(
--gray-700
);
color
:
#374151
;
margin-bottom
:
4px
;
}
}
.form-input
,
.form-select
,
.form-textarea
{
.form-input
,
.form-select
,
.form-textarea
{
width
:
100%
;
width
:
100%
;
padding
:
8px
12px
;
padding
:
8px
12px
;
border
:
1px
solid
#D1D5DB
;
border-radius
:
6px
;
font-size
:
14px
;
font-size
:
14px
;
font-family
:
inherit
;
font-family
:
inherit
;
border
:
1px
solid
var
(
--gray-300
);
border-radius
:
6px
;
background
:
#fff
;
background
:
#fff
;
color
:
var
(
--dark
)
;
color
:
#1A1A2E
;
transition
:
border-color
0.
15
s
;
transition
:
border-color
0.
2
s
;
direction
:
rtl
;
box-sizing
:
border-box
;
}
}
.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
);
}
.form-textarea
{
resize
:
vertical
;
min-height
:
60px
;
}
.form-select
{
appearance
:
auto
;
}
/* ── Tables ── */
.form-input
:focus
,
.table-responsive
{
.form-select
:focus
,
overflow-x
:
auto
;
.form-textarea
:focus
{
-webkit-overflow-scrolling
:
touch
;
outline
:
none
;
border-color
:
#0D7377
;
box-shadow
:
0
0
0
3px
rgba
(
13
,
115
,
119
,
0.1
);
}
}
.data-table
{
.form-textarea
{
width
:
100%
;
resize
:
vertical
;
border-collapse
:
collapse
;
min-height
:
80px
;
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
;
}
}
.data-table
tbody
tr
:hover
{
background
:
var
(
--gray-50
);
}
/* ── Alerts ── */
/* ── Alerts ── */
.alert
{
.alert
{
padding
:
12px
16
px
;
padding
:
12px
20
px
;
border-radius
:
6
px
;
border-radius
:
8
px
;
margin
-bottom
:
15px
;
margin
:
0
25px
15px
;
font-size
:
14px
;
font-size
:
14px
;
font-weight
:
500
;
font-weight
:
500
;
display
:
flex
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
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 ── */
.alert-success
{
.pagination-wrapper
{
display
:
flex
;
justify-content
:
center
;
}
background
:
#F0FDF4
;
.pagination
{
color
:
#059669
;
display
:
flex
;
border
:
1px
solid
#BBF7D0
;
gap
:
5px
;
list-style
:
none
;
padding
:
0
;
margin
:
0
;
flex-wrap
:
wrap
;
justify-content
:
center
;
}
}
/* ── Sidebar Overlay (mobile) ── */
.alert-error
{
.sidebar-overlay
{
background
:
#FEF2F2
;
display
:
none
;
color
:
#DC2626
;
position
:
fixed
;
border
:
1px
solid
#FECACA
;
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
;
}
.sidebar-link
{
padding
:
9px
15px
;
font-size
:
12px
;
}
.alert-warning
{
.sidebar-section
{
padding
:
8px
15px
4px
;
}
background
:
#FFF7ED
;
.main-content
{
padding
:
20px
;
}
color
:
#D97706
;
.main-header
{
padding
:
0
20px
;
}
border
:
1px
solid
#FED7AA
;
}
}
/* ══════════════════════════════════════════
.alert-info
{
RESPONSIVE — MOBILE (≤768px)
background
:
#EFF6FF
;
══════════════════════════════════════════ */
color
:
#0284C7
;
@media
(
max-width
:
768px
)
{
border
:
1px
solid
#BFDBFE
;
:root
{
}
--sidebar-width
:
270px
;
--header-height
:
54px
;
.alert-close
{
}
background
:
none
;
border
:
none
;
cursor
:
pointer
;
font-size
:
18px
;
color
:
inherit
;
opacity
:
0.6
;
padding
:
0
5px
;
}
/* Sidebar: off-canvas */
.alert-close
:hover
{
opacity
:
1
;
}
/* ── Responsive ── */
@media
(
max-width
:
1024px
)
{
.sidebar
{
.sidebar
{
transform
:
translateX
(
100%
);
transform
:
translateX
(
260px
);
width
:
260px
;
}
}
.sidebar.
open
{
.sidebar.
show
{
transform
:
translateX
(
0
);
transform
:
translateX
(
0
);
}
}
/* Main wrapper: full width */
.main-wrapper
{
.main-wrapper
{
margin-right
:
0
;
margin-right
:
0
;
}
}
.sidebar-toggle-btn
{
/* Show hamburger */
.hamburger-btn
{
display
:
block
;
display
:
block
;
}
}
}
/* Header */
/* ── Print ── */
.main-header
{
@media
print
{
padding
:
0
15px
;
.sidebar
,
.top-header
,
.page-footer
,
.btn
,
.sidebar-toggle-btn
{
gap
:
10px
;
display
:
none
!important
;
}
.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
;
}
}
.main-wrapper
{
/* ── Full-width grid children on mobile ── */
margin-right
:
0
!important
;
[
style
*=
"grid-column:1/-1"
],
[
style
*=
"grid-column: 1/-1"
]
{
grid-column
:
1
/
-1
!important
;
}
}
.page-content
{
/* ── Print button row ── */
padding
:
0
!important
;
[
style
*=
"display:flex;gap:10px;"
],
[
style
*=
"display: flex; gap: 10px;"
]
{
flex-wrap
:
wrap
!important
;
}
}
}
}
/* ══════════════════════════════════════════
/* ── Body Reset ── */
RESPONSIVE — SMALL MOBILE (≤480px)
*
{
══════════════════════════════════════════ */
box-sizing
:
border-box
;
@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
;
}
.btn
{
padding
:
6px
12px
;
font-size
:
12px
;
}
body
{
.btn-sm
{
padding
:
3px
8px
;
font-size
:
11px
;
}
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 */
a
{
.card
h3
{
font-size
:
15px
!important
;
}
color
:
#0D7377
;
.card
h4
{
font-size
:
14px
!important
;
}
text-decoration
:
none
;
}
/* Large money displays */
a
:hover
{
[
style
*=
"font-size:28px"
]
{
font-size
:
22px
!important
;
}
text-decoration
:
underline
;
[
style
*=
"font-size: 28px"
]
{
font-size
:
22px
!important
;
}
[
style
*=
"font-size:20px"
]
{
font-size
:
16px
!important
;
}
[
style
*=
"font-size: 20px"
]
{
font-size
:
16px
!important
;
}
}
}
/* ══════════════════════════════════════════
code
{
PRINT
background
:
#F3F4F6
;
══════════════════════════════════════════ */
padding
:
2px
6px
;
@media
print
{
border-radius
:
4px
;
.sidebar
,
.main-header
,
.hamburger-btn
,
.sidebar-overlay
{
display
:
none
!important
;
}
font-size
:
12px
;
.main-wrapper
{
margin-right
:
0
!important
;
}
font-family
:
'Courier New'
,
monospace
;
.main-content
{
padding
:
0
!important
;
}
.card
{
box-shadow
:
none
;
border
:
1px
solid
#ddd
;
}
.btn
{
display
:
none
!important
;
}
}
}
\ 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