diff --git a/hypha/apply/activity/templates/messages/email/ready_to_review.html b/hypha/apply/activity/templates/messages/email/ready_to_review.html index 4aa9a622a4af41cefb81900732d11d50af413e22..be03439ca12a14f27a1891796be44b79ecc3bed9 100644 --- a/hypha/apply/activity/templates/messages/email/ready_to_review.html +++ b/hypha/apply/activity/templates/messages/email/ready_to_review.html @@ -4,5 +4,11 @@ This application is awaiting your review. Title: {{ source.title }} +{% if related.title %} +Reminder Title: {{ related.title }} +{% endif %} +{% if related.description %} +Reminder Description: {{ related.description }} +{% endif %} Link: {{ request.scheme }}://{{ request.get_host }}{{ source.get_absolute_url }} {% endblock %} diff --git a/hypha/apply/api/v1/reminder/serializers.py b/hypha/apply/api/v1/reminder/serializers.py index 8da5183147b2d5103aefc1c520008f636981cc6d..8f99cbab7ebfecd1eec7807d213e156f27f4ef07 100644 --- a/hypha/apply/api/v1/reminder/serializers.py +++ b/hypha/apply/api/v1/reminder/serializers.py @@ -7,9 +7,9 @@ class SubmissionReminderSerializer(serializers.ModelSerializer): def validate(self, data): """ - Check title & assign are not empty. + Check title is empty. """ - required_fields = ['title', 'assign'] + required_fields = ['title'] for field in required_fields: if not data.get(field, None): raise serializers.ValidationError({field: "shouldn't be empty"}) @@ -17,5 +17,5 @@ class SubmissionReminderSerializer(serializers.ModelSerializer): class Meta: model = Reminder - fields = ('time', 'action_type', 'is_expired', 'id', 'action', 'title', 'assign', 'description') + fields = ('time', 'action_type', 'is_expired', 'id', 'action', 'title', 'description') read_only_fields = ('action_type', 'is_expired') diff --git a/hypha/apply/api/v1/reminder/views.py b/hypha/apply/api/v1/reminder/views.py index af63068710ff6c7c90ab391881c90ac158b88116..d390928ac8a68237c3a2e1b78905ccb27049e5e4 100644 --- a/hypha/apply/api/v1/reminder/views.py +++ b/hypha/apply/api/v1/reminder/views.py @@ -4,7 +4,6 @@ from rest_framework.response import Response from rest_framework_api_key.permissions import HasAPIKey from hypha.apply.funds.models import Reminder -from hypha.apply.funds.models.utils import CustomerTypes from ..mixin import SubmissionNestedMixin from ..permissions import IsApplyStaffUser @@ -64,9 +63,9 @@ class SubmissionReminderViewSet( "type": "DateTime" }, { - "id": "assign", - "kwargs": {"label": "Assign", "required": True, "choices": CustomerTypes.choices()}, + "id": "action", + "kwargs": {"label": "Action", "required": True, "choices": getattr(Reminder, 'ACTIONS').items(), "initial": getattr(Reminder, 'REVIEW')}, "type": "Select" - }, + } ] return Response(fields) diff --git a/hypha/apply/funds/forms.py b/hypha/apply/funds/forms.py index 3714761dcd8a14f656658bfe7de61ba58b7f9eb8..7c3942ff2db30779dae10ec4d939377e2819caac 100644 --- a/hypha/apply/funds/forms.py +++ b/hypha/apply/funds/forms.py @@ -464,10 +464,12 @@ class CreateReminderForm(forms.ModelForm): def save(self, *args, **kwargs): return Reminder.objects.create( + title=self.cleaned_data['title'], + description=self.cleaned_data['description'], time=self.cleaned_data['time'], submission=self.cleaned_data['submission'], user=self.user) class Meta: model = Reminder - fields = ['time', 'action'] + fields = ['title', 'description', 'time', 'action'] diff --git a/hypha/apply/funds/migrations/0088_auto_20210416_0543.py b/hypha/apply/funds/migrations/0088_auto_20210416_0543.py deleted file mode 100644 index c6d03a574d143144d9d3b01a3183327d3bc10142..0000000000000000000000000000000000000000 --- a/hypha/apply/funds/migrations/0088_auto_20210416_0543.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 2.2.19 on 2021-04-16 05:43 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('funds', '0087_applicationsettings'), - ] - - operations = [ - migrations.AddField( - model_name='reminder', - name='assign', - field=models.CharField(blank=True, choices=[('Applicant', 'APPLICANT'), ('Partners', 'PARTNERS'), ('Lead', 'LEAD'), ('Reviewers', 'REVIEWERS'), ('Team', 'TEAM')], default='none', max_length=50, null=True), - ), - migrations.AddField( - model_name='reminder', - name='description', - field=models.TextField(blank=True, null=True), - ), - migrations.AddField( - model_name='reminder', - name='title', - field=models.CharField(blank=True, max_length=60, null=True), - ), - ] diff --git a/hypha/apply/funds/migrations/0088_auto_20210423_1257.py b/hypha/apply/funds/migrations/0088_auto_20210423_1257.py new file mode 100644 index 0000000000000000000000000000000000000000..d3e96f8dc868a5464510fc1e6569c734cf928ec2 --- /dev/null +++ b/hypha/apply/funds/migrations/0088_auto_20210423_1257.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.19 on 2021-04-23 12:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('funds', '0087_applicationsettings'), + ] + + operations = [ + migrations.AddField( + model_name='reminder', + name='description', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='reminder', + name='title', + field=models.CharField(default='', max_length=60), + ), + ] diff --git a/hypha/apply/funds/models/reminders.py b/hypha/apply/funds/models/reminders.py index 5d6ba5563ecadbd3ac1f7184fc72e13ed3722d62..0fc1515ecb79d4a0a2471880037b6b490c0bac4d 100644 --- a/hypha/apply/funds/models/reminders.py +++ b/hypha/apply/funds/models/reminders.py @@ -1,11 +1,10 @@ from django.conf import settings +from django.core.exceptions import ValidationError from django.db import models from django.utils import timezone from hypha.apply.activity.messaging import MESSAGES -from .utils import CustomerTypes - class Reminder(models.Model): REVIEW = 'reviewers_review' @@ -35,9 +34,8 @@ class Reminder(models.Model): max_length=50, ) sent = models.BooleanField(default=False) - title = models.CharField(max_length=60, blank=True, null=True) - assign = models.CharField(choices=CustomerTypes.choices(), default="none", max_length=50, blank=True, null=True) - description = models.TextField(blank=True, null=True) + title = models.CharField(max_length=60, blank=False, default='') + description = models.TextField(blank=True) def __str__(self): return '{} at {}'.format( @@ -48,6 +46,10 @@ class Reminder(models.Model): class Meta: ordering = ['-time'] + def clean(self): + if self.title == '': + raise ValidationError('Title is Empty') + @property def is_expired(self): return timezone.now() > self.time diff --git a/hypha/apply/funds/models/utils.py b/hypha/apply/funds/models/utils.py index fd736a9287b69d3491afc1fce7adaaf581706978..1a171e76e3b59440a48dcf09d8b96066629d2d49 100644 --- a/hypha/apply/funds/models/utils.py +++ b/hypha/apply/funds/models/utils.py @@ -1,5 +1,3 @@ -from enum import Enum - from django.db import models from django.urls import reverse from wagtail.admin.edit_handlers import ( @@ -34,18 +32,6 @@ LIMIT_TO_COMMUNITY_REVIEWERS = {'groups__name': COMMUNITY_REVIEWER_GROUP_NAME} LIMIT_TO_REVIEWER_GROUPS = {'groups__name__in': REVIEW_GROUPS} -class CustomerTypes(Enum): - APPLICANT = 'Applicant' - PARTNERS = 'Partners' - LEAD = 'Lead' - REVIEWERS = 'Reviewers' - TEAM = 'Team' - - @classmethod - def choices(cls): - return [(choice.value, choice.name) for choice in cls] - - def admin_url(page): return reverse('wagtailadmin_pages:edit', args=(page.id,)) diff --git a/hypha/apply/funds/templates/funds/includes/create_reminder_form.html b/hypha/apply/funds/templates/funds/includes/create_reminder_form.html index 6e74066c408cce5cb2c58473f677bb9762c2d7f7..00fbb7381ce5ff1d83b36d8856e177469f5dd234 100644 --- a/hypha/apply/funds/templates/funds/includes/create_reminder_form.html +++ b/hypha/apply/funds/templates/funds/includes/create_reminder_form.html @@ -1,4 +1,4 @@ <div class="modal" id="create-reminder"> <h4 class="modal__header-bar">Create Reminder</h4> - {% include 'funds/includes/delegated_form_base.html' with form=reminder_form value='Create' %} + {% include 'funds/includes/delegated_form_base.html' with form=reminder_form value='Create' extra_classes='form__reminder'%} </div> diff --git a/hypha/apply/funds/templates/funds/includes/reminders_block.html b/hypha/apply/funds/templates/funds/includes/reminders_block.html index 2cb7144206ea96467671c76a763b99aacd9c46e3..01ce92acd40f2da089b3d5c85e5a4ce62e70e8b5 100644 --- a/hypha/apply/funds/templates/funds/includes/reminders_block.html +++ b/hypha/apply/funds/templates/funds/includes/reminders_block.html @@ -6,9 +6,15 @@ <li><strong>{{ action.grouper }}</strong> <ul> {% for reminder in action.list %} - <li class="{% if reminder.is_expired %}expired-reminder{% endif %}"> - {{ reminder.time|date:"SHORT_DATETIME_FORMAT" }} - <a class="link" href="{% url 'funds:submissions:reminders:delete' object.id reminder.id %}"> + <li class="{% if reminder.is_expired %}expired-reminder{% endif %} reminder-list"> + <div class="reminder-title"> + {% if reminder.title %} + {{ reminder.title }} + {% else %} + untitled reminder + {% endif %} + </div> + <a class="link reminder-delete" href="{% url 'funds:submissions:reminders:delete' object.id reminder.id %}"> <svg class="icon icon--delete"><use xlink:href="#delete"></use></svg> </a> </li> diff --git a/hypha/static_src/src/app/src/common/components/DateTime/index.js b/hypha/static_src/src/app/src/common/components/DateTime/index.js index d0f8086b7573d2edb4fd42f4355d8ab0cb712eb2..74ad0e168dc52abf74828561fdac7a4ba7af472a 100644 --- a/hypha/static_src/src/app/src/common/components/DateTime/index.js +++ b/hypha/static_src/src/app/src/common/components/DateTime/index.js @@ -29,6 +29,7 @@ const DateTime = props => { name={props.name} id={props.name} value={selectedDate} + format={'Y-MM-d H:mm'} /> </MuiPickersUtilsProvider> </div> diff --git a/hypha/static_src/src/app/src/containers/DisplayPanel/index.js b/hypha/static_src/src/app/src/containers/DisplayPanel/index.js index 1330d97fa208e40e4b55081b52cc5334ffc91c13..ce27751756925ccc777bb078db088bf52a3d9b68 100644 --- a/hypha/static_src/src/app/src/containers/DisplayPanel/index.js +++ b/hypha/static_src/src/app/src/containers/DisplayPanel/index.js @@ -79,8 +79,8 @@ const DisplayPanel = props => { <Determination submissionID={submissionID} submission={submission}/> : null} {/* <ScreeningOutcome submissionID={submissionID} /> */} <StatusActions submissionID={submissionID} /> - <ReminderContainer submissionID={submissionID}/> <ScreeningStatusContainer submissionID={submissionID} /> + <ReminderContainer submissionID={submissionID}/> <UserFlagContainer /> <StaffFlagContainer /> <ReviewInformation submissionID={submissionID} /> diff --git a/hypha/static_src/src/app/src/containers/ReminderContainer/components/ReminderList.js b/hypha/static_src/src/app/src/containers/ReminderContainer/components/ReminderList.js index ce6443674d2438146275e46817f238ebcb303de8..70c1a5ef5c43a661d62f087eacc9589a6a15c188 100644 --- a/hypha/static_src/src/app/src/containers/ReminderContainer/components/ReminderList.js +++ b/hypha/static_src/src/app/src/containers/ReminderContainer/components/ReminderList.js @@ -8,18 +8,23 @@ class ReminderList extends React.PureComponent { render() { return (<ul> - {this.props.reminders.map(reminder => { - return <li style={{color : reminder.is_expired ? 'grey' : 'black'}} className="list-item" key={reminder.id}> - <div className="title-text">{reminder.title ? reminder.title : "untitled reminder"}</div> - <Tooltip title={<span style={{fontSize: '14px'}}>Delete</span>} placement="right-start"> - <DeleteIcon - className="delete-icon" - fontSize="small" - onClick={() => this.props.deleteReminder(this.props.submissionID, reminder.id)} - /> - </Tooltip> - </li> - })} + <li> + <strong>{this.props.title}</strong> + <ul> + {this.props.reminders.map(reminder => { + return <li style={{color : reminder.is_expired ? 'grey' : 'black'}} className="list-item" key={reminder.id}> + <div className="title-text">{reminder.title ? reminder.title : "untitled reminder"}</div> + <Tooltip title={<span style={{fontSize: '14px'}}>Delete</span>} placement="right-start"> + <DeleteIcon + className="delete-icon" + fontSize="small" + onClick={() => this.props.deleteReminder(this.props.submissionID, reminder.id)} + /> + </Tooltip> + </li> + })} + </ul> + </li> </ul>) } } @@ -27,7 +32,8 @@ class ReminderList extends React.PureComponent { ReminderList.propTypes = { reminders: PropTypes.array, deleteReminder: PropTypes.func, - submissionID: PropTypes.number + submissionID: PropTypes.number, + title: PropTypes.string } export default ReminderList; diff --git a/hypha/static_src/src/app/src/containers/ReminderContainer/index.js b/hypha/static_src/src/app/src/containers/ReminderContainer/index.js index d0bb334c9a9bcfab219def7e085a6714cbdf4a0b..b9b82d5b91f7fd59be80db6c433856b12f3044bd 100644 --- a/hypha/static_src/src/app/src/containers/ReminderContainer/index.js +++ b/hypha/static_src/src/app/src/containers/ReminderContainer/index.js @@ -67,13 +67,16 @@ class ReminderContainer extends React.PureComponent { /> </> </Modal> - {this.props.reminderInfo.reminders && this.props.reminderInfo.reminders.length + {this.props.reminders && this.props.reminders.length ? - <ReminderList - reminders={this.props.reminderInfo.reminders} + this.props.reminders.map(reminders => + <ReminderList + key={reminders.grouper} + title={reminders.grouper} + reminders={reminders.list} submissionID={this.props.submissionID} deleteReminder={this.props.deleteReminder} - /> + />) : <div>No reminders yet.</div>} </div> @@ -88,11 +91,13 @@ ReminderContainer.propTypes = { initAction: PropTypes.func, deleteReminder: PropTypes.func, classes: PropTypes.object, - submissionID: PropTypes.number + submissionID: PropTypes.number, + reminders: PropTypes.array } const mapStateToProps = state => ({ reminderInfo : Selectors.selectReminderContainer(state), + reminders: Selectors.selectReminders(state) }); diff --git a/hypha/static_src/src/app/src/containers/ReminderContainer/selectors.js b/hypha/static_src/src/app/src/containers/ReminderContainer/selectors.js index d97315505976350ee9de7dc33bd14bf44a23c508..1c135cb09fa97520a46e4f217fb8ac6e814f9cf2 100644 --- a/hypha/static_src/src/app/src/containers/ReminderContainer/selectors.js +++ b/hypha/static_src/src/app/src/containers/ReminderContainer/selectors.js @@ -5,3 +5,20 @@ export const selectFieldsRenderer = state => state.ReminderContainer ? state.ReminderContainer : initialState; export const selectReminderContainer = createSelector(selectFieldsRenderer, domain => domain); + +export const selectReminders = createSelector(selectReminderContainer, domain => { + let reminders = [] + domain.reminders && domain.reminders.map(reminder => { + if(reminders.find(r => r.grouper == reminder.action_type)){ + const index = reminders.indexOf(reminders.find(r => r.grouper == reminder.action_type)) + reminders[index].list.push(reminder) + } + else { + reminders.push({ + grouper: reminder.action_type, + list: [reminder] + }) + } + }) + return reminders +}) diff --git a/hypha/static_src/src/sass/apply/components/_reminder-sidebar.scss b/hypha/static_src/src/sass/apply/components/_reminder-sidebar.scss index 520156dbe9d9781b4ce5422e22eaeb26cfd82b87..cf0dea315a030359bf359eade68b09726b3d669e 100644 --- a/hypha/static_src/src/sass/apply/components/_reminder-sidebar.scss +++ b/hypha/static_src/src/sass/apply/components/_reminder-sidebar.scss @@ -1,3 +1,23 @@ .expired-reminder { color: $color--mid-dark-grey; } + +.reminder-list { + padding: 3px 3px 0; +} + +.reminder-title { + width: 80%; + display: inline-block; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.reminder-delete { + float: right; +} + +.form__reminder { + padding-left: 70px; +}