This commit is contained in:
Xander 2025-04-08 16:49:44 +03:00
parent 41e390ac45
commit ed8b3068fc
12 changed files with 73665 additions and 81503 deletions

38793
logs/app.log

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,20 @@
from fastapi import FastAPI, APIRouter, Depends, Request, HTTPException, Form
from fastapi import Request, Query
from typing import List, Optional
from fastapi.templating import Jinja2Templates
from fastapi.responses import RedirectResponse, HTMLResponse
from fastapi.responses import JSONResponse
import jwt
from pydantic import BaseModel
from sqlalchemy.future import select
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import joinedload, selectinload
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import and_, between
from sqlalchemy.sql import func
from sqlalchemy import or_, and_, select
from sqlalchemy import null
from datetime import datetime
from routers.auth import get_current_user
from model.database import get_async_session, Job, Client, AppliedJob, User
@ -35,7 +39,7 @@ async def product(request: Request,
)
applied_jobs = result.scalars().all()
selected_client_ids = list({job.client.id for job in applied_jobs if job.client})
result1 = await session.execute(select(User))
users = result1.scalars().all()
@ -47,6 +51,7 @@ async def product(request: Request,
return templates.TemplateResponse("product.html", {"request": request, "size": size,
"jobs": applied_jobs, "role": username["role"],
"selected_client_ids": selected_client_ids,
"username": username['username'], "users": users,
"current_path": request.url.path })#
@ -185,6 +190,177 @@ async def productoj(request: Request,
"username": username['username'], "users": users,
"current_path": request.url.path })#
@router.post("/productf")
async def product_filtered(
request: Request,
username: str = Depends(get_current_user),
session: AsyncSession = Depends(get_async_session)
):
# Преобразуем строки в списки
size = "Work"
username = username
form_data = await request.form()
# Преобразуем FormData в словарь, где ключи - имена полей, значения - списки значений
form_dict = {}
for key, value in form_data.multi_items():
if key not in form_dict:
form_dict[key] = []
form_dict[key].append(value)
# Получаем значения для каждого поля
date_requested = form_dict.get('date_requested', [''])[0]
date_requested_from = form_dict.get('date_requested_from', [''])[0]
date_requested_to = form_dict.get('date_requested_to', [''])[0]
date_posted = form_dict.get('date_posted', [''])[0] # Обратите внимание на возможную опечатку в ключе ('date_posted' vs 'date_posted')
date_posted_from = form_dict.get('date_posted_from', [''])[0]
date_posted_to = form_dict.get('date_posted_to', [''])[0]
date_applied = form_dict.get('date_applied', [''])[0]
date_applied_from = form_dict.get('date_applied_from', [''])[0]
date_applied_to = form_dict.get('date_applied_to', [''])[0]
client = form_dict.get('client', [''])
assignees = form_dict.get('assignee', []) # Это будет список всех выбранных assignees
status = form_dict.get('status', [''])
# Теперь у вас есть все значения в отдельных переменных
print(f"Date requested: {date_requested}, from: {date_requested_from}, to: {date_requested_to}")
print(f"Date posted: {date_posted}, from: {date_posted_from}, to: {date_posted_to}")
print(f"Date applied: {date_applied}, from: {date_applied_from}, to: {date_applied_to}")
print(f"Client: {client}")
print(f"Assignees: {assignees}")
print(f"Status: {status}")
# Строим базовый запрос с join таблиц
# Строим базовый запрос
# Получаем все выбранные значения
filters = {
'date_requested': form_dict.get('date_requested', [''])[0],
'date_requested_from': form_dict.get('date_requested_from', [''])[0],
'date_requested_to': form_dict.get('date_requested_to', [''])[0],
'date_posted': form_dict.get('date_posted', [''])[0],
'date_posted_from': form_dict.get('date_posted_from', [''])[0],
'date_posted_to': form_dict.get('date_posted_to', [''])[0],
'date_applied': form_dict.get('date_applied', [''])[0],
'date_applied_from': form_dict.get('date_applied_from', [''])[0],
'date_applied_to': form_dict.get('date_applied_to', [''])[0],
'client': form_dict.get('client', []), # Список всех выбранных клиентов
'assignees': form_dict.get('assignee', []), # Список всех выбранных assignees
'status': form_dict.get('status', []), # Список всех выбранных статусов
}
# Строим базовый запрос с загрузкой связанных данных
stmt = select(AppliedJob).options(
selectinload(AppliedJob.job),
selectinload(AppliedJob.client),
selectinload(AppliedJob.users)
)
# Добавляем условия фильтрации
conditions = []
# Фильтр по дате requested
if date_requested == 'custom_date_requested' and date_requested_from and date_requested_to:
start = datetime.strptime(date_requested_from, '%Y-%m-%d')
end = datetime.strptime(date_requested_to, '%Y-%m-%d')
conditions.append(Job.data_requested.between(start, end))
elif date_requested == 'Today':
conditions.append(func.date(Job.data_requested) == func.current_date())
elif date_requested == 'Yesterday':
conditions.append(func.date(Job.data_requested) == func.date_sub(func.current_date(), 1))
elif date_requested == 'Last 7 days':
conditions.append(Job.data_requested >= func.date_sub(func.current_date(), 7))
# Фильтр по дате posted
if date_posted == 'custom_date_posted' and date_posted_from and date_posted_to:
start = datetime.strptime(date_posted_from, '%Y-%m-%d')
end = datetime.strptime(date_posted_to, '%Y-%m-%d')
conditions.append(Job.date_posted.between(start, end))
elif date_posted == 'Today':
conditions.append(func.date(Job.date_posted) == func.current_date())
elif date_posted == 'Yesterday':
conditions.append(func.date(Job.date_posted) == func.date_sub(func.current_date(), 1))
elif date_posted == 'Last 7 days':
conditions.append(Job.date_posted >= func.date_sub(func.current_date(), 7))
# Фильтр по дате applied
if date_applied == 'custom_date_applied' and date_applied_from and date_applied_to:
start = datetime.strptime(date_applied_from, '%Y-%m-%d')
end = datetime.strptime(date_applied_to, '%Y-%m-%d')
conditions.append(AppliedJob.applied_on.between(start, end))
elif date_applied == 'Today':
conditions.append(func.date(AppliedJob.applied_on) == func.current_date())
elif date_applied == 'Yesterday':
conditions.append(func.date(AppliedJob.applied_on) == func.date_sub(func.current_date(), 1))
elif date_applied == 'Last 7 days':
conditions.append(AppliedJob.applied_on >= func.date_sub(func.current_date(), 7))
# Фильтр по клиенту
if client and client[0]:
conditions.append(AppliedJob.client_id.in_([int(c) for c in client]))
# Фильтр по assignees
if assignees:
conditions.append(AppliedJob.assignee.in_([int(a) for a in assignees]))
# Фильтр по статусу
if status and status[0]:
conditions.append(AppliedJob.status.in_(status))
# Применяем все условия
if conditions:
stmt = stmt.join(Job).where(and_(*conditions))
# Выполняем запрос
try:
result = await session.execute(stmt)
applied_jobs = result.scalars().all()
# # Форматируем результат
# response_data = []
# for job in applied_jobs:
# response_data.append({
# "id": job.id,
# "client_id": job.client_id,
# "job_id": job.job_id,
# "applied_on": job.applied_on.isoformat() if job.applied_on else None,
# "status": job.status,
# "assignee": job.assignee,
# "priority": job.priority,
# "job": {
# "job_title": job.job.job_title,
# "company": job.job.job_company,
# "date_posted": job.job.date_posted.isoformat() if job.job.date_posted else None
# } if job.job else None
# })
# print(f'{"status": "success", "data": response_data}')
except Exception as e:
await session.rollback()
raise HTTPException(status_code=500, detail=str(e))
return templates.TemplateResponse(
"productf.html",
{
"request": request, "size": size,
"jobs": applied_jobs, "role": username["role"],
"username": username['username'],# "users": users,
"role": username["role"],
"current_path": request.url.path
}
)
# Pydantic модель запроса
class StatusUpdate(BaseModel):
job_id: int

