Skip to content
Open
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
f60a110
update dashboard and Tile, remove helper component
CalamityC Apr 15, 2026
7d766ee
change tasks handling
CalamityC Apr 15, 2026
6257685
add card click and invisible tasks
CalamityC Apr 21, 2026
be66b7d
add properties to issue serializer
CalamityC May 6, 2026
72b5b55
extend tests
CalamityC May 6, 2026
26dc21e
fix property
CalamityC May 6, 2026
d5cc957
update Dashboard code
CalamityC May 6, 2026
7cbd0c7
Use Modal component and add tasks fixtures
CalamityC May 7, 2026
0df9449
Extend tests
CalamityC May 7, 2026
036ea40
Limit resolve checks to tasks
CalamityC May 8, 2026
5968e32
Add render function
CalamityC May 8, 2026
74c8234
Improve sort and status
CalamityC May 11, 2026
284077f
Add update task functionality
CalamityC May 11, 2026
2ecabdf
Revert accidentally pushed sync setting
CalamityC May 11, 2026
614f696
Add perms
CalamityC May 11, 2026
9e331ab
Improve step button
CalamityC May 19, 2026
ca7412c
Add questions to ProjectIssueSerializer and IssueSerializer
jochenklar May 19, 2026
92fa145
Fix e2e test
CalamityC May 19, 2026
16c173a
Remove editors from new tasks fixtures to prevent tests from failing
CalamityC May 19, 2026
6cedb77
Fix questions in issue model
CalamityC May 21, 2026
0945936
Add questions to frontend display
CalamityC May 21, 2026
05f56b1
Extend useFormattedDateTime, tweak questions and date display
CalamityC May 21, 2026
aa11db0
Add ProjectIssueTaskConditionSerializer, add attribute field to Proje…
CalamityC Jun 11, 2026
b11c950
Tweak col display in modal
CalamityC Jun 16, 2026
a0ec5b1
Use models.TextChoices for RelationTypes and fix models and viewets a…
CalamityC Jun 11, 2026
42f7beb
Fix typo
CalamityC Jun 15, 2026
938f670
Fix typo again
CalamityC Jun 15, 2026
18ea762
Fix test and typo again
CalamityC Jun 15, 2026
729395c
Remove wrong fixes
CalamityC Jun 15, 2026
c75476f
Leave constants the same
CalamityC Jun 15, 2026
f842f53
Add migrations
CalamityC Jun 15, 2026
25adc8b
Replace lesser_than with less_than
CalamityC Jun 16, 2026
952481c
Add translation
CalamityC Jun 16, 2026
4221093
Merge pull request #1638 from rdmorganiser/3.0.0/fix/condition-relations
CalamityC Jun 16, 2026
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
14 changes: 14 additions & 0 deletions rdmo/conditions/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django.db import models
from django.utils.translation import gettext_lazy as _


class RelationTypes(models.TextChoices):
RELATION_EQUAL = 'eq', _('is equal to (==)')
RELATION_NOT_EQUAL = 'neq', _('is not equal to (!=)')
RELATION_CONTAINS = 'contains', _('contains')
RELATION_GREATER_THAN = 'gt', _('is greater than (>)')
RELATION_GREATER_THAN_EQUAL = 'gte', _('is greater than or equal to (>=)')
RELATION_LESS_THAN = 'lt', _('is less than (<)')
RELATION_LESS_THAN_EQUAL = 'lte', _('is less than or equal to (<=)')
RELATION_EMPTY = 'empty', _('is empty')
RELATION_NOT_EMPTY = 'notempty', _('is not empty')
18 changes: 18 additions & 0 deletions rdmo/conditions/migrations/0026_alter_condition_relation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.2.14 on 2026-06-16 09:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('conditions', '0025_alter_condition_uri_path'),
]

operations = [
migrations.AlterField(
model_name='condition',
name='relation',
field=models.CharField(choices=[('eq', 'is equal to (==)'), ('neq', 'is not equal to (!=)'), ('contains', 'contains'), ('gt', 'is greater than (>)'), ('gte', 'is greater than or equal to (>=)'), ('lt', 'is less than (<)'), ('lte', 'is less than or equal to (<=)'), ('empty', 'is empty'), ('notempty', 'is not empty')], help_text='The relation this condition is using.', max_length=8, verbose_name='Relation'),
),
]
51 changes: 16 additions & 35 deletions rdmo/conditions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,10 @@
from rdmo.core.utils import join_url
from rdmo.domain.models import Attribute

