fix: mobilni responzivita tabulek AuditLog a Users
- admin-table-wrapper -> admin-table-responsive (konzistentni s Projects) - pridany admin-card-body wrapper (padding 18px/12px) - nova CSS trida admin-form-row-5 pro 5-sloupcove filtry s breakpointy - odstranen ::after gradient overlay z admin-table-wrapper - odstraneny inline styly (whiteSpace, gridTemplateColumns) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -571,10 +571,17 @@ img {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
.admin-form-row-5 {
|
||||
grid-template-columns: 1.2fr 1fr 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.admin-form-row-4 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
.admin-form-row-5 {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
@@ -582,10 +589,14 @@ img {
|
||||
.admin-form-row-3 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.admin-form-row-5 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.admin-form-row-4 {
|
||||
.admin-form-row-4,
|
||||
.admin-form-row-5 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@@ -2204,18 +2215,6 @@ img {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.admin-table-wrapper::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 24px;
|
||||
background: linear-gradient(to right, transparent, var(--bg-primary));
|
||||
pointer-events: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.admin-table {
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ export default function AuditLog() {
|
||||
style={{ marginBottom: '1rem' }}
|
||||
>
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-form-row" style={{ gridTemplateColumns: '1.2fr 1fr 1fr 1fr 1fr' }}>
|
||||
<div className="admin-form-row admin-form-row-5">
|
||||
<FormField label="Hledat">
|
||||
<input
|
||||
type="text"
|
||||
@@ -355,69 +355,71 @@ export default function AuditLog() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.15 }}
|
||||
>
|
||||
<div className="admin-table-wrapper">
|
||||
<table className="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Čas</th>
|
||||
<th>Uživatel</th>
|
||||
<th>Akce</th>
|
||||
<th>Typ entity</th>
|
||||
<th>Popis</th>
|
||||
<th>IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{loading && Array.from({ length: 10 }, (_, i) => (
|
||||
<tr key={`skeleton-${i}`}>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '110px', height: '14px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '80px', height: '14px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '70px', height: '22px', borderRadius: '10px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '80px', height: '14px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '60%', height: '14px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '90px', height: '14px' }} /></td>
|
||||
</tr>
|
||||
))}
|
||||
{!loading && logs.length === 0 && (
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-table-responsive">
|
||||
<table className="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colSpan="6">
|
||||
<div className="admin-empty-state">
|
||||
<div className="admin-empty-icon">
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
<line x1="16" y1="13" x2="8" y2="13" />
|
||||
<line x1="16" y1="17" x2="8" y2="17" />
|
||||
</svg>
|
||||
<th>Čas</th>
|
||||
<th>Uživatel</th>
|
||||
<th>Akce</th>
|
||||
<th>Typ entity</th>
|
||||
<th>Popis</th>
|
||||
<th>IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{loading && Array.from({ length: 10 }, (_, i) => (
|
||||
<tr key={`skeleton-${i}`}>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '110px', height: '14px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '80px', height: '14px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '70px', height: '22px', borderRadius: '10px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '80px', height: '14px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '60%', height: '14px' }} /></td>
|
||||
<td><div className="admin-skeleton-line" style={{ width: '90px', height: '14px' }} /></td>
|
||||
</tr>
|
||||
))}
|
||||
{!loading && logs.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan="6">
|
||||
<div className="admin-empty-state">
|
||||
<div className="admin-empty-icon">
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
<line x1="16" y1="13" x2="8" y2="13" />
|
||||
<line x1="16" y1="17" x2="8" y2="17" />
|
||||
</svg>
|
||||
</div>
|
||||
<p>Žádné záznamy k zobrazení</p>
|
||||
</div>
|
||||
<p>Žádné záznamy k zobrazení</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{!loading && logs.map((log) => (
|
||||
<tr key={log.id}>
|
||||
<td className="admin-mono" style={{ whiteSpace: 'nowrap' }}>{formatDatetime(log.created_at)}</td>
|
||||
<td style={{ fontWeight: 500 }}>{log.username || '-'}</td>
|
||||
<td>
|
||||
<span className={`admin-badge ${ACTION_BADGE_CLASS[log.action] || 'admin-badge-secondary'}`}>
|
||||
{ACTION_LABELS[log.action] || log.action}
|
||||
</span>
|
||||
</td>
|
||||
<td>{ENTITY_TYPE_LABELS[log.entity_type] || log.entity_type || '-'}</td>
|
||||
<td>{log.description || '-'}</td>
|
||||
<td className="admin-mono">{log.user_ip || '-'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{!loading && logs.map((log) => (
|
||||
<tr key={log.id}>
|
||||
<td className="admin-mono">{formatDatetime(log.created_at)}</td>
|
||||
<td style={{ fontWeight: 500 }}>{log.username || '-'}</td>
|
||||
<td>
|
||||
<span className={`admin-badge ${ACTION_BADGE_CLASS[log.action] || 'admin-badge-secondary'}`}>
|
||||
{ACTION_LABELS[log.action] || log.action}
|
||||
</span>
|
||||
</td>
|
||||
<td>{ENTITY_TYPE_LABELS[log.entity_type] || log.entity_type || '-'}</td>
|
||||
<td>{log.description || '-'}</td>
|
||||
<td className="admin-mono">{log.user_ip || '-'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<Pagination
|
||||
pagination={pagination}
|
||||
onPageChange={handlePageChange}
|
||||
onPerPageChange={handlePerPageChange}
|
||||
/>
|
||||
<Pagination
|
||||
pagination={pagination}
|
||||
onPageChange={handlePageChange}
|
||||
onPerPageChange={handlePerPageChange}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -254,81 +254,83 @@ export default function Users() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.1 }}
|
||||
>
|
||||
<div className="admin-table-wrapper">
|
||||
<table className="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Uživatel</th>
|
||||
<th>E-mail</th>
|
||||
<th>Role</th>
|
||||
<th>Stav</th>
|
||||
<th>Akce</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.map((user) => (
|
||||
<tr key={user.id}>
|
||||
<td>
|
||||
<div className="admin-table-user">
|
||||
<div className="admin-table-avatar">
|
||||
{(user.first_name || user.username).charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<div>
|
||||
<div className="admin-table-name">
|
||||
{user.first_name} {user.last_name}
|
||||
<div className="admin-card-body">
|
||||
<div className="admin-table-responsive">
|
||||
<table className="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Uživatel</th>
|
||||
<th>E-mail</th>
|
||||
<th>Role</th>
|
||||
<th>Stav</th>
|
||||
<th>Akce</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.map((user) => (
|
||||
<tr key={user.id}>
|
||||
<td>
|
||||
<div className="admin-table-user">
|
||||
<div className="admin-table-avatar">
|
||||
{(user.first_name || user.username).charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<div>
|
||||
<div className="admin-table-name">
|
||||
{user.first_name} {user.last_name}
|
||||
</div>
|
||||
<div className="admin-table-username">@{user.username}</div>
|
||||
</div>
|
||||
<div className="admin-table-username">@{user.username}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>{user.email}</td>
|
||||
<td>
|
||||
<span className={getRoleBadgeClass(user.role_name)}>
|
||||
{user.role_display_name || user.role_name}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
onClick={() => user.id !== currentUser?.id && toggleActive(user)}
|
||||
disabled={user.id === currentUser?.id}
|
||||
className={`admin-badge ${user.is_active ? 'admin-badge-active' : 'admin-badge-inactive'}`}
|
||||
style={{ cursor: user.id === currentUser?.id ? 'not-allowed' : 'pointer' }}
|
||||
>
|
||||
{user.is_active ? 'Aktivní' : 'Neaktivní'}
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<div className="admin-table-actions">
|
||||
</td>
|
||||
<td>{user.email}</td>
|
||||
<td>
|
||||
<span className={getRoleBadgeClass(user.role_name)}>
|
||||
{user.role_display_name || user.role_name}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
onClick={() => openEditModal(user)}
|
||||
className="admin-btn-icon"
|
||||
title="Upravit"
|
||||
aria-label="Upravit"
|
||||
onClick={() => user.id !== currentUser?.id && toggleActive(user)}
|
||||
disabled={user.id === currentUser?.id}
|
||||
className={`admin-badge ${user.is_active ? 'admin-badge-active' : 'admin-badge-inactive'}`}
|
||||
style={{ cursor: user.id === currentUser?.id ? 'not-allowed' : 'pointer' }}
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
||||
</svg>
|
||||
{user.is_active ? 'Aktivní' : 'Neaktivní'}
|
||||
</button>
|
||||
{user.id !== currentUser?.id && (
|
||||
</td>
|
||||
<td>
|
||||
<div className="admin-table-actions">
|
||||
<button
|
||||
onClick={() => openDeleteModal(user)}
|
||||
className="admin-btn-icon danger"
|
||||
title="Smazat"
|
||||
aria-label="Smazat"
|
||||
onClick={() => openEditModal(user)}
|
||||
className="admin-btn-icon"
|
||||
title="Upravit"
|
||||
aria-label="Upravit"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<polyline points="3 6 5 6 21 6" />
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{user.id !== currentUser?.id && (
|
||||
<button
|
||||
onClick={() => openDeleteModal(user)}
|
||||
className="admin-btn-icon danger"
|
||||
title="Smazat"
|
||||
aria-label="Smazat"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<polyline points="3 6 5 6 21 6" />
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user