File diff suppressed because it is too large Load Diff

150
templates/filtr.html Normal file
View File

@ -0,0 +1,150 @@
<!-- BEGIN: Inbox Filter -->
<div class="intro-y flex flex-col-reverse sm:flex-row items-center">
<div class="w-full sm:w-auto relative mr-auto mt-3 sm:mt-0">
<i class="w-4 h-4 absolute my-auto inset-y-0 ml-3 left-0 z-10 text-gray-700" data-feather="search"></i>
<input type="text" class="input w-full sm:w-64 box px-10 text-gray-700 placeholder-theme-13" placeholder="Filter">
<div class="inbox-filter dropdown absolute inset-y-0 mr-3 right-0 flex items-center">
<i class="dropdown-toggle w-4 h-4 cursor-pointer text-gray-700" data-feather="chevron-down"></i>
<div class="inbox-filter__dropdown-box dropdown-box mt-10 absolute top-0 left-0 z-20">
<div class="dropdown-box__content box p-5">
<div class="grid grid-cols-12 gap-4 row-gap-3">
<div class="col-span-6">
<div class="text-xs">Requested o</div>
<select id="" class="input w-full border mt-2 flex-1">
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="date_range1">from date to date</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Posted on</div>
<select data-placeholder="Select Posted on" id="" class="input w-full border mt-2 flex-1">
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="date_range">from date to date</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Applied on</div>
<select id="applied-select" class="input w-full border mt-2 flex-1">
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="date_applied">from date to date</option>
</select>
<!-- обёртка для диапазона дат (по умолчанию скрыта) -->
<div id="date-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input id="date-from" class="datepicker input w-full border mt-2">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input id="date-to" class="datepicker input w-full border mt-2">
</div>
</div>
</div>
<script>
const select = document.getElementById('applied-select');
const dateRangeWrapper = document.getElementById('date-range-wrapper');
const dateFrom = document.getElementById('date-from');
const dateTo = document.getElementById('date-to');
select.addEventListener('change', function () {
if (this.value === 'date_applied') {
dateRangeWrapper.style.display = 'grid';
} else {
dateRangeWrapper.style.display = 'none';
dateFrom.value = '';
dateTo.value = '';
}
});
// Если хочешь — можно сохранить даты как data-атрибут или передать их куда нужно
dateFrom.addEventListener('change', function () {
select.setAttribute('data-date-from', this.value);
});
dateTo.addEventListener('change', function () {
select.setAttribute('data-date-to', this.value);
});
</script>
<div class="col-span-6">
<div class="mt-3">
<label>Clients</label>
<div class="mt-2">
<select data-placeholder="Select Clients" id="clients-multiselect" class="select2 w-full" multiple="multiple">
{% set seen_ids = [] %}
{% for job in jobs %}
{% if job.client.id not in seen_ids %}
<option value="{{ job.client.id }}">{{ job.client.user_nicename }}</option>
{% set _ = seen_ids.append(job.client.id) %}
{% endif %}
{% endfor %}
</select>
</div>
</div>
</div>
<div class="col-span-6">
<div class="mt-3">
<label>Assignee</label>
<div class="mt-2">
<select data-placeholder="Select Assignee" class="select2 w-full" multiple="multiple" name="assignee">
<option value="">— Not selected —</option>
{% for user in users %}
<option value="{{ user.id }}"
{% if user.id in assigned_users_ids %}selected{% endif %}>
{{ user.username }}
</option>
{% endfor %}
</select>
</div>
</div>
</div>
<div class="col-span-6">
<div class="mt-3">
<label>Status</label>
<div class="mt-2">
<select data-placeholder="Select Status" class="select2 w-full" multiple>
<option value="Scheduled" >Scheduled</option>
<option value="Requested">Requested</option>
<option value="In-Progress">In-Progress</option>
<option value="Applied">Applied</option>
<option value="Issues Applying">Issues Applying</option>
<option value="Cancelled">Cancelled</option>
</select>
</div>
</div>
</div>
<div class="col-span-12 flex items-center mt-3">
<button class="button w-32 justify-center block bg-theme-1 text-white ml-2">Search</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="w-full sm:w-auto flex">
<div class="dropdown relative">
</div>
</div>
</div>
<!-- END: Inbox Filter -->

View File

