first commit

This commit is contained in:
exercict
2026-07-14 08:10:11 +04:00
commit fdfb0dd62e
60 changed files with 9930 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
<h1 class="h3 mb-3">Синхронизация AD</h1>
<?php if (!empty($_SESSION['success'])): ?>
<div class="alert alert-success">
<?= htmlspecialchars($_SESSION['success']) ?>
</div>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars($_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<div class="row g-3 mb-3">
<div class="col-md-4">
<div class="card shadow-sm">
<div class="card-body">
<div class="text-muted small">Групп AD</div>
<div class="fs-4 fw-semibold"><?= (int)$groupCount ?></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm">
<div class="card-body">
<div class="text-muted small">Связей пользователь-группа</div>
<div class="fs-4 fw-semibold"><?= (int)$userGroupCount ?></div>
</div>
</div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-body">
<p class="mb-3">
Сейчас синхронизация берет группы из локальной таблицы пользователей, которые уже вошли в CRM через AD.
</p>
<form method="post" action="/admin/ad/sync">
<button type="submit" class="btn btn-primary">Синхронизировать группы из AD</button>
</form>
</div>
</div>
+62
View File
@@ -0,0 +1,62 @@
<h1 class="h3 mb-3">Доступ к доске</h1>
<?php if (!empty($_SESSION['success'])): ?>
<div class="alert alert-success">
<?= htmlspecialchars($_SESSION['success']) ?>
</div>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars($_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<div class="card shadow-sm mb-3">
<div class="card-body">
<div><strong>Название:</strong> <?= htmlspecialchars((string)$board['name']) ?></div>
<div><strong>Код:</strong> <?= htmlspecialchars((string)$board['code']) ?></div>
<?php if (!empty($board['description'])): ?>
<div><strong>Описание:</strong> <?= htmlspecialchars((string)$board['description']) ?></div>
<?php endif; ?>
</div>
</div>
<div class="card shadow-sm">
<div class="card-body">
<form method="post" action="/admin/boards/access/save">
<input type="hidden" name="board_id" value="<?= (int)$board['id'] ?>">
<label class="form-label fw-semibold">Группы, которым доступна доска</label>
<div class="border rounded p-3" style="max-height: 420px; overflow:auto;">
<?php if (empty($groups)): ?>
<div class="text-muted">Группы пока не загружены. Сначала синхронизируй AD.</div>
<?php else: ?>
<?php foreach ($groups as $group): ?>
<div class="form-check mb-2">
<input
class="form-check-input"
type="checkbox"
name="group_ids[]"
value="<?= (int)$group['id'] ?>"
id="group_<?= (int)$group['id'] ?>"
<?= in_array((int)$group['id'], $selectedGroupIds, true) ? 'checked' : '' ?>
>
<label class="form-check-label" for="group_<?= (int)$group['id'] ?>">
<?= htmlspecialchars((string)$group['name']) ?>
</label>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="mt-3">
<button type="submit" class="btn btn-success">Сохранить доступ</button>
<a href="/admin/boards" class="btn btn-secondary">Назад</a>
</div>
</form>
</div>
</div>
+46
View File
@@ -0,0 +1,46 @@
<h1 class="h3 mb-3">Создание доски</h1>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars($_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<div class="card shadow-sm">
<div class="card-body">
<form method="post" action="/admin/boards/store">
<div class="mb-3">
<label class="form-label">Название</label>
<input type="text" name="name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Код</label>
<input type="text" name="code" class="form-control" required>
<div class="form-text">Например: supply, it, hr</div>
</div>
<div class="mb-3">
<label class="form-label">Описание</label>
<textarea name="description" class="form-control" rows="3"></textarea>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="is_active" id="is_active" checked>
<label class="form-check-label" for="is_active">
Активная доска
</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="show_on_home" id="show_on_home" checked>
<label class="form-check-label" for="show_on_home">
Показывать на главной
</label>
</div>
<button type="submit" class="btn btn-success">Создать</button>
<a href="/admin/boards" class="btn btn-secondary">Назад</a>
</form>
</div>
</div>
+85
View File
@@ -0,0 +1,85 @@
<h1 class="h3 mb-3">Редактирование доски</h1>
<?php if (!empty($_SESSION['success'])): ?>
<div class="alert alert-success">
<?= htmlspecialchars($_SESSION['success']) ?>
</div>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars($_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<div class="card shadow-sm">
<div class="card-body">
<form method="post" action="/admin/boards/update">
<input type="hidden" name="id" value="<?= (int)$board['id'] ?>">
<div class="mb-3">
<label class="form-label">Название</label>
<input
type="text"
name="name"
class="form-control"
value="<?= htmlspecialchars((string)$board['name']) ?>"
required
>
</div>
<div class="mb-3">
<label class="form-label">Код</label>
<input
type="text"
name="code"
class="form-control"
value="<?= htmlspecialchars((string)$board['code']) ?>"
required
>
<div class="form-text">Используется в URL, например: /boards/supply</div>
</div>
<div class="mb-3">
<label class="form-label">Описание</label>
<textarea
name="description"
class="form-control"
rows="4"
><?= htmlspecialchars((string)($board['description'] ?? '')) ?></textarea>
</div>
<div class="form-check mb-3">
<input
class="form-check-input"
type="checkbox"
name="is_active"
id="is_active"
<?= (int)$board['is_active'] ? 'checked' : '' ?>
>
<label class="form-check-label" for="is_active">
Активная доска
</label>
</div>
<div class="form-check mb-3">
<input
class="form-check-input"
type="checkbox"
name="show_on_home"
id="show_on_home"
<?= (int)$board['show_on_home'] ? 'checked' : '' ?>
>
<label class="form-check-label" for="show_on_home">
Показывать на главной
</label>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-success">Сохранить</button>
<a href="/admin/boards" class="btn btn-secondary">Назад</a>
</div>
</form>
</div>
</div>
+134
View File
@@ -0,0 +1,134 @@
<h1 class="h3 mb-3">Создание поля</h1>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars((string)$_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<?php if (!empty($_SESSION['success'])): ?>
<div class="alert alert-success">
<?= htmlspecialchars((string)$_SESSION['success']) ?>
</div>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>
<div class="card shadow-sm">
<div class="card-body">
<form method="post" action="/admin/boards/fields/store">
<input type="hidden" name="board_id" value="<?= (int)$board['id'] ?>">
<div class="mb-3">
<label class="form-label">Доска</label>
<input type="text" class="form-control" value="<?= htmlspecialchars((string)$board['name']) ?>" disabled>
</div>
<div class="mb-3">
<label class="form-label">Код</label>
<input type="text" name="code" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Название</label>
<input type="text" name="name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Тип поля</label>
<select name="field_type" class="form-select" id="field_type">
<option value="text">text</option>
<option value="textarea">textarea</option>
<option value="number">number</option>
<option value="date">date</option>
<option value="select">select</option>
<option value="checkbox">checkbox</option>
</select>
</div>
<div class="mb-3" id="select_options_wrap" style="display:none;">
<label class="form-label">Варианты select</label>
<textarea name="select_options" class="form-control" rows="5" placeholder="Каждый вариант с новой строки"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Порядок</label>
<input type="number" name="sort_order" class="form-control" value="100">
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="is_required" id="is_required">
<label class="form-check-label" for="is_required">Обязательное</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="is_active" id="is_active" checked>
<label class="form-check-label" for="is_active">Активное</label>
</div>
<button type="submit" class="btn btn-success">Создать</button>
<a href="/admin/boards/fields?id=<?= (int)$board['id'] ?>" class="btn btn-secondary">Назад</a>
</form>
</div>
</div>
<?php if (!empty($fields)): ?>
<div class="card shadow-sm mt-4">
<div class="card-body">
<h5 class="mb-3">Уже созданные поля</h5>
<div class="table-responsive">
<table class="table table-sm align-middle mb-0">
<thead>
<tr>
<th>ID</th>
<th>Код</th>
<th>Название</th>
<th>Тип</th>
<th>Порядок</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($fields as $field): ?>
<tr>
<td><?= (int)$field['id'] ?></td>
<td><?= htmlspecialchars((string)$field['code']) ?></td>
<td><?= htmlspecialchars((string)$field['name']) ?></td>
<td><?= htmlspecialchars((string)$field['field_type']) ?></td>
<td><?= (int)$field['sort_order'] ?></td>
<td>
<div class="d-flex gap-2">
<a href="/admin/boards/fields/edit?id=<?= (int)$field['id'] ?>" class="btn btn-sm btn-outline-primary">
Изменить
</a>
<form method="post" action="/admin/boards/fields/delete" onsubmit="return confirm('Удалить поле и все его значения у задач?');" class="d-inline">
<input type="hidden" name="id" value="<?= (int)$field['id'] ?>">
<button type="submit" class="btn btn-sm btn-outline-danger">
Удалить
</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
const fieldType = document.getElementById('field_type');
const wrap = document.getElementById('select_options_wrap');
if (fieldType && wrap) {
fieldType.addEventListener('change', function () {
wrap.style.display = this.value === 'select' ? 'block' : 'none';
});
}
});
</script>
+73
View File
@@ -0,0 +1,73 @@
<?php
$settings = json_decode((string)($field['settings_json'] ?? ''), true);
$optionsText = '';
if (!empty($settings['options']) && is_array($settings['options'])) {
$optionsText = implode("\n", $settings['options']);
}
?>
<h1 class="h3 mb-3">Изменение поля</h1>
<div class="card shadow-sm">
<div class="card-body">
<form method="post" action="/admin/boards/fields/update">
<input type="hidden" name="id" value="<?= (int)$field['id'] ?>">
<div class="mb-3">
<label class="form-label">Доска</label>
<input type="text" class="form-control" value="<?= htmlspecialchars((string)$field['board_name']) ?>" disabled>
</div>
<div class="mb-3">
<label class="form-label">Код</label>
<input type="text" name="code" class="form-control" value="<?= htmlspecialchars((string)$field['code']) ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Название</label>
<input type="text" name="name" class="form-control" value="<?= htmlspecialchars((string)$field['name']) ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Тип поля</label>
<select name="field_type" class="form-select" id="field_type">
<?php foreach (['text','textarea','number','date','select','checkbox'] as $type): ?>
<option value="<?= $type ?>" <?= $field['field_type'] === $type ? 'selected' : '' ?>>
<?= $type ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3" id="select_options_wrap" style="<?= $field['field_type'] === 'select' ? '' : 'display:none;' ?>">
<label class="form-label">Варианты select</label>
<textarea name="select_options" class="form-control" rows="5"><?= htmlspecialchars($optionsText) ?></textarea>
</div>
<div class="mb-3">
<label class="form-label">Порядок</label>
<input type="number" name="sort_order" class="form-control" value="<?= (int)$field['sort_order'] ?>">
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="is_required" id="is_required" <?= (int)$field['is_required'] ? 'checked' : '' ?>>
<label class="form-check-label" for="is_required">Обязательное</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="is_active" id="is_active" <?= (int)$field['is_active'] ? 'checked' : '' ?>>
<label class="form-check-label" for="is_active">Активное</label>
</div>
<button type="submit" class="btn btn-success">Сохранить</button>
<a href="/admin/boards/fields?id=<?= (int)$field['board_id'] ?>" class="btn btn-secondary">Назад</a>
</form>
</div>
</div>
<script>
document.getElementById('field_type').addEventListener('change', function () {
document.getElementById('select_options_wrap').style.display = this.value === 'select' ? 'block' : 'none';
});
</script>
+93
View File
@@ -0,0 +1,93 @@
<h1 class="h3 mb-3">Создание поля</h1>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars($_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h1 class="h3 mb-0">Поля доски</h1>
<div class="text-muted small">
<?= htmlspecialchars((string)$board['name']) ?> (<?= htmlspecialchars((string)$board['code']) ?>)
</div>
</div>
<a href="/admin/boards/fields/create?board_id=<?= (int)$board['id'] ?>" class="btn btn-primary">
+ Создать поле
</a>
</div>
<?php if (!empty($_SESSION['success'])): ?>
<div class="alert alert-success">
<?= htmlspecialchars($_SESSION['success']) ?>
</div>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars($_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<div class="card shadow-sm">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Код</th>
<th>Название</th>
<th>Тип</th>
<th>Обязательное</th>
<th>Активно</th>
<th>Порядок</th>
<th style="width: 220px;">Действия</th>
</tr>
</thead>
<tbody>
<?php foreach ($fields as $field): ?>
<tr>
<td><?= (int)$field['id'] ?></td>
<td><?= htmlspecialchars((string)$field['code']) ?></td>
<td><?= htmlspecialchars((string)$field['name']) ?></td>
<td><?= htmlspecialchars((string)$field['field_type']) ?></td>
<td><?= (int)$field['is_required'] ? 'Да' : 'Нет' ?></td>
<td><?= (int)$field['is_active'] ? 'Да' : 'Нет' ?></td>
<td><?= (int)$field['sort_order'] ?></td>
<td>
<div class="d-flex gap-2">
<a href="/admin/boards/fields/edit?id=<?= (int)$field['id'] ?>" class="btn btn-sm btn-outline-primary">
Изменить
</a>
<form method="post" action="/admin/boards/fields/delete" onsubmit="return confirm('Удалить поле и все его значения у задач?');" class="d-inline">
<input type="hidden" name="id" value="<?= (int)$field['id'] ?>">
<button type="submit" class="btn btn-sm btn-outline-danger">
Удалить
</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($fields)): ?>
<tr>
<td colspan="8" class="text-center text-muted py-4">Поля пока не созданы</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<script>
document.getElementById('field_type').addEventListener('change', function () {
document.getElementById('select_options_wrap').style.display = this.value === 'select' ? 'block' : 'none';
});
</script>
+83
View File
@@ -0,0 +1,83 @@
<?php if (!empty($_SESSION['success'])): ?>
<div class="alert alert-success">
<?= htmlspecialchars($_SESSION['success']) ?>
</div>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars($_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<div class="d-flex justify-content-between align-items-center mb-3">
<h1 class="h3 mb-0">Доски</h1>
<a href="/admin/boards/create" class="btn btn-primary">+ Создать доску</a>
</div>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars($_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<div class="card shadow-sm">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Название</th>
<th>Код</th>
<th>Описание</th>
<th>Активна</th>
<th>Создана</th>
<th>Доступ</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
<?php foreach ($boards as $board): ?>
<tr>
<td><?= (int)$board['id'] ?></td>
<td>
<a href="/admin/boards/show?id=<?= (int)$board['id'] ?>"
data-board-modal
data-board-id="<?= (int)$board['id'] ?>"
class="text-decoration-none">
<?= htmlspecialchars((string)$board['name']) ?>
</a>
</td>
<td><?= htmlspecialchars((string)$board['code']) ?></td>
<td><?= htmlspecialchars((string)($board['description'] ?? '')) ?></td>
<td><?= (int)$board['is_active'] ? 'Да' : 'Нет' ?></td>
<td><?= htmlspecialchars((string)$board['created_at']) ?></td>
<td>
<div class="d-flex gap-2 flex-wrap">
<a href="/admin/boards/access?id=<?= (int)$board['id'] ?>" class="btn btn-sm btn-outline-secondary">Права</a>
<a href="/admin/boards/statuses?id=<?= (int)$board['id'] ?>" class="btn btn-sm btn-outline-dark">Статусы</a>
<a href="/admin/boards/fields?id=<?= (int)$board['id'] ?>" class="btn btn-sm btn-outline-info">Поля</a>
<a href="/admin/boards/source?id=<?= (int)$board['id'] ?>" class="btn btn-sm btn-outline-success">Google Sheets</a>
</div>
</td>
<td>
<div class="d-flex gap-2">
<a href="/admin/boards/edit?id=<?= (int)$board['id'] ?>" class="btn btn-sm btn-outline-primary">Изменить</a>
<form method="post" action="/admin/boards/delete" onsubmit="return confirm('Удалить доску и все связанные задачи, поля, комментарии, файлы и настройки?');" class="d-inline">
<input type="hidden" name="id" value="<?= (int)$board['id'] ?>">
<button type="submit" class="btn btn-sm btn-outline-danger">
Удалить
</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
+94
View File
@@ -0,0 +1,94 @@
<div class="modal-header">
<h5 class="modal-title">
<?= htmlspecialchars((string)$board['name']) ?>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-3 mb-4">
<div class="col-md-6">
<div><strong>ID:</strong> <?= (int)$board['id'] ?></div>
</div>
<div class="col-md-6">
<div><strong>Код:</strong> <?= htmlspecialchars((string)$board['code']) ?></div>
</div>
<div class="col-md-6">
<div>
<strong>Активна:</strong>
<?= (int)$board['is_active'] ? 'Да' : 'Нет' ?>
</div>
</div>
<div class="col-md-6">
<div><strong>Создана:</strong> <?= htmlspecialchars((string)$board['created_at']) ?></div>
</div>
</div>
<div class="mb-4">
<label class="form-label fw-bold">Описание доски</label>
<div class="border rounded p-3 bg-light">
<?= !empty($board['description'])
? nl2br(htmlspecialchars((string)$board['description']))
: '<span class="text-muted">Описание не заполнено</span>' ?>
</div>
</div>
<div class="mb-3">
<h6 class="mb-3">Статистика</h6>
<div class="row g-3">
<div class="col-6 col-md-4">
<div class="border rounded p-3 bg-light">
<div class="small text-muted">Всего задач</div>
<div class="fs-5 fw-semibold"><?= (int)$board['task_count'] ?></div>
</div>
</div>
<div class="col-6 col-md-4">
<div class="border rounded p-3 bg-light">
<div class="small text-muted">Новые</div>
<div class="fs-5 fw-semibold"><?= (int)$board['new_count'] ?></div>
</div>
</div>
<div class="col-6 col-md-4">
<div class="border rounded p-3 bg-light">
<div class="small text-muted">В работе</div>
<div class="fs-5 fw-semibold"><?= (int)$board['in_progress_count'] ?></div>
</div>
</div>
<div class="col-6 col-md-4">
<div class="border rounded p-3 bg-light">
<div class="small text-muted">На проверке</div>
<div class="fs-5 fw-semibold"><?= (int)$board['review_count'] ?></div>
</div>
</div>
<div class="col-6 col-md-4">
<div class="border rounded p-3 bg-light">
<div class="small text-muted">Закрыты</div>
<div class="fs-5 fw-semibold"><?= (int)$board['done_count'] ?></div>
</div>
</div>
<div class="col-6 col-md-4">
<div class="border rounded p-3 bg-light">
<div class="small text-muted">Отменены</div>
<div class="fs-5 fw-semibold"><?= (int)$board['canceled_count'] ?></div>
</div>
</div>
<div class="col-6 col-md-4">
<div class="border rounded p-3 bg-light">
<div class="small text-muted">Просрочены</div>
<div class="fs-5 fw-semibold"><?= (int)$board['overdue_count'] ?></div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
</div>
+121
View File
@@ -0,0 +1,121 @@
<h1 class="h3 mb-3">Интеграция Google Sheets</h1>
<?php if (!empty($_SESSION['success'])): ?>
<div class="alert alert-success">
<?= htmlspecialchars($_SESSION['success']) ?>
</div>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>
<?php if (!empty($_SESSION['error'])): ?>
<div class="alert alert-danger">
<?= htmlspecialchars($_SESSION['error']) ?>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>
<div class="card shadow-sm mb-3">
<div class="card-body">
<div><strong>Доска:</strong> <?= htmlspecialchars((string)$board['name']) ?></div>
<div><strong>Код:</strong> <?= htmlspecialchars((string)$board['code']) ?></div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-body">
<form method="post" action="/admin/boards/source/save">
<input type="hidden" name="board_id" value="<?= (int)$board['id'] ?>">
<div class="mb-3">
<label class="form-label">Ссылка на Google Sheets</label>
<input
type="text"
name="spreadsheet_url"
class="form-control"
value="<?= htmlspecialchars((string)($source['spreadsheet_url'] ?? '')) ?>"
placeholder="https://docs.google.com/spreadsheets/d/..."
required
>
</div>
<div class="mb-3">
<label class="form-label">Имя вкладки</label>
<input
type="text"
name="sheet_name"
class="form-control"
value="<?= htmlspecialchars((string)($source['sheet_name'] ?? '')) ?>"
placeholder="Лист1"
required
>
</div>
<div class="mb-3">
<label class="form-label">Режим синхронизации</label>
<select name="sync_mode" class="form-select">
<?php $mode = (string)($source['sync_mode'] ?? 'import_export'); ?>
<option value="import" <?= $mode === 'import' ? 'selected' : '' ?>>Только импорт</option>
<option value="export" <?= $mode === 'export' ? 'selected' : '' ?>>Только экспорт</option>
<option value="import_export" <?= $mode === 'import_export' ? 'selected' : '' ?>>Импорт + экспорт</option>
</select>
</div>
<div class="form-check mb-4">
<input class="form-check-input" type="checkbox" name="is_active" id="is_active" <?= (int)($source['is_active'] ?? 1) ? 'checked' : '' ?>>
<label class="form-check-label" for="is_active">Интеграция активна</label>
</div>
<hr class="my-4">
<h5 class="mb-3">Сопоставление базовых полей</h5>
<div class="row g-3 mb-4">
<?php foreach ($baseFields as $field): ?>
<?php $mapKey = 'base:' . $field['key']; ?>
<div class="col-md-6">
<label class="form-label"><?= htmlspecialchars((string)$field['name']) ?></label>
<input
type="text"
name="mapping_base[<?= htmlspecialchars((string)$field['key']) ?>]"
class="form-control"
value="<?= htmlspecialchars((string)($mappings[$mapKey] ?? '')) ?>"
placeholder="Название колонки в Google Sheets"
>
</div>
<?php endforeach; ?>
</div>
<h5 class="mb-3">Сопоставление кастомных полей</h5>
<?php if (empty($customFields)): ?>
<div class="alert alert-light border">
У доски пока нет кастомных полей.
</div>
<?php else: ?>
<div class="row g-3 mb-4">
<?php foreach ($customFields as $field): ?>
<?php $mapKey = 'custom:' . $field['code']; ?>
<div class="col-md-6">
<label class="form-label">
<?= htmlspecialchars((string)$field['name']) ?>
<span class="text-muted small">(<?= htmlspecialchars((string)$field['code']) ?>)</span>
</label>
<input
type="text"
name="mapping_custom[<?= htmlspecialchars((string)$field['code']) ?>]"
class="form-control"
value="<?= htmlspecialchars((string)($mappings[$mapKey] ?? '')) ?>"
placeholder="Название колонки в Google Sheets"
>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-success">Сохранить</button>
<a href="/admin/boards" class="btn btn-secondary">Назад</a>
</div>
</form>
</div>
</div>
@@ -0,0 +1,55 @@
<h1 class="h3 mb-3">Создание статуса</h1>
<div class="card shadow-sm">
<div class="card-body">
<form method="post" action="/admin/boards/statuses/store">
<input type="hidden" name="board_id" value="<?= (int)$board['id'] ?>">
<div class="mb-3">
<label class="form-label">Доска</label>
<input type="text" class="form-control" value="<?= htmlspecialchars((string)$board['name']) ?>" disabled>
</div>
<div class="mb-3">
<label class="form-label">Код</label>
<input type="text" name="code" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Название</label>
<input type="text" name="name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Цвет</label>
<select name="color" class="form-select">
<option value="secondary">secondary</option>
<option value="primary">primary</option>
<option value="warning">warning</option>
<option value="success">success</option>
<option value="danger">danger</option>
<option value="dark">dark</option>
<option value="info">info</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Порядок</label>
<input type="number" name="sort_order" class="form-control" value="100">
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="is_done" id="is_done">
<label class="form-check-label" for="is_done">Финальный статус</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="is_active" id="is_active" checked>
<label class="form-check-label" for="is_active">Активный</label>
</div>
<button type="submit" class="btn btn-success">Создать</button>
<a href="/admin/boards/statuses?id=<?= (int)$board['id'] ?>" class="btn btn-secondary">Назад</a>
</form>
</div>
</div>
+53
View File
@@ -0,0 +1,53 @@
<h1 class="h3 mb-3">Изменение статуса</h1>
<div class="card shadow-sm">
<div class="card-body">
<form method="post" action="/admin/boards/statuses/update">
<input type="hidden" name="id" value="<?= (int)$status['id'] ?>">
<div class="mb-3">
<label class="form-label">Доска</label>
<input type="text" class="form-control" value="<?= htmlspecialchars((string)$status['board_name']) ?>" disabled>
</div>
<div class="mb-3">
<label class="form-label">Код</label>
<input type="text" name="code" class="form-control" value="<?= htmlspecialchars((string)$status['code']) ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Название</label>
<input type="text" name="name" class="form-control" value="<?= htmlspecialchars((string)$status['name']) ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Цвет</label>
<select name="color" class="form-select">
<?php foreach (['secondary','primary','warning','success','danger','dark','info'] as $color): ?>
<option value="<?= $color ?>" <?= $status['color'] === $color ? 'selected' : '' ?>>
<?= $color ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Порядок</label>
<input type="number" name="sort_order" class="form-control" value="<?= (int)$status['sort_order'] ?>">
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="is_done" id="is_done" <?= (int)$status['is_done'] ? 'checked' : '' ?>>
<label class="form-check-label" for="is_done">Финальный статус</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="is_active" id="is_active" <?= (int)$status['is_active'] ? 'checked' : '' ?>>
<label class="form-check-label" for="is_active">Активный</label>
</div>
<button type="submit" class="btn btn-success">Сохранить</button>
<a href="/admin/boards/statuses?id=<?= (int)$status['board_id'] ?>" class="btn btn-secondary">Назад</a>
</form>
</div>
</div>
+54
View File
@@ -0,0 +1,54 @@
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h1 class="h3 mb-0">Статусы доски</h1>
<div class="text-muted small">
<?= htmlspecialchars((string)$board['name']) ?> (<?= htmlspecialchars((string)$board['code']) ?>)
</div>
</div>
<a href="/admin/boards/statuses/create?board_id=<?= (int)$board['id'] ?>" class="btn btn-primary">
+ Создать статус
</a>
</div>
<div class="card shadow-sm">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Код</th>
<th>Название</th>
<th>Цвет</th>
<th>Порядок</th>
<th>Финальный</th>
<th>Активен</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($statuses as $status): ?>
<tr>
<td><?= (int)$status['id'] ?></td>
<td><?= htmlspecialchars((string)$status['code']) ?></td>
<td><?= htmlspecialchars((string)$status['name']) ?></td>
<td><?= htmlspecialchars((string)$status['color']) ?></td>
<td><?= (int)$status['sort_order'] ?></td>
<td><?= (int)$status['is_done'] ? 'Да' : 'Нет' ?></td>
<td><?= (int)$status['is_active'] ? 'Да' : 'Нет' ?></td>
<td>
<a href="/admin/boards/statuses/edit?id=<?= (int)$status['id'] ?>" class="btn btn-sm btn-outline-primary">
Изменить
</a>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($statuses)): ?>
<tr>
<td colspan="8" class="text-center text-muted py-4">Статусов пока нет</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
+12
View File
@@ -0,0 +1,12 @@
<h1 class="h3 mb-3">Админка</h1>
<div class="card shadow-sm">
<div class="card-body">
<div class="d-flex flex-wrap gap-2">
<a href="/admin/boards" class="btn btn-primary">Доски</a>
<a href="/admin/ad" class="btn btn-outline-primary">Синхронизация AD</a>
<!-- <a href="/admin/permission" class="btn btn-primary">Права доступа AD к доскам</a>-->
</div>
</div>
</div>