[UPDT] BASE: CRUD operations of announcement method updates

This commit is contained in:
Horilla
2024-12-23 11:33:00 +05:30
parent 3a1a2c2693
commit 4ab8e3622e
11 changed files with 166 additions and 366 deletions

View File

@@ -3,10 +3,12 @@ Module for managing announcements, including creation, updates, comments, and vi
"""
import json
from datetime import datetime, timedelta
from django.contrib import messages
from django.contrib.auth.models import User
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.urls import reverse
@@ -14,41 +16,62 @@ from django.utils.translation import gettext_lazy as _
from base.forms import AnnouncementCommentForm, AnnouncementForm
from base.methods import closest_numbers, filter_own_records
from base.models import Announcement, AnnouncementComment, AnnouncementView
from base.models import (
Announcement,
AnnouncementComment,
AnnouncementExpire,
AnnouncementView,
)
from employee.models import Employee
from horilla.decorators import login_required, permission_required
from horilla.decorators import hx_request_required, login_required, permission_required
from notifications.signals import notify
@login_required
def announcement_view(request):
"""
This method is used to render all announcemnts.
"""
announcement_list = Announcement.objects.all().order_by("-created_at")
# Set the number of items per page
items_per_page = 10
paginator = Paginator(announcement_list, items_per_page)
page = request.GET.get("page")
try:
announcements = paginator.page(page)
except PageNotAnInteger:
# If the page is not an integer, deliver the first page.
announcements = paginator.page(1)
except EmptyPage:
# If the page is out of range (e.g., 9999), deliver the last page of results.
announcements = paginator.page(paginator.num_pages)
return render(
request, "announcement/announcement.html", {"announcements": announcements}
@hx_request_required
def announcement_list(request):
general_expire_date = (
AnnouncementExpire.objects.values_list("days", flat=True).first() or 30
)
announcements = Announcement.objects.all()
announcements_to_update = []
for announcement in announcements.filter(expire_date__isnull=True):
announcement.expire_date = announcement.created_at + timedelta(
days=general_expire_date
)
announcements_to_update.append(announcement)
if announcements_to_update:
Announcement.objects.bulk_update(announcements_to_update, ["expire_date"])
announcements = announcements.filter(expire_date__gte=datetime.today().date())
if request.user.has_perm("base.view_announcement"):
announcement_list = announcements
else:
announcement_list = announcements.filter(
Q(employees=request.user.employee_get) | Q(employees__isnull=True)
)
announcement_list = announcement_list.prefetch_related(
"announcementview_set"
).order_by("-created_at")
for announcement in announcement_list:
announcement.has_viewed = announcement.announcementview_set.filter(
user=request.user, viewed=True
).exists()
instance_ids = json.dumps([instance.id for instance in announcement_list])
context = {
"announcements": announcement_list,
"general_expire_date": general_expire_date,
"instance_ids": instance_ids,
}
return render(request, "announcement/announcements_list.html", context)
@login_required
@hx_request_required
def create_announcement(request):
"""
This method renders form and template to update Announcement
@@ -110,6 +133,7 @@ def create_announcement(request):
@login_required
@hx_request_required
def delete_announcement(request, anoun_id):
"""
This method is used to delete announcements.
@@ -137,6 +161,7 @@ def delete_announcement(request, anoun_id):
@login_required
@hx_request_required
def update_announcement(request, anoun_id):
"""
This method renders form and template to update Announcement
@@ -203,6 +228,7 @@ def update_announcement(request, anoun_id):
@login_required
@hx_request_required
def create_announcement_comment(request, anoun_id):
"""
This method renders form and template to create Announcement comments
@@ -250,6 +276,7 @@ def create_announcement_comment(request, anoun_id):
@login_required
@hx_request_required
def comment_view(request, anoun_id):
"""
This method is used to view all comments in the announcements
@@ -276,6 +303,7 @@ def comment_view(request, anoun_id):
@login_required
@hx_request_required
def delete_announcement_comment(request, comment_id):
"""
This method is used to delete announcement comments
@@ -287,6 +315,7 @@ def delete_announcement_comment(request, comment_id):
@login_required
@hx_request_required
def announcement_single_view(request, anoun_id=None):
"""
This method is used to render single announcements.
@@ -317,6 +346,7 @@ def announcement_single_view(request, anoun_id=None):
@login_required
@hx_request_required
@permission_required("base.view_announcement")
def viewed_by(request):
"""

View File

@@ -1,249 +0,0 @@
{% comment %} {% extends "index.html" %} {% endcomment %}
{% load i18n %}
{% load static %}
{% load basefilters %}
{% block content %}
<section class="oh-wrapper oh-main__topbar oh-announcement_topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{% trans "Announcements" %}</h1>
</div>
{% if perms.announcemnt.add_announcemnt or request.user|is_reportingmanager %}
<div class="oh-main__titlebar oh-main__titlebar--right">
<div class="oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<button class="oh-btn oh-btn--secondary oh-btn--shadow" hx-get='{% url "create-announcement" %}'
hx-target="#createannouncementForm" hx-swap="innerHTML" data-toggle="oh-modal-toggle" data-target="#anouncementModal">
<ion-icon class="me-1" name="add-outline"></ion-icon>{% trans "Create" %}</button>
</div>
</div>
</div>
{% endif %}
</section>
<div class="oh-wrapper">
{% for anoun in announcements %}
<div class="oh-announcement_feed-card">
<div class="oh-announcement_header">
<div>
<span class="oh-announcement-headline">{{ anoun.title }}</span><br>
<span class="oh-announcement-timing"><small>
{% trans "Posted on" %} &nbsp<span class="dateformat_changer">{{ anoun.created_at|date:"F j, Y" }}</span> &nbsp
{% trans "at" %} &nbsp <span class="timeformat_changer">{{ anoun.created_at|time:"g:i A" }}</span></small>
</span>
</div>
<span>
{% if perms.announcemnt.add_announcemnt or request.user|is_reportingmanager %}
<div class="oh-dropdown" x-data="{show: false}">
<button class="oh-btn oh-btn--transparent text-muted p-3" @click="show = !show" title='{% trans "Actions" %}'>
<ion-icon name="ellipsis-vertical-sharp" role="img" class="md hydrated"
aria-label="ellipsis vertical sharp"></ion-icon>
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--dark-border oh-dropdown__menu--right"
x-show="show" @click.outside="show = false">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a hx-get='{% url "update-announcement" anoun.id %}' hx-target='#announcementUpdate' data-toggle='oh-modal-toggle' data-target="#announcementUpdateModal" class="oh-dropdown__link">{% trans "Edit" %}</a>
</li>
<li class="oh-dropdown__item">
<form
action = "{% url 'delete-announcement' anoun.id %}"
onsubmit="return confirm('{% trans "Are you sure you want to delete this announcement?" %}');"
method='post'
action=""
>
{% csrf_token %}
<button type="submit" class="oh-dropdown__link oh-dropdown__link--danger">
{% trans "Delete" %}
</button>
</form>
</li>
</ul>
</div>
</div>
{% endif %}
</span>
</div>
<div class="oh-announcement-body">
<p class="oh-announcement_text">{{ anoun.description|safe }}</p>
{% if anoun.department.all %}
<div class="oh-announcement-hastag__container">
<span class="oh-announcement-headline">{% trans "Department" %}</span>
<div class="oh-announcement-hashtags" >
{% for dep in anoun.department.all %}
<span class="oh-announcement__tags fw-bold">#{{ dep.department }}</span>
{% endfor %}
</div>
</div>
{% endif %}
{% if anoun.job_position.all %}
<div class="oh-announcement-hastag__container">
<span class="oh-announcement-headline">{% trans "Job Position" %}</span>
<div class="oh-announcement-hashtags">
{% for job in anoun.job_position.all %}
<span class="oh-announcement__tags fw-bold">#{{ job.job_position }}</span>
{% endfor %}
</div>
</div>
{% endif %}
{% for attachment in anoun.attachments.all %}
{% if anoun.attachments.all|length > 1 %}
<hr>
{% endif %}
{% if attachment.file.url|slice:"-4:" == '.png' or attachment.file.url|slice:"-4:" == '.jpg' or attachment.file.url|slice:"-5:" == '.jpeg' or attachment.file.url|slice:"-4:" == '.gif' or attachment.file.url|slice:"-4:" == '.bmp' or attachment.file.url|slice:"-5:" == '.webp' or attachment.file.url|slice:"-5:" == '.tiff' or attachment.file.url|slice:"-4:" == '.tif' or attachment.file.url|slice:"-4:" == '.svg' %}
<!-- If not a PDF, display the image -->
<div class="oh-announcement-img_container">
<a href="{{ attachment.file.url }}" target="_blank">
<img src="{{ attachment.file.url }}" alt="" class="oh-posted_img"></a>
</div>
{% else %}
<div class="oh-helpdesk_attached--content">
<div class="oh-helpdesk__attachment-icon">
<a href="{{ attachment.file.url }}" target="_blank"><span class="oh-file-icon oh-file-icon--pdf"></span></a>
</div>
<a href="{{ attachment.file.url }}" target="_blank"><span class="oh-helpdesk__filename">{% trans "View Attachment" %}</span></a>
</div>
{% endif %}
{% endfor %}
<div class="oh-announcement__comment-view">
<button class="oh-btn me-1 oh-announcement-btn" hx-get='{% url "announcement-add-comment" anoun.id %}' type="button" title="Add Comment" hx-target="#commentForm"
hx-swap="innerHTML" data-toggle="oh-modal-toggle" data-target="#commentModal">
<ion-icon name="chatbox-outline" class="md hydrated" role="img" aria-label="chevron back outline"></ion-icon>
</button>
<button class="oh-btn me-1 oh-announcement-btn" type="button" hx-get="{% url 'announcement-view-comment' anoun.id %}" hx-target="#commentviewTarget" data-toggle='oh-modal-toggle'
data-target ='#commentviewModal' title="View Comment" class="oh-btn oh-btn--light">
<ion-icon name="eye-outline" class="md hydrated" role="img" aria-label="chevron back outline"></ion-icon>
</button>
</div>
</div>
</div>
{% endfor %}
</div>
<!--********************************************************************************************************-->
<!--********************************************************************************************************-->
<!--********************************************************************************************************-->
<!-- Pagination section start -->
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ announcements.number }} {% trans "of" %} {{ announcements.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{announcements.number}}"
min="1"
/>
<span class="oh-pagination__label"
>{% trans "of" %} {{announcements.paginator.num_pages}}</span
>
</div>
<ul class="oh-pagination__items">
{% if announcements.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
href="?page=1"
class="oh-pagination__link"
>{% trans "First" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
href="?page={{ announcements.previous_page_number }}"
class="oh-pagination__link"
>{% trans "Previous" %}</a
>
</li>
{% endif %} {% if announcements.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
href="?page={{ announcements.next_page_number }}"
class="oh-pagination__link"
>{% trans "Next" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
href="?page={{ announcements.paginator.num_pages }}"
class="oh-pagination__link"
>{% trans "Last" %}</a
>
</li>
{% endif %}
</ul>
</nav>
</div>
<!-- Pagination section end -->
</div>
<!-- start of comment modal -->
<div
class="oh-modal"
id="commentModal"
role="dialog"
aria-labelledby="emptagModal"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="createModalTitle">
{% trans "Add Comment." %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body" id="commentForm"></div>
</div>
</div>
<!-- end of comment modal -->
<!-- start of comment view modal -->
<div
class="oh-modal" style="z-index: 60;"
id="commentviewModal"
role="dialog"
aria-labelledby="commentviewModal"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2
class="oh-modal__dialog-title"
id=""
>
{% trans "Comments." %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div
class="oh-modal__dialog-body oh-modal__dialog-relative"
id="commentviewTarget"
></div>
</div>
</div>
<!-- end of comment view modal -->
{% endblock content %}

View File

@@ -0,0 +1,44 @@
{% load static %}
{% load i18n %}
<div class="oh-sticky-table" style="height:400px; border:none;">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__tbody">
{% if not announcements %}
<div style="margin-top:30%">
<div class="oh-404" style="position:revert; transform:none">
<img style="width: 80px;height: 80px; margin-bottom:20px"
src="{% static 'images/ui/no-announcement.svg' %}" class="oh-404__image"
alt="Page not found. 404." />
<h5 class="oh-404__subtitle">{% trans "No Announcements to show." %}</h5>
</div>
</div>
{% else %}
{% for announcement in announcements %}
<div class="oh-sticky-table__tr" draggable="true">
<div class="announcement_title">
<a class="oh-profile oh-profile--md" style="text-decoration:none;"
hx-on:click="$('#objectDetailsModal').addClass('oh-modal--show');"
hx-get="{% url 'announcement-single-view' announcement.id %}?instance_ids={{instance_ids}}"
hx-target="#objectDetailsModalTarget" onclick="closeNew(this)">
<div class="oh-profile__avatar mr-1">
<img src="https://ui-avatars.com/api/?name={{announcement.title}}&background=random"
class="oh-profile__image" />
</div>
<span class="oh-profile__name oh-text--dark fw-bold">
{{announcement.title}}
</span>
{% if announcement.created_at|date:"Y-m-d" == current_date and request.user not in announcement.viewed_by %}
<span class="oh-profile__info-value ms-2" id="newTab"
style="background-color: hsl(8,77%,56%); color:white;
font-size: 0.7rem; padding: 3px 8px; border-radius: 15px; font-weight: 800; width:auto;">
{% trans "NEW" %}
</span>
{% endif %}
</a>
</div>
</div>
{% endfor %}
{% endif %}
</div>
</div>
</div>

View File

@@ -1,28 +1,25 @@
{% load i18n %}
<form action="{% url 'general-settings' %}" class='settings-label mb-1' method="post">
{% csrf_token %}
<div class="oh-inner-sidebar-content__header mt-4">
<h2 class="oh-inner-sidebar-content__title">{% trans "Announcement Expire" %}</h2>
</div>
<div>
<div class="oh-label__info" for="defatul_expire">
<label class="oh-label" for="defatul_expire">{% trans "Default Expire Days" %}</label>
<span class="oh-info mr-2" title="{% trans "Set default announcement expire days" %}">
</span>
{% csrf_token %}
<div class="oh-inner-sidebar-content__header mt-4">
<h2 class="oh-inner-sidebar-content__title">{% trans "Announcement Expire" %}</h2>
</div>
<div>
<div class="oh-label__info" for="defatul_expire">
<label class="oh-label" for="defatul_expire">{% trans "Default Expire Days" %}</label>
<span class="oh-info mr-2" title="{% trans 'Set default announcement expire days' %}">
</span>
</div>
<input type="number" name="days" value="{{form.instance.days}}" class="oh-input w-25" placeholder="{% trans 'Days' %}" id="id_days">
{% if perms.payroll.change_payrollsettings %}
<button style="display: inline;margin-left: 10px;" type="submit"
class="oh-btn oh-btn--secondary mt-2 mr-0 oh-btn--w-100-resp">
{% trans "Save Changes" %}
</button>
{% endif %}
</div>
<input type="number" name="days" value="{{form.instance.days}}" class="oh-input w-25" placeholder="Days" id="id_days">
{% if perms.payroll.change_payrollsettings %}
<button
style="display: inline;margin-left: 10px;"
type="submit"
class="oh-btn oh-btn--secondary mt-2 mr-0 oh-btn--w-100-resp"
>
{% trans "Save Changes" %}
</button>
{% endif %}
</div>
<div class="oh-inner-sidebar-content__footer"></div>
<div class="oh-inner-sidebar-content__footer"></div>
</form>

View File

@@ -5,19 +5,19 @@
<div class="oh-sticky-table mt-3 m-2">
<div class="oh-sticky-table__tbody">
{% for instance in viewed_by %}
<div class="oh-sticky-table__tr" >
<div class="oh-sticky-table__sd" style="width: 1000px;">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="{{instance.user.employee_get.get_avatar}}" class="oh-profile__image" />
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__sd" style="width: 1000px;">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="{{instance.user.employee_get.get_avatar}}" class="oh-profile__image" />
</div>
<span class="oh-profile__name oh-text--dark">
{{instance.user.employee_get}}
<span class="oh-recuritment_tag" style="font-size: .5rem;">{{instance.created_at}}</span>
</span>
</div>
</div>
<span class="oh-profile__name oh-text--dark">
{{instance.user.employee_get}}
<span class="oh-recuritment_tag" style="font-size: .5rem;">{{instance.created_at}}</span>
</span>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>

View File

@@ -307,12 +307,6 @@
}
$(document).ready(function () {
var search = $("#selectAllWorktypes").attr("data-pd");
if (search) {
var pd = $(".oh-pagination").attr("data-pd");
var hxVals = JSON.stringify(search);
$("#workTypeRequestCreate").attr("hx-vals", `{"pd":${hxVals}}`);
}
$(".all-work-type-requests-row").change(function () {
var parentTable = $(this).closest(".oh-sticky-table");
var body = parentTable.find(".oh-sticky-table__tbody");

View File

@@ -45,11 +45,11 @@
{% if comments %}
<ol class="oh-activity-sidebar__qa-list" role="list">
{% for comment in comments %}
<li class="oh-activity-sidebar__qa-item" id="commentCard{{comment.id}}">
<li class="oh-activity-sidebar__qa-item fade-me-out" id="commentCard{{comment.id}}">
<span class="oh-activity-sidebar__q">{{ comment.comment }}
{% if request.user.employee_get == comment.employee_id or perms.base.delete_worktyperequestcomment or request.user|is_reportingmanager %}
<span class="float-end" hx-on:click="setTimeout(() => {reloadMessage(this);},100);"
hx-get="{% url 'worktype-request-delete-comment' comment.id %}" hx-swap="outerHTML"
hx-get="{% url 'worktype-request-delete-comment' comment.id %}" hx-swap="outerHTML swap:0.5s"
hx-target="#commentCard{{comment.id}}">
<ion-icon name="close-outline" style="font-size: 24px" role="img" class="md hydrated"
aria-label="close outline"></ion-icon>
@@ -103,7 +103,7 @@
<div class="oh-404">
<div class="">
<span class="oh-timeoff-title fw-bold" style="display: block">{% trans "There are no comments to show." %}</span>
<img style="display: block; width: 100px; margin: 20px auto" src="{% static "/images/ui/comment.png" %}"
<img style="display: block; width: 100px; margin: 20px auto" src="{% static '/images/ui/comment.png' %}"
class="" />
</div>
</div>

View File

@@ -128,7 +128,8 @@
hx-post="{% url 'work-type-request-delete' work_type_request.id %}?instances_ids={{requests_ids}}"
hx-target="#objectDetailsModalTarget" style="width: 50%">
{% csrf_token %}
<button type="submit" class="oh-btn oh-btn--secondary w-100" title="{% trans 'Remove' %}">
<button type="submit" class="oh-btn oh-btn--secondary w-100" title="{% trans 'Remove' %}"
data-action="delete">
<ion-icon name="trash-outline"></ion-icon>
</button>
</form>

View File

@@ -1,57 +1,40 @@
{% load i18n %}
{% if messages %}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
</div>
{% endfor %}
</div>
<script>
setTimeout(function () {
$(".oh-modal__close").click();
}, 1000);
</script>
<script>
setTimeout(function () {
reloadMessage();
$(".oh-modal__close").click();
}, 1000);
</script>
{% if close_hx_url %}
<span hx-get="{{close_hx_url}}" hx-target="{{close_hx_target}}" hx-trigger="load"></span>
{% endif %}
{% endif %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="objectCreateModalLabel"
>{% trans "Work Type Request" %}</span
>
<button
class="oh-modal__close"
aria-label="Close"
{% if close_hx_url and messages %}
hx-get="{{close_hx_url}}"
hx-target="{{close_hx_target}}"
{% endif %}
>
<ion-icon name="close-outline"></ion-icon>
</button>
<span class="oh-modal__dialog-title" id="objectCreateModalLabel">{% trans "Work Type Request" %}</span>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body" id="formBody">
<form
hx-post="{% url 'work-type-request' %}?{{pd}}"
hx-target="#objectCreateModalTarget"
>
{{form.as_p}}
</form>
<form hx-post="{% url 'work-type-request' %}?{{pd}}" hx-target="#objectCreateModalTarget">
{{form.as_p}}
</form>
</div>
<script>
function toggleFunctionWorkTypeRequestForm() {
if ($("#id_is_permanent_work_type").is(":checked")) {
$("#id_requested_till").parent().hide();
} else {
$("#id_requested_till").parent().show();
function toggleFunctionWorkTypeRequestForm() {
if ($("#id_is_permanent_work_type").is(":checked")) {
$("#id_requested_till").parent().hide();
} else {
$("#id_requested_till").parent().show();
}
}
}
$(document).ready(function () {
$("[type=checkbox]").change(function (e) {
e.preventDefault();
toggleFunctionWorkTypeRequestForm();
$(document).ready(function () {
$("[type=checkbox]").change(function (e) {
e.preventDefault();
toggleFunctionWorkTypeRequestForm();
});
});
});
toggleFunctionWorkTypeRequestForm();
</script>
toggleFunctionWorkTypeRequestForm();
</script>

View File

@@ -266,6 +266,7 @@ x-data="{searchShow: false}"
data-target='#objectCreateModal'
hx-get="{% url 'work-type-request' %}"
hx-target='#objectCreateModalTarget'
hx-include="#filterForm"
>
<ion-icon name='add-sharp'></ion-icon> {% trans "Create" %}
</button>

View File

@@ -36,7 +36,6 @@ from horilla_audit.models import AuditTag
urlpatterns = [
path("", views.home, name="home-page"),
path("announcement-list", views.announcement_list, name="announcement-list"),
path("initialize-database", views.initialize_database, name="initialize-database"),
path("load-demo-database", views.load_demo_database, name="load-demo-database"),
path(
@@ -920,7 +919,7 @@ urlpatterns = [
views.pagination_settings_view,
name="pagination-settings-view",
),
path("announcement/", announcement.announcement_view, name="announcement"),
path("announcement-list", announcement.announcement_list, name="announcement-list"),
path(
"create-announcement",
announcement.create_announcement,