@ -3,106 +3,206 @@
{% block title %}Главная{% endblock %}
{% block content %}
<style>
.select2-container {
width: 100% !important;
}
</style>
<style>
.filter-link {
font-size: 18px; /* Увеличиваем размер шрифта */
font-weight: bold; /* Делаем текст жирным */
padding: 10px 20px; /* Добавляем отступы для лучшего кликабельности */
background-color: #7a61fe; /* Добавляем зелёный фон */
color: white; /* Текст белый */
border-radius: 5px; /* Слегка скругляем углы */
text-align: center; /* Выравнивание по центру */
cursor: pointer; /* Курсор в виде указателя при наведении */
transition: background-color 0.3s ease, transform 0.2s ease; /* Плавные анимации */
}
.filter-link:hover {
background-color: #7a61fe; /* Темнее при наведении */
transform: scale(1.002); /* Немного увеличиваем размер при наведении */
}
</style>
<div class="intro-y flex flex-col sm:flex-row items-center mt-8">
<h2 class="text-lg font-medium mr-auto">
Tasks
</h2>
<!-- BEGIN: Inbox Filter -->
<div class="intro-y flex flex-col-reverse sm:flex-row items-center">
<div class="w-full sm:w-auto relative mr-auto mt-3 sm:mt-0">
<i class="w-4 h-4 absolute my-auto inset-y-0 ml-3 left-0 z-10 text-gray-700" data-feather="search"></i>
<input type="text" class="input w-full sm:w-64 box px-10 text-gray-700 placeholder-theme-13" placeholder="Filter">
<div class="inbox-filter dropdown absolute inset-y-0 mr-3 right-0 flex items-center">
<i class="dropdown-toggle w-4 h-4 cursor-pointer text-gray-700" data-feather="chevron-down"></i>
<div class="inbox-filter__dropdown-box dropdown-box mt-10 absolute top-0 left-0 z-20">
<div class="dropdown-box__content box p-5">
<div class="grid grid-cols-12 gap-4 row-gap-3">
<div class="col-span-6">
<div class="text-xs">Requested o</div>
<select class="input w-full border mt-2 flex-1">
<option value="" selected disabled>Выберите</option>
<option>Сегодня</option>
<option>Последние 7 дней</option>
<option>За все время</option>
<option>От даты - до даты.</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Posted on</div>
<select class="input w-full border mt-2 flex-1">
<option value="" selected disabled>Выберите</option>
<option>Сегодня</option>
<option>Последние 7 дней</option>
<option>За все время</option>
<option>От даты - до даты.</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Applied on</div>
<select class="input w-full border mt-2 flex-1">
<option value="" selected disabled>Выберите</option>
<option>Сегодня</option>
<option>Последние 7 дней</option>
<option>За все время</option>
<option>От даты - до даты.</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Client</div>
<select class="input w-full border mt-2 flex-1">
<option value="" selected disabled>Выберите</option>
<option>-</option>
<option>-</option>
<option>-</option>
<option>-</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Assignee</div>
<select class="select2 w-full" multiple>
<option value="" selected disabled>Выберите</option>
<option>-</option>
<option>-</option>
<option>-</option>
<option>-</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Status</div>
<select class="select2 w-full" multiple>
<option>Scheduled</option>
<option>Requested</option>
<option>Applying</option>
<option>Applied</option>
</select>
</div>
<div class="col-span-12 flex items-center mt-3">
<button class="button w-32 justify-center block bg-theme-1 text-white ml-2">Search</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="w-full sm:w-auto flex">
<div class="dropdown relative">
</div>
</div>
</div>
<!-- END: Inbox Filter -->
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productuj'">Unasigned Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productmj'">My Jobs</button>
<button class="button text-white bg-red-500 hover:bg-red-700 shadow-md mr-2" onclick="window.location.href='/product'">All Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productoj'">Outstanding Jobs</button>
</div>
<div class="p-5" id="boxed-accordion">
<div class="preview">
<div class="accordion">
<div class="accordion__pane border border-gray-200 p-4 mt-3">
<a href="javascript:;" class="accordion__pane__toggle filter-link font-medium block">Filter</a>
<div class="accordion__pane__content mt-3 text-gray-800 leading-relaxed">
<div class="dropdown-box__content box p-5">
<form method="POST" action="/productf">
<!-- Дата-фильтры (Requested, Posted, Applied) -->
<div class="flex gap-4">
<!-- Requested on -->
<div class="w-1/2">
<div class="text-xs">Requested on</div>
<select id="date_requested" class="input w-full border mt-2 flex-1" name="date_requested">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_requested">from date to date</option>
</select>
<div id="date-requested-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-requested-from" class="input w-full border mt-2" name="date_requested_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-requested-to" class="input w-full border mt-2" name="date_requested_to">
</div>
</div>
</div>
<!-- Posted on -->
<div class="w-1/2">
<div class="text-xs">Posted on</div>
<select id="date_posted" class="input w-full border mt-2 flex-1" name="date_posted">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_posted">from date to date</option>
</select>
<div id="date-posted-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-posted-from" class="input w-full border mt-2" name="date_posted_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-posted-to" class="input w-full border mt-2" name="date_posted_to">
</div>
</div>
</div>
</div>
<!-- Applied on -->
<div class="col-span-6 mt-4">
<div class="text-xs">Applied on</div>
<select id="date_applied" class="input w-full border mt-2 flex-1" name="date_applied">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_applied">from date to date</option>
</select>
<div id="date-applied-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-applied-from" class="input w-full border mt-2" name="date_applied_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-applied-to" class="input w-full border mt-2" name="date_applied_to">
</div>
</div>
</div>
<div class="flex flex-nowrap gap-4 mt-4"> <!-- flex-nowrap запрещает перенос на новую строку -->
<!-- Clients (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 + min-w-0 предотвращает "расползание" -->
<label class="block mb-1 text-sm">Clients</label>
<select id="clients-multiselect1" class="select2 w-full" multiple="multiple" data-placeholder="Select Clients" name="client">
{% set seen_ids = [] %}
{% for job in jobs %}
{% if job.client.id not in seen_ids %}
<option value="{{ job.client.id }}">{{ job.client.user_nicename }}</option>
{% set _ = seen_ids.append(job.client.id) %}
{% endif %}
{% endfor %}
</select>
</div>
<!-- Assignee (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 дает равную ширину -->
<label class="block mb-1 text-sm">Assignee</label>
<select id="assignee-multiselect" class="select2 w-full" multiple="multiple" data-placeholder="Select Assignee" name="assignee">
<option value="">— Not selected —</option>
{% for user in users %}
<option value="{{ user.id }}" {% if user.id in assigned_users_ids %}selected{% endif %}>{{ user.username }}</option>
{% endfor %}
</select>
</div>
</div>
<!-- Status и кнопка Apply Filters -->
<div class="col-span-6 mt-4">
<label>Status</label>
<div class="mt-2">
<select data-placeholder="Select Status" class="select2 w-full" multiple name="status">
<option value="Scheduled">Scheduled</option>
<option value="Requested">Requested</option>
<option value="In-Progress">In-Progress</option>
<option value="Applied">Applied</option>
<option value="Issues Applying">Issues Applying</option>
<option value="Cancelled">Cancelled</option>
</select>
</div>
<button type="submit" class="button bg-theme-1 text-white mt-5">Apply Filters</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Функция для настройки обработчиков событий для каждого селекта
function setupDateSelect(selectId, wrapperId, customValue) {
const select = document.getElementById(selectId);
const dateRangeWrapper = document.getElementById(wrapperId);
select.addEventListener('change', function() {
if (this.value === customValue) {
dateRangeWrapper.style.display = 'grid';
} else {
dateRangeWrapper.style.display = 'none';
}
});
// Инициализация при загрузке страницы
if (select.value === customValue) {
dateRangeWrapper.style.display = 'grid';
}
}
// Настройка всех трех селектов
document.addEventListener('DOMContentLoaded', function() {
setupDateSelect('date_requested', 'date-requested-range-wrapper', 'custom_date_requested');
setupDateSelect('date_posted', 'date-posted-range-wrapper', 'custom_date_posted');
setupDateSelect('date_applied', 'date-applied-range-wrapper', 'custom_date_applied');
});
</script>
<!-- BEGIN: Datatable -->
<div class="intro-y datatable-wrapper box p-5 mt-5">
<table class="table table-report table-report--bordered display datatable w-full">
<thead>
@ -163,10 +263,70 @@
</tbody>
</table>
</div>
<!-- END: Datatable -->
<!-- Включаем модалку -->
<!-- Модальное окно -->
{% include "modal.html" %}
<script src="/static/dist/js/prod.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Инициализация Select2
$('#clients-multiselect').select2();
// Получение выбранных ID при изменении
$('#clients-multiselect').on('change', function() {
const selectedIds = $(this).val(); // Массив выбранных ID
console.log("Selected Client IDs:", selectedIds);
// Отправка на сервер (пример)
fetch('/update-selected-clients', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({client_ids: selectedIds})
})
.then(response => response.json())
.then(data => console.log(data));
});
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Инициализация всех select2
$('.select2').select2();
// Обработчик кнопки Search
$('.button.bg-theme-1').on('click', function() {
// Собираем все данные фильтров
const filters = {
clients: $('#clients-multiselect').val() || [],
assignees: $('select[name="assignee"]').val() || [],
statuses: $('select[placeholder="Select categories"]').val() || [],
requested: $('#applied-select:eq(0)').val(),
posted: $('#applied-select:eq(1)').val(),
applied: $('#applied-select:eq(2)').val(),
date_range: $('#date-range-input').val()
};
// Переходим на /productf с параметрами
const queryString = new URLSearchParams();
if (filters.clients.length) queryString.append('clients', filters.clients.join(','));
if (filters.assignees.length) queryString.append('assignees', filters.assignees.join(','));
if (filters.statuses.length) queryString.append('statuses', filters.statuses.join(','));
if (filters.requested) queryString.append('requested', filters.requested);
if (filters.posted) queryString.append('posted', filters.posted);
if (filters.applied) queryString.append('applied', filters.applied);
if (filters.date_range) queryString.append('date_range', filters.date_range);
window.location.href = `/productf?${queryString.toString()}`;
});
});
</script>
{% endblock %}

