Files
ACORE/templates/partials/hr-requests-panel.html
T
lokses ac0418083b hiring: вакансии и найм, HR-портал
Модель вакансий, флоу рекрутинга и панели HR-портала (заявки/вакансии).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 18:03:32 +03:00

218 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% load i18n %}
<div id="hr-panel-requests" class="hr-panel"{% if hr_active_tab|default:'employees' != 'requests' %} style="display:none;"{% endif %}>
{% if hr_request_created %}
<div style="margin-bottom:1rem; padding:0.875rem 1rem; border-radius:0.75rem; border:1px solid hsl(152 40% 70%); background:hsl(152 45% 96%); color:var(--color-foreground); font-size:0.875rem; display:none;" aria-hidden="true">
{% trans "Заявку подано" %}: <strong>{{ hr_request_created.summary }}</strong>
</div>
{% endif %}
{% if hr_request_errors %}
<div style="margin-bottom:1rem; padding:0.75rem 1rem; border-radius:0.5rem; background:hsl(0 70% 96%); border:1px solid hsl(0 70% 85%); color:hsl(0 55% 35%); font-size:0.8125rem; display:none;" aria-hidden="true">
{% for err in hr_request_errors %}<div>{{ err }}</div>{% endfor %}
</div>
{% endif %}
<div style="display:grid; grid-template-columns:repeat(auto-fit,minmax(9rem,1fr)); gap:1rem; margin-bottom:1.25rem;">
{% for value, label in hr_request_types %}
<button type="button" onclick="hrShowRequestForm('{{ value }}', '{{ label|escapejs }}')"
class="hr-request-type-btn" data-request-type="{{ value }}" data-request-label="{{ label }}"
style="background:var(--color-card); border-radius:0.75rem; border:1px solid var(--color-border); padding:1.25rem; text-align:center; cursor:pointer;">
<div style="width:2.5rem; height:2.5rem; border-radius:0.625rem; background:hsl(205 80% 55% / 0.12); color:hsl(205 80% 45%); display:flex; align-items:center; justify-content:center; margin:0 auto 0.75rem;">
{% if value == 'substitution' %}
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
{% else %}
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>
{% endif %}
</div>
<div style="font-size:0.875rem; font-weight:500; color:var(--color-foreground);">{{ label }}</div>
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.25rem;">
{% if value == 'substitution' %}{% trans "Открыть запрос" %}{% else %}{% trans "Подать заявку" %}{% endif %}
</div>
</button>
{% empty %}
<div style="grid-column:1 / -1; padding:1rem; color:var(--color-muted-foreground); font-size:0.8125rem;">{% trans "Нет доступных типов заявок" %}</div>
{% endfor %}
</div>
<div id="hr-request-form-wrap" style="{% if hr_request_form_open %}display:block;{% else %}display:none;{% endif %} margin-bottom:1.25rem;">
<form hx-post="{% url 'hr_request_create' %}" hx-target="#main-content" hx-indicator="#loader" novalidate
style="background:var(--color-card); border-radius:0.75rem; border:1px solid var(--color-border); box-shadow:0 1px 2px rgb(0 0 0/0.05); overflow:hidden;">
{% csrf_token %}
<div style="padding:0.875rem 1.25rem; border-bottom:1px solid var(--color-border); display:flex; justify-content:space-between; align-items:center; gap:0.5rem;">
<span style="font-size:0.8125rem; font-weight:500; color:var(--color-foreground);"><span id="hr-request-type-label">{% if hr_request_form.type %}{% for value, label in hr_request_types %}{% if hr_request_form.type == value %}{{ label }}{% endif %}{% endfor %}{% else %}{% trans "Новая заявка" %}{% endif %}</span></span>
<button type="button" class="btn btn-ghost" style="font-size:0.75rem;" onclick="hrHideRequestForm()">{% trans "Закрыть" %}</button>
</div>
<input type="hidden" name="request_type" id="hr-request-type" value="{{ hr_request_form.type|default:hr_request_types.0.0|default:'vacation' }}">
<div style="padding:1.25rem; display:grid; grid-template-columns:repeat(auto-fit, minmax(10rem, 1fr)); gap:0.75rem; align-items:end;">
<div id="hr-request-dates-wrap">
<label style="display:block; font-size:0.8125rem; font-weight:500; margin-bottom:0.375rem;">{% trans "Дата с" %}</label>
<input type="date" name="date_from" id="hr-request-date-from" class="input" style="width:100%;" value="{{ hr_request_form.date_from|default:'' }}">
</div>
<div id="hr-request-date-to-wrap">
<label style="display:block; font-size:0.8125rem; font-weight:500; margin-bottom:0.375rem;">{% trans "Дата по" %}</label>
<input type="date" name="date_to" id="hr-request-date-to" class="input" style="width:100%;" value="{{ hr_request_form.date_to|default:'' }}">
</div>
<div style="grid-column:1 / -1;">
<label style="display:block; font-size:0.8125rem; font-weight:500; margin-bottom:0.375rem;">{% trans "Комментарий" %}</label>
<textarea name="note" rows="2" class="input" style="width:100%; resize:vertical;" placeholder="{% if hr_request_form.type == 'substitution' %}{% trans 'Например: смена, причина' %}{% else %}{% trans 'Необязательно' %}{% endif %}">{{ hr_request_form.note|default:'' }}</textarea>
</div>
</div>
<div style="padding:0.875rem 1.25rem; border-top:1px solid var(--color-border); display:flex; justify-content:flex-end;">
<button type="submit" class="btn btn-primary">{% trans "Отправить" %}</button>
</div>
</form>
</div>
{% if incoming_substitutions %}
<div style="margin-bottom:1.25rem; background:var(--color-card); border-radius:0.75rem; border:1px solid var(--color-border); overflow:hidden;">
<div style="padding:0.875rem 1.25rem; border-bottom:1px solid var(--color-border);">
<span style="font-size:0.8125rem; font-weight:600; color:var(--color-foreground);">{% trans "Открытые запросы на подмену" %} ({{ incoming_substitutions|length }})</span>
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.25rem;">{% trans "Подтвердите, если можете выйти на подмену" %}</div>
</div>
{% for req in incoming_substitutions %}
<div style="padding:0.875rem 1.25rem; border-bottom:1px solid var(--color-border); display:flex; align-items:center; justify-content:space-between; gap:0.75rem; flex-wrap:wrap;" class="li-hover">
<div style="min-width:0;">
<div style="font-size:0.8125rem; font-weight:500; color:var(--color-foreground);">
{{ req.employee.last_name }} {{ req.employee.first_name }} — {{ req.summary }}
</div>
{% if req.pharmacy_place %}
<div style="font-size:0.75rem; color:var(--color-foreground); margin-top:0.2rem;">{{ req.pharmacy_place }}</div>
{% endif %}
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.125rem;">
{% trans "Запрос" %} {{ req.created_at|date:"d.m.Y H:i" }}
{% if req.note %} · {{ req.note }}{% endif %}
</div>
</div>
<div style="display:flex; gap:0.5rem; flex-shrink:0;">
<form hx-post="{% url 'hr_substitution_confirm' req.pk %}" hx-target="#main-content" hx-indicator="#loader" style="margin:0;">
{% csrf_token %}
<button type="submit" class="btn btn-primary" style="font-size:0.75rem;">{% trans "Взять подмену" %}</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<div style="display:grid; grid-template-columns:1fr 1fr; gap:1.25rem;">
<div style="background:var(--color-card); border-radius:0.75rem; border:1px solid var(--color-border); box-shadow:0 1px 2px rgb(0 0 0/0.05); overflow:hidden;">
<div style="padding:0.875rem 1.25rem; border-bottom:1px solid var(--color-border); display:flex; align-items:center; justify-content:space-between;">
<span style="font-size:0.8125rem; font-weight:500; color:var(--color-foreground);">{% trans "Мои заявки" %}</span>
</div>
<div>
{% for req in my_hr_requests %}
<div style="padding:0.875rem 1.25rem; border-bottom:1px solid var(--color-border); display:flex; align-items:center; justify-content:space-between; gap:0.75rem;" class="li-hover">
<div style="min-width:0;">
<div style="font-size:0.8125rem; font-weight:500; color:var(--color-foreground);">{{ req.summary }}</div>
{% if req.pharmacy_place %}
<div style="font-size:0.75rem; color:var(--color-foreground); margin-top:0.2rem;">{{ req.pharmacy_place }}</div>
{% endif %}
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.125rem;">
{% trans "Подано" %} {{ req.created_at|date:"d.m.Y" }}
{% if req.day_count %} · {{ req.day_count }} {% trans "дн." %}{% endif %}
{% if req.request_type == 'substitution' and req.substitute %}
· {% trans "Подмена" %}: {{ req.substitute.last_name }} {{ req.substitute.first_name }}
{% endif %}
</div>
</div>
<div style="display:flex; align-items:center; gap:0.5rem; flex-shrink:0;">
{% if req.status == 'pending' %}
<span class="badge badge-warning">{{ req.status_label }}</span>
<form hx-post="/hr-requests/{{ req.pk }}/cancel/" hx-target="#main-content" hx-indicator="#loader" style="margin:0;">
{% csrf_token %}
<button type="submit" class="btn btn-ghost" style="font-size:0.6875rem; padding:0.2rem 0.4rem;">{% trans "Отменить" %}</button>
</form>
{% elif req.status == 'approved' %}
<span class="badge badge-success">{{ req.status_label }}</span>
{% elif req.status == 'rejected' %}
<span class="badge badge-danger">{{ req.status_label }}</span>
{% else %}
<span class="badge badge-muted">{{ req.status_label }}</span>
{% endif %}
</div>
</div>
{% empty %}
<div style="padding:1.5rem; text-align:center; color:var(--color-muted-foreground); font-size:0.8125rem;">{% trans "Заявок пока нет" %}</div>
{% endfor %}
</div>
</div>
<div style="background:var(--color-card); border-radius:0.75rem; border:1px solid var(--color-border); box-shadow:0 1px 2px rgb(0 0 0/0.05); overflow:hidden;">
<div style="padding:0.875rem 1.25rem; border-bottom:1px solid var(--color-border);">
<span style="font-size:0.8125rem; font-weight:500; color:var(--color-foreground);">{% trans "Баланс отпусков" %} — {{ leave_balance_year }}</span>
</div>
<div style="padding:1.25rem; display:flex; flex-direction:column; gap:1rem;">
{% for row in leave_balance %}
<div>
<div style="display:flex; justify-content:space-between; margin-bottom:0.375rem;">
<span style="font-size:0.8125rem; color:var(--color-foreground);">{{ row.label }}</span>
<span style="font-size:0.8125rem; font-weight:600; color:var(--color-foreground);">{{ row.used }} / {{ row.entitlement }} {% trans "дн." %}</span>
</div>
<div class="progress"><div class="progress-bar" style="width:{{ row.used_pct }}%;{% if row.key == 'sick' %} background:hsl(38 80% 45%);{% endif %}"></div></div>
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.25rem;">{% trans "Остаток" %}: {{ row.remaining }} {% trans "дн." %}{% if row.pending %} · {% trans "ожидает" %}: {{ row.pending }}{% endif %}</div>
</div>
{% endfor %}
{% if next_vacation_request %}
<div style="padding:0.875rem 1rem; background:var(--color-muted); border-radius:0.625rem; border:1px solid var(--color-border); margin-top:0.25rem;">
<div style="font-size:0.75rem; font-weight:600; color:var(--color-foreground); margin-bottom:0.5rem;">{% trans "Следующий отпуск" %}</div>
<div style="display:flex; justify-content:space-between; font-size:0.8125rem; gap:0.5rem;">
<span style="color:var(--color-muted-foreground);">{{ next_vacation_request.summary }}</span>
<span class="badge badge-warning">{{ next_vacation_request.get_status_display }}</span>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% if can_decide_hr_requests and hr_pending_requests %}
<div style="margin-top:1.5rem; background:var(--color-card); border-radius:0.75rem; border:1px solid var(--color-border); overflow:hidden;">
<div style="padding:0.875rem 1.25rem; border-bottom:1px solid var(--color-border);">
<span style="font-size:0.8125rem; font-weight:600; color:var(--color-foreground);">{% trans "Заявки на рассмотрении" %} ({{ hr_pending_requests|length }})</span>
</div>
<div style="overflow-x:auto;">
<table style="width:100%; border-collapse:collapse; font-size:0.875rem;">
<thead>
<tr style="border-bottom:1px solid var(--color-border);">
<th style="padding:0.75rem 1rem; text-align:left; font-size:0.8125rem; color:var(--color-muted-foreground);">{% trans "Сотрудник" %}</th>
<th style="padding:0.75rem 1rem; text-align:left; font-size:0.8125rem; color:var(--color-muted-foreground);">{% trans "Заявка" %}</th>
<th style="padding:0.75rem 1rem; text-align:left; font-size:0.8125rem; color:var(--color-muted-foreground);">{% trans "Действия" %}</th>
</tr>
</thead>
<tbody>
{% for req in hr_pending_requests %}
<tr style="border-bottom:1px solid var(--color-border); vertical-align:top;">
<td style="padding:0.75rem 1rem;">
<div style="font-weight:500;">{{ req.employee.last_name }} {{ req.employee.first_name }}</div>
{% if req.pharmacy_place %}
<div style="font-size:0.75rem; color:var(--color-foreground); margin-top:0.2rem;">{{ req.pharmacy_place }}</div>
{% endif %}
<div style="font-size:0.75rem; color:var(--color-muted-foreground);">{{ req.created_at|date:"d.m.Y H:i" }}</div>
</td>
<td style="padding:0.75rem 1rem; color:var(--color-muted-foreground);">
{{ req.summary }}
{% if req.note %}<div style="font-size:0.75rem; margin-top:0.25rem;">{{ req.note }}</div>{% endif %}
</td>
<td style="padding:0.75rem 1rem; min-width:14rem;">
<div style="display:flex; flex-direction:column; gap:0.375rem;">
<form hx-post="/hr-requests/{{ req.pk }}/approve/" hx-target="#main-content" hx-indicator="#loader">
{% csrf_token %}
<button type="submit" class="btn btn-secondary" style="width:100%; font-size:0.75rem; color:hsl(152 55% 30%);">{% trans "Утвердить" %}</button>
</form>
<form hx-post="/hr-requests/{{ req.pk }}/reject/" hx-target="#main-content" hx-indicator="#loader" style="display:flex; gap:0.375rem;">
{% csrf_token %}
<input type="text" name="decision_note" placeholder="{% trans 'Причина' %}" class="input" style="font-size:0.75rem; padding:0.3rem 0.5rem; flex:1;">
<button type="submit" class="btn btn-secondary" style="font-size:0.75rem; color:var(--color-destructive); white-space:nowrap;">{% trans "Отклонить" %}</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>