[ADD] Horilla Widgets
This commit is contained in:
0
horilla_widgets/__init__.py
Normal file
0
horilla_widgets/__init__.py
Normal file
3
horilla_widgets/admin.py
Normal file
3
horilla_widgets/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
horilla_widgets/apps.py
Normal file
6
horilla_widgets/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class HorillaWidgetsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'horilla_widgets'
|
||||
40
horilla_widgets/forms.py
Normal file
40
horilla_widgets/forms.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
forms.py
|
||||
|
||||
Horilla forms
|
||||
"""
|
||||
from typing import Any
|
||||
from django import forms
|
||||
from horilla_widgets.widgets.horilla_multi_select_field import HorillaMultiSelectField
|
||||
|
||||
|
||||
class HorillaForm(forms.Form):
|
||||
def clean(self) -> dict[str, Any]:
|
||||
for field_name, field_instance in self.fields.items():
|
||||
if isinstance(field_instance, HorillaMultiSelectField):
|
||||
self.errors.pop(field_name, None)
|
||||
if len(self.data.getlist(field_name)) < 1:
|
||||
raise forms.ValidationError({field_name: "Thif field is required"})
|
||||
cleaned_data = super().clean()
|
||||
employee_data = self.fields[field_name].queryset.filter(
|
||||
id__in=self.data.getlist(field_name)
|
||||
)
|
||||
cleaned_data[field_name] = employee_data
|
||||
cleaned_data = super().clean()
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class HorillaModelForm(forms.ModelForm):
|
||||
def clean(self) -> dict[str, Any]:
|
||||
for field_name, field_instance in self.fields.items():
|
||||
if isinstance(field_instance, HorillaMultiSelectField):
|
||||
self.errors.pop(field_name, None)
|
||||
if len(self.data.getlist(field_name)) < 1:
|
||||
raise forms.ValidationError({field_name: "Thif field is required"})
|
||||
cleaned_data = super().clean()
|
||||
employee_data = self.fields[field_name].queryset.filter(
|
||||
id__in=self.data.getlist(field_name)
|
||||
)
|
||||
cleaned_data[field_name] = employee_data
|
||||
cleaned_data = super().clean()
|
||||
return cleaned_data
|
||||
0
horilla_widgets/migrations/__init__.py
Normal file
0
horilla_widgets/migrations/__init__.py
Normal file
3
horilla_widgets/models.py
Normal file
3
horilla_widgets/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
@@ -0,0 +1,180 @@
|
||||
{% load i18n %}
|
||||
<style>
|
||||
.oh-modal__close-custom {
|
||||
border: none;
|
||||
background: none;
|
||||
font-size: 1.5rem;
|
||||
opacity: 0.7;
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
right: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tag-badge {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
/* background-color: orangered; */
|
||||
color: black;
|
||||
|
||||
border-radius: 3rem;
|
||||
text-align: center;
|
||||
|
||||
font-size: 1.6rem;
|
||||
font-weight: 400;
|
||||
padding: 0.05rem 0.8rem 0.1rem;
|
||||
line-height: inherit;
|
||||
padding: 7px;
|
||||
padding-right: 10px;
|
||||
margin-bottom: 5px;
|
||||
border: solid orangered 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tag-badge--primary {
|
||||
background-color: orangered;
|
||||
color: white;
|
||||
}
|
||||
.oh-profile__avatar-limit-height {
|
||||
height: 30px !important;
|
||||
}
|
||||
.oh-profile_name_custom {
|
||||
font-size: 13px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
.oh-profile__image_custm {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
.badge-container {
|
||||
height: 30vh;
|
||||
overflow: auto;
|
||||
}
|
||||
.tag-badge--outline {
|
||||
background: white;
|
||||
border: solid orangered 2px;
|
||||
color: black;
|
||||
}
|
||||
.oh-sticky-table__th--custom {
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
.oh-sticky-table__tr--selected {
|
||||
background: #ff450017 !important;
|
||||
}
|
||||
|
||||
.avatars {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.avatars__item {
|
||||
background-color: #596376;
|
||||
border: 2px solid white;
|
||||
border-radius: 100%;
|
||||
color: #ffffff;
|
||||
display: block;
|
||||
font-family: sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 100;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
line-height: 17px;
|
||||
text-align: center;
|
||||
transition: margin 0.1s ease-in-out;
|
||||
overflow: hidden;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.avatars__item:first-child {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.avatars__item:nth-child(2) {
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.avatars__item:nth-child(3) {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.avatars__item:nth-child(4) {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.avatars__item:nth-child(5) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.avatars__item:last-child {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.avatars__item img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.avatars:hover .avatars__item {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.select2{
|
||||
width: 100% !important;
|
||||
}
|
||||
#slectContainer{{self.attrs.id}} .select2-container .select2-selection{
|
||||
padding: 5px !important;
|
||||
height: 50px !important;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
<div id="slectContainer{{self.attrs.id}}">
|
||||
<select name="{{field_name}}" id="{{self.attrs.id}}" class="w-100" multiple>
|
||||
{% for instance in queryset %}
|
||||
<option value="{{instance.id}}">{{instance}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="avatars" id="avatarsContainer">
|
||||
|
||||
</div>
|
||||
<span
|
||||
style="cursor: pointer;padding: 10px;"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#filterChoose"
|
||||
>Filter and filterChoose</span
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="oh-modal"
|
||||
id="filterChoose"
|
||||
role="dialog"
|
||||
aria-labelledby="filterChoose"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="oh-modal__dialog">
|
||||
<div class="oh-modal__dialog-header">
|
||||
<button
|
||||
type="button"
|
||||
class="oh-modal__close-custom"
|
||||
onclick="event.stopPropagation();event.preventDefault;$('#filterChoose').toggleClass('oh-modal--show');"
|
||||
>
|
||||
<ion-icon name="close-outline"></ion-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oh-modal__dialog-body" id="filterChooseModalBody{{self.attrs.id}}">
|
||||
<div class="oh-wrapper">
|
||||
<!-- Nav -->
|
||||
{% include "horilla_widgets/multiselect_components/nav.html" %}
|
||||
<!-- Filter Tags -->
|
||||
{% include "horilla_widgets/multiselect_components/filter_tags.html" %}
|
||||
<!-- Sticky Table -->
|
||||
{% include "horilla_widgets/multiselect_components/table.html" %}
|
||||
<!-- Pagination -->
|
||||
{% include "horilla_widgets/multiselect_components/pagination.html" %}
|
||||
<div class="d-flex flex-row-reverse">
|
||||
<button class="oh-btn oh-btn--secondary oh-btn--shadow pr-3 pl-3" onclick="event.stopPropagation();event.preventDefault;$('#filterChoose').toggleClass('oh-modal--show');">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
<div
|
||||
class="oh-modal"
|
||||
id="viewSelectedModal"
|
||||
role="dialog"
|
||||
aria-labelledby="viewSelectedModal"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="oh-modal__dialog">
|
||||
<div class="oh-modal__dialog-header">
|
||||
<button
|
||||
class="oh-modal__close-custom"
|
||||
onclick="event.stopPropagation();$('#viewSelectedModal').toggleClass('oh-modal--show');"
|
||||
aria-label="Close"
|
||||
>
|
||||
<ion-icon name="close-outline"></ion-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oh-wrapper oh-modal__dialog-body" id="viewSelectedModalBody">
|
||||
<!-- Search and count badge -->
|
||||
{% include "horilla_widgets/multiselect_components/search.html" %}
|
||||
<!-- Profile pills -->
|
||||
{% include "horilla_widgets/multiselect_components/user_tags.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
<style>
|
||||
.oh-checkpoint-badge--success {
|
||||
color: yellowgreen;
|
||||
}
|
||||
.oh-checkpoint-badge {
|
||||
cursor: pointer;
|
||||
}
|
||||
.m-zero {
|
||||
margin: 0 !important;
|
||||
}
|
||||
</style>
|
||||
<div class="oh-filter-tag-container mb-2 p-0" id="filterTagContainer{{self.attrs.id}}">
|
||||
|
||||
</div>
|
||||
<div class="ration-container mb-2 d-flex justify-content-between">
|
||||
<div>
|
||||
<div
|
||||
class="oh-checkpoint-badge oh-checkpoint-badge--secondary"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#viewSelectedModal"
|
||||
>
|
||||
Selected <span class="selected-count">0</span>/<span class="total-count">{{queryset|length}}</span>
|
||||
</div>
|
||||
<div class="oh-checkpoint-badge oh-checkpoint-badge--secondary" id="selectAllUsers">
|
||||
Select All <span class="total-count visible-count">{{queryset|length}}</span> user
|
||||
</div>
|
||||
<div class="oh-checkpoint-badge" id="unselectAllUsers">
|
||||
Unselect All
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oh-checkpoint-badge oh-checkpoint-badge--success m-zero" id="selectAllInstances">
|
||||
Select All {{queryset|length}} Item
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,146 @@
|
||||
<section
|
||||
class="oh-main__topbar p-3 mb-1 m-0"
|
||||
style="padding-left: 0 !important; padding-right: 0 !important"
|
||||
x-data="{searchShow: false}"
|
||||
id="widgetFilterFormContainer{{self.attrs.id}}"
|
||||
>
|
||||
<div class="oh-main__titlebar oh-main__titlebar--left">
|
||||
<div class="mb-2 mt-2">
|
||||
<h1 class="oh-main__titlebar-title fw-bold">{{field.label}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-main__titlebar oh-main__titlebar--right">
|
||||
<div
|
||||
class="oh-input-group oh-input__search-group"
|
||||
:class="searchShow ? 'oh-input__search-group--show' : ''"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="oh-input oh-input__icon"
|
||||
name="search"
|
||||
aria-label="Search Input"
|
||||
placeholder="Search"
|
||||
/>
|
||||
</div>
|
||||
<div class="oh-main__titlebar-button-container">
|
||||
<div class="oh-dropdown" x-data="{open: false}">
|
||||
<button class="oh-btn ml-2" @click="open = !open">
|
||||
<ion-icon
|
||||
name="filter"
|
||||
class="mr-1 md hydrated"
|
||||
role="img"
|
||||
aria-label="filter"
|
||||
></ion-icon
|
||||
>Filter
|
||||
<div id="filterCount"></div>
|
||||
</button>
|
||||
<div
|
||||
class="oh-dropdown__menu oh-dropdown__menu--left oh-dropdown__filter p-4"
|
||||
x-show="open"
|
||||
@click.outside="open = false"
|
||||
>
|
||||
{% include filter_template_path %}
|
||||
<div class="oh-dropdown__filter-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="oh-btn oh-btn--secondary oh-btn--small w-100"
|
||||
data-action="{% url filter_route_name %}"
|
||||
id="widgetFilterButton{{self.attrs.id}}"
|
||||
>
|
||||
Filter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
function transformJson(inputJson) {
|
||||
// Clone the input JSON to avoid modifying the original object
|
||||
const modifiedJson = JSON.parse(JSON.stringify(inputJson));
|
||||
|
||||
// Iterate through each object in the JSON array
|
||||
modifiedJson.forEach((obj) => {
|
||||
// Remove "_id" when it appears at the end of a name
|
||||
|
||||
// If there are "__" in the name, keep only the word after the last "__" and capitalize it
|
||||
obj.name = obj.name.replace(/_id$/, "");
|
||||
obj.name.replace("_", " ");
|
||||
if (obj.name.includes("__")) {
|
||||
const parts = obj.name.split("__");
|
||||
obj.name = parts[parts.length - 1];
|
||||
}
|
||||
obj.name = obj.name.charAt(0).toUpperCase() + obj.name.slice(1);
|
||||
obj.name = obj.name.replace(/_/g, " ");
|
||||
});
|
||||
|
||||
return modifiedJson;
|
||||
}
|
||||
|
||||
$("#widgetFilterButton{{self.attrs.id}}").click(function (e) {
|
||||
e.preventDefault();
|
||||
const formFields = $("#widgetFilterFormContainer{{self.attrs.id}}").find(
|
||||
"[name]"
|
||||
);
|
||||
var formData = [];
|
||||
formFields.each(function () {
|
||||
const name = $(this).attr("name");
|
||||
const value = $(this).val();
|
||||
formData.push({ name, value });
|
||||
});
|
||||
var filterUrl = $(this).attr("data-action");
|
||||
$.ajax({
|
||||
type: "get",
|
||||
url: filterUrl,
|
||||
data: formData,
|
||||
success: function (response) {
|
||||
let ids = response.ids;
|
||||
$("#chooseTableHeaderParent .oh-sticky-table__tbody").hide();
|
||||
$.each(ids, function (indexInArray, valueOfElement) {
|
||||
$(
|
||||
`#chooseTableHeaderParent .oh-sticky-table__tbody[data-instance-id=${valueOfElement}]`
|
||||
).show();
|
||||
});
|
||||
$(".visible-count").html(
|
||||
$(".oh-sticky-table__tr--custom:visible .oh-sticky-table__sd")
|
||||
.length
|
||||
);
|
||||
var labeledTags = transformJson(formData);
|
||||
var tagContainer = $("#filterTagContainer{{self.attrs.id}}");
|
||||
tagContainer.html("");
|
||||
$.each(labeledTags, function (indexInArray, valueOfElement) {
|
||||
if (valueOfElement.value.length != 0) {
|
||||
tagContainer.append(
|
||||
$(`
|
||||
<span class="oh-filter-tag" id="tag-{{self.attrs.id}}"
|
||||
>${valueOfElement.name}<button class="oh-filter-tag__close" onclick="closeFilterTag(event, ${formData[indexInArray].name}, '#tag-{{self.attrs.id}}')">
|
||||
<ion-icon name="close-outline"></ion-icon></button
|
||||
>
|
||||
</span>`)
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
$("#widgetFilterFormContainer{{self.attrs.id}} [name=search]").keyup(
|
||||
function (e) {
|
||||
$("#widgetFilterButton{{self.attrs.id}}").click();
|
||||
}
|
||||
);
|
||||
});
|
||||
function closeFilterTag(e, fieldName, targetTag) {
|
||||
e.preventDefault();
|
||||
$(fieldName).val("");
|
||||
if ($(fieldName).is("select")) {
|
||||
$(fieldName)
|
||||
.next()
|
||||
.find(`#select2-${$(fieldName).attr("id")}-container`)
|
||||
.html("---------");
|
||||
}
|
||||
$("#widgetFilterButton{{self.attrs.id}}").click();
|
||||
$(targetTag).remove();
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="oh-pagination">
|
||||
<span
|
||||
class="oh-pagination__page"
|
||||
data-toggle="modal"
|
||||
data-target="#addEmployeeModal"
|
||||
>Page 1 of 1</span
|
||||
>
|
||||
</div>
|
||||
@@ -0,0 +1,60 @@
|
||||
<section
|
||||
class="oh-main__topbar p-3 mb-1 m-0"
|
||||
style="padding-left: 0 !important; padding-right: 0 !important"
|
||||
x-data="{searchShow: false}"
|
||||
>
|
||||
<div
|
||||
class="oh-input-group w-100 oh-input__search-group"
|
||||
:class="searchShow ? 'oh-input__search-group--show' : ''"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="oh-input oh-input__icon w-100"
|
||||
aria-label="Search Input"
|
||||
placeholder="Search"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="ration-container mb-2 d-flex justify-content-between">
|
||||
<div>
|
||||
<div
|
||||
class="oh-checkpoint-badge oh-checkpoint-badge--secondary"
|
||||
>
|
||||
Selected <span class="selected-count">0</span>/<span class="total-count">138</span>
|
||||
</div>
|
||||
<div class="oh-checkpoint-badge oh-checkpoint-badge--secondary" id="selectSelected">
|
||||
Select All
|
||||
</div>
|
||||
<div class="oh-checkpoint-badge" id="unselectSelected">Unselect All</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="oh-checkpoint-badge oh-checkpoint-badge--success m-zero">
|
||||
Add Selected
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
function updateSelectedCount(){
|
||||
let selectedCount = $(".tag-badge").not(".tag-badge--outline").length
|
||||
$(".selected-count").html(selectedCount);
|
||||
}
|
||||
$(".tag-badge").click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).toggleClass("tag-badge--outline");
|
||||
updateSelectedCount()
|
||||
});
|
||||
$("#selectSelected").click(function (e) {
|
||||
e.preventDefault();
|
||||
$(".tag-badge").removeClass("tag-badge--outline");
|
||||
updateSelectedCount()
|
||||
});
|
||||
|
||||
$("#unselectSelected").click(function (e) {
|
||||
e.preventDefault();
|
||||
$(".tag-badge").addClass("tag-badge--outline");
|
||||
updateSelectedCount()
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,205 @@
|
||||
<div class="oh-sticky-table" style="height: 50vh">
|
||||
<div
|
||||
class="oh-sticky-table__table oh-table--sortable"
|
||||
id="chooseTableHeaderParent"
|
||||
>
|
||||
<div class="oh-sticky-table__thead" id="chooseTableHeader">
|
||||
<div class="oh-sticky-table__tr">
|
||||
<div class="oh-sticky-table__th oh-sticky-table__th--custom">
|
||||
<div class="d-flex">
|
||||
<div class="">
|
||||
<input
|
||||
type="checkbox"
|
||||
title="Select all users"
|
||||
class="oh-input oh-input__checkbox mt-1 mr-2"
|
||||
id="choose-all-user"
|
||||
/>
|
||||
</div>
|
||||
Employee
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% for instance in queryset %}
|
||||
<div class="oh-sticky-table__tbody" data-instance-id="{{instance.id}}">
|
||||
<div
|
||||
class="oh-sticky-table__tr oh-sticky-table__tr--custom"
|
||||
data-instance-id="{{instance.id}}"
|
||||
data-label="{{instance}}"
|
||||
data-avatar="{% if instance.get_image %}{{instance.get_image}}{% else %}https://ui-avatars.com/api/?name={{instance}}&background=random{% endif %}"
|
||||
draggable="true"
|
||||
>
|
||||
<div
|
||||
class="oh-sticky-table__sd"
|
||||
id="selectRow{{self.attrs.id}}{{instance.id}}"
|
||||
>
|
||||
<div class="d-flex">
|
||||
<div class="">
|
||||
<input
|
||||
type="checkbox"
|
||||
value="{{instance.id}}"
|
||||
class="oh-input oh-input__checkbox mt-2 mr-2 all-choose-user-row"
|
||||
/>
|
||||
</div>
|
||||
<div class="oh-profile oh-profile--md">
|
||||
<div class="oh-profile__avatar mr-1">
|
||||
<img
|
||||
src="{% if instance.get_image %}{{instance.get_image}}{% else %}https://ui-avatars.com/api/?name={{instance}}&background=random{% endif %}"
|
||||
class="oh-profile__image"
|
||||
/>
|
||||
</div>
|
||||
<span class="oh-profile__name oh-text--dark">{{instance}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let selectedIds = [];
|
||||
$(document).ready(function () {
|
||||
$("#{{self.attrs.id}}").select2({
|
||||
multiple: true,
|
||||
});
|
||||
$("#{{self.attrs.id}}")
|
||||
.next()
|
||||
.find(".select2-container")
|
||||
.css("width", "100%");
|
||||
|
||||
$("#chooseTableHeader")
|
||||
.not("[type=checkbox]")
|
||||
.click(function (e) {
|
||||
e.preventDefault();
|
||||
if ($(this).find("[type=checkbox]:first").is(":checked")) {
|
||||
$(this).find("[type=checkbox]:first").prop("checked", false);
|
||||
} else {
|
||||
$(this).find("[type=checkbox]:first").prop("checked", true);
|
||||
}
|
||||
$("#choose-all-user").change();
|
||||
});
|
||||
$("#selectAllUsers").click(function (e) {
|
||||
e.preventDefault();
|
||||
$("#choose-all-user").prop("checked", true);
|
||||
$("#choose-all-user").change();
|
||||
});
|
||||
$("#unselectAllUsers").click(function (e) {
|
||||
e.preventDefault();
|
||||
$("#choose-all-user").prop("checked", false);
|
||||
$("#choose-all-user").change();
|
||||
});
|
||||
$("#choose-all-user").change(function (e) {
|
||||
e.preventDefault();
|
||||
if ($(this).is(":checked")) {
|
||||
$(".all-choose-user-row:visible").prop("checked", true);
|
||||
$(".oh-sticky-table__tr--custom:visible .oh-sticky-table__sd").addClass(
|
||||
"oh-sticky-table__tr--selected"
|
||||
);
|
||||
} else {
|
||||
$(".all-choose-user-row:visible").prop("checked", false);
|
||||
$(
|
||||
".oh-sticky-table__tr--custom:visible .oh-sticky-table__sd"
|
||||
).removeClass("oh-sticky-table__tr--selected");
|
||||
}
|
||||
});
|
||||
$(".oh-sticky-table__tr--custom").click(function (e) {
|
||||
var checkbox = $(this).find("[type=checkbox]");
|
||||
|
||||
// Toggle the checkbox's checked state
|
||||
checkbox.prop("checked", function (i, checked) {
|
||||
if (!checked) {
|
||||
console.log($(this));
|
||||
$(this)
|
||||
.parent()
|
||||
.parent()
|
||||
.parent()
|
||||
.addClass("oh-sticky-table__tr--selected");
|
||||
} else {
|
||||
$(this)
|
||||
.parent()
|
||||
.parent()
|
||||
.parent()
|
||||
.removeClass("oh-sticky-table__tr--selected");
|
||||
}
|
||||
return !checked;
|
||||
});
|
||||
});
|
||||
$("#filterChoose").click(function (e) {
|
||||
e.preventDefault();
|
||||
var selectedRows = $("#filterChoose div").find(
|
||||
".oh-sticky-table__tr--selected"
|
||||
);
|
||||
var ids = [];
|
||||
var instanceData = [];
|
||||
$.each(selectedRows, function (indexInArray, valueOfElement) {
|
||||
var id = $(valueOfElement).parent().attr("data-instance-id");
|
||||
var label = $(valueOfElement).parent().attr("data-label");
|
||||
ids.push(id);
|
||||
instanceData.push({ id: id, label: label });
|
||||
});
|
||||
var selectedCount = selectedRows.length;
|
||||
$(".selected-count").html(selectedCount);
|
||||
console.log(ids);
|
||||
console.log(instanceData);
|
||||
|
||||
$("#{{self.attrs.id}}").val(ids);
|
||||
$("#{{self.attrs.id}}").change();
|
||||
$("#avatarsContainer").html("");
|
||||
$.each(instanceData, function (indexInArray, valueOfElement) {
|
||||
var imgUrl = $(
|
||||
`.oh-sticky-table__tr.oh-sticky-table__tr--custom[data-instance-id=${valueOfElement.id}]`
|
||||
).attr("data-avatar");
|
||||
$("#avatarsContainer").append(
|
||||
$(
|
||||
`<a href="#" class="avatars__item" title="${valueOfElement.label}"><img class="avatar" src="${imgUrl}" alt=""></a>`
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
$("#selectAllInstances").click(function (e) {
|
||||
e.preventDefault();
|
||||
$("#choose-all-user").prop("checked", true);
|
||||
$(".all-choose-user-row").prop("checked", true);
|
||||
$(".oh-sticky-table__tr--custom .oh-sticky-table__sd").addClass(
|
||||
"oh-sticky-table__tr--selected"
|
||||
);
|
||||
$("#choose-all-user").change();
|
||||
});
|
||||
$("#{{ self.attrs.id }}").change(function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Get all selected options
|
||||
$("#avatarsContainer").html("");
|
||||
$(".oh-sticky-table__tr--selected")
|
||||
.find("[type=checkbox]")
|
||||
.prop("checked", false);
|
||||
$(".oh-sticky-table__tr--selected").removeClass(
|
||||
"oh-sticky-table__tr--selected"
|
||||
);
|
||||
var selectedOptions = $(this).find(`:selected`);
|
||||
$(".selected-count").html(selectedOptions.length);
|
||||
|
||||
// Loop through the selected options
|
||||
selectedOptions.each(function (indexInArray, valueOfElement) {
|
||||
// Get the HTML content of each selected option
|
||||
var optionHtml = $(this).html();
|
||||
// Append the selected option's HTML to the avatarsContainer
|
||||
var imgUrl = $(
|
||||
`.oh-sticky-table__tr.oh-sticky-table__tr--custom[data-instance-id=${$(
|
||||
valueOfElement
|
||||
).val()}]`
|
||||
).attr("data-avatar");
|
||||
$("#avatarsContainer").append(
|
||||
$(
|
||||
`<a href="#" class="avatars__item" title="${optionHtml}"><img class="avatar" src="${imgUrl}" alt=""></a>`
|
||||
)
|
||||
);
|
||||
var optValue = $(valueOfElement).val();
|
||||
var rowId = "#selectRow{{self.attrs.id}}" + optValue;
|
||||
$(rowId).addClass("oh-sticky-table__tr--selected");
|
||||
$(rowId).find("[type=checkbox]").prop("checked", true);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,30 @@
|
||||
<div class="badge-container">
|
||||
<span
|
||||
class="tag-badge tag-badge--primary"
|
||||
>
|
||||
<div class="user-info d-flex align-items-center">
|
||||
<div class="oh-profile__avatar" style="line-height: 0px">
|
||||
<img
|
||||
src="https://ui-avatars.com/api/?name=Abigail+Lee&background=random"
|
||||
class="oh-profile__image oh-profile__image_custm"
|
||||
alt="Username"
|
||||
/>
|
||||
</div>
|
||||
<span class="oh-profile_name_custom"> Abigail Lee </span>
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
class="tag-badge tag-badge--primary"
|
||||
>
|
||||
<div class="user-info d-flex align-items-center">
|
||||
<div class="oh-profile__avatar" style="line-height: 0px">
|
||||
<img
|
||||
src="https://ui-avatars.com/api/?name=Magdalene&background=random"
|
||||
class="oh-profile__image oh-profile__image_custm"
|
||||
alt="Username"
|
||||
/>
|
||||
</div>
|
||||
<span class="oh-profile_name_custom">Mary Magdalene</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
3
horilla_widgets/tests.py
Normal file
3
horilla_widgets/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
15
horilla_widgets/views.py
Normal file
15
horilla_widgets/views.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
|
||||
# urls.py
|
||||
# path("employee-widget-filter",views.widget_filter,name="employee-widget-filter")
|
||||
|
||||
# views.py
|
||||
# @login_required
|
||||
# def widget_filter(request):
|
||||
# """
|
||||
# This method is used to return all the ids of the employees
|
||||
# """
|
||||
# ids = EmployeeFilter(request.GET).qs.values_list("id", flat=True)
|
||||
# return JsonResponse({'ids':list(ids)})
|
||||
0
horilla_widgets/widgets/__init__.py
Normal file
0
horilla_widgets/widgets/__init__.py
Normal file
11
horilla_widgets/widgets/horilla_multi_select_field.py
Normal file
11
horilla_widgets/widgets/horilla_multi_select_field.py
Normal file
@@ -0,0 +1,11 @@
|
||||
"""
|
||||
horilla_multi_select_field.py
|
||||
This module is used to write cutom multiple select field
|
||||
"""
|
||||
from django import forms
|
||||
|
||||
|
||||
class HorillaMultiSelectField(forms.ModelMultipleChoiceField):
|
||||
"""
|
||||
HorillaMultiSelectField
|
||||
"""
|
||||
97
horilla_widgets/widgets/select_widgets.py
Normal file
97
horilla_widgets/widgets/select_widgets.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
select_widgets.py
|
||||
|
||||
This module is used to write horilla form select widgets
|
||||
"""
|
||||
from django import forms
|
||||
|
||||
|
||||
class HorillaMultiSelectWidget(forms.Widget):
|
||||
"""
|
||||
HorillaMultiSelectWidget
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
filter_route_name,
|
||||
filter_class=None,
|
||||
filter_instance_contex_name=None,
|
||||
filter_template_path=None,
|
||||
instance=None,
|
||||
**kwargs
|
||||
) -> None:
|
||||
self.filter_route_name = filter_route_name
|
||||
self.filter_class = filter_class
|
||||
self.filter_instance_contex_name = filter_instance_contex_name
|
||||
self.filter_template_path = filter_template_path
|
||||
self.instance = instance
|
||||
super().__init__()
|
||||
|
||||
template_name = "horilla_widgets/horilla_multiselect_widget.html"
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
# Get the default context from the parent class
|
||||
context = super().get_context(name, value, attrs)
|
||||
# Add your custom data to the context
|
||||
queryset = self.choices.queryset
|
||||
field = self.choices.field
|
||||
context["queryset"] = queryset
|
||||
context["field_name"] = name
|
||||
context["field"] = field
|
||||
context["self"] = self
|
||||
context["filter_template_path"] = self.filter_template_path
|
||||
context["filter_route_name"] = self.filter_route_name
|
||||
self.attrs["id"] = ("id_" + name ) if self.attrs.get('id') is None else self.attrs.get("id")
|
||||
context[self.filter_instance_contex_name] = self.filter_class
|
||||
if self.instance is not None:
|
||||
data = getattr(self.instance,field)
|
||||
print(data)
|
||||
return context
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
print(data)
|
||||
print('---------------------')
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
Attendance form field updations
|
||||
"""
|
||||
|
||||
# def clean(self) -> Dict[str, Any]:
|
||||
# self.instance.employee_id = Employee.objects.filter(
|
||||
# id=self.data.get("employee_id")
|
||||
# ).first()
|
||||
|
||||
# self.errors.pop("employee_id", None)
|
||||
# if self.instance.employee_id is None:
|
||||
# raise ValidationError({"employee_id": "This field is required"})
|
||||
# super().clean()
|
||||
# employee_ids = self.data.getlist("employee_id")
|
||||
# existing_attendance = Attendance.objects.filter(
|
||||
# attendance_date=self.data["attendance_date"]
|
||||
# ).filter(employee_id__id__in=employee_ids)
|
||||
# if existing_attendance.exists():
|
||||
# raise ValidationError(
|
||||
# {
|
||||
# "employee_id": f"""Already attendance exists for {list(existing_attendance.values_list("employee_id__employee_first_name",flat=True))} employees"""
|
||||
# }
|
||||
# )
|
||||
|
||||
|
||||
# class AttendanceForm(ModelForm):
|
||||
# """
|
||||
# Model form for Attendance model
|
||||
# """
|
||||
|
||||
# employee_id = HorillaMultiSelectField(
|
||||
# queryset=Employee.objects.filter(employee_work_info__isnull=False),
|
||||
# widget=HorillaMultiSelectWidget(
|
||||
# filter_route_name="employee-widget-filter",
|
||||
# filter_class=EmployeeFilter,
|
||||
# filter_instance_contex_name="f",
|
||||
# filter_template_path="employee_filters.html",
|
||||
# ),
|
||||
# label=_("Employees"),
|
||||
# )
|
||||
Reference in New Issue
Block a user