85
templates/productf.html Normal file
View File

@ -0,0 +1,85 @@
{% extends "base.html" %}
{% block title %}Главная{% endblock %}
{% block content %}
<div class="intro-y flex flex-col sm:flex-row items-center mt-8">
<h2 class="text-lg font-medium mr-auto">
Tasks
</h2>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productuj'">Unasigned Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productmj'">My Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/product'">All Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productoj'">Outstanding Jobs</button>
</div>
<!-- BEGIN: Datatable -->
<div class="intro-y datatable-wrapper box p-5 mt-5">
<table class="table table-report table-report--bordered display datatable w-full">
<thead>
<tr>
<th class="border-b-2 whitespace-no-wrap">TITLE &#10760;</th>
<th class="border-b-2 text-center whitespace-no-wrap">COMPANY &#10760;</th>
<th class="border-b-2 text-center whitespace-no-wrap">CLIENT &#10760;</th>
<th class="border-b-2 text-center whitespace-no-wrap">Requested on &#10760;</th>
<th class="border-b-2 text-center whitespace-no-wrap">Posted on &#10760;</th>
<th class="border-b-2 text-center whitespace-no-wrap">STATUS</th>
<th class="border-b-2 text-center whitespace-no-wrap">Assignee</th>
<th class="border-b-2 text-center whitespace-no-wrap">Applied on</th>
</tr>
</thead>
<tbody>
{% for job in jobs %}
<tr>
<td class="border-b">
<a href="{{ job.job.link }}" target="_blank" class="font-medium whitespace-no-wrap">{{ job.job.job_title [:50] }}</a>
<div class="text-gray-600 text-xs whitespace-no-wrap">{{ job.job.job_id }}</div>
</td>
<td class="text-center border-b">{{job.job.job_company}}</td>
<td class="text-center border-b">
<a href="#" class="client-link text-blue-500" data-client-id="{{ job.client.id }}">{{ job.client.user_nicename }}</a>
</td>
<td class="text-center border-b">{{job.job.data_requested}}</td>
<td class="text-center border-b">{{job.job.date_posted}}</td>
<td class="text-center border-b">
<select class="select2" onchange="updateStatus(this, '{{ job.id }}')">
<option value="" {% if job.status is none %}selected{% endif %}>— Not selected —</option>
<option value="Scheduled" {% if job.status == "Scheduled" %}selected{% endif %}>Scheduled</option>
<option value="Requested" {% if job.status == "Requested" %}selected{% endif %}>Requested</option>
<option value="In-Progress" {% if job.status == "In-Progress" %}selected{% endif %}>In-Progress</option>
<option value="Applied" {% if job.status == "Applied" %}selected{% endif %}>Applied</option>
<option value="Issues Applying" {% if job.status == "Issues Applying" %}selected{% endif %}>Issues Applying</option>
<option value="Cancelled" {% if job.status == "Cancelled" %}selected{% endif %}>Cancelled</option>
</select>
</td>
<td class="text-center border-b">
<select class="select2" onchange="updateAssignee(this, '{{ job.id }}')">
<option value="" {% if job.assignee is none %}selected{% endif %}>— Not selected —</option>
{% for user in users %}
<option value="{{ user.id }}" {% if job.assignee == user.id %}selected{% endif %}>
{{ user.username }}
</option>
{% endfor %}
</select>
</td>
<td class="text-center border-b">-</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- END: Datatable -->
<!-- Включаем модалку -->
<!-- Модальное окно -->
{% include "modal.html" %}
<script src="/static/dist/js/prod.js"></script>
{% endblock %}

View File

