Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement 'Add manual task' feature #4065

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1aceeac
Remove assigned task
sandeepsajan0 Jul 10, 2024
52ea82a
Added migration
sandeepsajan0 Jul 10, 2024
7d5f9d1
Replace confirm dialog box by hx-confirm
sandeepsajan0 Jul 21, 2024
f01b744
Remove dialog package
sandeepsajan0 Jul 21, 2024
2039e33
Remove tasks form task lists without reloading the page
sandeepsajan0 Jul 25, 2024
a8824e0
Resolve migrations conflicts
sandeepsajan0 Jul 25, 2024
f947a84
Show trash icon on hover and remove it for user group tasks
sandeepsajan0 Jul 25, 2024
64dd5c1
Use Dialog box inplace of fancybox modal, with htmx and alpine(only f…
sandeepsajan0 Jun 6, 2024
248dc80
Use htmx and dialog for project creation
sandeepsajan0 Jun 12, 2024
2f226a0
Use htmx and dialog for create reminder form
sandeepsajan0 Jun 12, 2024
67bc904
Use htmx and dialog for submission progress/status update
sandeepsajan0 Jun 12, 2024
2d94490
Fix requirements and progress button code
sandeepsajan0 Jun 13, 2024
ba175cb
Use htmx and dialog box for meta terms
sandeepsajan0 Jun 17, 2024
1b11c74
Remove Select2Widget for reviewers and partners, add defer to alpine …
sandeepsajan0 Jun 20, 2024
9f8f0bf
Use dialog modal for all other dialogs to avoid UI shift and closing …
sandeepsajan0 Jun 26, 2024
d663244
Add @fylgja/alpinejs-dialog as npm package
theskumar Jul 5, 2024
0c20039
New dialog implementation
theskumar Jul 7, 2024
21ae357
Delete modal
theskumar Jul 7, 2024
b662b37
Create reminder modal
theskumar Jul 13, 2024
7f0baff
Django messages as toast for htmx request
theskumar Jul 14, 2024
d1b066e
remove dependencies
theskumar Jul 15, 2024
3688cea
Finish lead update form
theskumar Jul 18, 2024
8da5eab
update meta terms modal and remove mobile actions
theskumar Jul 21, 2024
dec05e4
update how meta terms are rendered in submission detail
theskumar Jul 22, 2024
17d8f05
Add task button to bell icon dropdown
sandeepsajan0 Jul 21, 2024
0b94d26
Add raw form
sandeepsajan0 Jul 24, 2024
bb2e2b7
Add comments as task feature
sandeepsajan0 Jul 29, 2024
b27afa1
Show assigned user in comment for staff, link activity directly with …
sandeepsajan0 Jul 30, 2024
15409f9
Move assigned to below visibile to, update label and margins
sandeepsajan0 Aug 7, 2024
7e61895
Remove old form related things
sandeepsajan0 Aug 10, 2024
d194004
Fix lint fe
sandeepsajan0 Aug 10, 2024
9d8f121
Update comment form Ui with assigned task
sandeepsajan0 Aug 22, 2024
64b6aa6
Add logic for assigned to at initial load
sandeepsajan0 Aug 28, 2024
453587f
Remove partial urls and view
sandeepsajan0 Aug 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions hypha/apply/activity/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,28 @@
from pagedown.widgets import PagedownWidget

from hypha.apply.stream_forms.fields import MultiFileField
from hypha.apply.todo.options import COMMENT_TASK
from hypha.apply.todo.views import add_manual_task_to_user
from hypha.apply.users.models import STAFF_GROUP_NAME, User

from .models import Activity, ActivityAttachment


class CommentForm(FileFormMixin, forms.ModelForm):
attachments = MultiFileField(label=_("Attachments"), required=False)
assigned_to = forms.ModelChoiceField(
queryset=User.objects.filter(groups__name=STAFF_GROUP_NAME),
required=False,
label=_("Assign task(optional)"),
)

class Meta:
model = Activity
fields = ("message", "visibility")
fields = (
"message",
"visibility",
"assigned_to",
)
labels = {
"visibility": "Visible to",
"message": "Message",
Expand All @@ -23,7 +35,6 @@ class Meta:
"visibility": "Select a relevant user role. Staff can view every comment."
}
widgets = {
"visibility": forms.RadioSelect(),
"message": PagedownWidget(),
}

Expand All @@ -47,10 +58,21 @@ def __init__(self, *args, user=None, **kwargs):
visibility.choices = self.visibility_choices
visibility.initial = visibility.initial[0]
visibility.widget = forms.HiddenInput()
if not user.is_apply_staff:
self.fields["assigned_to"].widget = forms.HiddenInput()

@transaction.atomic
def save(self, commit=True):
instance = super().save(commit=True)
assigned_user = self.cleaned_data["assigned_to"]
if assigned_user:
# add task to assigned user
add_manual_task_to_user(
code=COMMENT_TASK,
message=instance.message,
user=assigned_user,
related_obj=instance,
)
added_files = self.cleaned_data["attachments"]
if added_files:
ActivityAttachment.objects.bulk_create(
Expand Down
11 changes: 11 additions & 0 deletions hypha/apply/activity/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,17 @@ def priviledged(self):
# Not visible to applicant
return self.visibility not in [APPLICANT, PARTNER, APPLICANT_PARTNERS, ALL]

# @property
def get_absolute_url(self):
if self.type == COMMENT:
return "{source_link}#communications".format(
source_link=self.source.get_absolute_url()
)
else:
return "{source_link}#activity_feed".format(
source_link=self.source.get_absolute_url()
)

@property
def private(self):
# not visible to all
Expand Down
18 changes: 16 additions & 2 deletions hypha/apply/activity/services.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models import OuterRef, Subquery

from hypha.apply.todo.models import Task

from .models import Activity


Expand Down Expand Up @@ -36,12 +41,21 @@ def get_related_comments_for_user(obj, user):
[`Activity`][hypha.apply.activity.models.Activity] queryset
"""
related_query = type(obj).activities.rel.related_query_name

return (
queryset = (
Activity.comments.filter(**{related_query: obj})
.select_related("user")
.prefetch_related(
"related_object",
)
.visible_to(user)
)

if user.is_apply_staff:
assigned_to_subquery = Task.objects.filter(
related_content_type=ContentType.objects.get_for_model(Activity),
related_object_id=OuterRef("id"),
).values("user__full_name")

queryset = queryset.annotate(assigned_to=Subquery(assigned_to_subquery))

return queryset
38 changes: 36 additions & 2 deletions hypha/apply/activity/templates/activity/include/comment_form.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,40 @@
{% load i18n %}

<div class="wrapper wrapper--comments">
{% trans "Submit" as submit %}
{% include "funds/includes/delegated_form_base.html" with form=comment_form value=submit extra_classes="form__comments" %}
{% trans "Submit" as value %}
{% load i18n %}
<form
class="form form__comments"
method="post"
id="{% if form_id %}{{ form_id }}{% else %}{{ comment_form.name }}{% endif %}"
enctype="multipart/form-data"
{% if action %}action="{{ action }}"{% endif %}
>
{% csrf_token %}

{{ comment_form.media }}
{% for hidden in comment_form.hidden_fields %}
{{ hidden }}
{% endfor %}

<div class="fields--visible">
{% for field in comment_form.visible_fields %}
{% if field.field %}
{% include "forms/includes/field.html" %}
{% else %}
{{ field }}
{% endif %}
{% endfor %}

<button
class="button button--primary"
id="{{ comment_form.name }}-submit"
name="{{ form_prefix }}{{ comment_form.name }}"
type="submit"
form="{% if form_id %}{{ form_id }}{% else %}{{ comment_form.name }}{% endif %}">
{{ value }}
</button>
</div>

</form>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
{% endif %}
</p>

{% if request.user.is_apply_staff and activity.assigned_to %}
<p class="feed__meta-item"><span>{% trans "Assigned to: " %} {{ activity.assigned_to }}</span></p>
{% endif %}
theskumar marked this conversation as resolved.
Show resolved Hide resolved

{% if editable and activity.user == request.user %}
<p class="feed__meta-item feed__meta-item--edit-button">
<a class="link link--edit-submission is-active js-edit-comment" href="#">
Expand Down
49 changes: 49 additions & 0 deletions hypha/apply/todo/migrations/0005_task_message_alter_task_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Generated by Django 4.2.11 on 2024-07-29 11:45

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("todo", "0004_alter_task_code"),
]

operations = [
migrations.AddField(
model_name="task",
name="message",
field=models.CharField(blank=True, max_length=250, null=True),
),
migrations.AlterField(
model_name="task",
name="code",
field=models.CharField(
choices=[
("comment_task", "Comment Task"),
("submission_draft", "Submission Draft"),
("determination_draft", "Determination draft"),
("review_draft", "Review Draft"),
("project_waiting_paf", "Project waiting PAF"),
("project_submit_paf", "Project submit PAF"),
("paf_required_changes", "PAF required changes"),
("paf_waiting_assignee", "PAF waiting assignee"),
("paf_waiting_approval", "PAF waiting approval"),
("project_waiting_contract", "Project waiting contract"),
(
"project_waiting_contract_document",
"Project waiting contract document",
),
(
"project_waiting_contract_review",
"Project waiting contract review",
),
("project_waiting_invoice", "Project waiting invoice"),
("invoice_required_changes", "Invoice required changes"),
("invoice_waiting_approval", "Invoice waiting approval"),
("invoice_waiting_paid", "Invoice waiting paid"),
("report_due", "Report due"),
],
max_length=50,
),
),
]
2 changes: 2 additions & 0 deletions hypha/apply/todo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class Task(models.Model):
)
related_object_id = models.PositiveIntegerField(blank=True, null=True)
related_object = GenericForeignKey("related_content_type", "related_object_id")
# for manually added tasks
message = models.CharField(max_length=250, null=True, blank=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull the message from the related item (i.e. the comment/activity object) itself? Maybe I'm missing something but it would better not to de-duplicate the message, as they can be edited.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think comment edited case should be handled but a separate task message can provide us flexibility in changing the language of the task message, it might be helpful later or in another kind of tasks. And duplicity in terms of storage shouldn't be an issue as these tasks are temporary and will be removed regularly.


class Meta:
ordering = ("-created_at",)
Expand Down
12 changes: 11 additions & 1 deletion hypha/apply/todo/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
INVOICE_WAITING_APPROVAL = "invoice_waiting_approval"
INVOICE_WAITING_PAID = "invoice_waiting_paid"
REPORT_DUE = "report_due"
COMMENT_TASK = "comment_task"

TASKS_CODE_CHOICES = (
(COMMENT_TASK, "Comment Task"),
(SUBMISSION_DRAFT, "Submission Draft"),
(DETERMINATION_DRAFT, "Determination draft"),
(REVIEW_DRAFT, "Review Draft"),
Expand All @@ -42,13 +44,20 @@


template_map = {
# ADD Manual Task
COMMENT_TASK: {
"text": _("{msg}"),
"icon": "comment",
"url": "{link}",
"type": _("Comment"),
},
# SUBMISSIONS ACTIONS
# :todo: actions for mupltiple stages of submission
SUBMISSION_DRAFT: {
"text": _(
'A Submission draft [<span class="truncate inline-block max-w-32 align-bottom ">{related.title}</span>]({link} "{related.title}") is waiting to be submitted'
),
"icon": "edit-draft",
"icon": "comment",
"url": "{link}",
"type": _("Draft"),
},
Expand Down Expand Up @@ -194,6 +203,7 @@ def get_task_template(request, task, **kwargs):
template_kwargs = {
"related": related_obj,
"link": link_to(related_obj, request),
"msg": task.message if task.message else "",
}
template["text"] = template["text"].format(**template_kwargs)
template["url"] = template["url"].format(**template_kwargs)
Expand Down
18 changes: 18 additions & 0 deletions hypha/apply/todo/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def dispatch(self, request, *args, **kwargs):

def delete(self, *args, **kwargs):
source = self.task.related_object
from hypha.apply.activity.models import Activity
from hypha.apply.determinations.models import Determination
from hypha.apply.projects.models import Invoice
from hypha.apply.review.models import Review
Expand All @@ -52,6 +53,8 @@ def delete(self, *args, **kwargs):
self.task.related_object, Review
):
source = self.task.related_object.submission
elif isinstance(self.task.related_object, Activity):
source = self.task.related_object.source
messenger(
MESSAGES.REMOVE_TASK,
user=self.request.user,
Expand Down Expand Up @@ -88,6 +91,21 @@ def add_task_to_user(code, user, related_obj):
return None


def add_manual_task_to_user(code, message, user, related_obj):
"""
Add task for a user
input:
message: message
user: User object
related_obj: Object - Submission, Project, Invoice, Report
output: task - Task object / None in case of no creation
"""
task = Task.objects.create(
code=code, user=user, related_object=related_obj, message=message
)
return task


def add_task_to_user_group(code, user_group, related_obj):
"""
Add task for user_groups
Expand Down
21 changes: 19 additions & 2 deletions hypha/static_src/sass/components/_form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -449,12 +449,12 @@
&__comments {
.fields--visible {
display: grid;
grid-template-areas: "message" "attachments" "visibility" "actions";
grid-template-areas: "message" "attachments" "visibility" "assigned_to" "actions";
grid-template-rows: auto auto auto auto;
grid-template-columns: 1fr;

@include media-query(lg) {
grid-template-areas: "message attachments" "message visibility";
grid-template-areas: "message attachments" "message visibility" "message assigned_to" "actions assigned_to";
grid-template-rows: auto auto;
grid-template-columns: 2fr 1fr;
column-gap: 2rem;
Expand All @@ -466,6 +466,16 @@
}
}

// stylelint-disable-next-line selector-class-pattern
.button {
max-width: 150px;
}

// stylelint-disable-next-line selector-class-pattern
.form__group {
margin-block-end: 0.5rem;
}

// stylelint-disable-next-line selector-class-pattern
.id_attachments {
grid-area: attachments;
Expand All @@ -484,6 +494,13 @@
.id_visibility_0 {
grid-area: visibility;
}

// stylelint-disable-next-line selector-class-pattern
.id_assigned_to {
grid-area: assigned_to;
display: flex;
flex-direction: column;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions hypha/templates/includes/menu-notifications.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<button type="button" @click='open = false' class="appearance-none opacity-70 hover:opacity-100">
{% heroicon_solid "x-mark" aria_hidden="true" class="" %}
</button>

</header>

<div
Expand Down
4 changes: 4 additions & 0 deletions hypha/templates/includes/sprites.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@
<path d="M12 20h9"/>
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
</svg>
<svg id="comment" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.625 9.75a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375m-13.5 3.01c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.184-4.183a1.14 1.14 0 0 1 .778-.332 48.294 48.294 0 0 0 5.83-.498c1.585-.233 2.708-1.626 2.708-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z" />
</svg>

</svg>
Loading