From 76090f0ba22474452f658d03fc65585e504be8f0 Mon Sep 17 00:00:00 2001 From: Nicholas Tindle Date: Mon, 25 Aug 2025 09:24:16 -0500 Subject: [PATCH] feat(backend,frontend): Send applicant email on review response (#10718) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Changes 🏗️ This PR implements email notifications for agent creators when their agent submissions are approved or rejected by an admin in the marketplace. Specifically, the changes include: - Added `AGENT_APPROVED` and `AGENT_REJECTED` notification types to `schema.prisma`. - Created `AgentApprovalData` and `AgentRejectionData` Pydantic models for notification data. - Configured the notification system to use immediate queues and new Jinja2 templates for these types. - Designed two new email templates: `agent_approved.html.jinja2` and `agent_rejected.html.jinja2`, with dynamic content for agent details, reviewer feedback, and relevant action links. - Modified the `review_store_submission` function to: - Include `User` and `Reviewer` data in the database query. - Construct and queue the appropriate email notification based on the approval/rejection status. - Ensure email sending failures do not block the agent review process. ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Approve an agent via the admin dashboard. - [x] Verify the agent creator receives an "Agent Approved" email with correct details and a link to the store. - [x] Reject an agent via the admin dashboard (providing a reason). - [x] Verify the agent creator receives an "Agent Rejected" email with correct details, the rejection reason, and a link to resubmit. - [x] Verify that if email sending fails (e.g., misconfigured SMTP), the agent approval/rejection process still completes successfully without error. image image --- Linear Issue: [SECRT-1168](https://linear.app/autogpt/issue/SECRT-1168) Open in Cursor Open in Web --------- Co-authored-by: Cursor Agent --- .../backend/backend/data/notifications.py | 44 +++++ .../backend/data/notifications_test.py | 151 ++++++++++++++++++ autogpt_platform/backend/backend/data/user.py | 12 ++ .../templates/agent_approved.html.jinja2 | 73 +++++++++ .../templates/agent_rejected.html.jinja2 | 77 +++++++++ .../backend/backend/server/v2/store/db.py | 99 +++++++++++- .../migration.sql | 14 ++ autogpt_platform/backend/schema.prisma | 4 + .../NotificationForm/NotificationForm.tsx | 54 +++++++ .../NotificationForm/useNotificationForm.ts | 6 + .../frontend/src/app/api/openapi.json | 4 +- .../src/lib/autogpt-server-api/types.ts | 4 +- 12 files changed, 539 insertions(+), 3 deletions(-) create mode 100644 autogpt_platform/backend/backend/data/notifications_test.py create mode 100644 autogpt_platform/backend/backend/notifications/templates/agent_approved.html.jinja2 create mode 100644 autogpt_platform/backend/backend/notifications/templates/agent_rejected.html.jinja2 create mode 100644 autogpt_platform/backend/migrations/20250824000317_add_notifications_for_approved_and_denied_agents/migration.sql diff --git a/autogpt_platform/backend/backend/data/notifications.py b/autogpt_platform/backend/backend/data/notifications.py index 9e64ef0a6a..bf8177af72 100644 --- a/autogpt_platform/backend/backend/data/notifications.py +++ b/autogpt_platform/backend/backend/data/notifications.py @@ -181,6 +181,42 @@ class RefundRequestData(BaseNotificationData): balance: int +class AgentApprovalData(BaseNotificationData): + agent_name: str + agent_id: str + agent_version: int + reviewer_name: str + reviewer_email: str + comments: str + reviewed_at: datetime + store_url: str + + @field_validator("reviewed_at") + @classmethod + def validate_timezone(cls, value: datetime): + if value.tzinfo is None: + raise ValueError("datetime must have timezone information") + return value + + +class AgentRejectionData(BaseNotificationData): + agent_name: str + agent_id: str + agent_version: int + reviewer_name: str + reviewer_email: str + comments: str + reviewed_at: datetime + resubmit_url: str + + @field_validator("reviewed_at") + @classmethod + def validate_timezone(cls, value: datetime): + if value.tzinfo is None: + raise ValueError("datetime must have timezone information") + return value + + NotificationData = Annotated[ Union[ AgentRunData, @@ -240,6 +276,8 @@ def get_notif_data_type( NotificationType.MONTHLY_SUMMARY: MonthlySummaryData, NotificationType.REFUND_REQUEST: RefundRequestData, NotificationType.REFUND_PROCESSED: RefundRequestData, + NotificationType.AGENT_APPROVED: AgentApprovalData, + NotificationType.AGENT_REJECTED: AgentRejectionData, }[notification_type] @@ -283,6 +321,8 @@ class NotificationTypeOverride: NotificationType.MONTHLY_SUMMARY: QueueType.SUMMARY, NotificationType.REFUND_REQUEST: QueueType.ADMIN, NotificationType.REFUND_PROCESSED: QueueType.ADMIN, + NotificationType.AGENT_APPROVED: QueueType.IMMEDIATE, + NotificationType.AGENT_REJECTED: QueueType.IMMEDIATE, } return BATCHING_RULES.get(self.notification_type, QueueType.IMMEDIATE) @@ -300,6 +340,8 @@ class NotificationTypeOverride: NotificationType.MONTHLY_SUMMARY: "monthly_summary.html", NotificationType.REFUND_REQUEST: "refund_request.html", NotificationType.REFUND_PROCESSED: "refund_processed.html", + NotificationType.AGENT_APPROVED: "agent_approved.html", + NotificationType.AGENT_REJECTED: "agent_rejected.html", }[self.notification_type] @property @@ -315,6 +357,8 @@ class NotificationTypeOverride: NotificationType.MONTHLY_SUMMARY: "We did a lot this month!", NotificationType.REFUND_REQUEST: "[ACTION REQUIRED] You got a ${{data.amount / 100}} refund request from {{data.user_name}}", NotificationType.REFUND_PROCESSED: "Refund for ${{data.amount / 100}} to {{data.user_name}} has been processed", + NotificationType.AGENT_APPROVED: "🎉 Your agent '{{data.agent_name}}' has been approved!", + NotificationType.AGENT_REJECTED: "Your agent '{{data.agent_name}}' needs some updates", }[self.notification_type] diff --git a/autogpt_platform/backend/backend/data/notifications_test.py b/autogpt_platform/backend/backend/data/notifications_test.py new file mode 100644 index 0000000000..bdbf64401e --- /dev/null +++ b/autogpt_platform/backend/backend/data/notifications_test.py @@ -0,0 +1,151 @@ +"""Tests for notification data models.""" + +from datetime import datetime, timezone + +import pytest +from pydantic import ValidationError + +from backend.data.notifications import AgentApprovalData, AgentRejectionData + + +class TestAgentApprovalData: + """Test cases for AgentApprovalData model.""" + + def test_valid_agent_approval_data(self): + """Test creating valid AgentApprovalData.""" + data = AgentApprovalData( + agent_name="Test Agent", + agent_id="test-agent-123", + agent_version=1, + reviewer_name="John Doe", + reviewer_email="john@example.com", + comments="Great agent, approved!", + reviewed_at=datetime.now(timezone.utc), + store_url="https://app.autogpt.com/store/test-agent-123", + ) + + assert data.agent_name == "Test Agent" + assert data.agent_id == "test-agent-123" + assert data.agent_version == 1 + assert data.reviewer_name == "John Doe" + assert data.reviewer_email == "john@example.com" + assert data.comments == "Great agent, approved!" + assert data.store_url == "https://app.autogpt.com/store/test-agent-123" + assert data.reviewed_at.tzinfo is not None + + def test_agent_approval_data_without_timezone_raises_error(self): + """Test that AgentApprovalData raises error without timezone.""" + with pytest.raises( + ValidationError, match="datetime must have timezone information" + ): + AgentApprovalData( + agent_name="Test Agent", + agent_id="test-agent-123", + agent_version=1, + reviewer_name="John Doe", + reviewer_email="john@example.com", + comments="Great agent, approved!", + reviewed_at=datetime.now(), # No timezone + store_url="https://app.autogpt.com/store/test-agent-123", + ) + + def test_agent_approval_data_with_empty_comments(self): + """Test AgentApprovalData with empty comments.""" + data = AgentApprovalData( + agent_name="Test Agent", + agent_id="test-agent-123", + agent_version=1, + reviewer_name="John Doe", + reviewer_email="john@example.com", + comments="", # Empty comments + reviewed_at=datetime.now(timezone.utc), + store_url="https://app.autogpt.com/store/test-agent-123", + ) + + assert data.comments == "" + + +class TestAgentRejectionData: + """Test cases for AgentRejectionData model.""" + + def test_valid_agent_rejection_data(self): + """Test creating valid AgentRejectionData.""" + data = AgentRejectionData( + agent_name="Test Agent", + agent_id="test-agent-123", + agent_version=1, + reviewer_name="Jane Doe", + reviewer_email="jane@example.com", + comments="Please fix the security issues before resubmitting.", + reviewed_at=datetime.now(timezone.utc), + resubmit_url="https://app.autogpt.com/build/test-agent-123", + ) + + assert data.agent_name == "Test Agent" + assert data.agent_id == "test-agent-123" + assert data.agent_version == 1 + assert data.reviewer_name == "Jane Doe" + assert data.reviewer_email == "jane@example.com" + assert data.comments == "Please fix the security issues before resubmitting." + assert data.resubmit_url == "https://app.autogpt.com/build/test-agent-123" + assert data.reviewed_at.tzinfo is not None + + def test_agent_rejection_data_without_timezone_raises_error(self): + """Test that AgentRejectionData raises error without timezone.""" + with pytest.raises( + ValidationError, match="datetime must have timezone information" + ): + AgentRejectionData( + agent_name="Test Agent", + agent_id="test-agent-123", + agent_version=1, + reviewer_name="Jane Doe", + reviewer_email="jane@example.com", + comments="Please fix the security issues.", + reviewed_at=datetime.now(), # No timezone + resubmit_url="https://app.autogpt.com/build/test-agent-123", + ) + + def test_agent_rejection_data_with_long_comments(self): + """Test AgentRejectionData with long comments.""" + long_comment = "A" * 1000 # Very long comment + data = AgentRejectionData( + agent_name="Test Agent", + agent_id="test-agent-123", + agent_version=1, + reviewer_name="Jane Doe", + reviewer_email="jane@example.com", + comments=long_comment, + reviewed_at=datetime.now(timezone.utc), + resubmit_url="https://app.autogpt.com/build/test-agent-123", + ) + + assert data.comments == long_comment + + def test_model_serialization(self): + """Test that models can be serialized and deserialized.""" + original_data = AgentRejectionData( + agent_name="Test Agent", + agent_id="test-agent-123", + agent_version=1, + reviewer_name="Jane Doe", + reviewer_email="jane@example.com", + comments="Please fix the issues.", + reviewed_at=datetime.now(timezone.utc), + resubmit_url="https://app.autogpt.com/build/test-agent-123", + ) + + # Serialize to dict + data_dict = original_data.model_dump() + + # Deserialize back + restored_data = AgentRejectionData.model_validate(data_dict) + + assert restored_data.agent_name == original_data.agent_name + assert restored_data.agent_id == original_data.agent_id + assert restored_data.agent_version == original_data.agent_version + assert restored_data.reviewer_name == original_data.reviewer_name + assert restored_data.reviewer_email == original_data.reviewer_email + assert restored_data.comments == original_data.comments + assert restored_data.reviewed_at == original_data.reviewed_at + assert restored_data.resubmit_url == original_data.resubmit_url diff --git a/autogpt_platform/backend/backend/data/user.py b/autogpt_platform/backend/backend/data/user.py index 5b37d54152..d43106df34 100644 --- a/autogpt_platform/backend/backend/data/user.py +++ b/autogpt_platform/backend/backend/data/user.py @@ -208,6 +208,8 @@ async def get_user_notification_preference(user_id: str) -> NotificationPreferen NotificationType.DAILY_SUMMARY: user.notifyOnDailySummary or False, NotificationType.WEEKLY_SUMMARY: user.notifyOnWeeklySummary or False, NotificationType.MONTHLY_SUMMARY: user.notifyOnMonthlySummary or False, + NotificationType.AGENT_APPROVED: user.notifyOnAgentApproved or False, + NotificationType.AGENT_REJECTED: user.notifyOnAgentRejected or False, } daily_limit = user.maxEmailsPerDay or 3 notification_preference = NotificationPreference( @@ -266,6 +268,14 @@ async def update_user_notification_preference( update_data["notifyOnMonthlySummary"] = data.preferences[ NotificationType.MONTHLY_SUMMARY ] + if NotificationType.AGENT_APPROVED in data.preferences: + update_data["notifyOnAgentApproved"] = data.preferences[ + NotificationType.AGENT_APPROVED + ] + if NotificationType.AGENT_REJECTED in data.preferences: + update_data["notifyOnAgentRejected"] = data.preferences[ + NotificationType.AGENT_REJECTED + ] if data.daily_limit: update_data["maxEmailsPerDay"] = data.daily_limit @@ -286,6 +296,8 @@ async def update_user_notification_preference( NotificationType.DAILY_SUMMARY: user.notifyOnDailySummary or True, NotificationType.WEEKLY_SUMMARY: user.notifyOnWeeklySummary or True, NotificationType.MONTHLY_SUMMARY: user.notifyOnMonthlySummary or True, + NotificationType.AGENT_APPROVED: user.notifyOnAgentApproved or True, + NotificationType.AGENT_REJECTED: user.notifyOnAgentRejected or True, } notification_preference = NotificationPreference( user_id=user.id, diff --git a/autogpt_platform/backend/backend/notifications/templates/agent_approved.html.jinja2 b/autogpt_platform/backend/backend/notifications/templates/agent_approved.html.jinja2 new file mode 100644 index 0000000000..14ca591580 --- /dev/null +++ b/autogpt_platform/backend/backend/notifications/templates/agent_approved.html.jinja2 @@ -0,0 +1,73 @@ +{# Agent Approved Notification Email Template #} +{# + Template variables: + data.agent_name: the name of the approved agent + data.agent_id: the ID of the agent + data.agent_version: the version of the agent + data.reviewer_name: the name of the reviewer who approved it + data.reviewer_email: the email of the reviewer + data.comments: comments from the reviewer + data.reviewed_at: when the agent was reviewed + data.store_url: URL to view the agent in the store + + Subject: 🎉 Your agent '{{ data.agent_name }}' has been approved! +#} + +{% block content %} +

+ 🎉 Congratulations! +

+ +

+ Your agent '{{ data.agent_name }}' has been approved and is now live in the store! +

+ +
+ +{% if data.comments %} +
+

+ 💬 Creator feedback area +

+

+ {{ data.comments }} +

+
+ +
+{% endif %} + +
+

+ What's Next? +

+
    +
  • Your agent is now live and discoverable in the AutoGPT Store
  • +
  • Users can find, install, and run your agent
  • +
  • You can update your agent anytime by submitting a new version
  • +
+
+ +
+ +
+ + View Your Agent in Store + +
+ +
+ +
+

+ 💡 Pro Tip: Share your agent with the community! Post about it on social media, forums, or your blog to help more users discover and benefit from your creation. +

+
+ +
+ +

+ Thank you for contributing to the AutoGPT ecosystem! 🚀 +

+ +{% endblock %} \ No newline at end of file diff --git a/autogpt_platform/backend/backend/notifications/templates/agent_rejected.html.jinja2 b/autogpt_platform/backend/backend/notifications/templates/agent_rejected.html.jinja2 new file mode 100644 index 0000000000..69a8081900 --- /dev/null +++ b/autogpt_platform/backend/backend/notifications/templates/agent_rejected.html.jinja2 @@ -0,0 +1,77 @@ +{# Agent Rejected Notification Email Template #} +{# + Template variables: + data.agent_name: the name of the rejected agent + data.agent_id: the ID of the agent + data.agent_version: the version of the agent + data.reviewer_name: the name of the reviewer who rejected it + data.reviewer_email: the email of the reviewer + data.comments: comments from the reviewer explaining the rejection + data.reviewed_at: when the agent was reviewed + data.resubmit_url: URL to resubmit the agent + + Subject: Your agent '{{ data.agent_name }}' needs some updates +#} + + +{% block content %} +

+ 📝 Review Complete +

+ +

+ Your agent '{{ data.agent_name }}' needs some updates before approval. +

+ +
+ +
+

+ 💬 Creator feedback area +

+

+ {{ data.comments }} +

+
+ +
+ +
+

+ ☑ Steps to Resubmit: +

+
    +
  • Review the feedback provided above carefully
  • +
  • Make the necessary updates to your agent
  • +
  • Test your agent thoroughly to ensure it works as expected
  • +
  • Submit your updated agent for review
  • +
+
+ +
+ +
+

+ 💡 Tip: Address all the points mentioned in the feedback to increase your chances of approval in the next review. +

+
+ +
+ + Update & Resubmit Agent + +
+ +
+

+ 🌟 Don't Give Up! Many successful agents go through multiple iterations before approval. Our review team is here to help you succeed! +

+
+ +
+ +

+ We're excited to see your improved agent submission! 🚀 +

+ +{% endblock %} \ No newline at end of file diff --git a/autogpt_platform/backend/backend/server/v2/store/db.py b/autogpt_platform/backend/backend/server/v2/store/db.py index 2f53b1cec1..83931d6d7d 100644 --- a/autogpt_platform/backend/backend/server/v2/store/db.py +++ b/autogpt_platform/backend/backend/server/v2/store/db.py @@ -17,8 +17,21 @@ from backend.data.graph import ( get_sub_graphs, ) from backend.data.includes import AGENT_GRAPH_INCLUDE +from backend.data.notifications import ( + AgentApprovalData, + AgentRejectionData, + NotificationEventModel, +) +from backend.notifications.notifications import queue_notification_async +from backend.util.settings import Settings logger = logging.getLogger(__name__) +settings = Settings() + + +# Constants for default admin values +DEFAULT_ADMIN_NAME = "AutoGPT Admin" +DEFAULT_ADMIN_EMAIL = "admin@autogpt.co" def sanitize_query(query: str | None) -> str | None: @@ -1241,7 +1254,8 @@ async def review_store_submission( where={"id": store_listing_version_id}, include={ "StoreListing": True, - "AgentGraph": {"include": AGENT_GRAPH_INCLUDE}, + "AgentGraph": {"include": {**AGENT_GRAPH_INCLUDE, "User": True}}, + "Reviewer": True, }, ) ) @@ -1348,6 +1362,89 @@ async def review_store_submission( f"Failed to update store listing version {store_listing_version_id}" ) + # Send email notification to the agent creator + if store_listing_version.AgentGraph and store_listing_version.AgentGraph.User: + agent_creator = store_listing_version.AgentGraph.User + reviewer = ( + store_listing_version.Reviewer + if store_listing_version.Reviewer + else None + ) + + try: + base_url = ( + settings.config.frontend_base_url + or settings.config.platform_base_url + ) + + if is_approved: + store_agent = ( + await prisma.models.StoreAgent.prisma().find_first_or_raise( + where={"storeListingVersionId": submission.id} + ) + ) + + # Send approval notification + notification_data = AgentApprovalData( + agent_name=submission.name, + agent_id=submission.agentGraphId, + agent_version=submission.agentGraphVersion, + reviewer_name=( + reviewer.name + if reviewer and reviewer.name + else DEFAULT_ADMIN_NAME + ), + reviewer_email=( + reviewer.email if reviewer else DEFAULT_ADMIN_EMAIL + ), + comments=external_comments, + reviewed_at=submission.reviewedAt + or datetime.now(tz=timezone.utc), + store_url=f"{base_url}/marketplace/agent/{store_agent.creator_username}/{store_agent.slug}", + ) + + notification_event = NotificationEventModel[AgentApprovalData]( + user_id=agent_creator.id, + type=prisma.enums.NotificationType.AGENT_APPROVED, + data=notification_data, + ) + else: + # Send rejection notification + notification_data = AgentRejectionData( + agent_name=submission.name, + agent_id=submission.agentGraphId, + agent_version=submission.agentGraphVersion, + reviewer_name=( + reviewer.name + if reviewer and reviewer.name + else DEFAULT_ADMIN_NAME + ), + reviewer_email=( + reviewer.email if reviewer else DEFAULT_ADMIN_EMAIL + ), + comments=external_comments, + reviewed_at=submission.reviewedAt + or datetime.now(tz=timezone.utc), + resubmit_url=f"{base_url}/build?flowID={submission.agentGraphId}", + ) + + notification_event = NotificationEventModel[AgentRejectionData]( + user_id=agent_creator.id, + type=prisma.enums.NotificationType.AGENT_REJECTED, + data=notification_data, + ) + + # Queue the notification for immediate sending + await queue_notification_async(notification_event) + logger.info( + f"Queued {'approval' if is_approved else 'rejection'} notification for user {agent_creator.id} and agent {submission.name}" + ) + + except Exception as e: + logger.error(f"Failed to send email notification for agent review: {e}") + # Don't fail the review process if email sending fails + pass + # Convert to Pydantic model for consistency return backend.server.v2.store.model.StoreSubmission( agent_id=submission.agentGraphId, diff --git a/autogpt_platform/backend/migrations/20250824000317_add_notifications_for_approved_and_denied_agents/migration.sql b/autogpt_platform/backend/migrations/20250824000317_add_notifications_for_approved_and_denied_agents/migration.sql new file mode 100644 index 0000000000..5e56f1f964 --- /dev/null +++ b/autogpt_platform/backend/migrations/20250824000317_add_notifications_for_approved_and_denied_agents/migration.sql @@ -0,0 +1,14 @@ +-- AlterEnum +-- This migration adds more than one value to an enum. +-- With PostgreSQL versions 11 and earlier, this is not possible +-- in a single migration. This can be worked around by creating +-- multiple migrations, each migration adding only one value to +-- the enum. + + +ALTER TYPE "NotificationType" ADD VALUE 'AGENT_APPROVED'; +ALTER TYPE "NotificationType" ADD VALUE 'AGENT_REJECTED'; + +-- AlterTable +ALTER TABLE "User" ADD COLUMN "notifyOnAgentApproved" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "notifyOnAgentRejected" BOOLEAN NOT NULL DEFAULT true; diff --git a/autogpt_platform/backend/schema.prisma b/autogpt_platform/backend/schema.prisma index e8c189862b..a859919719 100644 --- a/autogpt_platform/backend/schema.prisma +++ b/autogpt_platform/backend/schema.prisma @@ -33,6 +33,8 @@ model User { notifyOnDailySummary Boolean @default(true) notifyOnWeeklySummary Boolean @default(true) notifyOnMonthlySummary Boolean @default(true) + notifyOnAgentApproved Boolean @default(true) + notifyOnAgentRejected Boolean @default(true) // Relations @@ -187,6 +189,8 @@ enum NotificationType { MONTHLY_SUMMARY REFUND_REQUEST REFUND_PROCESSED + AGENT_APPROVED + AGENT_REJECTED } model NotificationEvent { diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/NotificationForm/NotificationForm.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/NotificationForm/NotificationForm.tsx index ccfd8e39bc..f0f4d01ab7 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/NotificationForm/NotificationForm.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/NotificationForm/NotificationForm.tsx @@ -106,6 +106,60 @@ export function NotificationForm({ preferences, user }: NotificationFormProps) { /> + {/* Store Notifications */} +
+ + Store Notifications + + ( + +
+ + Agent Approved + + + Get notified when your submitted agent is approved for the + store + +
+ + + +
+ )} + /> + + ( + +
+ + Agent Rejected + + + Receive notifications when your agent submission needs + updates + +
+ + + +
+ )} + /> +
+ {/* Balance Notifications */}
diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/NotificationForm/useNotificationForm.ts b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/NotificationForm/useNotificationForm.ts index ba639a35a6..5e998344ec 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/NotificationForm/useNotificationForm.ts +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/NotificationForm/useNotificationForm.ts @@ -18,6 +18,8 @@ const notificationFormSchema = z.object({ notifyOnDailySummary: z.boolean(), notifyOnWeeklySummary: z.boolean(), notifyOnMonthlySummary: z.boolean(), + notifyOnAgentApproved: z.boolean(), + notifyOnAgentRejected: z.boolean(), }); function createNotificationDefaultValues(preferences: { @@ -34,6 +36,8 @@ function createNotificationDefaultValues(preferences: { notifyOnDailySummary: preferences.preferences?.DAILY_SUMMARY, notifyOnWeeklySummary: preferences.preferences?.WEEKLY_SUMMARY, notifyOnMonthlySummary: preferences.preferences?.MONTHLY_SUMMARY, + notifyOnAgentApproved: preferences.preferences?.AGENT_APPROVED, + notifyOnAgentRejected: preferences.preferences?.AGENT_REJECTED, }; } @@ -80,6 +84,8 @@ export function useNotificationForm({ DAILY_SUMMARY: values.notifyOnDailySummary, WEEKLY_SUMMARY: values.notifyOnWeeklySummary, MONTHLY_SUMMARY: values.notifyOnMonthlySummary, + AGENT_APPROVED: values.notifyOnAgentApproved, + AGENT_REJECTED: values.notifyOnAgentRejected, }, daily_limit: 0, }; diff --git a/autogpt_platform/frontend/src/app/api/openapi.json b/autogpt_platform/frontend/src/app/api/openapi.json index 45e65272ab..3a072366ea 100644 --- a/autogpt_platform/frontend/src/app/api/openapi.json +++ b/autogpt_platform/frontend/src/app/api/openapi.json @@ -5496,7 +5496,9 @@ "WEEKLY_SUMMARY", "MONTHLY_SUMMARY", "REFUND_REQUEST", - "REFUND_PROCESSED" + "REFUND_PROCESSED", + "AGENT_APPROVED", + "AGENT_REJECTED" ], "title": "NotificationType" }, diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts index 8b5efb4a61..ec22543859 100644 --- a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts @@ -571,7 +571,9 @@ export type NotificationType = | "CONTINUOUS_AGENT_ERROR" | "DAILY_SUMMARY" | "WEEKLY_SUMMARY" - | "MONTHLY_SUMMARY"; + | "MONTHLY_SUMMARY" + | "AGENT_APPROVED" + | "AGENT_REJECTED"; // Mirror of backend/backend/data/notifications.py:NotificationPreference export type NotificationPreferenceDTO = {