from .constants import RelationTypes

class Condition(models.Model):

RELATION_EQUAL = 'eq'
RELATION_NOT_EQUAL = 'neq'
RELATION_CONTAINS = 'contains'
RELATION_GREATER_THAN = 'gt'
RELATION_GREATER_THAN_EQUAL = 'gte'
RELATION_LESSER_THAN = 'lt'
RELATION_LESSER_THAN_EQUAL = 'lte'
RELATION_EMPTY = 'empty'
RELATION_NOT_EMPTY = 'notempty'
RELATION_CHOICES = (
(RELATION_EQUAL, 'is equal to (==)'),
(RELATION_NOT_EQUAL, 'is not equal to (!=)'),
(RELATION_CONTAINS, 'contains'),
(RELATION_GREATER_THAN, 'is greater than (>)'),
(RELATION_GREATER_THAN_EQUAL, 'is greater than or equal (>=)'),
(RELATION_LESSER_THAN, 'is lesser than (<)'),
(RELATION_LESSER_THAN_EQUAL, 'is lesser than or equal (<=)'),
(RELATION_EMPTY, 'is empty'),
(RELATION_NOT_EMPTY, 'is not empty'),
)
class Condition(models.Model):

uri = models.URLField(
max_length=800, blank=True,
Expand Down Expand Up @@ -67,7 +48,7 @@ class Condition(models.Model):
help_text=_('The attribute of the value for this condition.')
)
relation = models.CharField(
max_length=8, choices=RELATION_CHOICES,
max_length=8, choices=RelationTypes,
verbose_name=_('Relation'),
help_text=_('The relation this condition is using.')
)
Expand Down Expand Up @@ -134,31 +115,31 @@ def resolve(self, values, set_prefix=None, set_index=None):
set_prefix, set_index = rpartition[0], int(rpartition[2])
return self.resolve(values, set_prefix, set_index)

if self.relation == self.RELATION_EQUAL:
if self.relation == RelationTypes.RELATION_EQUAL:
return self._resolve_equal(source_values)

elif self.relation == self.RELATION_NOT_EQUAL:
elif self.relation == RelationTypes.RELATION_NOT_EQUAL:
return not self._resolve_equal(source_values)

elif self.relation == self.RELATION_CONTAINS:
elif self.relation == RelationTypes.RELATION_CONTAINS:
return self._resolve_contains(source_values)

elif self.relation == self.RELATION_GREATER_THAN:
elif self.relation == RelationTypes.RELATION_GREATER_THAN:
return self._resolve_greater_than(source_values)

elif self.relation == self.RELATION_GREATER_THAN_EQUAL:
elif self.relation == RelationTypes.RELATION_GREATER_THAN_EQUAL:
return self._resolve_greater_than_equal(source_values)

elif self.relation == self.RELATION_LESSER_THAN:
return self._resolve_lesser_than(source_values)
elif self.relation == RelationTypes.RELATION_LESS_THAN:
return self._resolve_less_than(source_values)

elif self.relation == self.RELATION_LESSER_THAN_EQUAL:
return self._resolve_lesser_than_equal(source_values)
elif self.relation == RelationTypes.RELATION_LESS_THAN_EQUAL:
return self._resolve_less_than_equal(source_values)

elif self.relation == self.RELATION_EMPTY:
elif self.relation == RelationTypes.RELATION_EMPTY:
return not self._resolve_not_empty(source_values)

elif self.relation == self.RELATION_NOT_EMPTY:
elif self.relation == RelationTypes.RELATION_NOT_EMPTY:
return self._resolve_not_empty(source_values)

else:
Expand Down Expand Up @@ -205,7 +186,7 @@ def _resolve_greater_than_equal(self, values):

return False

def _resolve_lesser_than(self, values):
def _resolve_less_than(self, values):

for value in values:
try:
Expand All @@ -216,7 +197,7 @@ def _resolve_lesser_than(self, values):

return False

def _resolve_lesser_than_equal(self, values):
def _resolve_less_than_equal(self, values):

for value in values:
try:
Expand Down
3 changes: 2 additions & 1 deletion rdmo/conditions/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from rdmo.core.utils import is_truthy, render_to_format
from rdmo.core.views import ChoicesViewSet

