refactor(requests): настоящий табличный вид с фиксированными колонками
Структура таблицы: - Иконка (48px) - тип заявления - Заголовок (расширяемая, 2fr) - название, тип, превью комментария - Автор (1fr) - имя сотрудника с ссылкой - Даты (100px) - date_from и date_to - Статус (110px) - badge со статусом - Создано (80px) - дата и время создания - Действия (80px) - кнопки одобрить/отклонить/редактировать Особенности: - Grid layout с фиксированными шириной колонок - Автоматический перенос текста в пределах ячейки (word-wrap) - Выравнивание всех элементов по сетке - Футер с комментариями выровнен с содержимым - Адаптивный дизайн (планшеты, мобильные) - На мобильных - две колонки (иконка + остальное)
This commit is contained in:
parent
e1188b985a
commit
2ec6cdfce8
@ -1,13 +1,8 @@
|
||||
/**
|
||||
* request-list.css
|
||||
*
|
||||
* Табличный вид списка заявлений без разделяющих линий
|
||||
* между элементами, но с чистой сеткой как в таблице.
|
||||
*
|
||||
* Используется:
|
||||
* - card-list.css для базовой структуры карточек
|
||||
* - message-inputs.scss для форм комментариев
|
||||
* - common.css для утилит и бейджей
|
||||
* Настоящий табличный вид списка заявлений.
|
||||
* Все поля идут в одну строку с фиксированной шириной и переносом текста.
|
||||
*/
|
||||
|
||||
/* Контейнер списка заявлений */
|
||||
@ -17,7 +12,7 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Строка заявления - табличный вид */
|
||||
/* Строка заявления */
|
||||
#reqList .req-row {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@ -33,41 +28,108 @@
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
/* Заголовок заявления - сетка с выравниванием колонок */
|
||||
#reqList .req-row > .card-header {
|
||||
/* Табличная сетка */
|
||||
#reqList .req-table-row {
|
||||
display: grid;
|
||||
grid-template-columns: 42px 1fr auto;
|
||||
grid-template-columns:
|
||||
48px /* Иконка */
|
||||
minmax(200px, 2fr) /* Заголовок (расширяемая) */
|
||||
minmax(120px, 1fr) /* Автор */
|
||||
100px /* Даты */
|
||||
110px /* Статус */
|
||||
80px /* Создано */
|
||||
80px; /* Действия */
|
||||
gap: 0.75rem;
|
||||
align-items: start;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
/* Ячейка таблицы */
|
||||
#reqList .req-cell {
|
||||
min-width: 0; /* Для корректного word-wrap */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.875rem 1rem;
|
||||
border-bottom: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Иконка типа заявления */
|
||||
#reqList .card-icon {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
flex: 0 0 auto;
|
||||
/* Контент ячейки с переносом текста */
|
||||
#reqList .req-cell-content {
|
||||
width: 100%;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Метаданные заявления */
|
||||
#reqList .card-meta {
|
||||
min-width: 0; /* Для корректного truncate */
|
||||
/* Специфичные стили для ячеек */
|
||||
#reqList .req-cell-icon {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Тело карточки (комментарий) */
|
||||
#reqList .req-row > .card-body {
|
||||
padding: 0 1rem 0.875rem 1rem;
|
||||
margin-left: calc(42px + 1rem); /* Выравнивание с текстом */
|
||||
border-bottom: none;
|
||||
#reqList .req-cell-icon .card-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Футер с кнопками действий (комментарии, вложения) */
|
||||
#reqList .req-row > .card-actions {
|
||||
#reqList .req-cell-title {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#reqList .req-cell-title strong {
|
||||
display: block;
|
||||
margin-bottom: 0.125rem;
|
||||
}
|
||||
|
||||
#reqList .req-cell-title .badge {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.2rem 0.4rem;
|
||||
}
|
||||
|
||||
#reqList .req-cell-author {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
#reqList .req-cell-dates {
|
||||
font-size: 0.8rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
#reqList .req-cell-status {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
#reqList .req-cell-status .badge {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#reqList .req-cell-created {
|
||||
font-size: 0.75rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#reqList .req-cell-actions {
|
||||
justify-content: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
#reqList .req-cell-actions .req-cell-content {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Футер с комментариями и вложениями */
|
||||
#reqList .req-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
margin-left: calc(42px + 1rem); /* Выравнивание с текстом */
|
||||
border-top: none; /* Убираем разделяющую линию */
|
||||
margin-left: calc(48px + 0.75rem); /* Выравнивание с содержимым */
|
||||
border-top: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@ -77,67 +139,103 @@
|
||||
background: color-mix(in srgb, var(--bs-tertiary-bg), transparent 50%);
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
margin-left: calc(48px + 0.75rem); /* Выравнивание с содержимым */
|
||||
}
|
||||
|
||||
/* Отступ для списка комментариев */
|
||||
#reqList .comments-list {
|
||||
margin-left: calc(42px + 1rem); /* Выравнивание с текстом заявления */
|
||||
/* Адаптивность для планшетов */
|
||||
@media (max-width: 992px) {
|
||||
#reqList .req-table-row {
|
||||
grid-template-columns:
|
||||
42px /* Иконка */
|
||||
minmax(150px, 2fr) /* Заголовок */
|
||||
minmax(100px, 1fr) /* Автор */
|
||||
90px /* Даты */
|
||||
100px /* Статус */
|
||||
70px; /* Создано + Действия объединены */
|
||||
}
|
||||
|
||||
/* Прячем отдельную колонку действий */
|
||||
#reqList .req-cell-actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Добавляем действия в строку заголовка */
|
||||
#reqList .req-cell-title .req-cell-content::after {
|
||||
content: '';
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Форма нового комментария */
|
||||
#reqList .comment-new {
|
||||
margin-left: calc(42px + 1rem); /* Выравнивание с текстом заявления */
|
||||
}
|
||||
|
||||
/* Адаптивность для мобильных устройств */
|
||||
/* Адаптивность для мобильных */
|
||||
@media (max-width: 768px) {
|
||||
#reqList .req-row > .card-header {
|
||||
#reqList .req-table-row {
|
||||
grid-template-columns: 36px 1fr;
|
||||
gap: 0.75rem;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
#reqList .card-icon {
|
||||
/* Все ячейки в две колонки */
|
||||
#reqList .req-cell-icon {
|
||||
grid-row: 1 / span 3;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
#reqList .req-cell-icon .card-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
#reqList .req-row > .card-body {
|
||||
padding: 0 0.75rem 0.75rem 0.75rem;
|
||||
margin-left: calc(36px + 0.75rem);
|
||||
}
|
||||
|
||||
#reqList .req-row > .card-actions {
|
||||
padding: 0.5rem 0.75rem;
|
||||
margin-left: calc(36px + 0.75rem);
|
||||
}
|
||||
|
||||
#reqList .comments-list,
|
||||
#reqList .comment-new {
|
||||
margin-left: 0; /* На мобильных убираем отступ */
|
||||
}
|
||||
|
||||
/* Кнопки действий переносятся в следующую строку */
|
||||
#reqList .req-row > .card-header {
|
||||
grid-template-columns: 36px 1fr;
|
||||
}
|
||||
|
||||
#reqList .req-row > .card-header > .ms-auto {
|
||||
#reqList .req-cell-title {
|
||||
grid-column: 2;
|
||||
margin-top: 0.5rem;
|
||||
margin-left: 0 !important;
|
||||
justify-self: start;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#reqList .req-cell-author {
|
||||
grid-column: 2;
|
||||
font-size: 0.8rem;
|
||||
margin-top: -0.25rem;
|
||||
}
|
||||
|
||||
#reqList .req-cell-dates,
|
||||
#reqList .req-cell-status,
|
||||
#reqList .req-cell-created {
|
||||
grid-column: 2;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
#reqList .req-cell-actions {
|
||||
grid-column: 2;
|
||||
justify-content: flex-start;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
#reqList .req-footer {
|
||||
margin-left: calc(36px + 0.5rem);
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
#reqList .comments-block {
|
||||
margin-left: 0;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Дополнительные утилиты */
|
||||
#reqList .card-title {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
#reqList .card-subtitle {
|
||||
font-size: 0.85rem;
|
||||
color: var(--bs-secondary-color);
|
||||
/* Компактный режим для маленьких экранов */
|
||||
@media (max-width: 576px) {
|
||||
#reqList .req-table-row {
|
||||
padding: 0.625rem;
|
||||
}
|
||||
|
||||
#reqList .req-cell-title .small {
|
||||
display: none; /* Скрываем превью комментария */
|
||||
}
|
||||
|
||||
#reqList .req-cell-dates br {
|
||||
display: none; /* Даты в одну строку */
|
||||
}
|
||||
|
||||
#reqList .req-cell-dates .req-cell-content::after {
|
||||
content: ' — ';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,28 +26,27 @@
|
||||
{% if requests %}
|
||||
{% for obj in requests %}
|
||||
<article class="req-row" id="req-{{ obj.id }}">
|
||||
<header class="card-header">
|
||||
<!-- Иконка типа заявления -->
|
||||
<div class="card-icon d-flex align-items-center justify-content-center">
|
||||
{% with tp=obj.type|lower %}
|
||||
{% if tp == 'vacation' %}<i class="bi-calendar-check text-primary"></i>
|
||||
{% elif tp == 'sick_leave' %}<i class="bi-capsule text-danger"></i>
|
||||
{% elif tp == 'transfer' %}<i class="bi-box-arrow-right text-info"></i>
|
||||
{% elif tp == 'dismissal' %}<i class="bi-door-open text-warning"></i>
|
||||
{% else %}<i class="bi-clipboard-check text-secondary"></i>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<div class="req-table-row">
|
||||
<!-- Иконка типа -->
|
||||
<div class="req-cell req-cell-icon">
|
||||
<div class="card-icon d-flex align-items-center justify-content-center">
|
||||
{% with tp=obj.type|lower %}
|
||||
{% if tp == 'vacation' %}<i class="bi-calendar-check text-primary"></i>
|
||||
{% elif tp == 'sick_leave' %}<i class="bi-capsule text-danger"></i>
|
||||
{% elif tp == 'transfer' %}<i class="bi-box-arrow-right text-info"></i>
|
||||
{% elif tp == 'dismissal' %}<i class="bi-door-open text-warning"></i>
|
||||
{% else %}<i class="bi-clipboard-check text-secondary"></i>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Основная информация -->
|
||||
<div class="card-meta">
|
||||
<div class="card-title d-flex align-items-center gap-2 flex-wrap">
|
||||
<!-- Заголовок заявления -->
|
||||
<!-- Заголовок и тип -->
|
||||
<div class="req-cell req-cell-title">
|
||||
<div class="req-cell-content">
|
||||
<strong>{{ obj.title|default:"Без заголовка" }}</strong>
|
||||
|
||||
<!-- Тип заявления -->
|
||||
{% if obj.type %}{% with tp=obj.type|lower %}
|
||||
<span class="badge text-bg-light">
|
||||
<span class="badge text-bg-light ms-1">
|
||||
{% if tp == 'vacation' %}Отпуск
|
||||
{% elif tp == 'sick_leave' %}Больничный
|
||||
{% elif tp == 'transfer' %}Перевод
|
||||
@ -56,29 +55,15 @@
|
||||
{% else %}{{ obj.type }}{% endif %}
|
||||
</span>
|
||||
{% endwith %}{% endif %}
|
||||
|
||||
<!-- Статус -->
|
||||
{% if obj.status %}{% with st=obj.status|lower %}
|
||||
<span class="badge
|
||||
{% if st == 'approved' %}text-bg-success
|
||||
{% elif st == 'rejected' %}text-bg-danger
|
||||
{% elif st == 'pending' %}text-bg-warning
|
||||
{% elif st == 'cancelled' %}text-bg-secondary
|
||||
{% elif st == 'draft' %}text-bg-info
|
||||
{% else %}text-bg-light{% endif %}">
|
||||
{% if st == 'draft' %}Черновик
|
||||
{% elif st == 'pending' %}На рассмотрении
|
||||
{% elif st == 'approved' %}Одобрено
|
||||
{% elif st == 'rejected' %}Отклонено
|
||||
{% elif st == 'cancelled' %}Отменено
|
||||
{% else %}{{ obj.status }}{% endif %}
|
||||
</span>
|
||||
{% endwith %}{% endif %}
|
||||
{% if obj.comment %}
|
||||
<div class="small text-body-secondary mt-1">{{ obj.comment|truncatewords:15 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Дополнительная информация -->
|
||||
<div class="card-subtitle">
|
||||
<!-- Автор -->
|
||||
</div>
|
||||
|
||||
<!-- Автор -->
|
||||
<div class="req-cell req-cell-author">
|
||||
<div class="req-cell-content">
|
||||
{% if obj.employee %}
|
||||
{% if obj.employee.id == user.id %}
|
||||
<a href="{% url 'employees:profile' %}" class="text-decoration-none">Вы</a>
|
||||
@ -88,67 +73,90 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Период -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Период -->
|
||||
<div class="req-cell req-cell-dates">
|
||||
<div class="req-cell-content text-mono">
|
||||
{% if obj.date_from or obj.date_to %}
|
||||
<span>·</span>
|
||||
<span class="text-mono">
|
||||
{{ obj.date_from|date:"d.m.Y"|default:"—" }} → {{ obj.date_to|date:"d.m.Y"|default:"—" }}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
<!-- Дата создания -->
|
||||
{% if obj.created_at %}
|
||||
<span>·</span>
|
||||
<span>{{ obj.created_at|date:"d.m.Y H:i" }}</span>
|
||||
{{ obj.date_from|date:"d.m.Y"|default:"—" }}<br>
|
||||
{{ obj.date_to|date:"d.m.Y"|default:"—" }}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Кнопки действий -->
|
||||
{% if obj.employee.id != user.id and can_process %}
|
||||
<div class="ms-auto d-flex gap-1">
|
||||
<button type="button" class="btn btn-ghost btn-sm text-success"
|
||||
data-bs-toggle="modal" data-bs-target="#reqApproveModal"
|
||||
data-id="{{ obj.id }}" title="Одобрить">
|
||||
<i class="bi-hand-thumbs-up"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-sm text-danger"
|
||||
data-bs-toggle="modal" data-bs-target="#reqRejectModal"
|
||||
data-id="{{ obj.id }}" title="Отклонить">
|
||||
<i class="bi-hand-thumbs-down"></i>
|
||||
</button>
|
||||
<!-- Статус -->
|
||||
<div class="req-cell req-cell-status">
|
||||
<div class="req-cell-content">
|
||||
{% if obj.status %}{% with st=obj.status|lower %}
|
||||
<span class="badge
|
||||
{% if st == 'approved' %}text-bg-success
|
||||
{% elif st == 'rejected' %}text-bg-danger
|
||||
{% elif st == 'pending' %}text-bg-warning
|
||||
{% elif st == 'cancelled' %}text-bg-secondary
|
||||
{% elif st == 'draft' %}text-bg-info
|
||||
{% else %}text-bg-light{% endif %}">
|
||||
{% if st == 'draft' %}Черновик
|
||||
{% elif st == 'pending' %}На рассмотр.
|
||||
{% elif st == 'approved' %}Одобрено
|
||||
{% elif st == 'rejected' %}Отклонено
|
||||
{% elif st == 'cancelled' %}Отменено
|
||||
{% else %}{{ obj.status }}{% endif %}
|
||||
</span>
|
||||
{% endwith %}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% elif obj.employee.id == user.id %}
|
||||
<div class="ms-auto d-flex gap-1">
|
||||
<button type="button" class="btn btn-ghost btn-sm text-primary"
|
||||
data-bs-toggle="modal" data-bs-target="#reqEditModal"
|
||||
data-id="{{ obj.id }}"
|
||||
data-title="{{ obj.title|default_if_none:'' }}"
|
||||
data-type="{{ obj.type|default_if_none:'' }}"
|
||||
data-date_from="{{ obj.date_from|default_if_none:'' }}"
|
||||
data-date_to="{{ obj.date_to|default_if_none:'' }}"
|
||||
data-comment="{{ obj.comment|default_if_none:'' }}"
|
||||
data-status="{{ obj.status|lower }}" title="Изменить">
|
||||
<i class="bi-pencil"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-sm text-secondary"
|
||||
data-bs-toggle="modal" data-bs-target="#reqCancelModal"
|
||||
data-id="{{ obj.id }}" title="Отменить">
|
||||
<i class="bi-slash-circle"></i>
|
||||
</button>
|
||||
|
||||
<!-- Дата создания -->
|
||||
<div class="req-cell req-cell-created">
|
||||
<div class="req-cell-content small text-body-secondary">
|
||||
{{ obj.created_at|date:"d.m.Y" }}<br>
|
||||
{{ obj.created_at|date:"H:i" }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<!-- Действия -->
|
||||
<div class="req-cell req-cell-actions">
|
||||
<div class="req-cell-content">
|
||||
{% if obj.employee.id != user.id and can_process %}
|
||||
<button type="button" class="btn btn-ghost btn-sm text-success"
|
||||
data-bs-toggle="modal" data-bs-target="#reqApproveModal"
|
||||
data-id="{{ obj.id }}" title="Одобрить">
|
||||
<i class="bi-hand-thumbs-up"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-sm text-danger"
|
||||
data-bs-toggle="modal" data-bs-target="#reqRejectModal"
|
||||
data-id="{{ obj.id }}" title="Отклонить">
|
||||
<i class="bi-hand-thumbs-down"></i>
|
||||
</button>
|
||||
{% elif obj.employee.id == user.id %}
|
||||
<button type="button" class="btn btn-ghost btn-sm text-primary"
|
||||
data-bs-toggle="modal" data-bs-target="#reqEditModal"
|
||||
data-id="{{ obj.id }}"
|
||||
data-title="{{ obj.title|default_if_none:'' }}"
|
||||
data-type="{{ obj.type|default_if_none:'' }}"
|
||||
data-date_from="{{ obj.date_from|default_if_none:'' }}"
|
||||
data-date_to="{{ obj.date_to|default_if_none:'' }}"
|
||||
data-comment="{{ obj.comment|default_if_none:'' }}"
|
||||
data-status="{{ obj.status|lower }}" title="Изменить">
|
||||
<i class="bi-pencil"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-sm text-secondary"
|
||||
data-bs-toggle="modal" data-bs-target="#reqCancelModal"
|
||||
data-id="{{ obj.id }}" title="Отменить">
|
||||
<i class="bi-slash-circle"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if obj.comment %}
|
||||
<div class="card-body">
|
||||
<div class="small text-body-secondary">{{ obj.comment }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<footer class="card-actions">
|
||||
<button class="btn btn-ghost d-inline-flex align-items-center gap-1"
|
||||
<!-- Футер с комментариями -->
|
||||
<footer class="req-footer">
|
||||
<button class="btn btn-ghost btn-sm d-inline-flex align-items-center gap-1"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#rcoll-{{ obj.id }}"
|
||||
@ -159,7 +167,7 @@
|
||||
<span class="txt-close">Скрыть</span>
|
||||
</button>
|
||||
{% if obj.attachment %}
|
||||
<a href="{{ obj.attachment }}" class="btn btn-ghost d-inline-flex align-items-center gap-1" target="_blank">
|
||||
<a href="{{ obj.attachment }}" class="btn btn-ghost btn-sm d-inline-flex align-items-center gap-1" target="_blank">
|
||||
<i class="bi-paperclip"></i><span>Вложение</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user