Initial commit: InviteMe event registration app
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Аналитика — InviteMe{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="mb-4">Аналитика мероприятия</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-info mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Всего гостей</h5>
|
||||
<p class="card-text display-6">{{ total }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-success mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Пришло</h5>
|
||||
<p class="card-text display-6">{{ checked_in }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-secondary mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Осталось</h5>
|
||||
<p class="card-text display-6">{{ not_checked_in }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Посещаемость</h5>
|
||||
<p class="card-text display-6">{{ attendance_percent }}%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Быстрый поиск</h5>
|
||||
<form action="{{ url_for('main.admin_guests') }}" method="GET" class="d-flex">
|
||||
<input type="text" name="q" class="form-control me-2" placeholder="Фамилия или ФИО" required>
|
||||
<button type="submit" class="btn btn-primary">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Действия</h5>
|
||||
<a href="{{ url_for('main.admin_report') }}" class="btn btn-success">Выгрузить отчёт Excel</a>
|
||||
<form action="{{ url_for('main.admin_send_invitations') }}" method="POST" class="d-inline">
|
||||
<button type="submit" class="btn btn-outline-primary">Разослать приглашения</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-5">Последние входы</h3>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Время</th>
|
||||
<th>ФИО</th>
|
||||
<th>Отдел</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for check_in in recent_check_ins %}
|
||||
<tr>
|
||||
<td>{{ check_in.checked_in_at.strftime('%d.%m.%Y %H:%M:%S') }}</td>
|
||||
<td>{{ check_in.guest.full_name }}</td>
|
||||
<td>{{ check_in.guest.department }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="3" class="text-muted">Пока никто не отмечен</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,46 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ 'Редактировать' if guest else 'Добавить' }} гостя — InviteMe{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="mb-4">{{ 'Редактировать' if guest else 'Добавить' }} гостя</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="full_name" class="form-label">ФИО</label>
|
||||
<input type="text" class="form-control" id="full_name" name="full_name" value="{{ guest.full_name if guest else '' }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="department" class="form-label">Отдел</label>
|
||||
<input type="text" class="form-control" id="department" name="department" value="{{ guest.department if guest else '' }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="position" class="form-label">Должность</label>
|
||||
<input type="text" class="form-control" id="position" name="position" value="{{ guest.position if guest else '' }}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="{{ guest.email if guest else '' }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="photo_filename" class="form-label">Название фото</label>
|
||||
<input type="text" class="form-control" id="photo_filename" name="photo_filename" value="{{ guest.photo_filename if guest else '' }}">
|
||||
</div>
|
||||
{% if not guest %}
|
||||
<div class="mb-3">
|
||||
<label for="base_url" class="form-label">Базовый URL для QR-кода (опционально)</label>
|
||||
<input type="url" class="form-control" id="base_url" name="base_url" placeholder="https://events.company.com">
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="send_invitation" name="send_invitation" checked>
|
||||
<label class="form-check-label" for="send_invitation">Отправить приглашение сразу</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||||
<a href="{{ url_for('main.admin_guests') }}" class="btn btn-secondary">Отмена</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,78 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Гости — InviteMe{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>Гости</h1>
|
||||
<a href="{{ url_for('main.admin_guest_add') }}" class="btn btn-primary">Добавить гостя</a>
|
||||
</div>
|
||||
|
||||
<form method="GET" class="row g-3 mb-4">
|
||||
<div class="col-auto">
|
||||
<input type="text" name="q" class="form-control" placeholder="Поиск по ФИО" value="{{ search }}">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-outline-primary">Найти</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ФИО</th>
|
||||
<th>Отдел</th>
|
||||
<th>Должность</th>
|
||||
<th>Email</th>
|
||||
<th>Фото</th>
|
||||
<th>QR</th>
|
||||
<th>Статус</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for guest in guests %}
|
||||
<tr class="{{ 'table-secondary' if not guest.is_active }}">
|
||||
<td>{{ guest.full_name }}</td>
|
||||
<td>{{ guest.department }}</td>
|
||||
<td>{{ guest.position or '-' }}</td>
|
||||
<td>{{ guest.email }}</td>
|
||||
<td>
|
||||
{% if guest.photo_filename %}
|
||||
<span class="badge bg-success">Есть</span>
|
||||
{% else %}
|
||||
<span class="badge bg-warning text-dark">Нет</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if guest.qr_filename %}
|
||||
<span class="badge bg-success">Да</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Нет</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if guest.is_checked_in %}
|
||||
<span class="badge bg-success">Пришёл</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Не пришёл</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url_for('main.admin_guest_edit', guest_id=guest.id) }}" class="btn btn-sm btn-outline-primary">Изменить</a>
|
||||
<form action="{{ url_for('main.admin_guest_resend', guest_id=guest.id) }}" method="POST" class="d-inline">
|
||||
<button type="submit" class="btn btn-sm btn-outline-success">QR</button>
|
||||
</form>
|
||||
<form action="{{ url_for('main.admin_guest_delete', guest_id=guest.id) }}" method="POST" class="d-inline" onsubmit="return confirm('Заблокировать гостя?')">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">Блок</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="8" class="text-center text-muted">Гости не найдены</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,49 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Загрузка данных — InviteMe{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="mb-4">Загрузка данных</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">1. Загрузить Excel со списком гостей</h5>
|
||||
<p class="text-muted">Столбцы: Отдел, ФИО, Должность, e-mail, название фото</p>
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="excel_file" class="form-label">Excel-файл</label>
|
||||
<input type="file" class="form-control" id="excel_file" name="excel_file" accept=".xlsx,.xls" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="base_url" class="form-label">Базовый URL для QR-кодов (опционально)</label>
|
||||
<input type="url" class="form-control" id="base_url" name="base_url" placeholder="https://events.company.com">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Загрузить и сгенерировать QR</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">2. Загрузить фото гостей</h5>
|
||||
<p class="text-muted">Фото должны называться точно так же, как указано в столбце «название фото»</p>
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="photo_files" class="form-label">Фото (можно несколько)</label>
|
||||
<input type="file" class="form-control" id="photo_files" name="photo_files" accept="image/*" multiple>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="zip_file" class="form-label">Или ZIP-архив</label>
|
||||
<input type="file" class="form-control" id="zip_file" name="zip_file" accept=".zip">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Загрузить фото</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user