from .constants import RelationTypes
from .models import Condition
from .renderers import ConditionRenderer
from .serializers.export import ConditionExportSerializer
Expand Down Expand Up @@ -77,4 +78,4 @@ def get_export_renderer_context(self, request):

class RelationViewSet(ChoicesViewSet):
permission_classes = (IsAuthenticated, )
queryset = Condition.RELATION_CHOICES
queryset = RelationTypes.choices
18 changes: 12 additions & 6 deletions rdmo/core/assets/js/hooks/useFormattedDateTime.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import { format } from 'date-fns'
import { format, parseISO } from 'date-fns'
import { de, enUS } from 'date-fns/locale'

const getLocaleObject = (language) => {
return language === 'de' ? de : enUS
}

const FORMAT_STRINGS = {
en: 'MMM d, yyyy, h:mm a',
de: 'd. MMM yyyy, H:mm',
dateTime: {
en: 'MMM d, yyyy, h:mm a',
de: 'd. MMM yyyy, H:mm',
},
dateOnly: {
en: 'MMM d, yyyy',
de: 'd. MMM yyyy',
},
}

export const useFormattedDateTime = (date, language) => {
export const useFormattedDateTime = (date, language, formatType = 'dateTime') => {
const locale = getLocaleObject(language)
const formatString = language === 'de' ? FORMAT_STRINGS.de : FORMAT_STRINGS.en
const formatString = FORMAT_STRINGS[formatType][language] ?? FORMAT_STRINGS[formatType].en

return format(new Date(date), formatString, { locale })
return format(parseISO(date), formatString, { locale })
}

export default useFormattedDateTime
4 changes: 4 additions & 0 deletions rdmo/projects/assets/js/project/actions/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@ export const DELETE_PROJECT_VISIBILITY_ERROR = 'DELETE_PROJECT_VISIBILITY_ERROR'
export const FETCH_PROJECT_VISIBILITY_INIT = 'FETCH_PROJECT_VISIBILITY_INIT'
export const FETCH_PROJECT_VISIBILITY_SUCCESS = 'FETCH_PROJECT_VISIBILITY_SUCCESS'
export const FETCH_PROJECT_VISIBILITY_ERROR = 'FETCH_PROJECT_VISIBILITY_ERROR'

export const UPDATE_PROJECT_TASK_INIT = 'UPDATE_PROJECT_TASK_INIT'
export const UPDATE_PROJECT_TASK_SUCCESS = 'UPDATE_PROJECT_TASK_SUCCESS'
export const UPDATE_PROJECT_TASK_ERROR = 'UPDATE_PROJECT_TASK_ERROR'
35 changes: 35 additions & 0 deletions rdmo/projects/assets/js/project/actions/projectActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,41 @@ export function deleteProjectVisibility() {
}
}

// project task

export function updateProjectTask(issueId, data) {
return function (dispatch, getState) {
dispatch(addToPending('updateProjectTask'))
dispatch({ type: actionTypes.UPDATE_PROJECT_TASK_INIT })

return ProjectApi.updateProjectTask(projectId, issueId, data)
.then(() => {
const state = getState()
const currentBundle = state.project.project

return ProjectApi.fetchProjectTasks(projectId)
.then((tasks) => ({ currentBundle, tasks }))
})
.then(({ currentBundle, tasks }) => {
const updatedBundle = {
...currentBundle,
tasks,
}

dispatch(removeFromPending('updateProjectTask'))
dispatch({
type: actionTypes.UPDATE_PROJECT_SUCCESS,
project: updatedBundle,
})
})
.catch((error) => {
dispatch(removeFromPending('updateProjectTask'))
dispatch({ type: actionTypes.UPDATE_PROJECT_TASK_ERROR, error })
throw error
})
}
}

// memberships / invites / leave

export function fetchProjectInvites() {
Expand Down
4 changes: 4 additions & 0 deletions rdmo/projects/assets/js/project/api/ProjectApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export default class ProjectApi extends BaseApi {
return this.get(`/api/v1/projects/projects/${projectId}/issues/`)
}

static updateProjectTask(projectId, issueId, data) {
return this.put(`/api/v1/projects/projects/${projectId}/issues/${issueId}/`, data)
}

static fetchProjectMemberships(projectId) {
return this.get(`/api/v1/projects/projects/${projectId}/memberships/`)
}
Expand Down
Loading
Loading