@ -3,17 +3,202 @@
{% block title %}Главная{% endblock %}
{% block content %}
<style>
.select2-container {
width: 100% !important;
}
</style>
<style>
.filter-link {
font-size: 18px; /* Увеличиваем размер шрифта */
font-weight: bold; /* Делаем текст жирным */
padding: 10px 20px; /* Добавляем отступы для лучшего кликабельности */
background-color: #7a61fe; /* Добавляем зелёный фон */
color: white; /* Текст белый */
border-radius: 5px; /* Слегка скругляем углы */
text-align: center; /* Выравнивание по центру */
cursor: pointer; /* Курсор в виде указателя при наведении */
transition: background-color 0.3s ease, transform 0.2s ease; /* Плавные анимации */
}
.filter-link:hover {
background-color: #7a61fe; /* Темнее при наведении */
transform: scale(1.002); /* Немного увеличиваем размер при наведении */
}
</style>
<div class="intro-y flex flex-col sm:flex-row items-center mt-8">
<h2 class="text-lg font-medium mr-auto">
Tasks
</h2>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productuj'">Unasigned Jobs</button>
<button class="button text-white bg-red-500 hover:bg-red-700 shadow-md mr-2" onclick="window.location.href='/productmj'">My Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/product'">All Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productuj'">Unasigned Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productmj'">My Jobs</button>
<button class="button text-white bg-red-500 hover:bg-red-700 shadow-md mr-2" onclick="window.location.href='/product'">All Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productoj'">Outstanding Jobs</button>
</div>
<div class="p-5" id="boxed-accordion">
<div class="preview">
<div class="accordion">
<div class="accordion__pane border border-gray-200 p-4 mt-3">
<a href="javascript:;" class="accordion__pane__toggle filter-link font-medium block">Filter</a>
<div class="accordion__pane__content mt-3 text-gray-800 leading-relaxed">
<div class="dropdown-box__content box p-5">
<form method="POST" action="/productf">
<!-- Дата-фильтры (Requested, Posted, Applied) -->
<div class="flex gap-4">
<!-- Requested on -->
<div class="w-1/2">
<div class="text-xs">Requested on</div>
<select id="date_requested" class="input w-full border mt-2 flex-1" name="date_requested">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_requested">from date to date</option>
</select>
<div id="date-requested-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-requested-from" class="input w-full border mt-2" name="date_requested_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-requested-to" class="input w-full border mt-2" name="date_requested_to">
</div>
</div>
</div>
<!-- Posted on -->
<div class="w-1/2">
<div class="text-xs">Posted on</div>
<select id="date_posted" class="input w-full border mt-2 flex-1" name="date_posted">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_posted">from date to date</option>
</select>
<div id="date-posted-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-posted-from" class="input w-full border mt-2" name="date_posted_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-posted-to" class="input w-full border mt-2" name="date_posted_to">
</div>
</div>
</div>
</div>
<!-- Applied on -->
<div class="col-span-6 mt-4">
<div class="text-xs">Applied on</div>
<select id="date_applied" class="input w-full border mt-2 flex-1" name="date_applied">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_applied">from date to date</option>
</select>
<div id="date-applied-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-applied-from" class="input w-full border mt-2" name="date_applied_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-applied-to" class="input w-full border mt-2" name="date_applied_to">
</div>
</div>
</div>
<div class="flex flex-nowrap gap-4 mt-4"> <!-- flex-nowrap запрещает перенос на новую строку -->
<!-- Clients (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 + min-w-0 предотвращает "расползание" -->
<label class="block mb-1 text-sm">Clients</label>
<select id="clients-multiselect1" class="select2 w-full" multiple="multiple" data-placeholder="Select Clients" name="client">
{% set seen_ids = [] %}
{% for job in jobs %}
{% if job.client.id not in seen_ids %}
<option value="{{ job.client.id }}">{{ job.client.user_nicename }}</option>
{% set _ = seen_ids.append(job.client.id) %}
{% endif %}
{% endfor %}
</select>
</div>
<!-- Assignee (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 дает равную ширину -->
<label class="block mb-1 text-sm">Assignee</label>
<select id="assignee-multiselect" class="select2 w-full" multiple="multiple" data-placeholder="Select Assignee" name="assignee">
<option value="">— Not selected —</option>
{% for user in users %}
<option value="{{ user.id }}" {% if user.id in assigned_users_ids %}selected{% endif %}>{{ user.username }}</option>
{% endfor %}
</select>
</div>
</div>
<!-- Status и кнопка Apply Filters -->
<div class="col-span-6 mt-4">
<label>Status</label>
<div class="mt-2">
<select data-placeholder="Select Status" class="select2 w-full" multiple name="status">
<option value="Scheduled">Scheduled</option>
<option value="Requested">Requested</option>
<option value="In-Progress">In-Progress</option>
<option value="Applied">Applied</option>
<option value="Issues Applying">Issues Applying</option>
<option value="Cancelled">Cancelled</option>
</select>
</div>
<button type="submit" class="button bg-theme-1 text-white mt-5">Apply Filters</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Функция для настройки обработчиков событий для каждого селекта
function setupDateSelect(selectId, wrapperId, customValue) {
const select = document.getElementById(selectId);
const dateRangeWrapper = document.getElementById(wrapperId);
select.addEventListener('change', function() {
if (this.value === customValue) {
dateRangeWrapper.style.display = 'grid';
} else {
dateRangeWrapper.style.display = 'none';
}
});
// Инициализация при загрузке страницы
if (select.value === customValue) {
dateRangeWrapper.style.display = 'grid';
}
}
// Настройка всех трех селектов
document.addEventListener('DOMContentLoaded', function() {
setupDateSelect('date_requested', 'date-requested-range-wrapper', 'custom_date_requested');
setupDateSelect('date_posted', 'date-posted-range-wrapper', 'custom_date_posted');
setupDateSelect('date_applied', 'date-applied-range-wrapper', 'custom_date_applied');
});
</script>
<!-- BEGIN: Datatable -->
<div class="intro-y datatable-wrapper box p-5 mt-5">
<table class="table table-report table-report--bordered display datatable w-full">

View File

@ -3,18 +3,202 @@
{% block title %}Главная{% endblock %}
{% block content %}
<style>
.select2-container {
width: 100% !important;
}
</style>
<style>
.filter-link {
font-size: 18px; /* Увеличиваем размер шрифта */
font-weight: bold; /* Делаем текст жирным */
padding: 10px 20px; /* Добавляем отступы для лучшего кликабельности */
background-color: #7a61fe; /* Добавляем зелёный фон */
color: white; /* Текст белый */
border-radius: 5px; /* Слегка скругляем углы */
text-align: center; /* Выравнивание по центру */
cursor: pointer; /* Курсор в виде указателя при наведении */
transition: background-color 0.3s ease, transform 0.2s ease; /* Плавные анимации */
}
.filter-link:hover {
background-color: #7a61fe; /* Темнее при наведении */
transform: scale(1.002); /* Немного увеличиваем размер при наведении */
}
</style>
<div class="intro-y flex flex-col sm:flex-row items-center mt-8">
<h2 class="text-lg font-medium mr-auto">
Tasks
</h2>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productuj'">Unasigned Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productmj'">My Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/product'">All Jobs</button>
<button class="button text-white bg-red-500 hover:bg-red-700 shadow-md mr-2" onclick="window.location.href='/productoj'">Outstanding Jobs</button>
<button class="button text-white bg-red-500 hover:bg-red-700 shadow-md mr-2" onclick="window.location.href='/product'">All Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productoj'">Outstanding Jobs</button>
</div>
<div class="p-5" id="boxed-accordion">
<div class="preview">
<div class="accordion">
<div class="accordion__pane border border-gray-200 p-4 mt-3">
<a href="javascript:;" class="accordion__pane__toggle filter-link font-medium block">Filter</a>
<div class="accordion__pane__content mt-3 text-gray-800 leading-relaxed">
<div class="dropdown-box__content box p-5">
<form method="POST" action="/productf">
<!-- Дата-фильтры (Requested, Posted, Applied) -->
<div class="flex gap-4">
<!-- Requested on -->
<div class="w-1/2">
<div class="text-xs">Requested on</div>
<select id="date_requested" class="input w-full border mt-2 flex-1" name="date_requested">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_requested">from date to date</option>
</select>
<div id="date-requested-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-requested-from" class="input w-full border mt-2" name="date_requested_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-requested-to" class="input w-full border mt-2" name="date_requested_to">
</div>
</div>
</div>
<!-- Posted on -->
<div class="w-1/2">
<div class="text-xs">Posted on</div>
<select id="date_posted" class="input w-full border mt-2 flex-1" name="date_posted">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_posted">from date to date</option>
</select>
<div id="date-posted-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-posted-from" class="input w-full border mt-2" name="date_posted_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-posted-to" class="input w-full border mt-2" name="date_posted_to">
</div>
</div>
</div>
</div>
<!-- Applied on -->
<div class="col-span-6 mt-4">
<div class="text-xs">Applied on</div>
<select id="date_applied" class="input w-full border mt-2 flex-1" name="date_applied">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_applied">from date to date</option>
</select>
<div id="date-applied-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-applied-from" class="input w-full border mt-2" name="date_applied_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-applied-to" class="input w-full border mt-2" name="date_applied_to">
</div>
</div>
</div>
<div class="flex flex-nowrap gap-4 mt-4"> <!-- flex-nowrap запрещает перенос на новую строку -->
<!-- Clients (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 + min-w-0 предотвращает "расползание" -->
<label class="block mb-1 text-sm">Clients</label>
<select id="clients-multiselect1" class="select2 w-full" multiple="multiple" data-placeholder="Select Clients" name="client">
{% set seen_ids = [] %}
{% for job in jobs %}
{% if job.client.id not in seen_ids %}
<option value="{{ job.client.id }}">{{ job.client.user_nicename }}</option>
{% set _ = seen_ids.append(job.client.id) %}
{% endif %}
{% endfor %}
</select>
</div>
<!-- Assignee (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 дает равную ширину -->
<label class="block mb-1 text-sm">Assignee</label>
<select id="assignee-multiselect" class="select2 w-full" multiple="multiple" data-placeholder="Select Assignee" name="assignee">
<option value="">— Not selected —</option>
{% for user in users %}
<option value="{{ user.id }}" {% if user.id in assigned_users_ids %}selected{% endif %}>{{ user.username }}</option>
{% endfor %}
</select>
</div>
</div>
<!-- Status и кнопка Apply Filters -->
<div class="col-span-6 mt-4">
<label>Status</label>
<div class="mt-2">
<select data-placeholder="Select Status" class="select2 w-full" multiple name="status">
<option value="Scheduled">Scheduled</option>
<option value="Requested">Requested</option>
<option value="In-Progress">In-Progress</option>
<option value="Applied">Applied</option>
<option value="Issues Applying">Issues Applying</option>
<option value="Cancelled">Cancelled</option>
</select>
</div>
<button type="submit" class="button bg-theme-1 text-white mt-5">Apply Filters</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Функция для настройки обработчиков событий для каждого селекта
function setupDateSelect(selectId, wrapperId, customValue) {
const select = document.getElementById(selectId);
const dateRangeWrapper = document.getElementById(wrapperId);
select.addEventListener('change', function() {
if (this.value === customValue) {
dateRangeWrapper.style.display = 'grid';
} else {
dateRangeWrapper.style.display = 'none';
}
});
// Инициализация при загрузке страницы
if (select.value === customValue) {
dateRangeWrapper.style.display = 'grid';
}
}
// Настройка всех трех селектов
document.addEventListener('DOMContentLoaded', function() {
setupDateSelect('date_requested', 'date-requested-range-wrapper', 'custom_date_requested');
setupDateSelect('date_posted', 'date-posted-range-wrapper', 'custom_date_posted');
setupDateSelect('date_applied', 'date-applied-range-wrapper', 'custom_date_applied');
});
</script>
<!-- BEGIN: Datatable -->
<div class="intro-y datatable-wrapper box p-5 mt-5">
<table class="table table-report table-report--bordered display datatable w-full">

View File

@ -4,104 +4,202 @@
{% block content %}
<style>
.select2-container {
width: 100% !important;
}
</style>
<style>
.filter-link {
font-size: 18px; /* Увеличиваем размер шрифта */
font-weight: bold; /* Делаем текст жирным */
padding: 10px 20px; /* Добавляем отступы для лучшего кликабельности */
background-color: #7a61fe; /* Добавляем зелёный фон */
color: white; /* Текст белый */
border-radius: 5px; /* Слегка скругляем углы */
text-align: center; /* Выравнивание по центру */
cursor: pointer; /* Курсор в виде указателя при наведении */
transition: background-color 0.3s ease, transform 0.2s ease; /* Плавные анимации */
}
.filter-link:hover {
background-color: #7a61fe; /* Темнее при наведении */
transform: scale(1.002); /* Немного увеличиваем размер при наведении */
}
</style>
<div class="intro-y flex flex-col sm:flex-row items-center mt-8">
<h2 class="text-lg font-medium mr-auto">
Tasks
</h2>
<!-- BEGIN: Inbox Filter
<div class="intro-y flex flex-col-reverse sm:flex-row items-center">
<div class="w-full sm:w-auto relative mr-auto mt-3 sm:mt-0">
<i class="w-4 h-4 absolute my-auto inset-y-0 ml-3 left-0 z-10 text-gray-700" data-feather="search"></i>
<input type="text" class="input w-full sm:w-64 box px-10 text-gray-700 placeholder-theme-13" placeholder="Filter">
<div class="inbox-filter dropdown absolute inset-y-0 mr-3 right-0 flex items-center">
<i class="dropdown-toggle w-4 h-4 cursor-pointer text-gray-700" data-feather="chevron-down"></i>
<div class="inbox-filter__dropdown-box dropdown-box mt-10 absolute top-0 left-0 z-20">
<div class="dropdown-box__content box p-5">
<div class="grid grid-cols-12 gap-4 row-gap-3">
<div class="col-span-6">
<div class="text-xs">Requested o</div>
<select class="input w-full border mt-2 flex-1">
<option value="" selected disabled>Выберите</option>
<option>Сегодня</option>
<option>Последние 7 дней</option>
<option>За все время</option>
<option>От даты - до даты.</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Posted on</div>
<select class="input w-full border mt-2 flex-1">
<option value="" selected disabled>Выберите</option>
<option>Сегодня</option>
<option>Последние 7 дней</option>
<option>За все время</option>
<option>От даты - до даты.</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Applied on</div>
<select class="input w-full border mt-2 flex-1">
<option value="" selected disabled>Выберите</option>
<option>Сегодня</option>
<option>Последние 7 дней</option>
<option>За все время</option>
<option>От даты - до даты.</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Client</div>
<select class="input w-full border mt-2 flex-1">
<option value="" selected disabled>Выберите</option>
<option>-</option>
<option>-</option>
<option>-</option>
<option>-</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Assignee</div>
<select class="select2 w-full" multiple>
<option value="" selected disabled>Выберите</option>
<option>-</option>
<option>-</option>
<option>-</option>
<option>-</option>
</select>
</div>
<div class="col-span-6">
<div class="text-xs">Status</div>
<select class="select2 w-full" multiple>
<option>Scheduled</option>
<option>Requested</option>
<option>Applying</option>
<option>Applied</option>
</select>
</div>
<div class="col-span-12 flex items-center mt-3">
<button class="button w-32 justify-center block bg-theme-1 text-white ml-2">Search</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="w-full sm:w-auto flex">
<div class="dropdown relative">
</div>
</div>
</div>
END: Inbox Filter -->
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productuj'">Unasigned Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productmj'">My Jobs</button>
<button class="button text-white bg-red-500 hover:bg-red-700 shadow-md mr-2" onclick="window.location.href='/product'">All Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productoj'">Outstanding Jobs</button>
</div>
<div class="p-5" id="boxed-accordion">
<div class="preview">
<div class="accordion">
<div class="accordion__pane border border-gray-200 p-4 mt-3">
<a href="javascript:;" class="accordion__pane__toggle filter-link font-medium block">Filter</a>
<div class="accordion__pane__content mt-3 text-gray-800 leading-relaxed">
<div class="dropdown-box__content box p-5">
<form method="POST" action="/productf">
<!-- Дата-фильтры (Requested, Posted, Applied) -->
<div class="flex gap-4">
<!-- Requested on -->
<div class="w-1/2">
<div class="text-xs">Requested on</div>
<select id="date_requested" class="input w-full border mt-2 flex-1" name="date_requested">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_requested">from date to date</option>
</select>
<div id="date-requested-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-requested-from" class="input w-full border mt-2" name="date_requested_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-requested-to" class="input w-full border mt-2" name="date_requested_to">
</div>
</div>
</div>
<!-- Posted on -->
<div class="w-1/2">
<div class="text-xs">Posted on</div>
<select id="date_posted" class="input w-full border mt-2 flex-1" name="date_posted">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_posted">from date to date</option>
</select>
<div id="date-posted-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-posted-from" class="input w-full border mt-2" name="date_posted_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-posted-to" class="input w-full border mt-2" name="date_posted_to">
</div>
</div>
</div>
</div>
<!-- Applied on -->
<div class="col-span-6 mt-4">
<div class="text-xs">Applied on</div>
<select id="date_applied" class="input w-full border mt-2 flex-1" name="date_applied">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_applied">from date to date</option>
</select>
<div id="date-applied-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-applied-from" class="input w-full border mt-2" name="date_applied_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-applied-to" class="input w-full border mt-2" name="date_applied_to">
</div>
</div>
</div>
<div class="flex flex-nowrap gap-4 mt-4"> <!-- flex-nowrap запрещает перенос на новую строку -->
<!-- Clients (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 + min-w-0 предотвращает "расползание" -->
<label class="block mb-1 text-sm">Clients</label>
<select id="clients-multiselect1" class="select2 w-full" multiple="multiple" data-placeholder="Select Clients" name="client">
{% set seen_ids = [] %}
{% for job in jobs %}
{% if job.client.id not in seen_ids %}
<option value="{{ job.client.id }}">{{ job.client.user_nicename }}</option>
{% set _ = seen_ids.append(job.client.id) %}
{% endif %}
{% endfor %}
</select>
</div>
<!-- Assignee (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 дает равную ширину -->
<label class="block mb-1 text-sm">Assignee</label>
<select id="assignee-multiselect" class="select2 w-full" multiple="multiple" data-placeholder="Select Assignee" name="assignee">
<option value="">— Not selected —</option>
{% for user in users %}
<option value="{{ user.id }}" {% if user.id in assigned_users_ids %}selected{% endif %}>{{ user.username }}</option>
{% endfor %}
</select>
</div>
</div>
<!-- Status и кнопка Apply Filters -->
<div class="col-span-6 mt-4">
<label>Status</label>
<div class="mt-2">
<select data-placeholder="Select Status" class="select2 w-full" multiple name="status">
<option value="Scheduled">Scheduled</option>
<option value="Requested">Requested</option>
<option value="In-Progress">In-Progress</option>
<option value="Applied">Applied</option>
<option value="Issues Applying">Issues Applying</option>
<option value="Cancelled">Cancelled</option>
</select>
</div>
<button type="submit" class="button bg-theme-1 text-white mt-5">Apply Filters</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Функция для настройки обработчиков событий для каждого селекта
function setupDateSelect(selectId, wrapperId, customValue) {
const select = document.getElementById(selectId);
const dateRangeWrapper = document.getElementById(wrapperId);
select.addEventListener('change', function() {
if (this.value === customValue) {
dateRangeWrapper.style.display = 'grid';
} else {
dateRangeWrapper.style.display = 'none';
}
});
// Инициализация при загрузке страницы
if (select.value === customValue) {
dateRangeWrapper.style.display = 'grid';
}
}
// Настройка всех трех селектов
document.addEventListener('DOMContentLoaded', function() {
setupDateSelect('date_requested', 'date-requested-range-wrapper', 'custom_date_requested');
setupDateSelect('date_posted', 'date-posted-range-wrapper', 'custom_date_posted');
setupDateSelect('date_applied', 'date-applied-range-wrapper', 'custom_date_applied');
});
</script>
<!-- BEGIN: Datatable -->
<div class="intro-y datatable-wrapper box p-5 mt-5">
<table class="table table-report table-report--bordered display datatable w-full">

View File

@ -4,16 +4,202 @@
{% block content %}
<style>
.select2-container {
width: 100% !important;
}
</style>
<style>
.filter-link {
font-size: 18px; /* Увеличиваем размер шрифта */
font-weight: bold; /* Делаем текст жирным */
padding: 10px 20px; /* Добавляем отступы для лучшего кликабельности */
background-color: #7a61fe; /* Добавляем зелёный фон */
color: white; /* Текст белый */
border-radius: 5px; /* Слегка скругляем углы */
text-align: center; /* Выравнивание по центру */
cursor: pointer; /* Курсор в виде указателя при наведении */
transition: background-color 0.3s ease, transform 0.2s ease; /* Плавные анимации */
}
.filter-link:hover {
background-color: #7a61fe; /* Темнее при наведении */
transform: scale(1.002); /* Немного увеличиваем размер при наведении */
}
</style>
<div class="intro-y flex flex-col sm:flex-row items-center mt-8">
<h2 class="text-lg font-medium mr-auto">
Tasks
</h2>
<button class="button text-white bg-red-500 hover:bg-red-700 shadow-md mr-2" onclick="window.location.href='/productuj'">Unasigned Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productuj'">Unasigned Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productmj'">My Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/product'">All Jobs</button>
<button class="button text-white bg-red-500 hover:bg-red-700 shadow-md mr-2" onclick="window.location.href='/product'">All Jobs</button>
<button class="button text-white bg-theme-1 shadow-md mr-2" onclick="window.location.href='/productoj'">Outstanding Jobs</button>
</div>
<div class="p-5" id="boxed-accordion">
<div class="preview">
<div class="accordion">
<div class="accordion__pane border border-gray-200 p-4 mt-3">
<a href="javascript:;" class="accordion__pane__toggle filter-link font-medium block">Filter</a>
<div class="accordion__pane__content mt-3 text-gray-800 leading-relaxed">
<div class="dropdown-box__content box p-5">
<form method="POST" action="/productf">
<!-- Дата-фильтры (Requested, Posted, Applied) -->
<div class="flex gap-4">
<!-- Requested on -->
<div class="w-1/2">
<div class="text-xs">Requested on</div>
<select id="date_requested" class="input w-full border mt-2 flex-1" name="date_requested">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_requested">from date to date</option>
</select>
<div id="date-requested-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-requested-from" class="input w-full border mt-2" name="date_requested_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-requested-to" class="input w-full border mt-2" name="date_requested_to">
</div>
</div>
</div>
<!-- Posted on -->
<div class="w-1/2">
<div class="text-xs">Posted on</div>
<select id="date_posted" class="input w-full border mt-2 flex-1" name="date_posted">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_posted">from date to date</option>
</select>
<div id="date-posted-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-posted-from" class="input w-full border mt-2" name="date_posted_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-posted-to" class="input w-full border mt-2" name="date_posted_to">
</div>
</div>
</div>
</div>
<!-- Applied on -->
<div class="col-span-6 mt-4">
<div class="text-xs">Applied on</div>
<select id="date_applied" class="input w-full border mt-2 flex-1" name="date_applied">
<option value=""></option>
<option value="Today">Today</option>
<option value="Last 7 days">Last 7 days</option>
<option value="For all time">For all time</option>
<option value="custom_date_applied">from date to date</option>
</select>
<div id="date-applied-range-wrapper" class="p-5 grid grid-cols-12 gap-4 row-gap-3 mt-4" style="display: none;">
<div class="col-span-12 sm:col-span-6">
<label>From</label>
<input type="date" id="date-applied-from" class="input w-full border mt-2" name="date_applied_from">
</div>
<div class="col-span-12 sm:col-span-6">
<label>To</label>
<input type="date" id="date-applied-to" class="input w-full border mt-2" name="date_applied_to">
</div>
</div>
</div>
<div class="flex flex-nowrap gap-4 mt-4"> <!-- flex-nowrap запрещает перенос на новую строку -->
<!-- Clients (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 + min-w-0 предотвращает "расползание" -->
<label class="block mb-1 text-sm">Clients</label>
<select id="clients-multiselect1" class="select2 w-full" multiple="multiple" data-placeholder="Select Clients" name="client">
{% set seen_ids = [] %}
{% for job in jobs %}
{% if job.client.id not in seen_ids %}
<option value="{{ job.client.id }}">{{ job.client.user_nicename }}</option>
{% set _ = seen_ids.append(job.client.id) %}
{% endif %}
{% endfor %}
</select>
</div>
<!-- Assignee (50% ширины) -->
<div class="flex-1 min-w-0"> <!-- flex-1 дает равную ширину -->
<label class="block mb-1 text-sm">Assignee</label>
<select id="assignee-multiselect" class="select2 w-full" multiple="multiple" data-placeholder="Select Assignee" name="assignee">
<option value="">— Not selected —</option>
{% for user in users %}
<option value="{{ user.id }}" {% if user.id in assigned_users_ids %}selected{% endif %}>{{ user.username }}</option>
{% endfor %}
</select>
</div>
</div>
<!-- Status и кнопка Apply Filters -->
<div class="col-span-6 mt-4">
<label>Status</label>
<div class="mt-2">
<select data-placeholder="Select Status" class="select2 w-full" multiple name="status">
<option value="Scheduled">Scheduled</option>
<option value="Requested">Requested</option>
<option value="In-Progress">In-Progress</option>
<option value="Applied">Applied</option>
<option value="Issues Applying">Issues Applying</option>
<option value="Cancelled">Cancelled</option>
</select>
</div>
<button type="submit" class="button bg-theme-1 text-white mt-5">Apply Filters</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Функция для настройки обработчиков событий для каждого селекта
function setupDateSelect(selectId, wrapperId, customValue) {
const select = document.getElementById(selectId);
const dateRangeWrapper = document.getElementById(wrapperId);
select.addEventListener('change', function() {
if (this.value === customValue) {
dateRangeWrapper.style.display = 'grid';
} else {
dateRangeWrapper.style.display = 'none';
}
});
// Инициализация при загрузке страницы
if (select.value === customValue) {
dateRangeWrapper.style.display = 'grid';
}
}
// Настройка всех трех селектов
document.addEventListener('DOMContentLoaded', function() {
setupDateSelect('date_requested', 'date-requested-range-wrapper', 'custom_date_requested');
setupDateSelect('date_posted', 'date-posted-range-wrapper', 'custom_date_posted');
setupDateSelect('date_applied', 'date-applied-range-wrapper', 'custom_date_applied');
});
</script>
<!-- BEGIN: Datatable -->
<div class="intro-y datatable-wrapper box p-5 mt-5">
<table class="table table-report table-report--bordered display datatable w-full">