Upload files to "base/announcement"

Signed-off-by: nestict <developer@nestict.com>
This commit is contained in:
2026-01-16 15:44:39 +01:00
parent d78ab62298
commit 5964bb091e
8 changed files with 498 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
{% load i18n %}
{% if messages %}
<script>reloadMessage();</script>
<span hx-get="{% url 'announcement-list' %}" hx-trigger="load" hx-target="#announcementListCard"
hx-on-htmx-after-request="setTimeout(() => {$('.oh-modal__close').click();}, 500);"></span>
{% endif %}
{% if form.errors %}
<div class="oh-wrapper">
<div class="oh-alert-container">
{% for error in form.non_field_errors %}
<div class="oh-alert oh-alert--animated oh-alert--danger">
{{ error }}
</div>
{% endfor %}
</div>
</div>
{% endif %}
<div class="oh-modal__dialog-header pb-0">
<h2 class="oh-modal__dialog-title" id="createModalTitle">
{% trans "Create " %} {{form.verbose_name}}
</h2>
<button class="oh-modal__close" aria-label="Close"
onclick="setTimeout(function () { $('#objectCreateModalTarget').empty(); }, 200);">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body">
<form hx-post="{% url 'create-announcement' %}" hx-target="#objectCreateModalTarget"
hx-encoding="multipart/form-data" class="oh-profile-section">
{% csrf_token %}
{{form.as_p}}
<div class="d-flex flex-row-reverse">
<button type="submit" class="oh-btn oh-btn--secondary mt-2 mr-0 oh-btn--w-100-resp">
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,168 @@
{% load i18n %}
{% load static %}
{% load basefilters %}
{% if messages %}
<script>reloadMessage();</script>
<span hx-get="{% url 'announcement-list' %}" hx-trigger="load" hx-target="#announcementListCard"></span>
{% endif %}
<div class="oh-modal__dialog-header pb-4">
<div class="oh-main__titlebar oh-main__titlebar--left">
<span class="oh-main__titlebar-title fw-bold mb-0 text-dark">{% trans "Announcement" %} </span>
</div>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body">
{% if instance_ids %}
<div class="oh-modal__dialog oh-modal__dialog--navigation m-0 p-0">
<button
hx-get="{% url 'announcement-single-view' previous_instance_id %}?instance_ids={{instance_ids}}"
hx-target="#objectDetailsModalTarget" class="oh-modal__diaglog-nav oh-modal__nav-prev" data-action="previous">
<ion-icon name="chevron-back-outline" class="md hydrated" role="img"
aria-label="chevron back outline"></ion-icon>
</button>
<button
hx-get="{% url 'announcement-single-view' next_instance_id %}?instance_ids={{instance_ids}}"
hx-target="#objectDetailsModalTarget" class="oh-modal__diaglog-nav oh-modal__nav-next" data-action="next">
<ion-icon name="chevron-forward-outline" class="md hydrated" role="img"
aria-label="chevron forward outline"></ion-icon>
</button>
</div>
{% endif %}
{% if announcement %}
<div class="card mx-auto p-4 mb-4" style="max-width: 800px; border:1px solid lightgrey; border-radius:10px; box-shadow: 2px 2px 2px grey;">
{% if perms.base.change_announcement or perms.base.delete_announcement %}
<div class="oh-dropdown float-end" x-data="{open: false}">
<button class="oh-btn oh-stop-prop oh-btn--transparent oh-accordion-meta__btn" @click="open = !open"
@click.outside="open = false" title='{% trans "Actions" %}'>
<ion-icon name="ellipsis-vertical"></ion-icon>
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none;">
<ul class="oh-dropdown__items">
{% if perms.base.change_announcement %}
<li class="oh-dropdown__item">
<a hx-get="{% url 'update-announcement' announcement.id %}?instance_ids={{instance_ids}}" hx-target='#objectUpdateModalTarget'
data-toggle='oh-modal-toggle' data-target="#objectUpdateModal"
class="oh-dropdown__link" style="cursor:pointer;">{% trans "Edit" %}</a>
</li>
{% endif %}
{% if perms.base.delete_announcement %}
<li class="oh-dropdown__item">
<form hx-post="{% url 'delete-announcement' announcement.id %}?instance_ids={{instance_ids}}"
hx-confirm="{% trans 'Are you sure you want to delete this announcement?' %}"
hx-target="#objectDetailsModalTarget">
{% csrf_token %}
<button type="submit" class="oh-dropdown__link oh-dropdown__link--danger" data-action="delete">
{% trans "Delete" %}
</button>
</form>
</li>
{% endif %}
</ul>
</div>
</div>
{% endif %}
<div class="card-header mb-3">
<h5 class="card-title fw-bold">{{ announcement.title }}
{% if perms.base.view_announcement %}
<div data-toggle="oh-modal-toggle" hx-target="#viewedByBody"
hx-get="{% url 'announcement-viewed-by' %}?announcement_id={{announcement.id}}" data-target="#viewedBy"
title="{{announcement.get_views|length}} {% trans " Views" %}"
class="oh-checkpoint-badge text-success mb-2" id="selectAllInstances" style="cursor: pointer">
<span>{{announcement.get_views|length}}</span>
<span align="center">
<ion-icon name="eye-outline"></ion-icon>
</span>
</div>
{% endif %}
</h5>
<span class="text-muted fw-bold"><small>
{% trans "Posted on" %} &nbsp<span class="dateformat_changer">{{ announcement.created_at|date:"F j, Y"}}</span> &nbsp
{% trans "at" %} &nbsp <span class="timeformat_changer">{{ announcement.created_at|time:"g:i A"}}</span></small>
</span>
</div>
<div class="card-body">
<p class="card-text">{{ announcement.description|safe }}</p>
</div>
<div class="card-footer">
{% for attachment in announcement.attachments.all %}
{% if announcement.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 -->
<a href="{{ attachment.file.url }}" target="_blank">
<img src="{{ attachment.file.url }}" class="img-fluid rounded" alt="Attachment Image" style="max-width: 100%; max-height: 100%; object-fit: contain;">
</a>
{% else %}
<!-- If the file is a PDF or something, display a link to download -->
<i><a href="{{ attachment.file.url }}" class='text-danger fw-bold' target="_blank">
<span class="oh-file-icon oh-file-icon--pdf"></span>{% trans "View Attachment" %}</a></i>
{% endif %}
{% endfor %}
</div>
<div class="card-footer mt-2">
<div class="oh-announcement__comment-view">
{% if announcement.department.all or announcement.job_position.all %}
<button class="oh-btn me-1 oh-announcement-btn" type="button" onmouseover="showDetails()"
onmouseout="hideDetails()">
<ion-icon name="information"></ion-icon>
</button>
{% endif %}
{% if not announcement.disable_comments %}
<button class="oh-btn oh-btn--light oh-activity-sidebar__open" type="button"
hx-get="{% url 'announcement-view-comment' announcement.id %}" hx-target="#commentContainer"
data-target='#activitySidebar' title="Comments">
<ion-icon name="chatbox-outline" style="font-size:18px;" class="md hydrated" role="img"
aria-label="chevron back outline"></ion-icon>
</button>
{% endif %}
</div>
</div>
<div id="infoDiv" style="display:none;">
{% if announcement.department.all %}
<div class="oh-announcement-hastag__container" style="z-index:1000">
<span class="oh-announcement-hashtags" style="font-size:0.8rem;">{% trans "Department" %}</span>
<div class="oh-announcement-hashtags">
{% for dep in announcement.department.all %}
<span class="oh-announcement__tags">#{{ dep.department }}</span>
{% endfor %}
</div>
</div>
{% endif %}
{% if announcement.job_position.all %}
<div class="oh-announcement-hastag__container">
<span class="oh-announcement-hashtags" style="font-size:0.8rem;">{% trans "Job Position" %}</span>
<div class="oh-announcement-hashtags">
{% for job in announcement.job_position.all %}
<span class="oh-announcement__tags">#{{ job.job_position }}</span>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
{% else %}
<div class="oh-empty h-100">
<img src="{% static 'images/ui/search.svg' %}" class="oh-404__image" alt="Page not found. 404." />
<h1 class="oh-empty__title">{% trans "No Records found." %}</h1>
<p class="oh-empty__subtitle">{% trans "There are no announcements at the moment." %}</p>
</div>
{% endif %}
<div class="oh-modal" id="viewedBy" role="dialog" aria-hidden="true">
<div class="oh-modal__dialog" style="max-width: 550px">
<div class="oh-modal__dialog-header">
<button class="oh-modal__close--custom"
onclick="$(this).closest('.oh-modal--show').removeClass('oh-modal--show');"><ion-icon
name="close-outline"></ion-icon></button>
</div>
<div class="oh-modal__dialog-body" id="viewedByBody"></div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,41 @@
{% load i18n %}
{% if messages %}
<script>reloadMessage();</script>
<span hx-get="{% url 'announcement-single-view' form.instance.id %}?instance_ids={{instance_ids}}"
hx-target="#objectDetailsModalTarget" hx-trigger="load"></span>
<span hx-get="{% url 'announcement-list' %}" hx-trigger="load" hx-target="#announcementListCard"
hx-on-htmx-after-request="setTimeout(() => {$('.oh-modal_close--custom').click();}, 500);"></span>
{% endif %}
{% if form.errors %}
<div class="oh-wrapper">
<div class="oh-alert-container">
{% for error in form.non_field_errors %}
<div class="oh-alert oh-alert--animated oh-alert--danger">
{{ error }}
</div>
{% endfor %}
</div>
</div>
{% endif %}
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="createModalTitle">
{% trans "Update" %} {{form.verbose_name}}
</h2>
<button type="button" class="oh-modal_close--custom"
onclick="$(this).closest('.oh-modal--show').removeClass('oh-modal--show');setTimeout(function () { $('#{{hx_target}}').empty(); }, 200);">
<ion-icon name="close-outline" role="img" aria-label="close outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body">
<form hx-post="{{request.get_full_path}}" hx-target="#{{hx_target}}" hx-encoding="multipart/form-data"
class="oh-profile-section">
{% csrf_token %}
{{form.as_p}}
<div class="d-flex flex-row-reverse">
<button type="submit" class="oh-btn oh-btn--secondary mt-2 mr-0 oh-btn--w-100-resp">
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,41 @@
{% load static %}
{% load i18n %}
{% if not announcements %}
<div class="oh-empty h-100" style="padding-top: 50px;">
<img src="{% static 'images/ui/search.svg' %}" class="oh-404__image" alt="Page not found. 404." />
<h1 class="oh-empty__title">{% trans "No Records found." %}</h1>
<p class="oh-empty__subtitle">{% trans "There are no announcements at the moment." %}</p>
</div>
{% else %}
<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">
{% 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 %}
</div>
</div>
</div>
{% endif %}

View File

@@ -0,0 +1,62 @@
{% load static i18n widget_tweaks %}
<div class="row">
<div class="col-12">{{ form.non_field_errors }}</div>
{% for field in form.visible_fields %}
{% if field.field.widget.input_type != "checkbox" %}
<div class="col-12 mb-3" id="id_{{ field.name }}_parent_div">
<div class="oh-label__info" for="id_{{ field.name }}">
<label class="oh-label {% if field.field.required %}required-star{% endif %}" for="id_{{ field.name }}">{% trans field.label %}</label>
{% if field.help_text %}
<span class="oh-info mr-2" title="{{ field.help_text|safe }}"></span>
{% endif %}
</div>
{{ field|add_class:"form-control" }}
{{ field.errors }}
{% if field.name == "attachments" and field.field.widget.input_type == "file" and field.field.widget.attrs.multiple %}
{% if form.instance.pk %}
<div class="d-flex mt-2 mb-2">
{% for file in form.instance.attachments.all %}
<a href="{{ file.file.url }}" rel="noopener noreferrer" target="_blank" id="objectFileItem{{ file.id }}">
<span class="oh-file-icon oh-file-icon--pdf" onmouseover="enlargeImage('{{ file.file.url }}',$(this))"
style="width: 40px; height: 40px">
<img src="{% static 'images/ui/minus-icon.png' %}" style="display: block; width: 50%; height: 50%"
hx-confirm="{% trans 'Are you sure do you want to delete this file ?' %}"
hx-post="{% url 'remove-announcement-file' form.instance.pk file.id %}"
hx-target="#objectFileItem{{ file.id }}" hx-swap="outerHTML"
onclick="event.stopPropagation();event.preventDefault()" />
</span>
</a>
{% endfor %}
</div>
{% endif %}
{% endif %}
</div>
{% endif %}
{% endfor %}
<div class="col-12 d-flex mb-3">
{% for field in form.visible_fields %}
{% if field.field.widget.input_type == "checkbox" %}
<div class="col-6" id="id_{{ field.name }}_parent_div">
<div class="oh-label__info" for="id_{{ field.name }}">
<label class="oh-label {% if field.field.required %}required-star{% endif %}"
for="id_{{ field.name }}">{% trans field.label %}</label>
{% if field.help_text %}
<span class="oh-info mr-2" title="{{ field.help_text|safe }}"></span>
{% endif %}
</div>
<div class="oh-switch" style="width: 30px;">
{{ field|add_class:"oh-switch__checkbox" }}
</div>
{{ field.errors }}
</div>
{% endif %}
{% endfor %}
</div>
{# hidden fields #}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
</div>

View File

@@ -0,0 +1,100 @@
{% load basefilters static %}
{% load i18n %}
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
<div class="oh-activity-sidebar__header">
<a style="cursor: pointer;" onclick="$('.oh-activity-sidebar--show').removeClass('oh-activity-sidebar--show');">
<ion-icon name="chevron-forward-outline"
class="oh-activity-sidebar__header-icon me-2 oh-activity-sidebar__close"
data-target="#activitySidebar"></ion-icon>
</a>
<span class="oh-activity-sidebar__title"> {{announcement.title}}{% trans "'s comments" %} </span>
</div>
{% if not announcement.disable_comments %}
<form hx-post="{% url 'announcement-add-comment' request_id %}" hx-target="#commentContainer"
hx-encoding="multipart/form-data">
{% csrf_token %}
<div>
<input type="text" name="comment" id="commentInput" hx-on:keyup="toggleCommentButton(this);"
class="oh-input w-100" placeholder="Comment here">
<button type="submit" id="commentButton" class="oh-btn oh-btn--secondary mt-2 mr-0 oh-btn--w-100-resp"
style="display: none;">
{% trans "Comment" %}
</button>
</div>
<div class="oh-inner-sidebar-content__footer"></div>
</form>
{% if no_comments %}
<div class="oh-timeoff-modal__profile-content">
<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' %}"
class="" />
</div>
</div>
</div>
{% else %}
<ol class="oh-activity-sidebar__qa-list" role="list">
{% for comment in comments %}
<li class="oh-activity-sidebar__qa-item" id="announCommentCard{{comment.id}}">
<span class="oh-activity-sidebar__q" style="font-size:16px;">{{ comment.comment }}
{% if perms.base.delete_announcementcomment or request.user == comment.created_by %}
<span class="float-end" hx-get="{% url 'announcement-delete-comment' comment.id %}" hx-swap="delete"
hx-target="#announCommentCard{{comment.id}}"
hx-on:click="setTimeout(() => {reloadMessage(this);},100);">
<ion-icon name="close-outline" style="font-size: 24px" role="img" class="md hydrated"
aria-label="close outline"></ion-icon></span>
{% endif %}
</span>
<span class="d-flex mt-3">
<div>
<span class="oh-timeoff-modal__stat-title fw-bold">{% trans "By" %}</span>
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="{{comment.employee_id.get_avatar}}" class="oh-profile__image">
</div>
<span class="oh-timeoff-modal__stat-title fw-bold mt-2">
{{ comment.employee_id }}
</span>
</div>
</div>
<div class="float-end" style="margin-left: 50px;">
<span class="oh-timeoff-modal__stat-title fw-bold mb-2">{% trans "Date & Time" %}</span>
<span class="oh-timeoff-modal__stat-title">
{% trans "on" %} &nbsp<span class="dateformat_changer">{{ comment.created_at|date:"F j, Y" }}</span>
&nbsp
{% trans "at" %} &nbsp <span class="timeformat_changer">{{ comment.created_at|time:"g:i A" }}</span>
</span>
</div>
</span>
</li>
{% endfor %}
</ol>
{% endif %}
{% else %}
<div class="oh-404">
<img src="{% static 'images/ui/no_comments.png' %}" class="oh-404__image mb-4"
alt="Page not found. 404." />
<h5 class="oh-404__subtitle">{% trans "Comments have been disabled for this announcement." %}</h5>
</div>
{% endif %}

View File

@@ -0,0 +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>
</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>
<div class="oh-inner-sidebar-content__footer"></div>
</form>

View File

@@ -0,0 +1,23 @@
{% load i18n %}
<div class="oh-main__titlebar oh-main__titlebar--left">
<span class="oh-main__titlebar-title fw-bold mb-0 text-dark">{% trans "Viewed By" %} </span>
</div>
<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>
<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>