Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28c9245fe1 |
@@ -166,7 +166,6 @@ class IssueListEndpoint(BaseAPIView):
|
||||
"link_count",
|
||||
"is_draft",
|
||||
"archived_at",
|
||||
"deleted_at",
|
||||
)
|
||||
datetime_fields = ["created_at", "updated_at"]
|
||||
issues = user_timezone_converter(
|
||||
@@ -401,7 +400,6 @@ class IssueViewSet(BaseViewSet):
|
||||
"link_count",
|
||||
"is_draft",
|
||||
"archived_at",
|
||||
"deleted_at",
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -100,8 +100,10 @@ class OauthAdapter(Adapter):
|
||||
account, created = Account.objects.update_or_create(
|
||||
user=user,
|
||||
provider=self.provider,
|
||||
provider_account_id=self.user_data.get("user").get("provider_id"),
|
||||
defaults={
|
||||
"provider_account_id": self.user_data.get("user").get(
|
||||
"provider_id"
|
||||
),
|
||||
"access_token": self.token_data.get("access_token"),
|
||||
"refresh_token": self.token_data.get("refresh_token", None),
|
||||
"access_token_expired_at": self.token_data.get(
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
# Django imports
|
||||
from django.utils import timezone
|
||||
from django.apps import apps
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
# Third party imports
|
||||
from celery import shared_task
|
||||
|
||||
|
||||
@shared_task
|
||||
def soft_delete_related_objects(
|
||||
app_label, model_name, instance_pk, using=None
|
||||
):
|
||||
model_class = apps.get_model(app_label, model_name)
|
||||
instance = model_class.all_objects.get(pk=instance_pk)
|
||||
related_fields = instance._meta.get_fields()
|
||||
for field in related_fields:
|
||||
if field.one_to_many or field.one_to_one or field.many_to_many:
|
||||
try:
|
||||
if field.one_to_many or field.many_to_many:
|
||||
related_objects = getattr(instance, field.name).all()
|
||||
elif field.one_to_one:
|
||||
related_object = getattr(instance, field.name)
|
||||
related_objects = (
|
||||
[related_object] if related_object is not None else []
|
||||
)
|
||||
for obj in related_objects:
|
||||
if obj:
|
||||
obj.deleted_at = timezone.now()
|
||||
obj.save(using=using)
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
# @shared_task
|
||||
def restore_related_objects(app_label, model_name, instance_pk, using=None):
|
||||
pass
|
||||
|
||||
|
||||
@shared_task
|
||||
def hard_delete():
|
||||
|
||||
from plane.db.models import (
|
||||
Workspace,
|
||||
Project,
|
||||
Cycle,
|
||||
Module,
|
||||
Issue,
|
||||
Page,
|
||||
IssueView,
|
||||
Label,
|
||||
State,
|
||||
IssueActivity,
|
||||
IssueComment,
|
||||
IssueLink,
|
||||
IssueReaction,
|
||||
UserFavorite,
|
||||
ModuleIssue,
|
||||
CycleIssue,
|
||||
Estimate,
|
||||
EstimatePoint
|
||||
)
|
||||
|
||||
# check delete workspace
|
||||
_ = Workspace.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
# check delete project
|
||||
_ = Project.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
# check delete cycle
|
||||
_ = Cycle.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
# check delete module
|
||||
_ = Module.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
# check delete issue
|
||||
_ = Issue.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
# check delete page
|
||||
_ = Page.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
# check delete view
|
||||
_ = IssueView.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
# check delete label
|
||||
_ = Label.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
# check delete state
|
||||
_ = State.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
_ = IssueActivity.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
_ = IssueComment.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
_ = IssueLink.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
_ = IssueReaction.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
_ = UserFavorite.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
_ = ModuleIssue.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
_ = CycleIssue.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
_ = Estimate.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
_ = EstimatePoint.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
# at last, check for every thing which ever is left and delete it
|
||||
# Get all Django models
|
||||
all_models = apps.get_models()
|
||||
|
||||
# Iterate through all models
|
||||
for model in all_models:
|
||||
# Check if the model has a 'deleted_at' field
|
||||
if hasattr(model, "deleted_at"):
|
||||
# Get all instances where 'deleted_at' is greater than 30 days ago
|
||||
_ = model.all_objects.filter(
|
||||
deleted_at__lt=timezone.now() - timezone.timedelta(days=30)
|
||||
).delete()
|
||||
|
||||
return
|
||||
@@ -221,6 +221,7 @@ def notifications(
|
||||
else None
|
||||
)
|
||||
if type not in [
|
||||
"issue.activity.deleted",
|
||||
"cycle.activity.created",
|
||||
"cycle.activity.deleted",
|
||||
"module.activity.created",
|
||||
|
||||
@@ -36,10 +36,6 @@ app.conf.beat_schedule = {
|
||||
"task": "plane.bgtasks.api_logs_task.delete_api_logs",
|
||||
"schedule": crontab(hour=0, minute=0),
|
||||
},
|
||||
"check-every-day-to-delete-hard-delete": {
|
||||
"task": "plane.bgtasks.deletion_task.hard_delete",
|
||||
"schedule": crontab(hour=0, minute=0),
|
||||
},
|
||||
}
|
||||
|
||||
# Load task modules from all registered Django app configs.
|
||||
|
||||
-423
@@ -1,423 +0,0 @@
|
||||
# Generated by Django 4.2.11 on 2024-07-26 11:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0072_issueattachment_external_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='analyticview',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='apiactivitylog',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='apitoken',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='commentreaction',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cycle',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cyclefavorite',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cycleissue',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cycleuserproperties',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dashboard',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dashboardwidget',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='deployboard',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='emailnotificationlog',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='estimate',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='estimatepoint',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exporterhistory',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='fileasset',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='githubcommentsync',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='githubissuesync',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='githubrepository',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='githubrepositorysync',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='globalview',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='importer',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='inbox',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='inboxissue',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='integration',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issue',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issueactivity',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issueassignee',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issueattachment',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issueblocker',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuecomment',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuelabel',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuelink',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuemention',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuereaction',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuerelation',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuesequence',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuesubscriber',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuetype',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issueuserproperty',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issueview',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issueviewfavorite',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuevote',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='label',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='module',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='modulefavorite',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='moduleissue',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='modulelink',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='modulemember',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='moduleuserproperties',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='notification',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='page',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='pageblock',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='pagefavorite',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='pagelabel',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='pagelog',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='pageversion',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectdeployboard',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectfavorite',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectidentifier',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectmember',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectmemberinvite',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectpage',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectpublicmember',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='slackprojectsync',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='socialloginconnection',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='state',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='team',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='teammember',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='teampage',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userfavorite',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='usernotificationpreference',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userrecentvisit',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='webhook',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='webhooklog',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspace',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceintegration',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspacemember',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspacememberinvite',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspacetheme',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceuserproperties',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
]
|
||||
@@ -1,9 +1,7 @@
|
||||
# Python imports
|
||||
|
||||
# Django imports
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
# Module imports
|
||||
from plane.bgtasks.deletion_task import soft_delete_related_objects
|
||||
|
||||
|
||||
class TimeAuditModel(models.Model):
|
||||
@@ -43,45 +41,7 @@ class UserAuditModel(models.Model):
|
||||
abstract = True
|
||||
|
||||
|
||||
class SoftDeletionManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(deleted_at__isnull=True)
|
||||
|
||||
|
||||
class SoftDeleteModel(models.Model):
|
||||
"""To soft delete records"""
|
||||
|
||||
deleted_at = models.DateTimeField(
|
||||
verbose_name="Deleted At",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
objects = SoftDeletionManager()
|
||||
all_objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def delete(self, using=None, soft=True, *args, **kwargs):
|
||||
if soft:
|
||||
# Soft delete the current instance
|
||||
self.deleted_at = timezone.now()
|
||||
self.save(using=using)
|
||||
|
||||
soft_delete_related_objects.delay(
|
||||
self._meta.app_label,
|
||||
self._meta.model_name,
|
||||
self.pk,
|
||||
using=using,
|
||||
)
|
||||
|
||||
else:
|
||||
# Perform hard delete if soft deletion is not enabled
|
||||
return super().delete(using=using, *args, **kwargs)
|
||||
|
||||
|
||||
class AuditModel(TimeAuditModel, UserAuditModel, SoftDeleteModel):
|
||||
class AuditModel(TimeAuditModel, UserAuditModel):
|
||||
"""To path when the record was created and last modified"""
|
||||
|
||||
class Meta:
|
||||
|
||||
@@ -116,7 +116,6 @@ class CycleIssue(ProjectBaseModel):
|
||||
return f"{self.cycle}"
|
||||
|
||||
|
||||
# DEPRECATED TODO: - Remove in next release
|
||||
class CycleFavorite(ProjectBaseModel):
|
||||
"""_summary_
|
||||
CycleFavorite (model): To store all the cycle favorite of the user
|
||||
|
||||
@@ -89,7 +89,6 @@ class IssueManager(models.Manager):
|
||||
| models.Q(issue_inbox__status=2)
|
||||
| models.Q(issue_inbox__isnull=True)
|
||||
)
|
||||
.filter(deleted_at__isnull=True)
|
||||
.filter(state__is_triage=False)
|
||||
.exclude(archived_at__isnull=False)
|
||||
.exclude(project__archived_at__isnull=False)
|
||||
|
||||
@@ -169,7 +169,6 @@ class ModuleLink(ProjectBaseModel):
|
||||
return f"{self.module.name} {self.url}"
|
||||
|
||||
|
||||
# DEPRECATED TODO: - Remove in next release
|
||||
class ModuleFavorite(ProjectBaseModel):
|
||||
"""_summary_
|
||||
ModuleFavorite (model): To store all the module favorite of the user
|
||||
|
||||
@@ -119,7 +119,6 @@ class PageLog(BaseModel):
|
||||
return f"{self.page.name} {self.entity_name}"
|
||||
|
||||
|
||||
# DEPRECATED TODO: - Remove in next release
|
||||
class PageBlock(ProjectBaseModel):
|
||||
page = models.ForeignKey(
|
||||
"db.Page", on_delete=models.CASCADE, related_name="blocks"
|
||||
@@ -176,7 +175,6 @@ class PageBlock(ProjectBaseModel):
|
||||
return f"{self.page.name} <{self.name}>"
|
||||
|
||||
|
||||
# DEPRECATED TODO: - Remove in next release
|
||||
class PageFavorite(ProjectBaseModel):
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
|
||||
@@ -230,7 +230,6 @@ class ProjectIdentifier(AuditModel):
|
||||
ordering = ("-created_at",)
|
||||
|
||||
|
||||
# DEPRECATED TODO: - Remove in next release
|
||||
class ProjectFavorite(ProjectBaseModel):
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
|
||||
@@ -52,6 +52,7 @@ def get_default_display_properties():
|
||||
"updated_on": True,
|
||||
}
|
||||
|
||||
|
||||
# DEPRECATED TODO: - Remove in next release
|
||||
class GlobalView(BaseModel):
|
||||
workspace = models.ForeignKey(
|
||||
@@ -141,7 +142,6 @@ class IssueView(WorkspaceBaseModel):
|
||||
return f"{self.name} <{self.project.name}>"
|
||||
|
||||
|
||||
# DEPRECATED TODO: - Remove in next release
|
||||
class IssueViewFavorite(ProjectBaseModel):
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
# Generated by Django 4.2.11 on 2024-07-26 11:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('license', '0003_alter_changelog_title_alter_changelog_version_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='changelog',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='instance',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='instanceadmin',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='instanceconfiguration',
|
||||
name='deleted_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'),
|
||||
),
|
||||
]
|
||||
@@ -222,7 +222,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
const kanbanFilters = issuesFilter?.issueFilters?.kanbanFilters || { group_by: [], sub_group_by: [] };
|
||||
|
||||
return (
|
||||
<IssueLayoutHOC layout={EIssueLayoutTypes.KANBAN}>
|
||||
<>
|
||||
<DeleteIssueModal
|
||||
dataId={draggedIssueId}
|
||||
isOpen={deleteIssueModal}
|
||||
@@ -279,6 +279,6 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</IssueLayoutHOC>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -156,7 +156,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
||||
column_id={subList.id}
|
||||
icon={subList.icon}
|
||||
title={subList.name}
|
||||
count={getGroupIssueCount(subList.id, undefined, false) ?? 0}
|
||||
count={getGroupIssueCount(subList.id, undefined, false)}
|
||||
issuePayload={subList.payload}
|
||||
disableIssueCreation={disableIssueCreation || isGroupByCreatedBy}
|
||||
addIssuesToView={addIssuesToView}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { CreateUpdateIssueModal } from "@/components/issues";
|
||||
// hooks
|
||||
import { useEventTracker } from "@/hooks/store";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
import isNil from "lodash/isNil";
|
||||
// types
|
||||
|
||||
interface IHeaderGroupByCard {
|
||||
@@ -23,7 +24,7 @@ interface IHeaderGroupByCard {
|
||||
column_id: string;
|
||||
icon?: React.ReactNode;
|
||||
title: string;
|
||||
count: number;
|
||||
count: number | undefined;
|
||||
kanbanFilters: TIssueKanbanFilters;
|
||||
handleKanbanFilters: any;
|
||||
issuePayload: Partial<TIssue>;
|
||||
@@ -123,11 +124,13 @@ export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
<div
|
||||
className={`flex-shrink-0 text-sm font-medium text-custom-text-300 ${verticalAlignPosition ? `` : `pl-2`}`}
|
||||
>
|
||||
{count || 0}
|
||||
</div>
|
||||
{!isNil(count) && (
|
||||
<div
|
||||
className={`flex-shrink-0 text-sm font-medium text-custom-text-300 ${verticalAlignPosition ? `` : `pl-2`}`}
|
||||
>
|
||||
{count || 0}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{sub_group_by === null && (
|
||||
|
||||
@@ -28,11 +28,12 @@ import { GroupDragOverlay } from "../group-drag-overlay";
|
||||
import { TRenderQuickActions } from "../list/list-view-types";
|
||||
import { GroupDropLocation, getSourceFromDropPayload, getDestinationFromDropPayload, getIssueBlockId } from "../utils";
|
||||
import { KanbanIssueBlocksList, KanBanQuickAddIssueForm } from ".";
|
||||
import isNil from "lodash/isNil";
|
||||
|
||||
interface IKanbanGroup {
|
||||
groupId: string;
|
||||
issuesMap: IIssueMap;
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
sub_group_by: TIssueGroupByOptions | undefined;
|
||||
group_by: TIssueGroupByOptions | undefined;
|
||||
@@ -88,8 +89,8 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
|
||||
const containerRef = sub_group_by && scrollableContainerRef ? scrollableContainerRef : columnRef;
|
||||
|
||||
const loadMoreIssuesInThisGroup = useCallback(() => {
|
||||
loadMoreIssues(groupId, sub_group_id === "null"? undefined: sub_group_id)
|
||||
}, [loadMoreIssues, groupId, sub_group_id])
|
||||
loadMoreIssues(groupId, sub_group_id === "null" ? undefined : sub_group_id);
|
||||
}, [loadMoreIssues, groupId, sub_group_id]);
|
||||
|
||||
const isPaginating = !!getIssueLoader(groupId, sub_group_id);
|
||||
|
||||
@@ -218,11 +219,10 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
|
||||
? (groupedIssueIds as TSubGroupedIssues)?.[groupId]?.[sub_group_id] ?? []
|
||||
: (groupedIssueIds as TGroupedIssues)?.[groupId] ?? [];
|
||||
|
||||
const groupIssueCount = getGroupIssueCount(groupId, sub_group_id, false) ?? 0;
|
||||
const groupIssueCount = getGroupIssueCount(groupId, sub_group_id, false);
|
||||
|
||||
const nextPageResults = getPaginationData(groupId, sub_group_id)?.nextPageResults;
|
||||
|
||||
|
||||
const loadMore = isPaginating ? (
|
||||
<KanbanIssueBlockLoader />
|
||||
) : (
|
||||
@@ -235,8 +235,9 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const shouldLoadMore = nextPageResults === undefined ? issueIds?.length < groupIssueCount : !!nextPageResults;
|
||||
const canOverlayBeVisible = orderBy !== "sort_order" || isDropDisabled;
|
||||
const shouldLoadMore =
|
||||
nextPageResults === undefined ? isNil(groupIssueCount) || issueIds?.length < groupIssueCount : !!nextPageResults;
|
||||
const canOverlayBeVisible = orderBy !== "sort_order" || isDropDisabled;
|
||||
const shouldOverlayBeVisible = isDraggingOverColumn && canOverlayBeVisible;
|
||||
|
||||
return (
|
||||
@@ -270,7 +271,7 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
|
||||
canDropOverIssue={!canOverlayBeVisible}
|
||||
/>
|
||||
|
||||
{shouldLoadMore && (isSubGroup ? <>{loadMore}</> : <KanbanIssueBlockLoader ref={setIntersectionElement} />)}
|
||||
{shouldLoadMore && (isSubGroup ? <>{loadMore}</> : <KanbanIssueBlockLoader ref={setIntersectionElement} />)}
|
||||
|
||||
{enableQuickIssueCreate && !disableIssueCreation && (
|
||||
<div className="w-full bg-custom-background-90 py-0.5 sticky bottom-0">
|
||||
|
||||
@@ -56,9 +56,9 @@ const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = observer(
|
||||
{list &&
|
||||
list.length > 0 &&
|
||||
list.map((_list: IGroupByColumn) => {
|
||||
const groupCount = getGroupIssueCount(_list?.id, undefined, false) ?? 0;
|
||||
const groupCount = getGroupIssueCount(_list?.id, undefined, false);
|
||||
|
||||
const subGroupByVisibilityToggle = visibilitySubGroupByGroupCount(groupCount, showEmptyGroup);
|
||||
const subGroupByVisibilityToggle = visibilitySubGroupByGroupCount(groupCount ?? 0, showEmptyGroup);
|
||||
|
||||
if (subGroupByVisibilityToggle === false) return <></>;
|
||||
|
||||
|
||||
@@ -107,27 +107,25 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<IssueLayoutHOC layout={EIssueLayoutTypes.LIST}>
|
||||
<div className={`relative size-full bg-custom-background-90`}>
|
||||
<List
|
||||
issuesMap={issueMap}
|
||||
displayProperties={displayProperties}
|
||||
group_by={group_by}
|
||||
orderBy={orderBy}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={renderQuickActions}
|
||||
groupedIssueIds={groupedIssueIds ?? {}}
|
||||
loadMoreIssues={loadMoreIssues}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
quickAddCallback={quickAddIssue}
|
||||
enableIssueQuickAdd={!!enableQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
disableIssueCreation={!enableIssueCreation || !isEditingAllowed}
|
||||
addIssuesToView={addIssuesToView}
|
||||
isCompletedCycle={isCompletedCycle}
|
||||
handleOnDrop={handleOnDrop}
|
||||
/>
|
||||
</div>
|
||||
</IssueLayoutHOC>
|
||||
<div className={`relative size-full bg-custom-background-90`}>
|
||||
<List
|
||||
issuesMap={issueMap}
|
||||
displayProperties={displayProperties}
|
||||
group_by={group_by}
|
||||
orderBy={orderBy}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={renderQuickActions}
|
||||
groupedIssueIds={groupedIssueIds ?? {}}
|
||||
loadMoreIssues={loadMoreIssues}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
quickAddCallback={quickAddIssue}
|
||||
enableIssueQuickAdd={!!enableQuickAdd}
|
||||
canEditProperties={canEditProperties}
|
||||
disableIssueCreation={!enableIssueCreation || !isEditingAllowed}
|
||||
addIssuesToView={addIssuesToView}
|
||||
isCompletedCycle={isCompletedCycle}
|
||||
handleOnDrop={handleOnDrop}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -18,12 +18,13 @@ import { cn } from "@/helpers/common.helper";
|
||||
import { useEventTracker } from "@/hooks/store";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
import { TSelectionHelper } from "@/hooks/use-multiple-select";
|
||||
import isNil from "lodash/isNil";
|
||||
|
||||
interface IHeaderGroupByCard {
|
||||
groupID: string;
|
||||
icon?: React.ReactNode;
|
||||
title: string;
|
||||
count: number;
|
||||
count: number | undefined;
|
||||
issuePayload: Partial<TIssue>;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
toggleListGroup: () => void;
|
||||
@@ -98,7 +99,7 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
|
||||
)}
|
||||
groupID={groupID}
|
||||
selectionHelpers={selectionHelpers}
|
||||
disabled={count === 0}
|
||||
disabled={!count}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -106,10 +107,12 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
|
||||
{icon ?? <CircleDashed className="size-3.5" strokeWidth={2} />}
|
||||
</div>
|
||||
|
||||
<div className="relative flex w-full flex-row items-center gap-1 overflow-hidden cursor-pointer"
|
||||
onClick={toggleListGroup}>
|
||||
<div
|
||||
className="relative flex w-full flex-row items-center gap-1 overflow-hidden cursor-pointer"
|
||||
onClick={toggleListGroup}
|
||||
>
|
||||
<div className="inline-block line-clamp-1 truncate font-medium text-custom-text-100">{title}</div>
|
||||
<div className="pl-2 text-sm font-medium text-custom-text-300">{count || 0}</div>
|
||||
{!isNil(count) && <div className="pl-2 text-sm font-medium text-custom-text-300">{count || 0}</div>}
|
||||
</div>
|
||||
|
||||
{!disableIssueCreation &&
|
||||
|
||||
@@ -37,6 +37,7 @@ import { IssueBlocksList } from "./blocks-list";
|
||||
import { HeaderGroupByCard } from "./headers/group-by-card";
|
||||
import { TRenderQuickActions } from "./list-view-types";
|
||||
import { ListQuickAddIssueForm } from "./quick-add-issue-form";
|
||||
import isNil from "lodash/isNil";
|
||||
|
||||
interface Props {
|
||||
groupIssueIds: string[] | undefined;
|
||||
@@ -98,7 +99,7 @@ export const ListGroup = observer((props: Props) => {
|
||||
|
||||
const [intersectionElement, setIntersectionElement] = useState<HTMLDivElement | null>(null);
|
||||
|
||||
const groupIssueCount = getGroupIssueCount(group.id, undefined, false) ?? 0;
|
||||
const groupIssueCount = getGroupIssueCount(group.id, undefined, false);
|
||||
const nextPageResults = getPaginationData(group.id, undefined)?.nextPageResults;
|
||||
const isPaginating = !!getIssueLoader(group.id);
|
||||
|
||||
@@ -106,7 +107,7 @@ export const ListGroup = observer((props: Props) => {
|
||||
|
||||
const shouldLoadMore =
|
||||
nextPageResults === undefined && groupIssueCount !== undefined && groupIssueIds
|
||||
? groupIssueIds.length < groupIssueCount
|
||||
? isNil(groupIssueCount) || groupIssueIds.length < groupIssueCount
|
||||
: !!nextPageResults;
|
||||
|
||||
const loadMore = isPaginating ? (
|
||||
|
||||
@@ -39,6 +39,7 @@ import { IMemberRootStore } from "@/store/member";
|
||||
import { IModuleStore } from "@/store/module.store";
|
||||
import { IProjectStore } from "@/store/project/project.store";
|
||||
import { IStateStore } from "@/store/state.store";
|
||||
import { ALL_ISSUES } from "@plane/constants";
|
||||
|
||||
export const HIGHLIGHT_CLASS = "highlight";
|
||||
export const HIGHLIGHT_WITH_LINE = "highlight-with-line";
|
||||
@@ -92,8 +93,10 @@ export const getGroupByColumns = (
|
||||
case "created_by":
|
||||
return getCreatedByColumns(member) as any;
|
||||
default:
|
||||
if (includeNone) return [{ id: `All Issues`, name: `All Issues`, payload: {}, icon: undefined }];
|
||||
if (includeNone) return [{ id: ALL_ISSUES, name: ALL_ISSUES, payload: {}, icon: undefined }];
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
const getProjectColumns = (project: IProjectStore): IGroupByColumn[] | undefined => {
|
||||
|
||||
@@ -59,37 +59,37 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
||||
workspaceSlug && projectId ? () => fetchUserProjectInfo(workspaceSlug.toString(), projectId.toString()) : null
|
||||
);
|
||||
// fetching project labels
|
||||
useSWR(
|
||||
const { isLoading: isLabelsLoading } = useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_LABELS_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId ? () => fetchProjectLabels(workspaceSlug.toString(), projectId.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
// fetching project members
|
||||
useSWR(
|
||||
const { isLoading: isMembersLoading } = useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_MEMBERS_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId ? () => fetchProjectMembers(workspaceSlug.toString(), projectId.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
// fetching project states
|
||||
useSWR(
|
||||
const { isLoading: isStateLoading } = useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_STATES_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId ? () => fetchProjectStates(workspaceSlug.toString(), projectId.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
// fetching project estimates
|
||||
useSWR(
|
||||
const { isLoading: isEstimatesLoading } = useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_ESTIMATES_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId ? () => getProjectEstimates(workspaceSlug.toString(), projectId.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
// fetching project cycles
|
||||
useSWR(
|
||||
const { isLoading: isCyclesLoading } = useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_ALL_CYCLES_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId ? () => fetchAllCycles(workspaceSlug.toString(), projectId.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
// fetching project modules
|
||||
useSWR(
|
||||
const { isLoading: isModulesLoading } = useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_MODULES_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId ? () => fetchModules(workspaceSlug.toString(), projectId.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
@@ -102,8 +102,11 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
||||
);
|
||||
const projectExists = projectId ? getProjectById(projectId.toString()) : null;
|
||||
|
||||
const isLoading =
|
||||
isLabelsLoading || isMembersLoading || isStateLoading || isEstimatesLoading || isCyclesLoading || isModulesLoading;
|
||||
|
||||
// check if the project member apis is loading
|
||||
if (!projectMemberInfo && projectId && hasPermissionToProject[projectId.toString()] === null)
|
||||
if ((!projectMemberInfo && projectId && hasPermissionToProject[projectId.toString()] === null) || isLoading)
|
||||
return (
|
||||
<div className="grid h-screen place-items-center bg-custom-background-100 p-4">
|
||||
<div className="flex flex-col items-center gap-3 text-center">
|
||||
|
||||
@@ -119,12 +119,7 @@ export class CycleIssuesFilter extends IssueFilterHelperStore implements ICycleI
|
||||
groupId: string | undefined,
|
||||
subGroupId: string | undefined
|
||||
) => {
|
||||
let filterParams = this.getAppliedFilters(cycleId);
|
||||
|
||||
if (!filterParams) {
|
||||
filterParams = {};
|
||||
}
|
||||
filterParams["cycle"] = cycleId;
|
||||
const filterParams = this.getAppliedFilters(cycleId);
|
||||
|
||||
const paginationParams = this.getPaginationParams(filterParams, options, cursor, groupId, subGroupId);
|
||||
return paginationParams;
|
||||
|
||||
@@ -164,7 +164,7 @@ export class CycleIssues extends BaseIssuesStore implements ICycleIssues {
|
||||
// get params from pagination options
|
||||
const params = this.issueFilterStore?.getFilterParams(options, cycleId, undefined, undefined, undefined);
|
||||
// call the fetch issues API with the params
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params, {
|
||||
const response = await this.cycleService.getCycleIssues(workspaceSlug, projectId, cycleId, params, {
|
||||
signal: this.controller.signal,
|
||||
});
|
||||
|
||||
@@ -212,7 +212,7 @@ export class CycleIssues extends BaseIssuesStore implements ICycleIssues {
|
||||
subGroupId
|
||||
);
|
||||
// call the fetch issues API with the params for next page in issues
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, cycleId, params);
|
||||
const response = await this.cycleService.getCycleIssues(workspaceSlug, projectId, cycleId, params);
|
||||
|
||||
// after the next page of issues are fetched, call the base method to process the response
|
||||
this.onfetchNexIssues(response, groupId, subGroupId);
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
getSubGroupIssueKeyActions,
|
||||
} from "./base-issues-utils";
|
||||
import { IBaseIssueFilterStore } from "./issue-filter-helper.store";
|
||||
import { getGroupByColumns } from "@/components/issues/issue-layouts/utils";
|
||||
// constants
|
||||
// helpers
|
||||
// services
|
||||
@@ -419,6 +420,56 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
}
|
||||
);
|
||||
|
||||
getGroups = (isWorkspaceLevel = false) => {
|
||||
if (!this.groupBy || this.groupBy === "target_date") return;
|
||||
|
||||
const {
|
||||
projectRoot,
|
||||
cycle,
|
||||
module: moduleInfo,
|
||||
label,
|
||||
state: projectState,
|
||||
memberRoot,
|
||||
} = this.rootIssueStore.rootStore;
|
||||
|
||||
return getGroupByColumns(
|
||||
this.groupBy,
|
||||
projectRoot.project,
|
||||
cycle,
|
||||
moduleInfo,
|
||||
label,
|
||||
projectState,
|
||||
memberRoot,
|
||||
false,
|
||||
isWorkspaceLevel
|
||||
);
|
||||
};
|
||||
|
||||
getSubGroups = (isWorkspaceLevel = false) => {
|
||||
if (!this.subGroupBy || this.subGroupBy === "target_date") return;
|
||||
|
||||
const {
|
||||
projectRoot,
|
||||
cycle,
|
||||
module: moduleInfo,
|
||||
label,
|
||||
state: projectState,
|
||||
memberRoot,
|
||||
} = this.rootIssueStore.rootStore;
|
||||
|
||||
return getGroupByColumns(
|
||||
this.subGroupBy,
|
||||
projectRoot.project,
|
||||
cycle,
|
||||
moduleInfo,
|
||||
label,
|
||||
projectState,
|
||||
memberRoot,
|
||||
false,
|
||||
isWorkspaceLevel
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the next page cursor based on number of issues currently available
|
||||
* @param groupId groupId for the cursor
|
||||
@@ -451,9 +502,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
onfetchIssues(
|
||||
issuesResponse: TIssuesResponse,
|
||||
options: IssuePaginationOptions,
|
||||
workspaceSlug: string,
|
||||
projectId?: string,
|
||||
id?: string
|
||||
groupId?: string,
|
||||
subGroupId?: string
|
||||
) {
|
||||
// Process the Issue Response to get the following data from it
|
||||
const { issueList, groupedIssues, groupedIssueCount } = this.processIssueResponse(issuesResponse);
|
||||
@@ -463,15 +513,12 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
|
||||
// Update all the GroupIds to this Store's groupedIssueIds and update Individual group issue counts
|
||||
runInAction(() => {
|
||||
this.updateGroupedIssueIds(groupedIssues, groupedIssueCount);
|
||||
this.loader[getGroupKey()] = undefined;
|
||||
this.updateGroupedIssueIds(groupedIssues, groupedIssueCount, groupId, subGroupId);
|
||||
this.loader[getGroupKey(groupId, subGroupId)] = undefined;
|
||||
});
|
||||
|
||||
// fetch parent stats if required, to be handled in the Implemented class
|
||||
this.fetchParentStats(workspaceSlug, projectId, id);
|
||||
|
||||
// store Pagination options for next subsequent calls and data like next cursor etc
|
||||
this.storePreviousPaginationValues(issuesResponse, options);
|
||||
this.storePreviousPaginationValues(issuesResponse, options, groupId, subGroupId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -282,7 +282,7 @@ export class IssueFilterHelperStore implements IIssueFilterHelperStore {
|
||||
subGroupId?: string
|
||||
) {
|
||||
// if cursor exists, use the cursor. If it doesn't exist construct the cursor based on per page count
|
||||
const pageCursor = cursor ? cursor : groupId ? `${options.perPageCount}:1:0` : `${options.perPageCount}:0:0`;
|
||||
const pageCursor = cursor ? cursor : groupId ? `${options.perPageCount}:0:0` : `${options.perPageCount}:0:0`;
|
||||
|
||||
// pagination params
|
||||
const paginationParams: Partial<Record<TIssueParams, string | boolean>> = {
|
||||
|
||||
@@ -119,12 +119,7 @@ export class ModuleIssuesFilter extends IssueFilterHelperStore implements IModul
|
||||
groupId: string | undefined,
|
||||
subGroupId: string | undefined
|
||||
) => {
|
||||
let filterParams = this.getAppliedFilters(moduleId);
|
||||
|
||||
if (!filterParams) {
|
||||
filterParams = {};
|
||||
}
|
||||
filterParams["module"] = moduleId;
|
||||
const filterParams = this.getAppliedFilters(moduleId);
|
||||
|
||||
const paginationParams = this.getPaginationParams(filterParams, options, cursor, groupId, subGroupId);
|
||||
return paginationParams;
|
||||
|
||||
@@ -118,7 +118,7 @@ export class ModuleIssues extends BaseIssuesStore implements IModuleIssues {
|
||||
// get params from pagination options
|
||||
const params = this.issueFilterStore?.getFilterParams(options, moduleId, undefined, undefined, undefined);
|
||||
// call the fetch issues API with the params
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params, {
|
||||
const response = await this.moduleService.getModuleIssues(workspaceSlug, projectId, moduleId, params, {
|
||||
signal: this.controller.signal,
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ export class ModuleIssues extends BaseIssuesStore implements IModuleIssues {
|
||||
subGroupId
|
||||
);
|
||||
// call the fetch issues API with the params for next page in issues
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
|
||||
const response = await this.moduleService.getModuleIssues(workspaceSlug, projectId, moduleId, params);
|
||||
|
||||
// after the next page of issues are fetched, call the base method to process the response
|
||||
this.onfetchNexIssues(response, groupId, subGroupId);
|
||||
|
||||
@@ -86,21 +86,74 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
|
||||
isExistingPaginationOptions: boolean = false
|
||||
) => {
|
||||
try {
|
||||
// set loader and clear store
|
||||
runInAction(() => {
|
||||
this.setLoader(loadType);
|
||||
});
|
||||
const groups = this.getGroups();
|
||||
const subGroups = this.getSubGroups();
|
||||
|
||||
this.clear(!isExistingPaginationOptions);
|
||||
|
||||
if (!groups || (!groups && !subGroups))
|
||||
return await this.fetchParallelIssues(workspaceSlug, projectId, loadType, options);
|
||||
|
||||
const promises = [];
|
||||
|
||||
for (const group of groups) {
|
||||
if (!subGroups) {
|
||||
promises.push(this.fetchParallelIssues(workspaceSlug, projectId, loadType, options, group.id));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const subGroup of subGroups) {
|
||||
promises.push(this.fetchParallelIssues(workspaceSlug, projectId, loadType, options, group.id, subGroup.id));
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
// fetch parent stats if required, to be handled in the Implemented class
|
||||
this.fetchParentStats(workspaceSlug, projectId);
|
||||
} catch (error) {
|
||||
// set loader to undefined if errored out
|
||||
this.setLoader(undefined);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method is called to fetch the first issues of pagination
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @param loadType
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
fetchParallelIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
options: IssuePaginationOptions,
|
||||
groupId?: string,
|
||||
subGroupId?: string
|
||||
) => {
|
||||
try {
|
||||
// set loader and clear store
|
||||
runInAction(() => {
|
||||
// set Loader
|
||||
if (groupId || subGroupId) {
|
||||
this.setLoader("pagination", groupId, subGroupId);
|
||||
} else {
|
||||
this.setLoader(loadType);
|
||||
}
|
||||
});
|
||||
|
||||
// get params from pagination options
|
||||
const params = this.issueFilterStore?.getFilterParams(options, projectId, undefined, undefined, undefined);
|
||||
const params = this.issueFilterStore?.getFilterParams(options, projectId, undefined, groupId, subGroupId);
|
||||
// call the fetch issues API with the params
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params, {
|
||||
signal: this.controller.signal,
|
||||
});
|
||||
|
||||
// after fetching issues, call the base method to process the response further
|
||||
this.onfetchIssues(response, options, workspaceSlug, projectId);
|
||||
this.onfetchIssues(response, options, groupId, subGroupId);
|
||||
return response;
|
||||
} catch (error) {
|
||||
// set loader to undefined if errored out
|
||||
|
||||
Reference in New Issue
Block a user