diff --git a/apps/api/plane/db/migrations/0113_webhook_version.py b/apps/api/plane/db/migrations/0113_webhook_version.py new file mode 100644 index 00000000000..3be3120eb43 --- /dev/null +++ b/apps/api/plane/db/migrations/0113_webhook_version.py @@ -0,0 +1,57 @@ +# Generated by Django 4.2.26 on 2025-12-15 10:29 + +from django.db import migrations, models +import plane.db.models.workspace + + +def get_default_product_tour(): + return { + "work_items": True, + "cycles": True, + "modules": True, + "intake": True, + "pages": True, + } + + +def populate_product_tour(apps, _schema_editor): + WorkspaceUserProperties = apps.get_model('db', 'WorkspaceUserProperties') + default_value = get_default_product_tour() + # Use bulk update for better performance + WorkspaceUserProperties.objects.all().update(product_tour=default_value) + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0112_auto_20251124_0603'), + ] + + operations = [ + migrations.AddField( + model_name='webhook', + name='version', + field=models.CharField(default='v1', max_length=50), + ), + migrations.AddField( + model_name='profile', + name='is_navigation_tour_completed', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='workspaceuserproperties', + name='product_tour', + field=models.JSONField(default=plane.db.models.workspace.get_default_product_tour), + ), + migrations.AddField( + model_name='apitoken', + name='allowed_rate_limit', + field=models.CharField(default='60/min', max_length=255), + ), + migrations.AddField( + model_name='profile', + name='is_subscribed_to_changelog', + field=models.BooleanField(default=False), + ), + migrations.RunPython(populate_product_tour, reverse_code=migrations.RunPython.noop), + ] diff --git a/apps/api/plane/db/models/api.py b/apps/api/plane/db/models/api.py index 7d040ebc284..75449a74283 100644 --- a/apps/api/plane/db/models/api.py +++ b/apps/api/plane/db/models/api.py @@ -32,6 +32,7 @@ class APIToken(BaseModel): workspace = models.ForeignKey("db.Workspace", related_name="api_tokens", on_delete=models.CASCADE, null=True) expired_at = models.DateTimeField(blank=True, null=True) is_service = models.BooleanField(default=False) + allowed_rate_limit = models.CharField(max_length=255, default="60/min") class Meta: verbose_name = "API Token" diff --git a/apps/api/plane/db/models/user.py b/apps/api/plane/db/models/user.py index 247c04745a9..b0f571be9c8 100644 --- a/apps/api/plane/db/models/user.py +++ b/apps/api/plane/db/models/user.py @@ -233,8 +233,12 @@ class Profile(TimeAuditModel): goals = models.JSONField(default=dict) background_color = models.CharField(max_length=255, default=get_random_color) + # navigation tour + is_navigation_tour_completed = models.BooleanField(default=False) + # marketing has_marketing_email_consent = models.BooleanField(default=False) + is_subscribed_to_changelog = models.BooleanField(default=False) class Meta: verbose_name = "Profile" diff --git a/apps/api/plane/db/models/webhook.py b/apps/api/plane/db/models/webhook.py index 8872d0bb235..298b0dba3b0 100644 --- a/apps/api/plane/db/models/webhook.py +++ b/apps/api/plane/db/models/webhook.py @@ -38,6 +38,7 @@ class Webhook(BaseModel): cycle = models.BooleanField(default=False) issue_comment = models.BooleanField(default=False) is_internal = models.BooleanField(default=False) + version = models.CharField(default="v1", max_length=50) def __str__(self): return f"{self.workspace.slug} {self.url}" diff --git a/apps/api/plane/db/models/workspace.py b/apps/api/plane/db/models/workspace.py index d3470d531ea..9690168a11a 100644 --- a/apps/api/plane/db/models/workspace.py +++ b/apps/api/plane/db/models/workspace.py @@ -112,6 +112,16 @@ def slug_validator(value): raise ValidationError("Slug is not valid") +def get_default_product_tour(): + return { + "work_items": False, + "cycles": False, + "modules": False, + "intake": False, + "pages": False, + } + + class Workspace(BaseModel): TIMEZONE_CHOICES = tuple(zip(pytz.common_timezones, pytz.common_timezones)) @@ -325,6 +335,7 @@ class NavigationControlPreference(models.TextChoices): choices=NavigationControlPreference.choices, default=NavigationControlPreference.ACCORDION, ) + product_tour = models.JSONField(default=get_default_product_tour) class Meta: unique_together = ["workspace", "user", "deleted_at"]