календарь для документов
This commit is contained in:
@@ -89,7 +89,7 @@ function showMainInterface() {
|
|||||||
|
|
||||||
// Функция для перезагрузки всех скриптов
|
// Функция для перезагрузки всех скриптов
|
||||||
function reloadAllScripts() {
|
function reloadAllScripts() {
|
||||||
console.log('🔄 Перезагрузка всех скриптов после авторизации...');
|
//console.log('🔄 Перезагрузка всех скриптов после авторизации...');
|
||||||
|
|
||||||
// Список скриптов для перезагрузки (в правильном порядке)
|
// Список скриптов для перезагрузки (в правильном порядке)
|
||||||
const scriptsToReload = [
|
const scriptsToReload = [
|
||||||
@@ -134,7 +134,7 @@ function loadScriptsSequentially(scripts, index) {
|
|||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = scripts[index];
|
script.src = scripts[index];
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
console.log(`✅ Загружен: ${scripts[index]}`);
|
// console.log(`✅ Загружен: ${scripts[index]}`);
|
||||||
loadScriptsSequentially(scripts, index + 1);
|
loadScriptsSequentially(scripts, index + 1);
|
||||||
};
|
};
|
||||||
script.onerror = (error) => {
|
script.onerror = (error) => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// document-fields.js - Скрипт для управления полями документа в задачах
|
// document-fields.js - Скрипт для управления полями документа в задачах
|
||||||
// Показывает кнопку "Реквизиты" только для задач с типом "document" и только когда задача раскрыта
|
// Показывает кнопку "Реквизиты" только для задач с типом "document",
|
||||||
|
// только когда задача раскрыта и только для пользователей из групп "Подписант" или "Секретарь"
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -7,6 +8,10 @@
|
|||||||
// Конфигурация
|
// Конфигурация
|
||||||
const CONFIG = {
|
const CONFIG = {
|
||||||
modalId: 'documentFieldsModal',
|
modalId: 'documentFieldsModal',
|
||||||
|
signerGroup: 'Подписант',
|
||||||
|
secretaryGroup: 'Секретарь',
|
||||||
|
apiEndpoint: '/api2/idusers',
|
||||||
|
usersEndpoint: '/api/users',
|
||||||
modalStyles: `
|
modalStyles: `
|
||||||
<style>
|
<style>
|
||||||
.document-fields-modal {
|
.document-fields-modal {
|
||||||
@@ -81,6 +86,7 @@
|
|||||||
|
|
||||||
.document-field-group {
|
.document-field-group {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.document-field-group label {
|
.document-field-group label {
|
||||||
@@ -111,6 +117,150 @@
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Стили для поля с календарем */
|
||||||
|
.date-input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-input-wrapper input {
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-icon {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-icon:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Календарь */
|
||||||
|
.inline-calendar {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1000;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-month-year {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav button {
|
||||||
|
background: none;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #555;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav button:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-weekdays {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-weekday {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #666;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-days {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day {
|
||||||
|
aspect-ratio: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day:hover {
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day.selected {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day.today {
|
||||||
|
border: 1px solid #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day.empty {
|
||||||
|
cursor: default;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day.empty:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.document-fields-buttons {
|
.document-fields-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
@@ -257,6 +407,9 @@
|
|||||||
// Текущий пользователь
|
// Текущий пользователь
|
||||||
let currentUser = null;
|
let currentUser = null;
|
||||||
|
|
||||||
|
// Группы пользователя (все группы из разных источников)
|
||||||
|
let userGroups = [];
|
||||||
|
|
||||||
// Кэш для типов задач
|
// Кэш для типов задач
|
||||||
const taskTypeCache = new Map();
|
const taskTypeCache = new Map();
|
||||||
|
|
||||||
@@ -272,6 +425,14 @@
|
|||||||
'.task-content.expanded'
|
'.task-content.expanded'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Состояние календаря
|
||||||
|
let calendarState = {
|
||||||
|
currentDate: new Date(),
|
||||||
|
selectedDate: null,
|
||||||
|
isOpen: false,
|
||||||
|
inputElement: null
|
||||||
|
};
|
||||||
|
|
||||||
// Получение текущего пользователя
|
// Получение текущего пользователя
|
||||||
async function getCurrentUser() {
|
async function getCurrentUser() {
|
||||||
try {
|
try {
|
||||||
@@ -283,6 +444,9 @@
|
|||||||
if (data.user) {
|
if (data.user) {
|
||||||
currentUser = data.user;
|
currentUser = data.user;
|
||||||
console.log('✅ DocumentFields: текущий пользователь', currentUser.login);
|
console.log('✅ DocumentFields: текущий пользователь', currentUser.login);
|
||||||
|
|
||||||
|
// Получаем все группы пользователя
|
||||||
|
await getAllUserGroups(currentUser.login || currentUser.id);
|
||||||
}
|
}
|
||||||
return currentUser;
|
return currentUser;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -291,6 +455,291 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Получение ВСЕХ групп пользователя из разных источников
|
||||||
|
async function getAllUserGroups(userLogin) {
|
||||||
|
const allGroups = new Set();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Пробуем получить через API /api2/idusers
|
||||||
|
await fetchGroupsFromApi2(userLogin, allGroups);
|
||||||
|
|
||||||
|
// 2. Пробуем получить через API /api/users
|
||||||
|
await fetchGroupsFromUsersApi(userLogin, allGroups);
|
||||||
|
|
||||||
|
// 3. Проверяем, есть ли группы в currentUser
|
||||||
|
if (currentUser) {
|
||||||
|
addGroupsFromCurrentUser(allGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Преобразуем Set в массив и сохраняем
|
||||||
|
userGroups = Array.from(allGroups);
|
||||||
|
|
||||||
|
console.log('✅ DocumentFields: ВСЕ группы пользователя:', userGroups);
|
||||||
|
|
||||||
|
// Детальный вывод для отладки
|
||||||
|
console.log('🔍 Проверка доступа:');
|
||||||
|
console.log(' - Группы:', userGroups);
|
||||||
|
console.log(' - Ищем "Подписант":', userGroups.some(g =>
|
||||||
|
g.toLowerCase().includes(CONFIG.signerGroup.toLowerCase())));
|
||||||
|
console.log(' - Ищем "Секретарь":', userGroups.some(g =>
|
||||||
|
g.toLowerCase().includes(CONFIG.secretaryGroup.toLowerCase())));
|
||||||
|
|
||||||
|
return userGroups;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ DocumentFields: ошибка получения групп', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение групп из /api2/idusers
|
||||||
|
async function fetchGroupsFromApi2(userLogin, groupsSet) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${CONFIG.apiEndpoint}?login=${userLogin}`);
|
||||||
|
if (!response.ok) {
|
||||||
|
console.warn('⚠️ DocumentFields: API /api2/idusers вернул ошибку', response.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('📦 Данные из /api2/idusers:', data);
|
||||||
|
|
||||||
|
// Обрабатываем массив пользователей
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
const userData = data.find(u =>
|
||||||
|
u.login === userLogin ||
|
||||||
|
u.user_login === userLogin ||
|
||||||
|
u.name === userLogin ||
|
||||||
|
u.user_name === userLogin
|
||||||
|
);
|
||||||
|
|
||||||
|
if (userData) {
|
||||||
|
extractGroupsFromUserData(userData, groupsSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Обрабатываем объект пользователя
|
||||||
|
else if (data && typeof data === 'object') {
|
||||||
|
extractGroupsFromUserData(data, groupsSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ DocumentFields: ошибка fetchGroupsFromApi2', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение групп из /api/users
|
||||||
|
async function fetchGroupsFromUsersApi(userLogin, groupsSet) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${CONFIG.usersEndpoint}?login=${userLogin}`);
|
||||||
|
if (!response.ok) {
|
||||||
|
console.warn('⚠️ DocumentFields: API /api/users вернул ошибку', response.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('📦 Данные из /api/users:', data);
|
||||||
|
|
||||||
|
// Обрабатываем данные в зависимости от формата
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
const userData = data.find(u =>
|
||||||
|
u.login === userLogin ||
|
||||||
|
u.username === userLogin ||
|
||||||
|
u.email === userLogin
|
||||||
|
);
|
||||||
|
|
||||||
|
if (userData) {
|
||||||
|
extractGroupsFromUserData(userData, groupsSet);
|
||||||
|
}
|
||||||
|
} else if (data && typeof data === 'object') {
|
||||||
|
extractGroupsFromUserData(data, groupsSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ DocumentFields: ошибка fetchGroupsFromUsersApi', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Извлечение групп из данных пользователя
|
||||||
|
function extractGroupsFromUserData(userData, groupsSet) {
|
||||||
|
if (!userData) return;
|
||||||
|
|
||||||
|
console.log('🔍 Извлекаем группы из:', userData);
|
||||||
|
|
||||||
|
// 1. Проверяем group_name (может быть строкой или массивом)
|
||||||
|
if (userData.group_name) {
|
||||||
|
if (Array.isArray(userData.group_name)) {
|
||||||
|
userData.group_name.forEach(g => {
|
||||||
|
if (g) groupsSet.add(String(g).trim());
|
||||||
|
});
|
||||||
|
} else if (typeof userData.group_name === 'string') {
|
||||||
|
// Может быть строкой с разделителями
|
||||||
|
const groups = userData.group_name.split(/[,;|]/).map(g => g.trim());
|
||||||
|
groups.forEach(g => {
|
||||||
|
if (g) groupsSet.add(g);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Проверяем ldap_group
|
||||||
|
if (userData.ldap_group) {
|
||||||
|
if (Array.isArray(userData.ldap_group)) {
|
||||||
|
userData.ldap_group.forEach(g => {
|
||||||
|
if (g) groupsSet.add(String(g).trim());
|
||||||
|
});
|
||||||
|
} else if (typeof userData.ldap_group === 'string') {
|
||||||
|
groupsSet.add(userData.ldap_group.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Проверяем metadata.groups
|
||||||
|
if (userData.metadata?.groups) {
|
||||||
|
if (Array.isArray(userData.metadata.groups)) {
|
||||||
|
userData.metadata.groups.forEach(g => {
|
||||||
|
if (g) groupsSet.add(String(g).trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Проверяем просто groups (если есть)
|
||||||
|
if (userData.groups) {
|
||||||
|
if (Array.isArray(userData.groups)) {
|
||||||
|
userData.groups.forEach(g => {
|
||||||
|
if (g) groupsSet.add(String(g).trim());
|
||||||
|
});
|
||||||
|
} else if (typeof userData.groups === 'string') {
|
||||||
|
const groups = userData.groups.split(/[,;|]/).map(g => g.trim());
|
||||||
|
groups.forEach(g => {
|
||||||
|
if (g) groupsSet.add(g);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Проверяем roles (если есть)
|
||||||
|
if (userData.roles) {
|
||||||
|
if (Array.isArray(userData.roles)) {
|
||||||
|
userData.roles.forEach(r => {
|
||||||
|
if (r) groupsSet.add(String(r).trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Проверяем department (может содержать группу)
|
||||||
|
if (userData.department) {
|
||||||
|
groupsSet.add(String(userData.department).trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Проверяем position (может содержать группу)
|
||||||
|
if (userData.position) {
|
||||||
|
groupsSet.add(String(userData.position).trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавление групп из currentUser
|
||||||
|
function addGroupsFromCurrentUser(groupsSet) {
|
||||||
|
if (!currentUser) return;
|
||||||
|
|
||||||
|
// Проверяем различные поля в currentUser
|
||||||
|
const fieldsToCheck = [
|
||||||
|
'group',
|
||||||
|
'groups',
|
||||||
|
'role',
|
||||||
|
'roles',
|
||||||
|
'department',
|
||||||
|
'position',
|
||||||
|
'user_group',
|
||||||
|
'user_groups'
|
||||||
|
];
|
||||||
|
|
||||||
|
fieldsToCheck.forEach(field => {
|
||||||
|
if (currentUser[field]) {
|
||||||
|
if (Array.isArray(currentUser[field])) {
|
||||||
|
currentUser[field].forEach(g => {
|
||||||
|
if (g) groupsSet.add(String(g).trim());
|
||||||
|
});
|
||||||
|
} else if (typeof currentUser[field] === 'string') {
|
||||||
|
groupsSet.add(currentUser[field].trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка, имеет ли пользователь доступ (Подписант или Секретарь)
|
||||||
|
function hasUserAccess() {
|
||||||
|
if (!userGroups || userGroups.length === 0) {
|
||||||
|
console.log('❌ DocumentFields: у пользователя нет групп');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Приводим искомые группы к нижнему регистру
|
||||||
|
const signerGroupLower = CONFIG.signerGroup.toLowerCase();
|
||||||
|
const secretaryGroupLower = CONFIG.secretaryGroup.toLowerCase();
|
||||||
|
|
||||||
|
// Проверяем КАЖДУЮ группу пользователя
|
||||||
|
for (const group of userGroups) {
|
||||||
|
if (!group) continue;
|
||||||
|
|
||||||
|
const groupLower = String(group).toLowerCase().trim();
|
||||||
|
|
||||||
|
// Точное совпадение
|
||||||
|
if (groupLower === signerGroupLower || groupLower === secretaryGroupLower) {
|
||||||
|
console.log(`✅ Найдено точное совпадение: "${group}"`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Частичное совпадение (содержит подстроку)
|
||||||
|
if (groupLower.includes(signerGroupLower) || groupLower.includes(secretaryGroupLower)) {
|
||||||
|
console.log(`✅ Найдено частичное совпадение: "${group}" содержит искомую группу`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Совпадение по словам (если группа состоит из нескольких слов)
|
||||||
|
const groupWords = groupLower.split(/[\s\-_]/);
|
||||||
|
const signerWords = signerGroupLower.split(/[\s\-_]/);
|
||||||
|
const secretaryWords = secretaryGroupLower.split(/[\s\-_]/);
|
||||||
|
|
||||||
|
// Проверяем, содержит ли группа все слова из искомой группы
|
||||||
|
const matchesSigner = signerWords.every(word =>
|
||||||
|
groupLower.includes(word) || groupWords.some(gw => gw.includes(word))
|
||||||
|
);
|
||||||
|
|
||||||
|
const matchesSecretary = secretaryWords.every(word =>
|
||||||
|
groupLower.includes(word) || groupWords.some(gw => gw.includes(word))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (matchesSigner || matchesSecretary) {
|
||||||
|
console.log(`✅ Найдено совпадение по словам: "${group}"`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('❌ DocumentFields: пользователь НЕ имеет доступа');
|
||||||
|
console.log(' Группы пользователя:', userGroups);
|
||||||
|
console.log(' Нужные группы:', [CONFIG.signerGroup, CONFIG.secretaryGroup]);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для отладки - показывает все группы пользователя
|
||||||
|
function debugUserGroups() {
|
||||||
|
console.log('=== ОТЛАДКА ГРУПП ПОЛЬЗОВАТЕЛЯ ===');
|
||||||
|
console.log('Текущий пользователь:', currentUser);
|
||||||
|
console.log('Все группы:', userGroups);
|
||||||
|
console.log('Ищем "Подписант":', userGroups.filter(g =>
|
||||||
|
g.toLowerCase().includes(CONFIG.signerGroup.toLowerCase())
|
||||||
|
));
|
||||||
|
console.log('Ищем "Секретарь":', userGroups.filter(g =>
|
||||||
|
g.toLowerCase().includes(CONFIG.secretaryGroup.toLowerCase())
|
||||||
|
));
|
||||||
|
console.log('Доступ:', hasUserAccess());
|
||||||
|
console.log('================================');
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: currentUser,
|
||||||
|
groups: userGroups,
|
||||||
|
hasAccess: hasUserAccess()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Получение типа задачи по ID
|
// Получение типа задачи по ID
|
||||||
async function getTaskType(taskId) {
|
async function getTaskType(taskId) {
|
||||||
// Проверяем кэш
|
// Проверяем кэш
|
||||||
@@ -371,6 +820,153 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Функции для работы с календарем
|
||||||
|
function formatDate(date) {
|
||||||
|
if (!date) return '';
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const year = date.getFullYear();
|
||||||
|
return `${day}.${month}.${year}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDate(dateStr) {
|
||||||
|
if (!dateStr) return null;
|
||||||
|
const parts = dateStr.split('.');
|
||||||
|
if (parts.length === 3) {
|
||||||
|
const day = parseInt(parts[0], 10);
|
||||||
|
const month = parseInt(parts[1], 10) - 1;
|
||||||
|
const year = parseInt(parts[2], 10);
|
||||||
|
return new Date(year, month, day);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMonthName(month, year) {
|
||||||
|
const date = new Date(year, month, 1);
|
||||||
|
return date.toLocaleString('ru-RU', { month: 'long' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCalendar(year, month, selectedDate) {
|
||||||
|
const firstDay = new Date(year, month, 1).getDay();
|
||||||
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||||
|
|
||||||
|
// Корректировка первого дня (0 - воскресенье, делаем понедельник первым)
|
||||||
|
let startOffset = firstDay === 0 ? 6 : firstDay - 1;
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const todayStr = formatDate(today);
|
||||||
|
|
||||||
|
let html = '';
|
||||||
|
let dayCount = 1;
|
||||||
|
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
for (let j = 0; j < 7; j++) {
|
||||||
|
if (i === 0 && j < startOffset) {
|
||||||
|
html += '<div class="calendar-day empty"></div>';
|
||||||
|
} else if (dayCount <= daysInMonth) {
|
||||||
|
const currentDate = new Date(year, month, dayCount);
|
||||||
|
const dateStr = formatDate(currentDate);
|
||||||
|
const isSelected = selectedDate && dateStr === formatDate(selectedDate);
|
||||||
|
const isToday = dateStr === todayStr;
|
||||||
|
|
||||||
|
html += `<div class="calendar-day ${isSelected ? 'selected' : ''} ${isToday ? 'today' : ''}" onclick="documentFields.selectDate('${dateStr}')">${dayCount}</div>`;
|
||||||
|
dayCount++;
|
||||||
|
} else {
|
||||||
|
html += '<div class="calendar-day empty"></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCalendar() {
|
||||||
|
const calendar = document.getElementById('inlineCalendar');
|
||||||
|
if (!calendar) return;
|
||||||
|
|
||||||
|
const year = calendarState.currentDate.getFullYear();
|
||||||
|
const month = calendarState.currentDate.getMonth();
|
||||||
|
|
||||||
|
const monthName = getMonthName(month, year);
|
||||||
|
|
||||||
|
let html = `
|
||||||
|
<div class="calendar-header">
|
||||||
|
<div class="calendar-month-year">${monthName} ${year}</div>
|
||||||
|
<div class="calendar-nav">
|
||||||
|
<button onclick="documentFields.prevMonth()">←</button>
|
||||||
|
<button onclick="documentFields.nextMonth()">→</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="calendar-weekdays">
|
||||||
|
<div class="calendar-weekday">Пн</div>
|
||||||
|
<div class="calendar-weekday">Вт</div>
|
||||||
|
<div class="calendar-weekday">Ср</div>
|
||||||
|
<div class="calendar-weekday">Чт</div>
|
||||||
|
<div class="calendar-weekday">Пт</div>
|
||||||
|
<div class="calendar-weekday">Сб</div>
|
||||||
|
<div class="calendar-weekday">Вс</div>
|
||||||
|
</div>
|
||||||
|
<div class="calendar-days">
|
||||||
|
${generateCalendar(year, month, calendarState.selectedDate)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
calendar.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleCalendar() {
|
||||||
|
if (calendarState.isOpen) {
|
||||||
|
closeCalendar();
|
||||||
|
} else {
|
||||||
|
openCalendar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCalendar() {
|
||||||
|
const calendar = document.getElementById('inlineCalendar');
|
||||||
|
if (!calendar) return;
|
||||||
|
|
||||||
|
const dateInput = document.getElementById('documentDate');
|
||||||
|
if (dateInput && dateInput.value) {
|
||||||
|
const parsedDate = parseDate(dateInput.value);
|
||||||
|
if (parsedDate && !isNaN(parsedDate.getTime())) {
|
||||||
|
calendarState.selectedDate = parsedDate;
|
||||||
|
calendarState.currentDate = new Date(parsedDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calendarState.isOpen = true;
|
||||||
|
calendar.style.display = 'block';
|
||||||
|
renderCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeCalendar() {
|
||||||
|
const calendar = document.getElementById('inlineCalendar');
|
||||||
|
if (calendar) {
|
||||||
|
calendar.style.display = 'none';
|
||||||
|
}
|
||||||
|
calendarState.isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectDate(dateStr) {
|
||||||
|
const dateInput = document.getElementById('documentDate');
|
||||||
|
if (dateInput) {
|
||||||
|
dateInput.value = dateStr;
|
||||||
|
calendarState.selectedDate = parseDate(dateStr);
|
||||||
|
}
|
||||||
|
closeCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
function prevMonth() {
|
||||||
|
calendarState.currentDate.setMonth(calendarState.currentDate.getMonth() - 1);
|
||||||
|
renderCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextMonth() {
|
||||||
|
calendarState.currentDate.setMonth(calendarState.currentDate.getMonth() + 1);
|
||||||
|
renderCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
// Создание модального окна
|
// Создание модального окна
|
||||||
function createModal() {
|
function createModal() {
|
||||||
if (!document.getElementById('document-fields-styles')) {
|
if (!document.getElementById('document-fields-styles')) {
|
||||||
@@ -411,7 +1007,11 @@
|
|||||||
|
|
||||||
<div class="document-field-group">
|
<div class="document-field-group">
|
||||||
<label for="documentDate">Дата документа:</label>
|
<label for="documentDate">Дата документа:</label>
|
||||||
<input type="text" id="documentDate" placeholder="ДД.ММ.ГГГГ" maxlength="10">
|
<div class="date-input-wrapper">
|
||||||
|
<input type="text" id="documentDate" placeholder="ДД.ММ.ГГГГ" maxlength="10" readonly>
|
||||||
|
<div class="calendar-icon" onclick="documentFields.toggleCalendar()">📅</div>
|
||||||
|
<div id="inlineCalendar" class="inline-calendar" style="display: none;"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="document-field-group">
|
<div class="document-field-group">
|
||||||
@@ -430,6 +1030,18 @@
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||||||
|
|
||||||
|
// Добавляем обработчик клика вне календаря для его закрытия
|
||||||
|
document.addEventListener('click', function(event) {
|
||||||
|
const calendar = document.getElementById('inlineCalendar');
|
||||||
|
const calendarIcon = document.querySelector('.calendar-icon');
|
||||||
|
|
||||||
|
if (calendar && calendarState.isOpen) {
|
||||||
|
if (!calendar.contains(event.target) && !calendarIcon.contains(event.target)) {
|
||||||
|
closeCalendar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Открытие модального окна
|
// Открытие модального окна
|
||||||
@@ -482,7 +1094,21 @@
|
|||||||
const dateInput = document.getElementById('documentDate');
|
const dateInput = document.getElementById('documentDate');
|
||||||
|
|
||||||
if (numberInput) numberInput.value = result.data.document_n || '';
|
if (numberInput) numberInput.value = result.data.document_n || '';
|
||||||
if (dateInput) dateInput.value = result.data.document_d || '';
|
if (dateInput) {
|
||||||
|
dateInput.value = result.data.document_d || '';
|
||||||
|
|
||||||
|
// Устанавливаем выбранную дату в календарь
|
||||||
|
if (result.data.document_d) {
|
||||||
|
const parsedDate = parseDate(result.data.document_d);
|
||||||
|
if (parsedDate && !isNaN(parsedDate.getTime())) {
|
||||||
|
calendarState.selectedDate = parsedDate;
|
||||||
|
calendarState.currentDate = new Date(parsedDate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
calendarState.selectedDate = null;
|
||||||
|
calendarState.currentDate = new Date();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result.data.document_a && authorInput && !authorInput.value) {
|
if (result.data.document_a && authorInput && !authorInput.value) {
|
||||||
authorInput.value = result.data.document_a;
|
authorInput.value = result.data.document_a;
|
||||||
@@ -510,6 +1136,17 @@
|
|||||||
if (numberInput) numberInput.value = '';
|
if (numberInput) numberInput.value = '';
|
||||||
if (dateInput) dateInput.value = '';
|
if (dateInput) dateInput.value = '';
|
||||||
if (authorInput) authorInput.value = '';
|
if (authorInput) authorInput.value = '';
|
||||||
|
|
||||||
|
// Закрываем календарь
|
||||||
|
closeCalendar();
|
||||||
|
|
||||||
|
// Сбрасываем состояние календаря
|
||||||
|
calendarState = {
|
||||||
|
currentDate: new Date(),
|
||||||
|
selectedDate: null,
|
||||||
|
isOpen: false,
|
||||||
|
inputElement: null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,6 +1300,11 @@
|
|||||||
async function addButtonToExpandedTask(taskCard) {
|
async function addButtonToExpandedTask(taskCard) {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
|
|
||||||
|
// Проверяем, имеет ли пользователь доступ (Подписант или Секретарь)
|
||||||
|
if (!hasUserAccess()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const taskId = taskCard.dataset.taskId;
|
const taskId = taskCard.dataset.taskId;
|
||||||
if (!taskId) return;
|
if (!taskId) return;
|
||||||
|
|
||||||
@@ -772,16 +1414,34 @@
|
|||||||
console.log('🗑️ Кэш типов задач очищен');
|
console.log('🗑️ Кэш типов задач очищен');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Функция для ручного обновления групп пользователя
|
||||||
|
async function refreshUserGroups() {
|
||||||
|
if (currentUser) {
|
||||||
|
await getAllUserGroups(currentUser.login || currentUser.id);
|
||||||
|
return hasUserAccess();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Инициализация
|
// Инициализация
|
||||||
async function init() {
|
async function init() {
|
||||||
console.log('🔄 DocumentFields module initializing...');
|
console.log('🔄 DocumentFields module initializing...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await getCurrentUser();
|
await getCurrentUser();
|
||||||
|
|
||||||
|
// Проверяем доступ пользователя
|
||||||
|
if (hasUserAccess()) {
|
||||||
createModal();
|
createModal();
|
||||||
observeDOM();
|
observeDOM();
|
||||||
|
console.log('✅ DocumentFields module loaded (with access)');
|
||||||
|
|
||||||
console.log('✅ DocumentFields module loaded');
|
// Для отладки показываем группы
|
||||||
|
setTimeout(debugUserGroups, 1000);
|
||||||
|
} else {
|
||||||
|
console.log('ℹ️ DocumentFields module loaded (no access - user is not Signer or Secretary)');
|
||||||
|
debugUserGroups(); // Показываем отладку даже при отсутствии доступа
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка инициализации:', error);
|
console.error('❌ Ошибка инициализации:', error);
|
||||||
}
|
}
|
||||||
@@ -796,7 +1456,16 @@
|
|||||||
clearCache,
|
clearCache,
|
||||||
init,
|
init,
|
||||||
addButtonToExpandedTask,
|
addButtonToExpandedTask,
|
||||||
isTaskExpanded
|
isTaskExpanded,
|
||||||
|
hasUserAccess,
|
||||||
|
getAllUserGroups,
|
||||||
|
refreshUserGroups,
|
||||||
|
debugUserGroups,
|
||||||
|
// Функции календаря
|
||||||
|
toggleCalendar,
|
||||||
|
selectDate,
|
||||||
|
prevMonth,
|
||||||
|
nextMonth
|
||||||
};
|
};
|
||||||
|
|
||||||
// Запускаем инициализацию
|
// Запускаем инициализацию
|
||||||
|
|||||||
@@ -82,15 +82,6 @@ navButtons.push(
|
|||||||
id: "create-task-btn"
|
id: "create-task-btn"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (currentUser && navbar_checkUserGroup('1Секретарь') || currentUser && currentUser.role === 'admin') {
|
|
||||||
navButtons.push({
|
|
||||||
onclick: "TasksType.show('document')",
|
|
||||||
className: "nav-btn tasks",
|
|
||||||
icon: "fas fa-list",
|
|
||||||
text: "Согласование",
|
|
||||||
id: "create-task-btn"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
navButtons.push(
|
navButtons.push(
|
||||||
{
|
{
|
||||||
onclick: "showKanbanSection()",
|
onclick: "showKanbanSection()",
|
||||||
|
|||||||
Reference in New Issue
Block a user