From 96194c331be0cf38b40abaae3017270b828d5caf Mon Sep 17 00:00:00 2001
From: Aditya Patil
Date: Fri, 19 Jan 2024 17:43:18 +0530
Subject: [PATCH 01/32] feat: Raven Bot Doctype Setup
---
mobile/src/types/Raven/RavenUser.ts | 10 +-
mobile/src/types/RavenBot/RavenBot.ts | 25 ++
.../src/types/RavenMessaging/RavenMessage.ts | 8 +
raven-app/src/types/Raven/RavenUser.ts | 10 +-
raven-app/src/types/RavenBot/RavenBot.ts | 25 ++
.../src/types/RavenMessaging/RavenMessage.ts | 8 +
raven/modules.txt | 3 +-
.../raven/doctype/raven_user/raven_user.json | 27 +-
raven/raven/doctype/raven_user/raven_user.py | 232 ++++++++++--------
raven/raven_bot/__init__.py | 0
raven/raven_bot/doctype/__init__.py | 0
raven/raven_bot/doctype/raven_bot/__init__.py | 0
.../raven_bot/doctype/raven_bot/raven_bot.js | 8 +
.../doctype/raven_bot/raven_bot.json | 80 ++++++
.../raven_bot/doctype/raven_bot/raven_bot.py | 14 ++
.../doctype/raven_bot/test_raven_bot.py | 9 +
.../doctype/raven_message/raven_message.json | 20 +-
17 files changed, 361 insertions(+), 118 deletions(-)
create mode 100644 mobile/src/types/RavenBot/RavenBot.ts
create mode 100644 raven-app/src/types/RavenBot/RavenBot.ts
create mode 100644 raven/raven_bot/__init__.py
create mode 100644 raven/raven_bot/doctype/__init__.py
create mode 100644 raven/raven_bot/doctype/raven_bot/__init__.py
create mode 100644 raven/raven_bot/doctype/raven_bot/raven_bot.js
create mode 100644 raven/raven_bot/doctype/raven_bot/raven_bot.json
create mode 100644 raven/raven_bot/doctype/raven_bot/raven_bot.py
create mode 100644 raven/raven_bot/doctype/raven_bot/test_raven_bot.py
diff --git a/mobile/src/types/Raven/RavenUser.ts b/mobile/src/types/Raven/RavenUser.ts
index e3dbd8ec8..4e44ab791 100644
--- a/mobile/src/types/Raven/RavenUser.ts
+++ b/mobile/src/types/Raven/RavenUser.ts
@@ -10,12 +10,18 @@ export interface RavenUser{
parentfield?: string
parenttype?: string
idx?: number
+ /** Type : Select */
+ type: "Human" | "Bot"
/** User : Link - User */
- user: string
+ user?: string
+ /** Bot : Link - Raven Bot */
+ bot?: string
/** Full Name : Data */
- full_name: string
+ full_name?: string
/** First Name : Data */
first_name?: string
/** User Image : Attach Image */
user_image?: string
+ /** Enabled : Check */
+ enabled?: 0 | 1
}
\ No newline at end of file
diff --git a/mobile/src/types/RavenBot/RavenBot.ts b/mobile/src/types/RavenBot/RavenBot.ts
new file mode 100644
index 000000000..74db4f47d
--- /dev/null
+++ b/mobile/src/types/RavenBot/RavenBot.ts
@@ -0,0 +1,25 @@
+
+export interface RavenBot{
+ creation: string
+ name: string
+ modified: string
+ owner: string
+ modified_by: string
+ docstatus: 0 | 1 | 2
+ parent?: string
+ parentfield?: string
+ parenttype?: string
+ idx?: number
+ /** Bot Name : Data */
+ bot_name?: string
+ /** Description : Data */
+ description?: string
+ /** Image : Attach Image */
+ image?: string
+ /** Enabled : Check */
+ enabled?: 0 | 1
+ /** Is Standard : Check */
+ is_standard?: 0 | 1
+ /** Module : Link - Module Def */
+ module?: string
+}
\ No newline at end of file
diff --git a/mobile/src/types/RavenMessaging/RavenMessage.ts b/mobile/src/types/RavenMessaging/RavenMessage.ts
index 93f2f4380..188568bff 100644
--- a/mobile/src/types/RavenMessaging/RavenMessage.ts
+++ b/mobile/src/types/RavenMessaging/RavenMessage.ts
@@ -36,4 +36,12 @@ export interface RavenMessage{
is_reply?: 0 | 1
/** Linked Message : Link - Raven Message */
linked_message?: string
+ /** Link Doctype : Link - DocType */
+ link_doctype?: string
+ /** Link Document : Dynamic Link */
+ link_document?: string
+ /** Is Bot Message : Check */
+ is_bot_message?: 0 | 1
+ /** Bot : Link - Raven User */
+ bot?: string
}
\ No newline at end of file
diff --git a/raven-app/src/types/Raven/RavenUser.ts b/raven-app/src/types/Raven/RavenUser.ts
index e3dbd8ec8..4e44ab791 100644
--- a/raven-app/src/types/Raven/RavenUser.ts
+++ b/raven-app/src/types/Raven/RavenUser.ts
@@ -10,12 +10,18 @@ export interface RavenUser{
parentfield?: string
parenttype?: string
idx?: number
+ /** Type : Select */
+ type: "Human" | "Bot"
/** User : Link - User */
- user: string
+ user?: string
+ /** Bot : Link - Raven Bot */
+ bot?: string
/** Full Name : Data */
- full_name: string
+ full_name?: string
/** First Name : Data */
first_name?: string
/** User Image : Attach Image */
user_image?: string
+ /** Enabled : Check */
+ enabled?: 0 | 1
}
\ No newline at end of file
diff --git a/raven-app/src/types/RavenBot/RavenBot.ts b/raven-app/src/types/RavenBot/RavenBot.ts
new file mode 100644
index 000000000..74db4f47d
--- /dev/null
+++ b/raven-app/src/types/RavenBot/RavenBot.ts
@@ -0,0 +1,25 @@
+
+export interface RavenBot{
+ creation: string
+ name: string
+ modified: string
+ owner: string
+ modified_by: string
+ docstatus: 0 | 1 | 2
+ parent?: string
+ parentfield?: string
+ parenttype?: string
+ idx?: number
+ /** Bot Name : Data */
+ bot_name?: string
+ /** Description : Data */
+ description?: string
+ /** Image : Attach Image */
+ image?: string
+ /** Enabled : Check */
+ enabled?: 0 | 1
+ /** Is Standard : Check */
+ is_standard?: 0 | 1
+ /** Module : Link - Module Def */
+ module?: string
+}
\ No newline at end of file
diff --git a/raven-app/src/types/RavenMessaging/RavenMessage.ts b/raven-app/src/types/RavenMessaging/RavenMessage.ts
index 93f2f4380..188568bff 100644
--- a/raven-app/src/types/RavenMessaging/RavenMessage.ts
+++ b/raven-app/src/types/RavenMessaging/RavenMessage.ts
@@ -36,4 +36,12 @@ export interface RavenMessage{
is_reply?: 0 | 1
/** Linked Message : Link - Raven Message */
linked_message?: string
+ /** Link Doctype : Link - DocType */
+ link_doctype?: string
+ /** Link Document : Dynamic Link */
+ link_document?: string
+ /** Is Bot Message : Check */
+ is_bot_message?: 0 | 1
+ /** Bot : Link - Raven User */
+ bot?: string
}
\ No newline at end of file
diff --git a/raven/modules.txt b/raven/modules.txt
index c5387266a..47c760840 100644
--- a/raven/modules.txt
+++ b/raven/modules.txt
@@ -1,3 +1,4 @@
Raven
Raven Messaging
-Raven Channel Management
\ No newline at end of file
+Raven Channel Management
+Raven Bot
\ No newline at end of file
diff --git a/raven/raven/doctype/raven_user/raven_user.json b/raven/raven/doctype/raven_user/raven_user.json
index d15dde972..20613279f 100644
--- a/raven/raven/doctype/raven_user/raven_user.json
+++ b/raven/raven/doctype/raven_user/raven_user.json
@@ -1,14 +1,15 @@
{
"actions": [],
"allow_rename": 1,
- "autoname": "field:user",
"creation": "2023-09-06 14:36:48.631681",
"default_view": "List",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
+ "type",
"user",
+ "bot",
"full_name",
"first_name",
"user_image",
@@ -17,11 +18,12 @@
],
"fields": [
{
+ "depends_on": "eval: doc.type == 'Human'",
"fieldname": "user",
"fieldtype": "Link",
"label": "User",
+ "mandatory_depends_on": "eval: doc.type == 'Human'",
"options": "User",
- "reqd": 1,
"unique": 1
},
{
@@ -32,7 +34,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Full Name",
- "reqd": 1
+ "mandatory_depends_on": "eval: doc.type == 'Human'"
},
{
"fetch_from": "user.first_name",
@@ -41,6 +43,7 @@
"label": "First Name"
},
{
+ "depends_on": "eval: doc.type == 'Human'",
"fetch_from": ".",
"fieldname": "user_image",
"fieldtype": "Attach Image",
@@ -57,15 +60,29 @@
"fieldname": "html_xuuw",
"fieldtype": "HTML",
"options": "To disable the user from accessing Raven, go to \"Users\" and remove the \"Raven User\" role.
"
+ },
+ {
+ "fieldname": "type",
+ "fieldtype": "Select",
+ "label": "Type",
+ "options": "Human\nBot",
+ "reqd": 1
+ },
+ {
+ "depends_on": "eval: doc.type == 'Bot'",
+ "fieldname": "bot",
+ "fieldtype": "Link",
+ "label": "Bot",
+ "options": "Raven Bot"
}
],
"image_field": "user_image",
"links": [],
- "modified": "2023-12-08 04:14:02.588812",
+ "modified": "2024-01-19 16:53:51.574064",
"modified_by": "Administrator",
"module": "Raven",
"name": "Raven User",
- "naming_rule": "By fieldname",
+ "naming_rule": "By script",
"owner": "Administrator",
"permissions": [
{
diff --git a/raven/raven/doctype/raven_user/raven_user.py b/raven/raven/doctype/raven_user/raven_user.py
index aea5caaeb..76eb1460e 100644
--- a/raven/raven/doctype/raven_user/raven_user.py
+++ b/raven/raven/doctype/raven_user/raven_user.py
@@ -5,110 +5,130 @@
from frappe.model.document import Document
from frappe import _
+
class RavenUser(Document):
- # begin: auto-generated types
- # This code is auto-generated. Do not modify anything in this block.
-
- from typing import TYPE_CHECKING
-
- if TYPE_CHECKING:
- from frappe.types import DF
-
- enabled: DF.Check
- first_name: DF.Data | None
- full_name: DF.Data
- user: DF.Link
- user_image: DF.AttachImage | None
- # end: auto-generated types
-
- def before_validate(self):
- if not self.full_name:
- self.full_name = self.first_name
-
- def before_save(self):
- self.update_photo_from_user()
-
- def after_delete(self):
- '''
- Remove the Raven User role from the user.
- '''
- user = frappe.get_doc("User", self.user)
- user.flags.ignore_permissions = True
- user.flags.deleting_raven_user = True
- user.remove_roles("Raven User")
- user.save()
-
- def update_photo_from_user(self):
- '''
- We need to create a new File record for the user image and attach it to the Raven User record.
- Why not just copy the URL from the User record? Because the URL is not accessible to the Raven User,
- and Frappe creates a duplicate file in the system (that is public) but does not update the URL in the field.
- '''
- user_image = frappe.db.get_value("User", self.user, "user_image")
- if user_image and not self.user_image:
- image_file = frappe.get_doc(
- {
- "doctype": "File",
- "file_url": user_image,
- "attached_to_doctype": "Raven User",
- "attached_to_name": self.name,
- "attached_to_field": "user_image",
- "is_private": 1,
- }
- ).insert()
- self.user_image = image_file.file_url
- pass
-
-
-def add_user_to_raven(doc,method):
- # called when the user is inserted or updated
- # If the auto-create setting is set to True, check if the user is a System user. If yes, then create a Raven User record for the user.
- # Else, check if the user has a Raven User role. If yes, then create a Raven User record for the user if not already created.
-
- # If the user is already added to Raven, do nothing.
- if not doc.flags.deleting_raven_user:
- if frappe.db.exists("Raven User", {"user": doc.name}):
- # Check if the role is still present. If not, then inactivate the Raven User record.
- has_raven_role = False
- for role in doc.get("roles"):
- if role.role == "Raven User":
- has_raven_role = True
- break
-
- if has_raven_role:
- raven_user = frappe.get_doc("Raven User", {"user": doc.name})
- if not doc.full_name:
- raven_user.full_name = doc.first_name
- raven_user.enabled = 1
- raven_user.save(ignore_permissions=True)
- else:
- raven_user = frappe.get_doc("Raven User", {"user": doc.name})
- if not doc.full_name:
- raven_user.full_name = doc.first_name
- raven_user.enabled = 0
- raven_user.save(ignore_permissions=True)
- else:
- # Raven user does not exist. Check if the user is a system user.
- if doc.user_type == "System User":
- # Only create raven user if it exists in the system.
- if frappe.db.exists("User", doc.name):
- auto_add = frappe.db.get_single_value("Raven Settings", "auto_add_system_users")
-
- if auto_add:
- doc.append("roles", {"role": "Raven User"})
- # Create a Raven User record for the user.
- raven_user = frappe.new_doc("Raven User")
- raven_user.user = doc.name
- if not doc.full_name:
- raven_user.full_name = doc.first_name
- raven_user.enabled = 1
- raven_user.insert(ignore_permissions=True)
- else:
- if "Raven User" in [d.role for d in doc.get("roles")]:
- # Create a Raven User record for the user.
- raven_user = frappe.new_doc("Raven User")
- raven_user.user = doc.name
- if not doc.full_name:
- raven_user.full_name = doc.first_name
- raven_user.enabled = 1
- raven_user.insert(ignore_permissions=True)
+ # begin: auto-generated types
+ # This code is auto-generated. Do not modify anything in this block.
+
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from frappe.types import DF
+
+ enabled: DF.Check
+ first_name: DF.Data | None
+ full_name: DF.Data
+ user: DF.Link
+ user_image: DF.AttachImage | None
+ # end: auto-generated types
+
+ def autoname(self):
+ if self.type == "Bot":
+ self.name = self.bot
+ else:
+ self.name = self.user
+
+ def before_validate(self):
+ if not self.full_name:
+ self.full_name = self.first_name
+
+ def before_save(self):
+ if self.type == "Bot":
+ self.get_bot_details()
+ else:
+ self.update_photo_from_user()
+
+ def after_delete(self):
+ '''
+ Remove the Raven User role from the user.
+ '''
+ user = frappe.get_doc("User", self.user)
+ user.flags.ignore_permissions = True
+ user.flags.deleting_raven_user = True
+ user.remove_roles("Raven User")
+ user.save()
+
+ def update_photo_from_user(self):
+ '''
+ We need to create a new File record for the user image and attach it to the Raven User record.
+ Why not just copy the URL from the User record? Because the URL is not accessible to the Raven User,
+ and Frappe creates a duplicate file in the system (that is public) but does not update the URL in the field.
+ '''
+ user_image = frappe.db.get_value("User", self.user, "user_image")
+ if user_image and not self.user_image:
+ image_file = frappe.get_doc(
+ {
+ "doctype": "File",
+ "file_url": user_image,
+ "attached_to_doctype": "Raven User",
+ "attached_to_name": self.name,
+ "attached_to_field": "user_image",
+ "is_private": 1,
+ }
+ ).insert()
+ self.user_image = image_file.file_url
+
+ def get_bot_details(self):
+ '''
+ Get the bot details for the user.
+ '''
+ bot_details = frappe.db.get_value(
+ "Raven Bot", self.bot, ["bot_name", "image", "enabled"], as_dict=True)
+ self.full_name = bot_details.bot_name
+ self.user_image = bot_details.image
+ self.enabled = bot_details.enabled
+
+
+def add_user_to_raven(doc, method):
+ # called when the user is inserted or updated
+ # If the auto-create setting is set to True, check if the user is a System user. If yes, then create a Raven User record for the user.
+ # Else, check if the user has a Raven User role. If yes, then create a Raven User record for the user if not already created.
+
+ # If the user is already added to Raven, do nothing.
+ if not doc.flags.deleting_raven_user:
+ if frappe.db.exists("Raven User", {"user": doc.name}):
+ # Check if the role is still present. If not, then inactivate the Raven User record.
+ has_raven_role = False
+ for role in doc.get("roles"):
+ if role.role == "Raven User":
+ has_raven_role = True
+ break
+
+ if has_raven_role:
+ raven_user = frappe.get_doc("Raven User", {"user": doc.name})
+ if not doc.full_name:
+ raven_user.full_name = doc.first_name
+ raven_user.enabled = 1
+ raven_user.save(ignore_permissions=True)
+ else:
+ raven_user = frappe.get_doc("Raven User", {"user": doc.name})
+ if not doc.full_name:
+ raven_user.full_name = doc.first_name
+ raven_user.enabled = 0
+ raven_user.save(ignore_permissions=True)
+ else:
+ # Raven user does not exist. Check if the user is a system user.
+ if doc.user_type == "System User":
+ # Only create raven user if it exists in the system.
+ if frappe.db.exists("User", doc.name):
+ auto_add = frappe.db.get_single_value(
+ "Raven Settings", "auto_add_system_users")
+
+ if auto_add:
+ doc.append("roles", {"role": "Raven User"})
+ # Create a Raven User record for the user.
+ raven_user = frappe.new_doc("Raven User")
+ raven_user.user = doc.name
+ if not doc.full_name:
+ raven_user.full_name = doc.first_name
+ raven_user.enabled = 1
+ raven_user.insert(ignore_permissions=True)
+ else:
+ if "Raven User" in [d.role for d in doc.get("roles")]:
+ # Create a Raven User record for the user.
+ raven_user = frappe.new_doc("Raven User")
+ raven_user.user = doc.name
+ if not doc.full_name:
+ raven_user.full_name = doc.first_name
+ raven_user.enabled = 1
+ raven_user.insert(ignore_permissions=True)
diff --git a/raven/raven_bot/__init__.py b/raven/raven_bot/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_bot/doctype/__init__.py b/raven/raven_bot/doctype/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_bot/doctype/raven_bot/__init__.py b/raven/raven_bot/doctype/raven_bot/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_bot/doctype/raven_bot/raven_bot.js b/raven/raven_bot/doctype/raven_bot/raven_bot.js
new file mode 100644
index 000000000..f6dd2dea3
--- /dev/null
+++ b/raven/raven_bot/doctype/raven_bot/raven_bot.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2024, The Commit Company and contributors
+// For license information, please see license.txt
+
+// frappe.ui.form.on("Raven Bot", {
+// refresh(frm) {
+
+// },
+// });
diff --git a/raven/raven_bot/doctype/raven_bot/raven_bot.json b/raven/raven_bot/doctype/raven_bot/raven_bot.json
new file mode 100644
index 000000000..953e0426d
--- /dev/null
+++ b/raven/raven_bot/doctype/raven_bot/raven_bot.json
@@ -0,0 +1,80 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:bot_name",
+ "creation": "2024-01-19 16:05:10.754915",
+ "default_view": "List",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "bot_name",
+ "description",
+ "image",
+ "enabled",
+ "is_standard",
+ "module"
+ ],
+ "fields": [
+ {
+ "fieldname": "description",
+ "fieldtype": "Data",
+ "label": "Description"
+ },
+ {
+ "fieldname": "image",
+ "fieldtype": "Attach Image",
+ "label": "Image"
+ },
+ {
+ "default": "0",
+ "fieldname": "enabled",
+ "fieldtype": "Check",
+ "label": "Enabled"
+ },
+ {
+ "fieldname": "bot_name",
+ "fieldtype": "Data",
+ "label": "Bot Name",
+ "unique": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "is_standard",
+ "fieldtype": "Check",
+ "label": "Is Standard"
+ },
+ {
+ "fieldname": "module",
+ "fieldtype": "Link",
+ "label": "Module",
+ "mandatory_depends_on": "eval: doc.is_standard == 1",
+ "options": "Module Def"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2024-01-19 17:31:56.102999",
+ "modified_by": "Administrator",
+ "module": "Raven Bot",
+ "name": "Raven Bot",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/raven/raven_bot/doctype/raven_bot/raven_bot.py b/raven/raven_bot/doctype/raven_bot/raven_bot.py
new file mode 100644
index 000000000..3b07f8d96
--- /dev/null
+++ b/raven/raven_bot/doctype/raven_bot/raven_bot.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2024, The Commit Company and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe.modules.export_file import export_to_files
+from frappe.model.document import Document
+
+
+class RavenBot(Document):
+ def on_update(self):
+ if frappe.conf.developer_mode and self.is_standard:
+ export_to_files(
+ record_list=[["Raven Bot", self.name]], record_module=self.module
+ )
diff --git a/raven/raven_bot/doctype/raven_bot/test_raven_bot.py b/raven/raven_bot/doctype/raven_bot/test_raven_bot.py
new file mode 100644
index 000000000..b163ffe62
--- /dev/null
+++ b/raven/raven_bot/doctype/raven_bot/test_raven_bot.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2024, The Commit Company and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestRavenBot(FrappeTestCase):
+ pass
diff --git a/raven/raven_messaging/doctype/raven_message/raven_message.json b/raven/raven_messaging/doctype/raven_message/raven_message.json
index 7eee68fda..b4d246ed8 100644
--- a/raven/raven_messaging/doctype/raven_message/raven_message.json
+++ b/raven/raven_messaging/doctype/raven_message/raven_message.json
@@ -22,7 +22,9 @@
"is_reply",
"linked_message",
"link_doctype",
- "link_document"
+ "link_document",
+ "is_bot_message",
+ "bot"
],
"fields": [
{
@@ -109,11 +111,25 @@
"fieldtype": "Dynamic Link",
"label": "Link Document",
"options": "link_doctype"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_bot_message",
+ "fieldtype": "Check",
+ "label": "Is Bot Message"
+ },
+ {
+ "depends_on": "eval: doc.is_bot_message == 1",
+ "fieldname": "bot",
+ "fieldtype": "Link",
+ "label": "Bot",
+ "mandatory_depends_on": "eval: doc.is_bot_message == 1",
+ "options": "Raven User"
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-12-22 18:25:02.237368",
+ "modified": "2024-01-19 17:41:28.463058",
"modified_by": "Administrator",
"module": "Raven Messaging",
"name": "Raven Message",
From 4da8451791151307ffc5fc2a67249433fb7ccf9a Mon Sep 17 00:00:00 2001
From: Aditya Patil
Date: Fri, 19 Jan 2024 17:59:06 +0530
Subject: [PATCH 02/32] fix: Not assuming User as Human
---
mobile/src/types/Raven/RavenUser.ts | 2 +-
raven-app/src/types/Raven/RavenUser.ts | 2 +-
raven/raven/doctype/raven_user/raven_user.json | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/mobile/src/types/Raven/RavenUser.ts b/mobile/src/types/Raven/RavenUser.ts
index 4e44ab791..e1b7e6168 100644
--- a/mobile/src/types/Raven/RavenUser.ts
+++ b/mobile/src/types/Raven/RavenUser.ts
@@ -11,7 +11,7 @@ export interface RavenUser{
parenttype?: string
idx?: number
/** Type : Select */
- type: "Human" | "Bot"
+ type: "User" | "Bot"
/** User : Link - User */
user?: string
/** Bot : Link - Raven Bot */
diff --git a/raven-app/src/types/Raven/RavenUser.ts b/raven-app/src/types/Raven/RavenUser.ts
index 4e44ab791..e1b7e6168 100644
--- a/raven-app/src/types/Raven/RavenUser.ts
+++ b/raven-app/src/types/Raven/RavenUser.ts
@@ -11,7 +11,7 @@ export interface RavenUser{
parenttype?: string
idx?: number
/** Type : Select */
- type: "Human" | "Bot"
+ type: "User" | "Bot"
/** User : Link - User */
user?: string
/** Bot : Link - Raven Bot */
diff --git a/raven/raven/doctype/raven_user/raven_user.json b/raven/raven/doctype/raven_user/raven_user.json
index 20613279f..b57c1004b 100644
--- a/raven/raven/doctype/raven_user/raven_user.json
+++ b/raven/raven/doctype/raven_user/raven_user.json
@@ -65,7 +65,7 @@
"fieldname": "type",
"fieldtype": "Select",
"label": "Type",
- "options": "Human\nBot",
+ "options": "User\nBot",
"reqd": 1
},
{
@@ -78,7 +78,7 @@
],
"image_field": "user_image",
"links": [],
- "modified": "2024-01-19 16:53:51.574064",
+ "modified": "2024-01-19 17:53:07.854127",
"modified_by": "Administrator",
"module": "Raven",
"name": "Raven User",
From 8fd9782f35b59617527cde85cd0c038b70bd0c9a Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Thu, 25 Jan 2024 16:44:45 +0530
Subject: [PATCH 03/32] feat: add route for Settings page
---
raven-app/src/App.tsx | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/raven-app/src/App.tsx b/raven-app/src/App.tsx
index 31aba6e1a..1c1b72b25 100644
--- a/raven-app/src/App.tsx
+++ b/raven-app/src/App.tsx
@@ -5,11 +5,11 @@ import { ProtectedRoute } from './utils/auth/ProtectedRoute'
import { UserProvider } from './utils/auth/UserProvider'
import { ChannelRedirect } from './utils/channel/ChannelRedirect'
import "cal-sans";
-import { useState } from 'react'
import { ThemeProvider } from './ThemeProvider'
import { Toaster } from './components/common/Toast/Toaster'
import { FullPageLoader } from './components/layout/Loaders'
import { useStickyState } from './hooks/useStickyState'
+import { Settings } from './pages/settings/Settings'
const router = createBrowserRouter(
@@ -23,6 +23,12 @@ const router = createBrowserRouter(
import('./components/feature/saved-messages/SavedMessages')} />
import('@/pages/ChatSpace')} />
+ }>
+
+ Webhooks
} />
+ SS} />
+
+
>
), {
From 287f14bfb9fd14a8868638996002dd39a8462279 Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Thu, 25 Jan 2024 16:45:12 +0530
Subject: [PATCH 04/32] feat: Sidebar for Settings page (basic routes and
layout)
---
.../feature/settings/Integrations.tsx | 39 +++++++++++++++++++
.../components/layout/Settings/Sidebar.tsx | 24 ++++++++++++
.../layout/Settings/SidebarBody.tsx | 14 +++++++
.../layout/Settings/SidebarHeader.tsx | 26 +++++++++++++
.../layout/Sidebar/SidebarFooter.tsx | 5 +++
raven-app/src/pages/settings/Settings.tsx | 20 ++++++++++
6 files changed, 128 insertions(+)
create mode 100644 raven-app/src/components/feature/settings/Integrations.tsx
create mode 100644 raven-app/src/components/layout/Settings/Sidebar.tsx
create mode 100644 raven-app/src/components/layout/Settings/SidebarBody.tsx
create mode 100644 raven-app/src/components/layout/Settings/SidebarHeader.tsx
create mode 100644 raven-app/src/pages/settings/Settings.tsx
diff --git a/raven-app/src/components/feature/settings/Integrations.tsx b/raven-app/src/components/feature/settings/Integrations.tsx
new file mode 100644
index 000000000..3444967bd
--- /dev/null
+++ b/raven-app/src/components/feature/settings/Integrations.tsx
@@ -0,0 +1,39 @@
+import { SidebarGroup, SidebarGroupItem, SidebarGroupLabel, SidebarGroupList, SidebarItem } from "@/components/layout/Sidebar"
+import { Flex, Text } from "@radix-ui/themes"
+import { BiPlug } from "react-icons/bi"
+
+export interface Props { }
+
+export const Integrations = (props: Props) => {
+ return (
+
+
+ {/* */}
+
+
+
+ Integrations
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+
+export const IntegrationsItem = ({ route, label }: { route: string, label: string }) => {
+
+ return (
+
+
+ {label}
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/components/layout/Settings/Sidebar.tsx b/raven-app/src/components/layout/Settings/Sidebar.tsx
new file mode 100644
index 000000000..8f097089f
--- /dev/null
+++ b/raven-app/src/components/layout/Settings/Sidebar.tsx
@@ -0,0 +1,24 @@
+import { Flex, Box, Separator } from "@radix-ui/themes"
+import { SidebarBody } from "./SidebarBody"
+import { SidebarFooter } from "../Sidebar/SidebarFooter"
+import { SidebarHeader } from "./SidebarHeader"
+
+export interface Props { }
+
+/**
+ * Sidebar for Settings page
+ */
+export const Sidebar = (props: Props) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/components/layout/Settings/SidebarBody.tsx b/raven-app/src/components/layout/Settings/SidebarBody.tsx
new file mode 100644
index 000000000..f26284c6e
--- /dev/null
+++ b/raven-app/src/components/layout/Settings/SidebarBody.tsx
@@ -0,0 +1,14 @@
+import { Integrations } from "@/components/feature/settings/Integrations"
+import { ScrollArea, Flex } from "@radix-ui/themes"
+
+export interface Props { }
+
+export const SidebarBody = (props: Props) => {
+ return (
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/components/layout/Settings/SidebarHeader.tsx b/raven-app/src/components/layout/Settings/SidebarHeader.tsx
new file mode 100644
index 000000000..01f6fcf6a
--- /dev/null
+++ b/raven-app/src/components/layout/Settings/SidebarHeader.tsx
@@ -0,0 +1,26 @@
+import { Flex, Text } from "@radix-ui/themes"
+import { BiChevronLeft } from "react-icons/bi"
+import { useNavigate } from "react-router-dom"
+
+export interface Props { }
+
+export const SidebarHeader = (props: Props) => {
+
+ const navigate = useNavigate()
+
+ return (
+
+ navigate('/channel')}
+ >
+
+ Settings
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/components/layout/Sidebar/SidebarFooter.tsx b/raven-app/src/components/layout/Sidebar/SidebarFooter.tsx
index 65c88f8b3..0dbb6aff3 100644
--- a/raven-app/src/components/layout/Sidebar/SidebarFooter.tsx
+++ b/raven-app/src/components/layout/Sidebar/SidebarFooter.tsx
@@ -57,6 +57,11 @@ export const SidebarFooter = () => {
Mobile App
+
+
+ Settings
+
+
Log Out
diff --git a/raven-app/src/pages/settings/Settings.tsx b/raven-app/src/pages/settings/Settings.tsx
new file mode 100644
index 000000000..d319c370e
--- /dev/null
+++ b/raven-app/src/pages/settings/Settings.tsx
@@ -0,0 +1,20 @@
+import { Sidebar } from "@/components/layout/Settings/Sidebar"
+import { Flex, Box } from "@radix-ui/themes"
+import { Outlet } from "react-router-dom"
+
+export interface Props { }
+
+export const Settings = (props: Props) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ >
+ )
+}
\ No newline at end of file
From bd03e2c54d7f53be2d32d9983a164ad5b8a39a0b Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Thu, 25 Jan 2024 17:13:24 +0530
Subject: [PATCH 05/32] style: cursor pointer
---
raven-app/src/components/layout/Settings/SidebarHeader.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/raven-app/src/components/layout/Settings/SidebarHeader.tsx b/raven-app/src/components/layout/Settings/SidebarHeader.tsx
index 01f6fcf6a..365d30dd7 100644
--- a/raven-app/src/components/layout/Settings/SidebarHeader.tsx
+++ b/raven-app/src/components/layout/Settings/SidebarHeader.tsx
@@ -16,6 +16,7 @@ export const SidebarHeader = (props: Props) => {
gap={'3'}
pt='1'
height='8'
+ className="cursor-pointer"
onClick={() => navigate('/channel')}
>
From 536671406db4c6fc85a59b00894466105a9f143d Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Thu, 25 Jan 2024 17:13:52 +0530
Subject: [PATCH 06/32] fix: hide 'Settings' options from Footer menu
---
raven-app/src/components/layout/Settings/Sidebar.tsx | 2 +-
raven-app/src/components/layout/Sidebar/SidebarFooter.tsx | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/raven-app/src/components/layout/Settings/Sidebar.tsx b/raven-app/src/components/layout/Settings/Sidebar.tsx
index 8f097089f..4cd74e0a8 100644
--- a/raven-app/src/components/layout/Settings/Sidebar.tsx
+++ b/raven-app/src/components/layout/Settings/Sidebar.tsx
@@ -18,7 +18,7 @@ export const Sidebar = (props: Props) => {
-
+
)
}
\ No newline at end of file
diff --git a/raven-app/src/components/layout/Sidebar/SidebarFooter.tsx b/raven-app/src/components/layout/Sidebar/SidebarFooter.tsx
index 0dbb6aff3..f35d016bf 100644
--- a/raven-app/src/components/layout/Sidebar/SidebarFooter.tsx
+++ b/raven-app/src/components/layout/Sidebar/SidebarFooter.tsx
@@ -7,7 +7,7 @@ import { BiDotsHorizontalRounded } from 'react-icons/bi'
import { UserAvatar } from '@/components/common/UserAvatar'
import { isSystemManager } from '@/utils/roles'
-export const SidebarFooter = () => {
+export const SidebarFooter = ({ isSettingsPage = false }: { isSettingsPage?: boolean }) => {
const userData = useUserData()
const { logout } = useContext(UserContext)
@@ -57,11 +57,11 @@ export const SidebarFooter = () => {
Mobile App
-
+ {!isSettingsPage &&
Settings
-
+ }
Log Out
From 78b66ee6a3747314b5c0d676568d13ff7badaa86 Mon Sep 17 00:00:00 2001
From: Aditya Patil
Date: Fri, 2 Feb 2024 18:07:36 +0530
Subject: [PATCH 07/32] fix: updated Raven Message doctype
---
.../doctype/raven_message/raven_message.json | 354 +++++++++---------
1 file changed, 178 insertions(+), 176 deletions(-)
diff --git a/raven/raven_messaging/doctype/raven_message/raven_message.json b/raven/raven_messaging/doctype/raven_message/raven_message.json
index d440c297f..b9bd7c360 100644
--- a/raven/raven_messaging/doctype/raven_message/raven_message.json
+++ b/raven/raven_messaging/doctype/raven_message/raven_message.json
@@ -1,178 +1,180 @@
{
- "actions": [],
- "allow_rename": 1,
- "autoname": "hash",
- "creation": "2023-02-12 17:29:25.498988",
- "default_view": "List",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "channel_id",
- "text",
- "json",
- "message_type",
- "file",
- "image_width",
- "image_height",
- "file_thumbnail",
- "thumbnail_width",
- "thumbnail_height",
- "message_reactions",
- "is_reply",
- "linked_message",
- "link_doctype",
- "link_document",
- "is_bot_message",
- "bot"
- "content"
- ],
- "fields": [
- {
- "fieldname": "channel_id",
- "fieldtype": "Link",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Channel ID",
- "options": "Raven Channel",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "text",
- "fieldtype": "Long Text",
- "label": "Text"
- },
- {
- "fieldname": "json",
- "fieldtype": "JSON",
- "label": "JSON"
- },
- {
- "fieldname": "file",
- "fieldtype": "Attach",
- "label": "File"
- },
- {
- "fieldname": "message_type",
- "fieldtype": "Select",
- "label": "Message Type",
- "options": "Text\nImage\nFile"
- },
- {
- "fieldname": "message_reactions",
- "fieldtype": "JSON",
- "label": "Message Reactions"
- },
- {
- "default": "0",
- "fieldname": "is_reply",
- "fieldtype": "Check",
- "label": "Is Reply"
- },
- {
- "fieldname": "linked_message",
- "fieldtype": "Link",
- "label": "Linked Message",
- "options": "Raven Message"
- },
- {
- "fieldname": "file_thumbnail",
- "fieldtype": "Attach",
- "label": "File Thumbnail"
- },
- {
- "fieldname": "image_width",
- "fieldtype": "Data",
- "label": "Image Width"
- },
- {
- "fieldname": "image_height",
- "fieldtype": "Data",
- "label": "Image Height"
- },
- {
- "fieldname": "thumbnail_width",
- "fieldtype": "Data",
- "label": "Thumbnail Width"
- },
- {
- "fieldname": "thumbnail_height",
- "fieldtype": "Data",
- "label": "Thumbnail Height"
- },
- {
- "fieldname": "link_doctype",
- "fieldtype": "Link",
- "label": "Link Doctype",
- "options": "DocType"
- },
- {
- "fieldname": "link_document",
- "fieldtype": "Dynamic Link",
- "label": "Link Document",
- "options": "link_doctype"
- },
- {
- "default": "0",
- "fieldname": "is_bot_message",
- "fieldtype": "Check",
- "label": "Is Bot Message"
- },
- {
- "depends_on": "eval: doc.is_bot_message == 1",
- "fieldname": "bot",
- "fieldtype": "Link",
- "label": "Bot",
- "mandatory_depends_on": "eval: doc.is_bot_message == 1",
- "options": "Raven User"
- "fieldname": "content",
- "fieldtype": "Long Text",
- "label": "Content",
- "read_only": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2024-01-19 17:41:28.463058",
- "modified_by": "Administrator",
- "module": "Raven Messaging",
- "name": "Raven Message",
- "naming_rule": "Random",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "select": 1,
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 1,
- "print": 1,
- "report": 1,
- "role": "Raven User",
- "share": 1,
- "write": 1
- },
- {
- "read": 1,
- "role": "Raven User"
- }
- ],
- "search_fields": "text",
- "sort_field": "modified",
- "sort_order": "DESC",
- "states": []
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "hash",
+ "creation": "2023-02-12 17:29:25.498988",
+ "default_view": "List",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "channel_id",
+ "text",
+ "json",
+ "message_type",
+ "file",
+ "image_width",
+ "image_height",
+ "file_thumbnail",
+ "thumbnail_width",
+ "thumbnail_height",
+ "message_reactions",
+ "is_reply",
+ "linked_message",
+ "link_doctype",
+ "link_document",
+ "is_bot_message",
+ "bot",
+ "content"
+ ],
+ "fields": [
+ {
+ "fieldname": "channel_id",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Channel ID",
+ "options": "Raven Channel",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "fieldname": "text",
+ "fieldtype": "Long Text",
+ "label": "Text"
+ },
+ {
+ "fieldname": "json",
+ "fieldtype": "JSON",
+ "label": "JSON"
+ },
+ {
+ "fieldname": "file",
+ "fieldtype": "Attach",
+ "label": "File"
+ },
+ {
+ "fieldname": "message_type",
+ "fieldtype": "Select",
+ "label": "Message Type",
+ "options": "Text\nImage\nFile"
+ },
+ {
+ "fieldname": "message_reactions",
+ "fieldtype": "JSON",
+ "label": "Message Reactions"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_reply",
+ "fieldtype": "Check",
+ "label": "Is Reply"
+ },
+ {
+ "fieldname": "linked_message",
+ "fieldtype": "Link",
+ "label": "Linked Message",
+ "options": "Raven Message"
+ },
+ {
+ "fieldname": "file_thumbnail",
+ "fieldtype": "Attach",
+ "label": "File Thumbnail"
+ },
+ {
+ "fieldname": "image_width",
+ "fieldtype": "Data",
+ "label": "Image Width"
+ },
+ {
+ "fieldname": "image_height",
+ "fieldtype": "Data",
+ "label": "Image Height"
+ },
+ {
+ "fieldname": "thumbnail_width",
+ "fieldtype": "Data",
+ "label": "Thumbnail Width"
+ },
+ {
+ "fieldname": "thumbnail_height",
+ "fieldtype": "Data",
+ "label": "Thumbnail Height"
+ },
+ {
+ "fieldname": "link_doctype",
+ "fieldtype": "Link",
+ "label": "Link Doctype",
+ "options": "DocType"
+ },
+ {
+ "fieldname": "link_document",
+ "fieldtype": "Dynamic Link",
+ "label": "Link Document",
+ "options": "link_doctype"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_bot_message",
+ "fieldtype": "Check",
+ "label": "Is Bot Message"
+ },
+ {
+ "depends_on": "eval: doc.is_bot_message == 1",
+ "fieldname": "bot",
+ "fieldtype": "Link",
+ "label": "Bot",
+ "mandatory_depends_on": "eval: doc.is_bot_message == 1",
+ "options": "Raven User"
+ },
+ {
+ "fieldname": "content",
+ "fieldtype": "Long Text",
+ "label": "Content",
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2024-01-19 17:41:28.463058",
+ "modified_by": "Administrator",
+ "module": "Raven Messaging",
+ "name": "Raven Message",
+ "naming_rule": "Random",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "select": 1,
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 1,
+ "print": 1,
+ "report": 1,
+ "role": "Raven User",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "read": 1,
+ "role": "Raven User"
+ }
+ ],
+ "search_fields": "text",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
}
\ No newline at end of file
From d21b0b142767cc63d850314a67b200c219fc0db6 Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Mon, 12 Feb 2024 12:39:07 +0530
Subject: [PATCH 08/32] feat:type files
---
raven-app/src/types/Integrations/Webhook.ts | 54 +++++++++++++++++++
.../src/types/Integrations/WebhookData.ts | 17 ++++++
.../src/types/Integrations/WebhookHeader.ts | 17 ++++++
raven-app/src/utils/removeFrappeFields.ts | 14 +++++
4 files changed, 102 insertions(+)
create mode 100644 raven-app/src/types/Integrations/Webhook.ts
create mode 100644 raven-app/src/types/Integrations/WebhookData.ts
create mode 100644 raven-app/src/types/Integrations/WebhookHeader.ts
create mode 100644 raven-app/src/utils/removeFrappeFields.ts
diff --git a/raven-app/src/types/Integrations/Webhook.ts b/raven-app/src/types/Integrations/Webhook.ts
new file mode 100644
index 000000000..13390acb3
--- /dev/null
+++ b/raven-app/src/types/Integrations/Webhook.ts
@@ -0,0 +1,54 @@
+import { WebhookHeader } from './WebhookHeader'
+import { WebhookData } from './WebhookData'
+
+export interface Webhook{
+ creation: string
+ name: string
+ modified: string
+ owner: string
+ modified_by: string
+ docstatus: 0 | 1 | 2
+ parent?: string
+ parentfield?: string
+ parenttype?: string
+ idx?: number
+ /** DocType : Link - DocType */
+ webhook_doctype: string
+ /** Doc Event : Select */
+ webhook_docevent?: "after_insert" | "on_update" | "on_submit" | "on_cancel" | "on_trash" | "on_update_after_submit" | "on_change"
+ /** Enabled : Check */
+ enabled?: 0 | 1
+ /** Condition : Small Text - The webhook will be triggered if this expression is true */
+ condition?: string
+ /** Request URL : Data */
+ request_url: string
+ /** Request Timeout : Int - The number of seconds until the request expires */
+ timeout: number
+ /** Is Dynamic URL? : Check - On checking this option, URL will be treated like a jinja template string */
+ is_dynamic_url?: 0 | 1
+ /** Request Method : Select */
+ request_method: "POST" | "PUT" | "DELETE"
+ /** Request Structure : Select */
+ request_structure?: "" | "Form URL-Encoded" | "JSON"
+ /** Enable Security : Check */
+ enable_security?: 0 | 1
+ /** Webhook Secret : Password */
+ webhook_secret?: string
+ /** Headers : Table - Webhook Header */
+ webhook_headers?: WebhookHeader[]
+ /** Data : Table - Webhook Data */
+ webhook_data?: WebhookData[]
+ /** JSON Request Body : Code - To add dynamic values from the document, use jinja tags like
+
+
+
{ "id": "{{ doc.name }}" }
+
+
*/
+ webhook_json?: string
+ /** Select Document : Dynamic Link */
+ preview_document?: string
+ /** Meets Condition? : Data */
+ meets_condition?: string
+ /** Request Body : Code */
+ preview_request_body?: string
+}
\ No newline at end of file
diff --git a/raven-app/src/types/Integrations/WebhookData.ts b/raven-app/src/types/Integrations/WebhookData.ts
new file mode 100644
index 000000000..87b044c18
--- /dev/null
+++ b/raven-app/src/types/Integrations/WebhookData.ts
@@ -0,0 +1,17 @@
+
+export interface WebhookData{
+ creation: string
+ name: string
+ modified: string
+ owner: string
+ modified_by: string
+ docstatus: 0 | 1 | 2
+ parent?: string
+ parentfield?: string
+ parenttype?: string
+ idx?: number
+ /** Fieldname : Select */
+ fieldname: string
+ /** Key : Data */
+ key: string
+}
\ No newline at end of file
diff --git a/raven-app/src/types/Integrations/WebhookHeader.ts b/raven-app/src/types/Integrations/WebhookHeader.ts
new file mode 100644
index 000000000..587bb6e0f
--- /dev/null
+++ b/raven-app/src/types/Integrations/WebhookHeader.ts
@@ -0,0 +1,17 @@
+
+export interface WebhookHeader{
+ creation: string
+ name: string
+ modified: string
+ owner: string
+ modified_by: string
+ docstatus: 0 | 1 | 2
+ parent?: string
+ parentfield?: string
+ parenttype?: string
+ idx?: number
+ /** Key : Small Text */
+ key?: string
+ /** Value : Small Text */
+ value?: string
+}
\ No newline at end of file
diff --git a/raven-app/src/utils/removeFrappeFields.ts b/raven-app/src/utils/removeFrappeFields.ts
new file mode 100644
index 000000000..099cb188d
--- /dev/null
+++ b/raven-app/src/utils/removeFrappeFields.ts
@@ -0,0 +1,14 @@
+import { FrappeDoc } from "frappe-react-sdk";
+
+type BaseFields = 'owner' | 'creation' | 'modified' | 'modified_by' | 'docstatus';
+type ChildFields = 'parent' | 'parenttype' | 'parentfield';
+export const removeFrappeFields = (data: FrappeDoc): Omit, BaseFields> => {
+
+ try {
+ const { owner, creation, modified, modified_by, docstatus, ...withoutBaseFields } = data;
+ return withoutBaseFields;
+ }
+ catch (e) {
+ return data
+ }
+}
\ No newline at end of file
From 9b5b88b307ca86591568848563c2f7a48dd3d25b Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Mon, 12 Feb 2024 12:40:42 +0530
Subject: [PATCH 09/32] feat:webhook form
---
raven-app/src/App.tsx | 7 +-
.../integrations/webhooks/WebhookForm.tsx | 158 ++++++++
.../integrations/webhooks/WebhookHeaders.tsx | 73 ++++
.../webhooks/WebhookReturnDataFieldTable.tsx | 246 ++++++++++++
.../feature/integrations/webhooks/utils.ts | 367 ++++++++++++++++++
5 files changed, 850 insertions(+), 1 deletion(-)
create mode 100644 raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx
create mode 100644 raven-app/src/components/feature/integrations/webhooks/WebhookHeaders.tsx
create mode 100644 raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
create mode 100644 raven-app/src/components/feature/integrations/webhooks/utils.ts
diff --git a/raven-app/src/App.tsx b/raven-app/src/App.tsx
index 1c1b72b25..c5ef1129b 100644
--- a/raven-app/src/App.tsx
+++ b/raven-app/src/App.tsx
@@ -10,6 +10,9 @@ import { Toaster } from './components/common/Toast/Toaster'
import { FullPageLoader } from './components/layout/Loaders'
import { useStickyState } from './hooks/useStickyState'
import { Settings } from './pages/settings/Settings'
+import { CreateWebhook } from './components/feature/integrations/webhooks/CreateWebhook'
+import ViewWebhook from './components/feature/integrations/webhooks/ViewWebhook'
+import { WebhookList } from './components/feature/integrations/webhooks/WebhookList'
const router = createBrowserRouter(
@@ -25,7 +28,9 @@ const router = createBrowserRouter(
}>
- Webhooks} />
+ } />
+ } />
+ } />
SS} />
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx
new file mode 100644
index 000000000..9ec259d34
--- /dev/null
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx
@@ -0,0 +1,158 @@
+import React, { useMemo } from 'react';
+import { Controller, useFormContext } from 'react-hook-form';
+import { Webhook } from '@/types/Integrations/Webhook'
+import { Box, Checkbox, Flex, TextFieldInput, Select, TextArea, Heading, Text } from '@radix-ui/themes';
+import { ErrorText, HelperText, Label } from '@/components/common/Form';
+import { WebhookData } from './WebhookReturnDataFieldTable';
+import { WebhookHeaders } from './WebhookHeaders';
+import { TriggerEvents } from './utils';
+
+export interface WebhookFormField extends Webhook {
+ need_condition: boolean
+}
+
+export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
+ const { register, formState: { errors }, control, setValue, watch } = useFormContext()
+
+ const onTriggerEventChange = (value: string) => {
+ if (value) {
+ setValue('webhook_data', [])
+ const field = TriggerEvents?.find(event => event.key === value)
+ if (field) {
+ setValue('webhook_doctype', field.doctype)
+ setValue('webhook_docevent', field.event)
+ }
+ }
+ }
+
+ const webhookDoctype = watch('webhook_doctype')
+
+ const webhookDocevent = watch('webhook_docevent')
+
+ const security = watch('enable_security')
+
+ const needCondition = watch('need_condition')
+
+ const triggerValue = useMemo(() => {
+ if (webhookDoctype && webhookDocevent) {
+ const field = TriggerEvents?.find(event => event.doctype === webhookDoctype && event.event === webhookDocevent)
+ return field?.key
+ }
+ return ''
+ }, [webhookDoctype, webhookDocevent])
+
+ return (
+
+
+ Name
+
+ {errors?.name && {errors.name.message} }
+
+
+ Request URL
+
+ {errors?.request_url && {errors.request_url.message} }
+
+
+ Request Timeout
+
+ The number of seconds until the request expires
+ {errors?.timeout && {errors.timeout.message} }
+
+
+ (
+
+ field.onChange(!field.value)} />
+
+ Enable Security
+
+
+ )}
+ />
+
+ {errors?.enable_security && {errors.enable_security.message} }
+
+ {security ?
+ Webhook Secret
+
+ {errors?.webhook_secret && {errors.webhook_secret.message} }
+ : null}
+
+
+ Trigger Event
+
+
+
+
+ Trigger Events
+ {
+ TriggerEvents?.map((event, index) => (
+ {event.label}
+ ))
+ }
+
+
+
+
+
+
+ (
+
+ field.onChange(!field.value)} />
+
+ Trigger this webhook based on a condition
+
+
+ )}
+ />
+
+ {errors?.enable_security && {errors.enable_security.message} }
+
+ {needCondition &&
+
+
+ Condition
+
+ The webhook will be triggered if this expression is true
+
+
+ Condition Examples :
+
+ doc.channel_id == 'general'
+ doc.is_direct_message == 1
+
+
+
+ }
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookHeaders.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookHeaders.tsx
new file mode 100644
index 000000000..d7e040698
--- /dev/null
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookHeaders.tsx
@@ -0,0 +1,73 @@
+import { HelperText } from "@/components/common/Form";
+import { Webhook } from "@/types/Integrations/Webhook";
+import { Flex, Box, Heading, Table, TextFieldInput, IconButton, Button } from "@radix-ui/themes";
+import { useState } from "react";
+import { useFieldArray, useFormContext } from "react-hook-form";
+import { BiMinusCircle } from "react-icons/bi";
+import { BsPlus } from "react-icons/bs";
+
+export const WebhookHeaders = () => {
+ const { register } = useFormContext()
+
+ const { fields, append, remove } = useFieldArray({
+ name: 'webhook_headers'
+ });
+
+ return (
+
+
+
+
+ Headers
+ The headers will be sent with the request. You can add multiple headers.
+
+ append({ fieldname: '', key: '' })} variant="outline" style={{
+ width: 'fit-content',
+ }}>
+ Add
+
+
+
+
+
+ Key *
+ value
+
+
+
+
+ {fields.map((field, index) => (
+
+
+
+
+
+
+
+
+ remove(index)}>
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
new file mode 100644
index 000000000..5fea2baf0
--- /dev/null
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
@@ -0,0 +1,246 @@
+import { HelperText } from "@/components/common/Form";
+import { Webhook } from "@/types/Integrations/Webhook";
+import { Flex, Box, Heading, Table, TextFieldInput, IconButton, Button, Select, Tooltip, Text, Dialog, Badge } from "@radix-ui/themes";
+import { useCallback, useMemo, useState } from "react";
+import { Controller, useFieldArray, useFormContext } from "react-hook-form";
+import { BiInfoCircle, BiMinusCircle } from "react-icons/bi";
+import { BsPlus } from "react-icons/bs";
+import { FieldsData } from "./utils";
+import { DoctypeFieldList } from './utils'
+import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog";
+import { EditorContent, useEditor } from "@tiptap/react";
+import StarterKit from "@tiptap/starter-kit";
+import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
+import { CustomBlockquote } from "../../chat/ChatMessage/Renderers/TiptapRenderer/Blockquote";
+import { CustomBold } from "../../chat/ChatMessage/Renderers/TiptapRenderer/Bold";
+import { CustomItalic } from "../../chat/ChatMessage/Renderers/TiptapRenderer/Italic";
+import { CustomLink } from "../../chat/ChatMessage/Renderers/TiptapRenderer/Link";
+import { CustomUserMention } from "../../chat/ChatMessage/Renderers/TiptapRenderer/Mention";
+import { CustomUnderline } from "../../chat/ChatMessage/Renderers/TiptapRenderer/Underline";
+import { createLowlight, common } from "lowlight";
+import css from 'highlight.js/lib/languages/css'
+import js from 'highlight.js/lib/languages/javascript'
+import ts from 'highlight.js/lib/languages/typescript'
+import html from 'highlight.js/lib/languages/xml'
+import json from 'highlight.js/lib/languages/json'
+import python from 'highlight.js/lib/languages/python'
+const lowlight = createLowlight(common)
+
+lowlight.register('html', html)
+lowlight.register('css', css)
+lowlight.register('js', js)
+lowlight.register('ts', ts)
+lowlight.register('json', json)
+lowlight.register('python', python)
+
+export const WebhookData = () => {
+ const { register, control, watch, setValue, getValues } = useFormContext()
+
+ const { fields, append, remove } = useFieldArray({
+ name: 'webhook_data'
+ });
+
+ const webhookDoctype = watch('webhook_doctype')
+
+ const webhookDataFieldName = useMemo(() => {
+ return DoctypeFieldList?.find(field => field.doctype === webhookDoctype)?.fields || []
+ }, [webhookDoctype])
+
+ const getDoctypeField = useCallback((index: number) => {
+ const webhookData = getValues('webhook_data')
+ const fieldname = webhookData?.[index]?.fieldname
+ if (fieldname) {
+ return webhookDataFieldName?.find(field => field.fieldname === fieldname)
+ }
+ return undefined
+ }, [webhookDataFieldName])
+
+ const [fieldIndex, setFieldIndex] = useState(null)
+
+ const [open, setOpen] = useState(false);
+
+ const onClose = () => {
+ setOpen(false)
+ setFieldIndex(null)
+ }
+
+ return (
+
+
+
+
+ Response Data
+ The fields you want to return in the webhook response.
+
+ append({ fieldname: '', key: '' })} variant="outline" style={{
+ width: 'fit-content',
+ }}>
+ Add
+
+
+
+
+ Fieldname *
+ Key
+
+
+
+
+
+ {fields.map((field, index) => {
+ const fieldname = watch(`webhook_data.${index}.fieldname`)
+ return (
+
+
+ setValue(`webhook_data.${index}.key`, e.target.value)
+ }}
+ render={({ field }) => (
+ field.onChange(e)} >
+
+
+
+ Fieldname
+ {webhookDataFieldName.map((field, index) => (
+ {`${field.label} (${field.fieldtype})`}
+ ))}
+
+
+
+ )}
+ />
+
+
+
+
+
+
+
+ setFieldIndex(index)}
+ style={{
+ // @ts-ignore
+ '--icon-button-ghost-padding': '0',
+ height: 'var(--base-button-height)',
+ width: 'var(--base-button-height)',
+ }}
+ aria-label="Click to see field info"
+ title='See field info'
+ >
+
+
+
+
+ {fieldIndex !== null && }
+
+
+
+
+ remove(index)}>
+
+
+
+
+ )
+ })}
+
+
+
+
+ )
+}
+export const FieldInfoModal = ({ fieldIndex, doctype, onClose }: { fieldIndex: number, doctype: string, onClose: () => void }) => {
+
+ const { watch } = useFormContext()
+ const fieldData = useMemo(() => {
+ const fieldname = watch(`webhook_data.${fieldIndex}.fieldname`)
+ return DoctypeFieldList?.find(field => field.doctype === doctype)?.fields.find(field => field.fieldname === fieldname)
+ }, [fieldIndex, doctype])
+
+ const editor = useEditor({
+ content: fieldData?.example,
+ editable: false,
+ enableCoreExtensions: true,
+ extensions: [
+ StarterKit.configure({
+ heading: false,
+ codeBlock: false,
+ bold: false,
+ blockquote: false,
+ italic: false,
+ listItem: {
+ HTMLAttributes: {
+ class: 'ml-5 rt-Text rt-r-size-2'
+ }
+ },
+ paragraph: {
+ HTMLAttributes: {
+ class: 'rt-Text rt-r-size-2'
+ }
+ }
+ }),
+ CustomUnderline,
+ CodeBlockLowlight.configure({
+ lowlight
+ }),
+ CustomBlockquote,
+ CustomBold,
+ CustomUserMention,
+ CustomLink,
+ CustomItalic
+ // TODO: Add channel mention
+ // CustomChannelMention
+ ]
+ })
+
+
+ return (
+
+
+
+
+
+ {fieldData?.label}
+
+ {fieldData?.fieldtype}
+
+ {fieldData?.description}
+
+
+
+
+ Example:
+
+
+
+
+
+ Close
+
+
+
+ )
+
+}
\ No newline at end of file
diff --git a/raven-app/src/components/feature/integrations/webhooks/utils.ts b/raven-app/src/components/feature/integrations/webhooks/utils.ts
new file mode 100644
index 000000000..bbe528b6c
--- /dev/null
+++ b/raven-app/src/components/feature/integrations/webhooks/utils.ts
@@ -0,0 +1,367 @@
+
+export interface TriggerEventField {
+ key: string,
+ label: string,
+ doctype: string,
+ event: "after_insert" | "on_update" | "on_submit" | "on_cancel" | "on_trash" | "on_update_after_submit" | "on_change",
+}
+
+export const TriggerEvents: TriggerEventField[] = [
+ {
+ key: 'message_sent',
+ label: 'Message Sent',
+ doctype: 'Raven Message',
+ event: 'after_insert',
+ },
+ {
+ key: 'message_edited',
+ label: 'Message Edited',
+ doctype: 'Raven Message',
+ event: 'on_update'
+ },
+ {
+ key: 'message_deleted',
+ label: 'Message Deleted',
+ doctype: 'Raven Message',
+ event: 'on_trash'
+ },
+ {
+ key: 'emoji_reaction',
+ label: 'Message Reaction',
+ doctype: 'Raven Message Reaction',
+ event: 'after_insert'
+ },
+ {
+ key: 'channel_created',
+ label: 'Channel Created',
+ doctype: 'Raven Channel',
+ event: 'after_insert'
+ },
+ {
+ key: 'channel_deleted',
+ label: 'Channel Deleted',
+ doctype: 'Raven Channel',
+ event: 'on_trash'
+ },
+ {
+ key: 'channel_member_added',
+ label: 'Member Added to the Channel',
+ doctype: 'Raven Channel Member',
+ event: 'after_insert'
+ },
+ {
+ key: 'channel_member_deleted',
+ label: 'Member Deleted from the Channel',
+ doctype: 'Raven Channel Member',
+ event: 'on_trash'
+ },
+ {
+ key: 'raven_user_added',
+ label: 'Raven User Added',
+ doctype: 'Raven User',
+ event: 'after_insert'
+ },
+ {
+ key: 'raven_user_deleted',
+ label: 'Raven User Deleted',
+ doctype: 'Raven User',
+ event: 'on_trash'
+ }
+]
+
+export interface FieldsData {
+ fieldname: string,
+ label: string,
+ fieldtype?: string,
+ description?: string,
+ example?: string,
+}
+
+const commonFields = [
+ {
+ fieldname: 'name',
+ label: 'ID',
+ fieldtype: 'Data',
+ description: 'ID of the document'
+ },
+ {
+ fieldname: 'creation',
+ label: 'Creation Time',
+ fieldtype: 'DateTime',
+ description: `Time when the document was created in string format.`,
+ example: '2021-08-12 12:00:00'
+ },
+ {
+ fieldname: 'modified',
+ label: 'Last Updated Time',
+ fieldtype: 'DateTime',
+ description: `Time when the document was last updated in string format.`,
+ example: '2021-08-12 12:00:00'
+ },
+ {
+ fieldname: 'modified_by',
+ label: 'Last Updated By',
+ fieldtype: 'Data',
+ description: 'User ID of the person who last updated the document.',
+ example: 'Administrator'
+ },
+ {
+ fieldname: 'owner',
+ label: 'Document Created By',
+ fieldtype: 'Data',
+ description: 'User ID of the person who created the document.',
+ example: 'Administrator'
+ },
+]
+
+export const DoctypeFieldList: {
+ doctype: 'Raven Message' | 'Raven Channel' | 'Raven Channel Member' | 'Raven User' | 'Raven Message Reaction',
+ fields: FieldsData[]
+}[] = [
+ {
+ doctype: 'Raven Message',
+ fields: [
+ {
+ fieldname: 'channel_id',
+ label: 'Channel ID',
+ fieldtype: 'Link',
+ description: 'ID of the channel where the message was sent.',
+ example: 'general'
+ },
+ {
+ fieldname: 'text',
+ label: 'Text',
+ fieldtype: 'Long Text',
+ description: 'Text of the message, in pure html format.',
+ example: 'Hello, World!
'
+ },
+ {
+ fieldname: 'json',
+ label: 'JSON',
+ fieldtype: 'JSON',
+ description: 'JSON data of the message.',
+ example: `{
+ content: [
+ {
+ content: [
+ {
+ text: "Hello, World!",
+ type: "text"
+ }
+ ],
+ type: "paragraph"
+ }
+ ],
+ type: "doc"
+ }`
+ },
+ {
+ fieldname: 'message_type',
+ label: 'Message Type',
+ fieldtype: 'Select',
+ description: 'Type of the message.',
+ example: 'Text or Image or File'
+ },
+ {
+ fieldname: 'file',
+ label: 'File',
+ fieldtype: 'Attach',
+ description: 'File attached with the message.',
+ example: 'https://example.com/file.pdf'
+ },
+ {
+ fieldname: 'message_reactions',
+ label: 'Message Reactions',
+ fieldtype: 'JSON',
+ description: 'Reactions on the message.',
+ example: `{
+ 'unicode_string 1':{
+ 'count': 1,
+ 'users':['user1'],
+ 'reaction': 'unicode_string 1'
+
+ },
+ 'unicode_string 2':{
+ 'count': 2,
+ 'users':['user1', 'user2'],
+ 'reaction': 'unicode_string 2'
+ }
+ }`
+ },
+ {
+ fieldname: 'is_reply',
+ label: 'Is Reply',
+ fieldtype: 'Check',
+ description: 'Whether the message is a reply to another message.',
+ example: '1 or 0'
+ },
+ {
+ fieldname: 'linked_message',
+ label: 'Linked Message',
+ fieldtype: 'Link',
+ description: 'ID of the message to which this message is a reply.',
+ example: 'message-id'
+ },
+
+ {
+ fieldname: 'content',
+ label: 'Content',
+ fieldtype: 'Long Text',
+ description: 'Content of the message in plain text.',
+ example: 'Hello, World!'
+ },
+ ...commonFields
+ ]
+ },
+ {
+ doctype: 'Raven Channel',
+ fields: [
+ {
+ fieldname: 'channel_name',
+ label: 'Channel Name',
+ fieldtype: 'Data',
+ description: 'Name of the channel.',
+ example: 'general'
+ },
+ {
+ fieldname: 'channel_description',
+ label: 'Channel Description',
+ fieldtype: 'Data',
+ description: 'Description of the channel.',
+ example: 'General discussion'
+ },
+ {
+ fieldname: 'type',
+ label: 'Type',
+ fieldtype: 'Select',
+ description: 'Type of the channel.',
+ example: 'Public or Private or Open'
+ },
+ {
+ fieldname: 'is_direct_message',
+ label: 'Is Direct Message',
+ fieldtype: 'Check',
+ description: 'Whether the channel is a direct message channel.',
+ example: '1 or 0'
+ },
+ {
+ fieldname: 'is_self_message',
+ label: 'Is Self Message',
+ fieldtype: 'Check',
+ description: 'Whether the channel is a self message channel.',
+ example: '1 or 0'
+ },
+ {
+ fieldname: 'is_archived',
+ label: 'Is Archived',
+ fieldtype: 'Check',
+ description: 'Whether the channel is archived.',
+ example: '1 or 0'
+ },
+ ...commonFields
+
+ ]
+ },
+ {
+ 'doctype': 'Raven Channel Member',
+ fields: [
+ {
+ fieldname: 'channel_id',
+ label: 'Channel ID',
+ fieldtype: 'Link',
+ description: 'ID of the channel.',
+ example: 'general'
+ },
+ {
+ fieldname: 'user_id',
+ label: 'User ID',
+ fieldtype: 'Link',
+ description: 'ID of the user.',
+ example: 'user1'
+ },
+ {
+ fieldname: 'is_admin',
+ label: 'Is Admin',
+ fieldtype: 'Check',
+ description: 'Whether the user is an admin of the channel.',
+ example: '1 or 0'
+ },
+ {
+ fieldname: 'last_visit',
+ label: 'Last Visit',
+ fieldtype: 'Datetime',
+ description: 'Time when the user last visited the channel.',
+ example: '2021-08-12 12:00:00'
+ },
+ ...commonFields
+ ]
+ },
+ {
+ doctype: 'Raven User',
+ fields: [
+ {
+ fieldname: 'user',
+ label: 'User',
+ fieldtype: 'Link',
+ description: 'User ID.',
+ example: 'user1'
+ },
+ {
+ fieldname: 'full_name',
+ label: 'Full Name',
+ fieldtype: 'Data',
+ description: 'Full name of the user.',
+ example: 'John Doe'
+ },
+ {
+ fieldname: 'first_name',
+ label: 'First Name',
+ fieldtype: 'Data',
+ description: 'First name of the user.',
+ example: 'John'
+ },
+ {
+ fieldname: 'user_image',
+ label: 'User Image',
+ fieldtype: 'Attach Image',
+ description: 'Image of the user.',
+ example: 'https://example.com/image.jpg'
+ },
+ {
+ fieldname: 'enabled',
+ label: 'Enabled',
+ fieldtype: 'Check',
+ description: 'Whether the user is enabled.',
+ example: '1 or 0'
+ },
+ ...commonFields
+ ]
+ },
+ {
+ doctype: 'Raven Message Reaction',
+ fields: [
+ {
+ fieldname: 'reaction',
+ label: 'Reaction',
+ fieldtype: 'Data',
+ description: 'Reaction emoji.',
+ example: '👍'
+ },
+ {
+ fieldname: 'reaction_escaped',
+ label: 'Reaction Escaped',
+ fieldtype: 'Data',
+ description: 'Escaped reaction emoji.',
+ example: '\\ud83d\\udc4d'
+ },
+ {
+ fieldname: 'message',
+ label: 'Message',
+ fieldtype: 'Link',
+ description: 'ID of the message.',
+ example: 'message-id'
+ },
+ ...commonFields
+ ]
+ }
+ ]
\ No newline at end of file
From 73fac17c5fc8e020a7f16b21dc04133d61c0199b Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Mon, 12 Feb 2024 12:40:51 +0530
Subject: [PATCH 10/32] feat:webhook list
---
.../integrations/webhooks/WebhookList.tsx | 173 ++++++++++++++++++
1 file changed, 173 insertions(+)
create mode 100644 raven-app/src/components/feature/integrations/webhooks/WebhookList.tsx
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookList.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookList.tsx
new file mode 100644
index 000000000..fee4779f9
--- /dev/null
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookList.tsx
@@ -0,0 +1,173 @@
+import { HelperText } from "@/components/common/Form"
+import { Loader } from "@/components/common/Loader"
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { FullPageLoader } from "@/components/layout/Loaders"
+import { useToast } from "@/hooks/useToast"
+import { Webhook } from "@/types/Integrations/Webhook"
+import { DateMonthYear } from "@/utils/dateConversions"
+import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog"
+import { Flex, Separator, Button, Text, Badge, IconButton, AlertDialog } from "@radix-ui/themes"
+import { useFrappeDeleteDoc, useFrappeGetDocList } from "frappe-react-sdk"
+import { useState } from "react"
+import { BiEdit, BiTrash } from "react-icons/bi"
+import { Link, useNavigate } from "react-router-dom"
+
+export const WebhookList = () => {
+
+ const { data, error, isLoading, mutate } = useFrappeGetDocList('Webhook', {
+ fields: ['name', 'request_url', 'enabled', 'owner', 'creation']
+ })
+
+ const navigate = useNavigate()
+
+ return (
+
+
+
+
+
+ Webhooks allow you to receive HTTP requests whenever an entity is created, updated, or deleted.
+
+
+ eg. User can create Webhook on Message Send, Delete, Edit.
+
+
+
+ {isLoading && }
+ {data?.length === 0 ? No webhooks found. Create new webhook by
+ click here.
+ :
+ {data?.map((webhook, index) => (
+
+ ))}
+ }
+ navigate('./create')} variant='solid' style={{
+ width: 'fit-content',
+ marginTop: '1rem'
+ }} >
+ New Webhook
+
+
+
+
+ )
+}
+
+export const WebhookItem = ({ webhook, mutate }: { webhook: Webhook, mutate: () => void }) => {
+
+ const navigate = useNavigate()
+
+ const [open, setOpen] = useState(false)
+ const onClose = () => {
+ setOpen(false)
+ }
+
+ return (
+
+
+
+
+ {webhook.name}
+ {webhook.enabled ? 'Enabled' : 'Disabled'}
+
+ Created by {webhook.owner} on
+
+
+ navigate(`./${webhook.name}`)}
+ style={{
+ // @ts-ignore
+ '--icon-button-ghost-padding': '0',
+ height: 'var(--base-button-height)',
+ width: 'var(--base-button-height)',
+ }}>
+
+
+
+
+ { }}
+ style={{
+ // @ts-ignore
+ '--icon-button-ghost-padding': '0',
+ height: 'var(--base-button-height)',
+ width: 'var(--base-button-height)',
+ }}>
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export const DeleteWebhookAlertContent = ({ webhhookID, onClose, mutate }: { webhhookID: string, onClose: () => void, mutate: () => void }) => {
+
+ const { deleteDoc, error, loading } = useFrappeDeleteDoc()
+
+ const { toast } = useToast()
+
+ const onDelete = () => {
+ deleteDoc('Webhook', webhhookID).then(() => {
+ mutate()
+ onClose()
+ toast({
+ title: "Webhook deleted successfully.",
+ variant: 'success',
+ })
+ })
+ }
+
+ return (
+ <>
+
+ {webhhookID}
+
+
+
+ Are you sure you want to delete this webhook?
+
+
+
+
+ Cancel
+
+
+
+
+ {loading && }
+ {loading ? "Deleting" : `Delete`}
+
+
+
+ >
+ )
+
+}
\ No newline at end of file
From 68aceb66605086f09c58879334fb33d3012e6787 Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Mon, 12 Feb 2024 12:41:05 +0530
Subject: [PATCH 11/32] feat:create and view webhook
---
.../integrations/webhooks/CreateWebhook.tsx | 94 ++++++++++
.../integrations/webhooks/ViewWebhook.tsx | 166 ++++++++++++++++++
2 files changed, 260 insertions(+)
create mode 100644 raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx
create mode 100644 raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
diff --git a/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx b/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx
new file mode 100644
index 000000000..9bf2f0b9b
--- /dev/null
+++ b/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx
@@ -0,0 +1,94 @@
+import { Webhook } from '@/types/Integrations/Webhook'
+import { useFrappeCreateDoc } from 'frappe-react-sdk'
+import { FormProvider, useForm } from 'react-hook-form'
+import { useNavigate } from 'react-router-dom'
+import { Button, Flex, Separator, Text } from '@radix-ui/themes'
+import { BiChevronLeft } from 'react-icons/bi'
+import { ErrorBanner } from '@/components/layout/AlertBanner'
+import { WebhookForm, WebhookFormField } from './WebhookForm'
+import { useToast } from '@/hooks/useToast'
+
+export const CreateWebhook = () => {
+
+ const navigate = useNavigate()
+
+ const methods = useForm({
+ defaultValues: {
+ webhook_docevent: 'after_insert',
+ timeout: 5,
+ request_method: 'POST',
+ enabled: 1,
+ request_structure: 'Form URL-Encoded',
+ need_condition: false,
+ }
+ })
+ const { createDoc, loading, reset, error } = useFrappeCreateDoc()
+
+ const { toast } = useToast()
+
+ const onSubmit = (data: Webhook) => {
+ createDoc('Webhook', data)
+ .then((doc) => {
+ reset()
+ methods.reset()
+ toast({
+ title: "Webhook created successfully.",
+ variant: 'success',
+ })
+ return doc
+ }).then((doc) => {
+ navigate(`../webhooks/${doc.name}`)
+ })
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ Create Webhook
+
+
+
+
+
+ )
+}
+
+export const BackToList = () => {
+
+ const navigate = useNavigate()
+
+ return (
+
+ navigate('/settings/integrations/webhooks')}
+ >
+
+ Back to the list
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
new file mode 100644
index 000000000..e26d17542
--- /dev/null
+++ b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
@@ -0,0 +1,166 @@
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { FullPageLoader } from "@/components/layout/Loaders"
+import { Webhook } from "@/types/Integrations/Webhook"
+import { removeFrappeFields } from "@/utils/removeFrappeFields"
+import { AlertDialog, Box, Button, DropdownMenu, Flex, IconButton, Separator, Text } from "@radix-ui/themes"
+import { FrappeDoc, useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
+import { FieldValues, FormProvider, useForm } from "react-hook-form"
+import { useParams } from "react-router-dom"
+import { KeyedMutator } from 'swr'
+import { BackToList } from "./CreateWebhook"
+import { WebhookForm, WebhookFormField } from "./WebhookForm"
+import { useToast } from "@/hooks/useToast"
+import { BiDotsVerticalRounded } from "react-icons/bi"
+import { useState } from "react"
+import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog"
+import { Loader } from "@/components/common/Loader"
+
+const ViewWebhook = () => {
+
+ const { ID } = useParams<{ ID: string }>()
+ const { data, error, isLoading, mutate } = useFrappeGetDoc('Webhook', ID, undefined, {
+ shouldRetryOnError: false,
+ })
+
+ return (
+
+ {isLoading && }
+ {error && }
+ {data && }
+
+
+ )
+}
+
+export default ViewWebhook
+ViewWebhook.displayName = 'ViewWebhook'
+
+export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc, mutate: KeyedMutator> }) => {
+
+ const formFields = removeFrappeFields(data)
+ const methods = useForm({
+ defaultValues: {
+ ...formFields,
+ need_condition: data.condition ? true : false,
+ docstatus: data.docstatus
+ }
+ })
+
+ const { updateDoc, loading, reset, error } = useFrappeUpdateDoc()
+
+ const { toast } = useToast()
+
+ const isDirty = methods.formState.isDirty
+
+ const onSubmit = async (data: FieldValues) => {
+ return updateDoc('Webhook', data.name, data)
+ .then((doc) => {
+ toast({
+ title: "Webhook updated successfully.",
+ variant: 'success',
+ })
+ reset()
+ mutate()
+ if (doc) {
+ const formFields = removeFrappeFields(doc)
+ methods.reset({
+ ...formFields,
+ need_condition: doc.condition ? true : false,
+ docstatus: doc.docstatus
+ })
+ }
+ })
+ }
+
+ const [open, setOpen] = useState(false)
+ const onClose = () => {
+ setOpen(false)
+ }
+
+ const onUpdateEnabled = () => {
+ updateDoc('Webhook', data.name, {
+ enabled: !data.enabled
+ }).then((doc) => {
+ toast({
+ title: "Webhook updated successfully.",
+ variant: 'success',
+ })
+ reset()
+ mutate()
+ })
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setOpen(true)} className="cursor-pointer">
+ {data.enabled ? 'Disable' : 'Enable'} Webhook
+
+
+
+
+
+
+
+
+
+
+ Save Webhook
+
+
+
+
+
+
+ {data.enabled ? 'Disable' : 'Enable'} Webhook
+
+
+
+ Are you sure you want to {data.enabled ? 'disable' : 'enable'} this webhook?
+
+
+
+
+ Cancel
+
+
+
+
+ {loading && }
+ {loading ? "Updating" : data.enabled ? "Disable" : "Enable"}
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
From 9743a3f6ea68af7cb4dc0214e5bdf7927336acaa Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Mon, 12 Feb 2024 23:48:06 +0530
Subject: [PATCH 12/32] feat: add routes and links
---
raven-app/src/App.tsx | 30 ++++++++++++++++---
.../feature/settings/Integrations.tsx | 4 ++-
2 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/raven-app/src/App.tsx b/raven-app/src/App.tsx
index 1c1b72b25..148e84c35 100644
--- a/raven-app/src/App.tsx
+++ b/raven-app/src/App.tsx
@@ -1,5 +1,5 @@
import { FrappeProvider } from 'frappe-react-sdk'
-import { Route, RouterProvider, createBrowserRouter, createRoutesFromElements } from 'react-router-dom'
+import { Outlet, Route, RouterProvider, createBrowserRouter, createRoutesFromElements } from 'react-router-dom'
import { MainPage } from './pages/MainPage'
import { ProtectedRoute } from './utils/auth/ProtectedRoute'
import { UserProvider } from './utils/auth/UserProvider'
@@ -10,6 +10,15 @@ import { Toaster } from './components/common/Toast/Toaster'
import { FullPageLoader } from './components/layout/Loaders'
import { useStickyState } from './hooks/useStickyState'
import { Settings } from './pages/settings/Settings'
+import { DocTypeEvents } from './pages/settings/ServerScripts/DocTypeEvents'
+import { CreateDocTypeEvent } from './pages/settings/ServerScripts/CreateDocTypeEvent'
+import { ViewDocTypeEvent } from './pages/settings/ServerScripts/ViewDocTypeEvent'
+import { APIEvents } from './pages/settings/ServerScripts/APIEvents/APIEvents'
+import { CreateAPIEvent } from './pages/settings/ServerScripts/APIEvents/CreateAPIEvents'
+import { ViewAPIEvent } from './pages/settings/ServerScripts/APIEvents/ViewAPIEvents'
+import { TemporalEvents } from './pages/settings/ServerScripts/TemporalEvents/TemporalEvents'
+import { ViewTemporalEvent } from './pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent'
+import { CreateTemporalEvent } from './pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent'
const router = createBrowserRouter(
@@ -25,11 +34,24 @@ const router = createBrowserRouter(
}>
- Webhooks} />
- SS} />
+ }>
+ } />
+ } />
+ } />
+
+ }>
+ } />
+ } />
+ } />
+
+ }>
+ } />
+ } />
+ } />
+
-
+
>
), {
basename: `/${import.meta.env.VITE_BASE_NAME}` ?? '',
diff --git a/raven-app/src/components/feature/settings/Integrations.tsx b/raven-app/src/components/feature/settings/Integrations.tsx
index 3444967bd..1ee4c9d8e 100644
--- a/raven-app/src/components/feature/settings/Integrations.tsx
+++ b/raven-app/src/components/feature/settings/Integrations.tsx
@@ -19,7 +19,9 @@ export const Integrations = (props: Props) => {
-
+
+
+
From 84d8ecfb97ad5f299f31de8c28248961469d8ba4 Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Mon, 12 Feb 2024 23:48:30 +0530
Subject: [PATCH 13/32] feat: common delete Alert for Integration pages
---
.../feature/settings/common/DeleteAlert.tsx | 109 ++++++++++++++++++
1 file changed, 109 insertions(+)
create mode 100644 raven-app/src/components/feature/settings/common/DeleteAlert.tsx
diff --git a/raven-app/src/components/feature/settings/common/DeleteAlert.tsx b/raven-app/src/components/feature/settings/common/DeleteAlert.tsx
new file mode 100644
index 000000000..80c5c49b6
--- /dev/null
+++ b/raven-app/src/components/feature/settings/common/DeleteAlert.tsx
@@ -0,0 +1,109 @@
+import { Loader } from "@/components/common/Loader"
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { useToast } from "@/hooks/useToast"
+import { AlertDialog, Button, Callout, Checkbox, Flex, Text } from "@radix-ui/themes"
+import { useFrappeDeleteDoc } from "frappe-react-sdk"
+import { useState } from "react"
+import { FiAlertTriangle } from "react-icons/fi"
+import { useNavigate } from "react-router-dom"
+
+export interface Props {
+ isOpen: boolean,
+ onClose: () => void
+ docname: string
+ path: string
+}
+
+export const DeleteAlert = ({ isOpen, onClose, docname, path }: Props) => {
+ return (
+
+
+ {/* Hii */}
+
+
+
+ )
+}
+
+
+type DeleteDocModalProps = {
+ onClose: () => void,
+ docname: string
+ path: string
+}
+
+const AlertContent = ({ onClose, docname, path }: DeleteDocModalProps) => {
+
+ const { deleteDoc, error, loading: deletingDoc, reset } = useFrappeDeleteDoc()
+
+ const handleClose = () => {
+ onClose()
+ reset()
+ }
+
+ const { toast } = useToast()
+ const navigate = useNavigate()
+
+ const onSubmit = () => {
+ if (docname) {
+ deleteDoc('Server Script', docname)
+ .then(() => {
+ onClose()
+ navigate(path)
+ toast({
+ title: `${docname} deleted`,
+ variant: 'success',
+ })
+ })
+ }
+ }
+
+ const [allowDelete, setAllowDelete] = useState(false)
+
+ return (
+ <>
+
+ Delete {docname}?
+
+
+
+
+
+
+
+
+
+ This action is permanent and cannot be undone.
+
+
+ {/* When you delete a channel, all messages from this channel will be removed immediately. */}
+ {/*
+
+ All messages, including files and images will be removed
+ You can archive this channel instead to preserve your messages
+
+ */}
+
+
+ setAllowDelete(!allowDelete)} color='red' />
+ Yes, I understand, permanently delete this channel
+
+
+
+
+
+
+
+ Cancel
+
+
+
+
+ {deletingDoc && }
+ {deletingDoc ? "Deleting" : "Delete"}
+
+
+
+ >
+ )
+}
\ No newline at end of file
From 53e7384ef6581dfb75070c24490db48577f070b2 Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Mon, 12 Feb 2024 23:48:58 +0530
Subject: [PATCH 14/32] feat: API events pages
---
.../settings/api-events/APIEventsForm.tsx | 105 +++++++++++++++
.../ServerScripts/APIEvents/APIEvents.tsx | 75 +++++++++++
.../APIEvents/CreateAPIEvents.tsx | 63 +++++++++
.../ServerScripts/APIEvents/ViewAPIEvents.tsx | 127 ++++++++++++++++++
4 files changed, 370 insertions(+)
create mode 100644 raven-app/src/components/feature/settings/api-events/APIEventsForm.tsx
create mode 100644 raven-app/src/pages/settings/ServerScripts/APIEvents/APIEvents.tsx
create mode 100644 raven-app/src/pages/settings/ServerScripts/APIEvents/CreateAPIEvents.tsx
create mode 100644 raven-app/src/pages/settings/ServerScripts/APIEvents/ViewAPIEvents.tsx
diff --git a/raven-app/src/components/feature/settings/api-events/APIEventsForm.tsx b/raven-app/src/components/feature/settings/api-events/APIEventsForm.tsx
new file mode 100644
index 000000000..cbd8624b7
--- /dev/null
+++ b/raven-app/src/components/feature/settings/api-events/APIEventsForm.tsx
@@ -0,0 +1,105 @@
+import { Label, HelperText } from "@/components/common/Form"
+import { Flex, Box, TextField, TextArea, Checkbox, Grid, Code } from "@radix-ui/themes"
+import { Controller, useFormContext } from "react-hook-form"
+
+export interface Props {
+ edit?: boolean
+}
+
+export const APIEventsForm = ({ edit = false }: Props) => {
+
+ const { register, watch, control } = useFormContext()
+
+ const enableRateLimit = watch('enable_rate_limit')
+
+ return (
+
+ {!edit &&
+ Name
+
+ }
+
+
+ API Method
+
+ The API endpoint for which this event will be triggered (automatically prefixed with /api/method
).
+
+
+
+ (
+
+ field.onChange(!field.value)} />
+ Allow anyone to use without logging in
+
+ )}
+ />
+
+
+
+ {/* TODO: Add a script editor here (maybe use Monaco Editor) */}
+ Script
+
+ Your custom script to be be called via this API event.
+
+
+
+ (
+
+ field.onChange(!field.value)} />
+ Enable Rate Limit
+
+ )}
+ />
+
+
+ {enableRateLimit ?
+
+ Rate Limit Count
+
+ Number of requests allowed in the specified time.
+
+
+
+ Rate Limit Seconds
+
+ Time period in seconds.
+
+ : null}
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/APIEvents/APIEvents.tsx b/raven-app/src/pages/settings/ServerScripts/APIEvents/APIEvents.tsx
new file mode 100644
index 000000000..8bb7746f7
--- /dev/null
+++ b/raven-app/src/pages/settings/ServerScripts/APIEvents/APIEvents.tsx
@@ -0,0 +1,75 @@
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { DateMonthAtHourMinuteAmPm } from "@/utils/dateConversions"
+import { Badge, Blockquote, Box, Button, Card, Code, Flex, Heading, Section, Table, Text } from "@radix-ui/themes"
+import { useFrappeGetDocList } from "frappe-react-sdk"
+import { Link, useNavigate } from "react-router-dom"
+
+export interface Props { }
+
+export const APIEvents = (props: Props) => {
+
+ const navigate = useNavigate()
+
+ const { data, error } = useFrappeGetDocList('Server Script', {
+ fields: ['name', 'script_type', 'api_method', 'creation', 'modified', 'disabled'],
+ filters: [['script_type', '=', 'API']],
+ })
+
+ return (
+
+
+ API Events
+ navigate('create')}>Add Event
+
+
+
+ Lets say you want to perform some actions when an API Endpoint (method) is called.
+ API Events enables you to write custom scripts that are triggered by events in an API Endpoint.
+
+
+
+ {error && }
+ {data &&
}
+
+
+ )
+}
+
+
+const List = ({ data }: { data: any }) => {
+ return (
+
+
+
+
+
+ ID
+ Status
+ Endpoint
+ Last modified
+
+
+
+
+ {data?.map((item: any) => (
+
+
+ {item.name}
+
+
+
+ {item.disabled ? "Disabled" : "Enabled"}
+
+
+ {item.api_method}
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/APIEvents/CreateAPIEvents.tsx b/raven-app/src/pages/settings/ServerScripts/APIEvents/CreateAPIEvents.tsx
new file mode 100644
index 000000000..d13632a98
--- /dev/null
+++ b/raven-app/src/pages/settings/ServerScripts/APIEvents/CreateAPIEvents.tsx
@@ -0,0 +1,63 @@
+import { APIEventsForm } from "@/components/feature/settings/api-events/APIEventsForm"
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { useToast } from "@/hooks/useToast"
+import { Box, Button, Flex, Heading, Section } from "@radix-ui/themes"
+import { useFrappeCreateDoc } from "frappe-react-sdk"
+import { FormProvider, useForm } from "react-hook-form"
+import { FiArrowLeft } from "react-icons/fi"
+import { useNavigate } from "react-router-dom"
+
+export interface Props { }
+
+export const CreateAPIEvent = (props: Props) => {
+
+ const navigate = useNavigate()
+
+ const methods = useForm()
+
+ const { createDoc, error } = useFrappeCreateDoc()
+
+ const { toast } = useToast()
+
+ const onSubmit = (data: any) => {
+ createDoc('Server Script', {
+ name: data.name,
+ script_type: 'API',
+ api_method: data.api_method,
+ allow_guest: data.allow_guest ?? false,
+ script: data.script,
+ enable_rate_limit: data.enable_rate_limit ?? false,
+ rate_limit_count: data.rate_limit_count,
+ rate_limit_seconds: data.rate_limit_seconds,
+ })
+ .then((doc) => {
+ if (doc) {
+ navigate(`../${doc?.name}`)
+ }
+ toast({
+ title: `API Event ${data.name} updated`,
+ variant: 'success',
+ })
+ })
+ }
+ //TODO: Figure out a way to show _server_messages in the UI (especially the script editor might have some errors that we need to show to the user)
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/APIEvents/ViewAPIEvents.tsx b/raven-app/src/pages/settings/ServerScripts/APIEvents/ViewAPIEvents.tsx
new file mode 100644
index 000000000..db572b290
--- /dev/null
+++ b/raven-app/src/pages/settings/ServerScripts/APIEvents/ViewAPIEvents.tsx
@@ -0,0 +1,127 @@
+import { APIEventsForm } from "@/components/feature/settings/api-events/APIEventsForm"
+import { DeleteAlert } from "@/components/feature/settings/common/DeleteAlert"
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { useToast } from "@/hooks/useToast"
+import { Badge, Box, Button, DropdownMenu, Flex, Heading, Section } from "@radix-ui/themes"
+import { useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
+import { useState } from "react"
+import { FormProvider, useForm } from "react-hook-form"
+import { FiArrowLeft } from "react-icons/fi"
+import { useNavigate, useParams } from "react-router-dom"
+
+export interface Props { }
+
+export const ViewAPIEvent = (props: Props) => {
+
+ const { apiID } = useParams<{ apiID: string }>()
+
+ const { data: docEventData, error, mutate } = useFrappeGetDoc('Server Script', apiID)
+
+ return (
+ <>
+ {error && }
+ {docEventData && }
+ >
+ )
+}
+
+
+const ViewAPIEventPage = ({ data, onUpdate }: { data: any, onUpdate: () => void }) => {
+
+ const [isOpen, setIsOpen] = useState(false)
+
+ const navigate = useNavigate()
+
+ const methods = useForm({
+ defaultValues: {
+ name: data.name,
+ api_method: data.api_method,
+ allow_guest: data.allow_guest ?? false,
+ script: data.script,
+ enable_rate_limit: data.enable_rate_limit,
+ rate_limit_count: data.rate_limit_count ?? 5,
+ rate_limit_seconds: data.rate_limit_seconds ?? 86400,
+ }
+ })
+
+ const { updateDoc, error } = useFrappeUpdateDoc()
+
+ const { toast } = useToast()
+
+ const onSubmit = (data: any) => {
+ console.log(data)
+ updateDoc('Server Script', data.name, {
+ api_method: data.api_method,
+ allow_guest: data.allow_guest,
+ script: data.script,
+ enable_rate_limit: data.enable_rate_limit,
+ rate_limit_count: data.rate_limit_count,
+ rate_limit_seconds: data.rate_limit_seconds,
+ })
+ .then(() => {
+ onUpdate()
+ toast({
+ title: `API Event ${data.name} updated`,
+ variant: 'success',
+ })
+ })
+ }
+
+ const onStatusToggle = () => {
+ updateDoc('Server Script', data.name, {
+ disabled: !data.disabled
+ })
+ .then(() => {
+ onUpdate()
+ toast({
+ title: `API Event ${data.name} ${data.disabled ? "enabled" : "disabled"}`,
+ variant: 'success',
+ })
+ })
+ }
+
+ const onClose = () => {
+ setIsOpen(false)
+ }
+
+ return (
+
+
+
+ )
+}
\ No newline at end of file
From ab5c0494660aae0dff9d59ed633f4eed73a491a9 Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Mon, 12 Feb 2024 23:49:17 +0530
Subject: [PATCH 15/32] feat: DocType Events pages
---
.../doctype-events/DocTypeEventsForm.tsx | 81 ++++++++++++
.../ServerScripts/CreateDocTypeEvent.tsx | 60 +++++++++
.../settings/ServerScripts/DocTypeEvents.tsx | 73 +++++++++++
.../ServerScripts/ViewDocTypeEvent.tsx | 120 ++++++++++++++++++
4 files changed, 334 insertions(+)
create mode 100644 raven-app/src/components/feature/settings/doctype-events/DocTypeEventsForm.tsx
create mode 100644 raven-app/src/pages/settings/ServerScripts/CreateDocTypeEvent.tsx
create mode 100644 raven-app/src/pages/settings/ServerScripts/DocTypeEvents.tsx
create mode 100644 raven-app/src/pages/settings/ServerScripts/ViewDocTypeEvent.tsx
diff --git a/raven-app/src/components/feature/settings/doctype-events/DocTypeEventsForm.tsx b/raven-app/src/components/feature/settings/doctype-events/DocTypeEventsForm.tsx
new file mode 100644
index 000000000..88267c433
--- /dev/null
+++ b/raven-app/src/components/feature/settings/doctype-events/DocTypeEventsForm.tsx
@@ -0,0 +1,81 @@
+import { HelperText, Label } from "@/components/common/Form"
+import { Box, Flex, Grid, Link, Select, TextArea, TextField } from "@radix-ui/themes"
+import { useFormContext, Controller } from "react-hook-form"
+
+export interface Props {
+ edit?: boolean
+}
+
+export const DocTypeEventsForm = ({ edit = false }: Props) => {
+
+ const { register, control } = useFormContext()
+
+ return (
+
+ {!edit &&
+ Name
+
+ }
+
+
+
+ Reference Document Type
+
+ The event will be triggered for this DocType.
+
+
+
+ DocType Event
+ (
+ field.onChange(value)} defaultValue="Before Insert">
+
+
+
+ DocType Event
+ Before Insert
+ Before Validate
+ Before Save
+ After Insert
+ After Save
+ Before Submit
+ After Submit
+ Before Cancel
+ After Cancel
+ Before Save (Submitted Documents)
+ After Save (Submitted Documents)
+ On Payment Authorization
+
+
+
+ )}
+ />
+ The event on which you want to run this script. Learn more
+
+
+
+
+ {/* TODO: Add a script editor here (maybe use Monaco Editor) */}
+ Script
+
+ Your custom script to be be called via this document event.
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/CreateDocTypeEvent.tsx b/raven-app/src/pages/settings/ServerScripts/CreateDocTypeEvent.tsx
new file mode 100644
index 000000000..2c0466ba3
--- /dev/null
+++ b/raven-app/src/pages/settings/ServerScripts/CreateDocTypeEvent.tsx
@@ -0,0 +1,60 @@
+import { DocTypeEventsForm } from "@/components/feature/settings/doctype-events/DocTypeEventsForm"
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { useToast } from "@/hooks/useToast"
+import { Box, Button, Flex, Heading, Section } from "@radix-ui/themes"
+import { useFrappeCreateDoc } from "frappe-react-sdk"
+import { FormProvider, useForm } from "react-hook-form"
+import { FiArrowLeft } from "react-icons/fi"
+import { useNavigate } from "react-router-dom"
+
+export interface Props { }
+
+export const CreateDocTypeEvent = (props: Props) => {
+
+ const navigate = useNavigate()
+
+ const methods = useForm()
+
+ const { createDoc, error } = useFrappeCreateDoc()
+
+ const { toast } = useToast()
+
+ const onSubmit = (data: any) => {
+ createDoc('Server Script', {
+ name: data.name,
+ script_type: 'DocType Event',
+ reference_doctype: data.reference_document_type,
+ doctype_event: data.document_event,
+ script: data.script,
+ })
+ .then((doc) => {
+ if (doc) {
+ navigate(`../${doc?.name}`)
+ }
+ toast({
+ title: `DocType Event ${data.name} updated`,
+ variant: 'success',
+ })
+ })
+ }
+ //TODO: Figure out a way to show _server_messages in the UI (especially the script editor might have some errors that we need to show to the user)
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/DocTypeEvents.tsx b/raven-app/src/pages/settings/ServerScripts/DocTypeEvents.tsx
new file mode 100644
index 000000000..a562f10e4
--- /dev/null
+++ b/raven-app/src/pages/settings/ServerScripts/DocTypeEvents.tsx
@@ -0,0 +1,73 @@
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { DateMonthAtHourMinuteAmPm } from "@/utils/dateConversions"
+import { Badge, Blockquote, Box, Button, Card, Flex, Heading, Section, Table, Text } from "@radix-ui/themes"
+import { useFrappeGetDocList } from "frappe-react-sdk"
+import { Link, useNavigate } from "react-router-dom"
+
+export interface Props { }
+
+export const DocTypeEvents = (props: Props) => {
+
+ const navigate = useNavigate()
+
+ const { data, error } = useFrappeGetDocList('Server Script', {
+ fields: ['name', 'script_type', 'reference_doctype', 'creation', 'modified', 'disabled'],
+ filters: [['script_type', '=', 'DocType Event']],
+ })
+
+ return (
+
+
+ DocType Events
+ navigate('create')}>Add Event
+
+
+
+ You might want to perform some actions when a Sales Invoice is saved or a Purchase Order is submitted.
+ DocType Events enables you to write custom scripts that are triggered by events in a DocType.
+
+
+
+ {error && }
+ {data &&
}
+
+
+ )
+}
+
+
+const List = ({ data }: { data: any }) => {
+ return (
+
+
+
+
+
+ ID
+ Status
+ DocType
+ Last modified
+
+
+
+
+ {data?.map((item: any) => (
+
+
+ {item.name}
+
+
+
+ {item.disabled ? "Disabled" : "Enabled"}
+
+ {item.reference_doctype}
+
+
+
+ ))}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/ViewDocTypeEvent.tsx b/raven-app/src/pages/settings/ServerScripts/ViewDocTypeEvent.tsx
new file mode 100644
index 000000000..8d939dc63
--- /dev/null
+++ b/raven-app/src/pages/settings/ServerScripts/ViewDocTypeEvent.tsx
@@ -0,0 +1,120 @@
+import { DeleteAlert } from "@/components/feature/settings/common/DeleteAlert"
+import { DocTypeEventsForm } from "@/components/feature/settings/doctype-events/DocTypeEventsForm"
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { useToast } from "@/hooks/useToast"
+import { Badge, Box, Button, DropdownMenu, Flex, Heading, Section } from "@radix-ui/themes"
+import { useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
+import { useState } from "react"
+import { FormProvider, useForm } from "react-hook-form"
+import { FiArrowLeft } from "react-icons/fi"
+import { useNavigate, useParams } from "react-router-dom"
+
+export interface Props { }
+
+export const ViewDocTypeEvent = (props: Props) => {
+
+ const { eventID } = useParams<{ eventID: string }>()
+
+ const { data: docEventData, error, mutate } = useFrappeGetDoc('Server Script', eventID)
+
+ return (
+ <>
+ {error && }
+ {docEventData && }
+ >
+ )
+}
+
+
+const ViewDocTypeEventPage = ({ data, onUpdate }: { data: any, onUpdate: () => void }) => {
+
+ const [isOpen, setIsOpen] = useState(false)
+
+ const navigate = useNavigate()
+
+ const methods = useForm({
+ defaultValues: {
+ name: data.name,
+ reference_document_type: data.reference_doctype,
+ document_event: data.doctype_event,
+ script: data.script,
+ }
+ })
+
+ const { updateDoc, error } = useFrappeUpdateDoc()
+
+ const { toast } = useToast()
+
+ const onSubmit = (data: any) => {
+ updateDoc('Server Script', data.name, {
+ reference_doctype: data.reference_document_type,
+ doctype_event: data.document_event,
+ script: data.script,
+ })
+ .then(() => {
+ onUpdate()
+ toast({
+ title: `DocType Event ${data.name} updated`,
+ variant: 'success',
+ })
+ })
+ }
+
+ const onStatusToggle = () => {
+ updateDoc('Server Script', data.name, {
+ disabled: !data.disabled
+ })
+ .then(() => {
+ onUpdate()
+ toast({
+ title: `DocType Event ${data.name} ${data.disabled ? "enabled" : "disabled"}`,
+ variant: 'success',
+ })
+ })
+ }
+
+ const onClose = () => {
+ setIsOpen(false)
+ }
+
+ return (
+
+
+
+ )
+}
\ No newline at end of file
From 2f58e7546c0a1987287ef566764b6fc3f6f3aa6b Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Mon, 12 Feb 2024 23:49:29 +0530
Subject: [PATCH 16/32] feat: Scheduler Events pages
---
.../temporal-events/TemporalEventsForm.tsx | 139 ++++++++++++++++++
.../TemporalEvents/CreateTemporalEvent.tsx | 65 ++++++++
.../TemporalEvents/TemporalEvents.tsx | 75 ++++++++++
.../TemporalEvents/ViewTemporalEvent.tsx | 130 ++++++++++++++++
4 files changed, 409 insertions(+)
create mode 100644 raven-app/src/components/feature/settings/temporal-events/TemporalEventsForm.tsx
create mode 100644 raven-app/src/pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent.tsx
create mode 100644 raven-app/src/pages/settings/ServerScripts/TemporalEvents/TemporalEvents.tsx
create mode 100644 raven-app/src/pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent.tsx
diff --git a/raven-app/src/components/feature/settings/temporal-events/TemporalEventsForm.tsx b/raven-app/src/components/feature/settings/temporal-events/TemporalEventsForm.tsx
new file mode 100644
index 000000000..d7c5fa722
--- /dev/null
+++ b/raven-app/src/components/feature/settings/temporal-events/TemporalEventsForm.tsx
@@ -0,0 +1,139 @@
+import { Label, HelperText } from "@/components/common/Form"
+import { Flex, Box, TextField, TextArea, Link, Select } from "@radix-ui/themes"
+import { Controller, useFormContext } from "react-hook-form"
+
+
+export interface Props {
+ edit?: boolean
+}
+
+export const TemporalEventsForm = ({ edit = false }: Props) => {
+
+ const { register, watch, control } = useFormContext()
+
+ const frequency = watch('event_frequency')
+
+ return (
+
+ {!edit &&
+ Name
+
+ }
+
+
+ How often should this event be triggered?
+ (
+ field.onChange(value)} defaultValue="Daily">
+
+
+
+ Temporal Events
+ Daily
+ Weekly
+ Monthly
+ Yearly
+ Daily Long
+ Weekly Long
+ Monthly Long
+ Yearly Long
+ Custom
+
+
+
+ )}
+ />
+ The frequency at which this event will be triggered.
+
+
+ {frequency === 'Cron' &&
+ Cron Format
+ {/* */}
+
+ {/* //FIXME: Needs work - unstable component */}
+
+ Starts with minute, hour, day of month, month, day of week. Learn more about cron format
+ }
+
+
+ {/* TODO: Add a script editor here (maybe use Monaco Editor) */}
+ Script
+
+ Your custom script to be be called via this API event.
+
+
+ )
+}
+
+
+
+/**
+ * AdvancedCronInput
+ * This component is used to input cron format in a more advanced way
+ * @param {string} name
+ * */
+const AdvancedCronInput = ({ name, label, ...props }: { name: string; label: string }) => {
+
+ const { register, setValue, watch, formState } = useFormContext();
+
+ const error = formState.errors[name];
+ const hasError = Boolean(error);
+
+ const cronParts = ['minute', 'hour', 'day', 'month', 'dayOfWeek'];
+
+ return (
+
+
+ {cronParts.map((part, index) => (
+ setValue(`${name}.${part}`, event.target.value)}
+ style={{
+ width: '40px',
+ height: '40px',
+ textAlign: 'center',
+ fontSize: '16px',
+ border: `1px solid ${hasError ? 'red' : 'gray'}`,
+ borderRadius: '4px',
+ borderColor: hasError ? 'red' : 'blue',
+ }}
+ />
+ ))}
+
+ {hasError && (
+
+ {String(error?.message)}
+
+ )}
+
+ );
+};
+
+export default AdvancedCronInput;
diff --git a/raven-app/src/pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent.tsx b/raven-app/src/pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent.tsx
new file mode 100644
index 000000000..4e7c5db52
--- /dev/null
+++ b/raven-app/src/pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent.tsx
@@ -0,0 +1,65 @@
+import { APIEventsForm } from "@/components/feature/settings/api-events/APIEventsForm"
+import { TemporalEventsForm } from "@/components/feature/settings/temporal-events/TemporalEventsForm"
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { useToast } from "@/hooks/useToast"
+import { Box, Button, Flex, Heading, Section } from "@radix-ui/themes"
+import { useFrappeCreateDoc } from "frappe-react-sdk"
+import { FormProvider, useForm } from "react-hook-form"
+import { FiArrowLeft } from "react-icons/fi"
+import { useNavigate } from "react-router-dom"
+
+export interface Props { }
+
+export const CreateTemporalEvent = (props: Props) => {
+
+ const navigate = useNavigate()
+
+ const methods = useForm()
+
+ const { createDoc, error } = useFrappeCreateDoc()
+
+ const { toast } = useToast()
+
+ const onSubmit = (data: any) => {
+ let cronFormat = ''
+ if (data.event_frequency === 'Cron') {
+ cronFormat = data.cron_format?.minute + data.cron_format?.hour + data.cron_format?.day + data.cron_format?.month + data.cron_format?.dayOfWeek
+ }
+ createDoc('Server Script', {
+ name: data.name,
+ script_type: 'Scheduler Event',
+ event_frequency: data.event_frequency,
+ cron_format: cronFormat,
+ script: data.script,
+ })
+ .then((doc) => {
+ if (doc) {
+ navigate(`../${doc?.name}`)
+ }
+ toast({
+ title: `Temporal Event created`,
+ variant: 'success',
+ })
+ })
+ }
+ //TODO: Figure out a way to show _server_messages in the UI (especially the script editor might have some errors that we need to show to the user)
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/TemporalEvents/TemporalEvents.tsx b/raven-app/src/pages/settings/ServerScripts/TemporalEvents/TemporalEvents.tsx
new file mode 100644
index 000000000..ea2046b1e
--- /dev/null
+++ b/raven-app/src/pages/settings/ServerScripts/TemporalEvents/TemporalEvents.tsx
@@ -0,0 +1,75 @@
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { DateMonthAtHourMinuteAmPm } from "@/utils/dateConversions"
+import { Box, Flex, Heading, Button, Section, Blockquote, Badge, Card, Table, Text, Code } from "@radix-ui/themes"
+import { useFrappeGetDocList } from "frappe-react-sdk"
+import { Link, useNavigate } from "react-router-dom"
+
+export interface Props { }
+
+export const TemporalEvents = (props: Props) => {
+
+ const navigate = useNavigate()
+
+ const { data, error } = useFrappeGetDocList('Server Script', {
+ fields: ['name', 'script_type', 'event_frequency', 'creation', 'modified', 'disabled'],
+ filters: [['script_type', '=', 'Scheduler Event']],
+ })
+
+ return (
+
+
+ Temporal Events
+ navigate('create')}>Add Event
+
+
+
+ Lets say you want to pay your dues every month. You can write a script that runs every month and pays your dues.
+ Temporal Events enables you to write custom scripts that are triggered by time or interval.
+
+
+
+ {error && }
+ {data &&
}
+
+
+ )
+}
+
+
+const List = ({ data }: { data: any }) => {
+ return (
+
+
+
+
+
+ ID
+ Status
+ Frequency
+ Last modified
+
+
+
+
+ {data?.map((item: any) => (
+
+
+ {item.name}
+
+
+
+ {item.disabled ? "Disabled" : "Enabled"}
+
+
+ {item.event_frequency}
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent.tsx b/raven-app/src/pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent.tsx
new file mode 100644
index 000000000..25c6e8319
--- /dev/null
+++ b/raven-app/src/pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent.tsx
@@ -0,0 +1,130 @@
+import { DeleteAlert } from "@/components/feature/settings/common/DeleteAlert"
+import { TemporalEventsForm } from "@/components/feature/settings/temporal-events/TemporalEventsForm"
+import { ErrorBanner } from "@/components/layout/AlertBanner"
+import { useToast } from "@/hooks/useToast"
+import { Badge, Box, Button, DropdownMenu, Flex, Heading, Section } from "@radix-ui/themes"
+import { useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
+import { useState } from "react"
+import { FormProvider, useForm } from "react-hook-form"
+import { FiArrowLeft } from "react-icons/fi"
+import { useNavigate, useParams } from "react-router-dom"
+
+export interface Props { }
+
+export const ViewTemporalEvent = (props: Props) => {
+
+ const { scriptID } = useParams<{ scriptID: string }>()
+
+ const { data: eventData, error, mutate } = useFrappeGetDoc('Server Script', scriptID)
+
+ return (
+ <>
+ {error && }
+ {eventData && }
+ >
+ )
+}
+
+
+const ViewTemporalEventPage = ({ data, onUpdate }: { data: any, onUpdate: () => void }) => {
+
+ const [isOpen, setIsOpen] = useState(false)
+
+ const navigate = useNavigate()
+
+ const methods = useForm({
+ defaultValues: {
+ name: data.name,
+ event_frequency: data.event_frequency,
+ script: data.script,
+ cron_format: {
+ 'minute': data.cron_format?.slice(0, 2) ?? '',
+ 'hour': data.cron_format?.slice(2, 4) ?? '',
+ 'day': data.cron_format?.slice(4, 6) ?? '',
+ 'month': data.cron_format?.slice(6, 8) ?? '',
+ 'dayOfWeek': data.cron_format?.slice(8, 10) ?? '',
+ }
+ }
+ })
+
+ const { updateDoc, error } = useFrappeUpdateDoc()
+
+ const { toast } = useToast()
+
+ const onSubmit = (data: any) => {
+ let cronFormat = ''
+ if (data.event_frequency === 'Cron') {
+ cronFormat = data.cron_format?.minute + data.cron_format?.hour + data.cron_format?.day + data.cron_format?.month + data.cron_format?.dayOfWeek
+ }
+ updateDoc('Server Script', data.name, {
+ event_frequency: data.event_frequency,
+ cron_format: cronFormat,
+ script: data.script,
+ })
+ .then(() => {
+ onUpdate()
+ toast({
+ title: `Temporal Event ${data.name} updated`,
+ variant: 'success',
+ })
+ })
+ }
+
+ const onStatusToggle = () => {
+ updateDoc('Server Script', data.name, {
+ disabled: !data.disabled
+ })
+ .then(() => {
+ onUpdate()
+ toast({
+ title: `Temporal Event ${data.name} ${data.disabled ? "enabled" : "disabled"}`,
+ variant: 'success',
+ })
+ })
+ }
+
+ const onClose = () => {
+ setIsOpen(false)
+ }
+
+ return (
+
+
+
+ )
+}
\ No newline at end of file
From 85201b51fceb519962d28428a9f2816d6ba1ff9a Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Tue, 13 Feb 2024 12:15:19 +0530
Subject: [PATCH 17/32] feat:removed useEditor
---
.../integrations/webhooks/ViewWebhook.tsx | 9 +-
.../integrations/webhooks/WebhookForm.tsx | 6 +-
.../webhooks/WebhookReturnDataFieldTable.tsx | 125 ++++++++----------
3 files changed, 64 insertions(+), 76 deletions(-)
diff --git a/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
index e26d17542..5095beb27 100644
--- a/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
@@ -2,7 +2,7 @@ import { ErrorBanner } from "@/components/layout/AlertBanner"
import { FullPageLoader } from "@/components/layout/Loaders"
import { Webhook } from "@/types/Integrations/Webhook"
import { removeFrappeFields } from "@/utils/removeFrappeFields"
-import { AlertDialog, Box, Button, DropdownMenu, Flex, IconButton, Separator, Text } from "@radix-ui/themes"
+import { AlertDialog, Badge, Box, Button, DropdownMenu, Flex, IconButton, Separator, Text } from "@radix-ui/themes"
import { FrappeDoc, useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
import { FieldValues, FormProvider, useForm } from "react-hook-form"
import { useParams } from "react-router-dom"
@@ -102,9 +102,10 @@ export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc, mu
-
+
+ {data?.name}
+ {data.enabled ? 'Enabled' : 'Disabled'}
+
{
return (
-
+ {isEdit === false ?
Name
{
message: "Name should not exceed 140 characters"
}
- })} readOnly={isEdit} />
+ })} />
{errors?.name && {errors.name.message} }
-
+ : null}
Request URL
{
const { register, control, watch, setValue, getValues } = useFormContext()
@@ -64,6 +40,12 @@ export const WebhookData = () => {
setFieldIndex(null)
}
+ // const [previewOpen, setPreviewOpen] = useState(false);
+
+ // const onPreviewClose = () => {
+ // setPreviewOpen(false)
+ // }
+
return (
@@ -72,10 +54,24 @@ export const WebhookData = () => {
Response Data
The fields you want to return in the webhook response.
- append({ fieldname: '', key: '' })} variant="outline" style={{
- width: 'fit-content',
- }}>
- Add
+
+ {/*
+
+ { }} variant="ghost" color="gray" style={{
+ width: 'fit-content',
+ }}>
+
+ Preview
+
+
+
+
+ */}
+ append({ fieldname: '', key: '' })} variant="outline" style={{
+ width: 'fit-content',
+ }}>
+ Add
+
@@ -179,43 +175,6 @@ export const FieldInfoModal = ({ fieldIndex, doctype, onClose }: { fieldIndex: n
return DoctypeFieldList?.find(field => field.doctype === doctype)?.fields.find(field => field.fieldname === fieldname)
}, [fieldIndex, doctype])
- const editor = useEditor({
- content: fieldData?.example,
- editable: false,
- enableCoreExtensions: true,
- extensions: [
- StarterKit.configure({
- heading: false,
- codeBlock: false,
- bold: false,
- blockquote: false,
- italic: false,
- listItem: {
- HTMLAttributes: {
- class: 'ml-5 rt-Text rt-r-size-2'
- }
- },
- paragraph: {
- HTMLAttributes: {
- class: 'rt-Text rt-r-size-2'
- }
- }
- }),
- CustomUnderline,
- CodeBlockLowlight.configure({
- lowlight
- }),
- CustomBlockquote,
- CustomBold,
- CustomUserMention,
- CustomLink,
- CustomItalic
- // TODO: Add channel mention
- // CustomChannelMention
- ]
- })
-
-
return (
@@ -230,11 +189,16 @@ export const FieldInfoModal = ({ fieldIndex, doctype, onClose }: { fieldIndex: n
-
+ {fieldData?.example && <>
Example:
-
+
+
+ {JSON.parse(JSON.stringify(fieldData?.example || ''))}
+
+ >}
+
Close
@@ -243,4 +207,27 @@ export const FieldInfoModal = ({ fieldIndex, doctype, onClose }: { fieldIndex: n
)
+}
+
+export const PreviewModal = ({ onClose }: { onClose: () => void }) => {
+
+ const { register, watch } = useFormContext()
+
+ return (
+
+
+ Preview
+
+
+
+
+
+
+
+
+ Close
+
+
+
+ )
}
\ No newline at end of file
From 988262bc62777036d97c169ebe75fe880e742a1a Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Fri, 23 Feb 2024 13:36:58 +0530
Subject: [PATCH 18/32] feat: Raven Integrations module w/ wrapper doctype for
Scheduler Events
---
raven/modules.txt | 4 +-
raven/raven_integrations/__init__.py | 0
raven/raven_integrations/doctype/__init__.py | 0
.../doctype/raven_scheduler_event/__init__.py | 0
.../raven_scheduler_event.js | 8 ++
.../raven_scheduler_event.json | 117 ++++++++++++++++++
.../raven_scheduler_event.py | 28 +++++
.../test_raven_scheduler_event.py | 9 ++
8 files changed, 165 insertions(+), 1 deletion(-)
create mode 100644 raven/raven_integrations/__init__.py
create mode 100644 raven/raven_integrations/doctype/__init__.py
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/__init__.py
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.js
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.json
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.py
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/test_raven_scheduler_event.py
diff --git a/raven/modules.txt b/raven/modules.txt
index c5387266a..b89028cab 100644
--- a/raven/modules.txt
+++ b/raven/modules.txt
@@ -1,3 +1,5 @@
Raven
Raven Messaging
-Raven Channel Management
\ No newline at end of file
+Raven Channel Management
+Raven Bot
+Raven Integrations
diff --git a/raven/raven_integrations/__init__.py b/raven/raven_integrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_integrations/doctype/__init__.py b/raven/raven_integrations/doctype/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/__init__.py b/raven/raven_integrations/doctype/raven_scheduler_event/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.js b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.js
new file mode 100644
index 000000000..44f4718bf
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2024, The Commit Company and contributors
+// For license information, please see license.txt
+
+// frappe.ui.form.on("Raven Scheduler Event", {
+// refresh(frm) {
+
+// },
+// });
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.json b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.json
new file mode 100644
index 000000000..cc4c7ceda
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.json
@@ -0,0 +1,117 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2024-02-23 13:05:16.692071",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "event_name",
+ "disabled",
+ "event_frequency",
+ "cron_expression",
+ "bot",
+ "column_break_pjem",
+ "send_to",
+ "channel",
+ "dm",
+ "content",
+ "scheduler_event_id"
+ ],
+ "fields": [
+ {
+ "fieldname": "event_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Name",
+ "reqd": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "disabled",
+ "fieldtype": "Check",
+ "label": "Disabled"
+ },
+ {
+ "fieldname": "event_frequency",
+ "fieldtype": "Select",
+ "label": "Event Frequency",
+ "options": "Hourly\nDaily\nWeekly\nMonthly\nYearly\nHourly Long\nDaily Long\nWeekly Long\nMonthly Long\nCron"
+ },
+ {
+ "depends_on": "eval: doc.event_frequency === \"Cron\"",
+ "fieldname": "cron_expression",
+ "fieldtype": "Data",
+ "label": "CRON Expression"
+ },
+ {
+ "description": "This Bot will be used to send the message.",
+ "fieldname": "bot",
+ "fieldtype": "Link",
+ "label": "Bot",
+ "options": "Raven Bot",
+ "reqd": 1
+ },
+ {
+ "fieldname": "send_to",
+ "fieldtype": "Select",
+ "label": "Send to",
+ "options": "Channel\nDM"
+ },
+ {
+ "depends_on": "eval: doc.send_to === \"Channel\"",
+ "fieldname": "channel",
+ "fieldtype": "Link",
+ "label": "Channel",
+ "options": "Raven Channel",
+ "reqd": 1
+ },
+ {
+ "depends_on": "eval: doc.send_to === \"DM\"",
+ "fieldname": "dm",
+ "fieldtype": "Link",
+ "label": "DM",
+ "options": "Raven Channel",
+ "reqd": 1
+ },
+ {
+ "fieldname": "content",
+ "fieldtype": "Small Text",
+ "label": "Content",
+ "reqd": 1
+ },
+ {
+ "fieldname": "scheduler_event_id",
+ "fieldtype": "Link",
+ "label": "Scheduler Event ID",
+ "options": "Server Script"
+ },
+ {
+ "fieldname": "column_break_pjem",
+ "fieldtype": "Column Break"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2024-02-23 13:25:29.515386",
+ "modified_by": "Administrator",
+ "module": "Raven Integrations",
+ "name": "Raven Scheduler Event",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.py b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.py
new file mode 100644
index 000000000..781951398
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2024, The Commit Company and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class RavenSchedulerEvent(Document):
+ # begin: auto-generated types
+ # This code is auto-generated. Do not modify anything in this block.
+
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from frappe.types import DF
+
+ bot: DF.Link
+ channel: DF.Link
+ content: DF.SmallText
+ cron_expression: DF.Data | None
+ disabled: DF.Check
+ dm: DF.Link
+ event_frequency: DF.Literal["Hourly", "Daily", "Weekly", "Monthly", "Yearly", "Hourly Long", "Daily Long", "Weekly Long", "Monthly Long", "Cron"]
+ event_name: DF.Data
+ scheduler_event_id: DF.Link | None
+ send_to: DF.Literal["Channel", "DM"]
+ # end: auto-generated types
+ pass
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/test_raven_scheduler_event.py b/raven/raven_integrations/doctype/raven_scheduler_event/test_raven_scheduler_event.py
new file mode 100644
index 000000000..72221b805
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_scheduler_event/test_raven_scheduler_event.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2024, The Commit Company and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestRavenSchedulerEvent(FrappeTestCase):
+ pass
From eb41335de49b2abd64f0c90c8790044ef3b6ee60 Mon Sep 17 00:00:00 2001
From: Yash Jane
Date: Fri, 23 Feb 2024 13:43:10 +0530
Subject: [PATCH 19/32] feat: Raven Scheduler Event DocType.
---
raven/modules.txt | 3 +-
raven/raven_integrations/__init__.py | 0
raven/raven_integrations/doctype/__init__.py | 0
.../doctype/raven_scheduler_event/__init__.py | 0
.../raven_scheduler_event.js | 8 ++
.../raven_scheduler_event.json | 117 ++++++++++++++++++
.../raven_scheduler_event.py | 28 +++++
.../test_raven_scheduler_event.py | 9 ++
8 files changed, 164 insertions(+), 1 deletion(-)
create mode 100644 raven/raven_integrations/__init__.py
create mode 100644 raven/raven_integrations/doctype/__init__.py
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/__init__.py
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.js
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.json
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.py
create mode 100644 raven/raven_integrations/doctype/raven_scheduler_event/test_raven_scheduler_event.py
diff --git a/raven/modules.txt b/raven/modules.txt
index 47c760840..54ebc74a0 100644
--- a/raven/modules.txt
+++ b/raven/modules.txt
@@ -1,4 +1,5 @@
Raven
Raven Messaging
Raven Channel Management
-Raven Bot
\ No newline at end of file
+Raven Bot
+Raven Integrations
\ No newline at end of file
diff --git a/raven/raven_integrations/__init__.py b/raven/raven_integrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_integrations/doctype/__init__.py b/raven/raven_integrations/doctype/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/__init__.py b/raven/raven_integrations/doctype/raven_scheduler_event/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.js b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.js
new file mode 100644
index 000000000..44f4718bf
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2024, The Commit Company and contributors
+// For license information, please see license.txt
+
+// frappe.ui.form.on("Raven Scheduler Event", {
+// refresh(frm) {
+
+// },
+// });
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.json b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.json
new file mode 100644
index 000000000..cc4c7ceda
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.json
@@ -0,0 +1,117 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2024-02-23 13:05:16.692071",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "event_name",
+ "disabled",
+ "event_frequency",
+ "cron_expression",
+ "bot",
+ "column_break_pjem",
+ "send_to",
+ "channel",
+ "dm",
+ "content",
+ "scheduler_event_id"
+ ],
+ "fields": [
+ {
+ "fieldname": "event_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Name",
+ "reqd": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "disabled",
+ "fieldtype": "Check",
+ "label": "Disabled"
+ },
+ {
+ "fieldname": "event_frequency",
+ "fieldtype": "Select",
+ "label": "Event Frequency",
+ "options": "Hourly\nDaily\nWeekly\nMonthly\nYearly\nHourly Long\nDaily Long\nWeekly Long\nMonthly Long\nCron"
+ },
+ {
+ "depends_on": "eval: doc.event_frequency === \"Cron\"",
+ "fieldname": "cron_expression",
+ "fieldtype": "Data",
+ "label": "CRON Expression"
+ },
+ {
+ "description": "This Bot will be used to send the message.",
+ "fieldname": "bot",
+ "fieldtype": "Link",
+ "label": "Bot",
+ "options": "Raven Bot",
+ "reqd": 1
+ },
+ {
+ "fieldname": "send_to",
+ "fieldtype": "Select",
+ "label": "Send to",
+ "options": "Channel\nDM"
+ },
+ {
+ "depends_on": "eval: doc.send_to === \"Channel\"",
+ "fieldname": "channel",
+ "fieldtype": "Link",
+ "label": "Channel",
+ "options": "Raven Channel",
+ "reqd": 1
+ },
+ {
+ "depends_on": "eval: doc.send_to === \"DM\"",
+ "fieldname": "dm",
+ "fieldtype": "Link",
+ "label": "DM",
+ "options": "Raven Channel",
+ "reqd": 1
+ },
+ {
+ "fieldname": "content",
+ "fieldtype": "Small Text",
+ "label": "Content",
+ "reqd": 1
+ },
+ {
+ "fieldname": "scheduler_event_id",
+ "fieldtype": "Link",
+ "label": "Scheduler Event ID",
+ "options": "Server Script"
+ },
+ {
+ "fieldname": "column_break_pjem",
+ "fieldtype": "Column Break"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2024-02-23 13:25:29.515386",
+ "modified_by": "Administrator",
+ "module": "Raven Integrations",
+ "name": "Raven Scheduler Event",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.py b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.py
new file mode 100644
index 000000000..781951398
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_scheduler_event/raven_scheduler_event.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2024, The Commit Company and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class RavenSchedulerEvent(Document):
+ # begin: auto-generated types
+ # This code is auto-generated. Do not modify anything in this block.
+
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from frappe.types import DF
+
+ bot: DF.Link
+ channel: DF.Link
+ content: DF.SmallText
+ cron_expression: DF.Data | None
+ disabled: DF.Check
+ dm: DF.Link
+ event_frequency: DF.Literal["Hourly", "Daily", "Weekly", "Monthly", "Yearly", "Hourly Long", "Daily Long", "Weekly Long", "Monthly Long", "Cron"]
+ event_name: DF.Data
+ scheduler_event_id: DF.Link | None
+ send_to: DF.Literal["Channel", "DM"]
+ # end: auto-generated types
+ pass
diff --git a/raven/raven_integrations/doctype/raven_scheduler_event/test_raven_scheduler_event.py b/raven/raven_integrations/doctype/raven_scheduler_event/test_raven_scheduler_event.py
new file mode 100644
index 000000000..72221b805
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_scheduler_event/test_raven_scheduler_event.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2024, The Commit Company and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestRavenSchedulerEvent(FrappeTestCase):
+ pass
From a24bd3169e0d4a1163d7af5afd2945c44eaf119f Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 23 Feb 2024 16:08:36 +0530
Subject: [PATCH 20/32] feat:raven Integrations module created
---
raven/api/raven_channel.py | 6 ++----
raven/api/raven_message.py | 10 +++++-----
raven/modules.txt | 3 ++-
raven/raven_integrations/__init__.py | 0
raven/raven_integrations/doctype/__init__.py | 0
.../doctype/raven_webhook/__init__.py | 0
6 files changed, 9 insertions(+), 10 deletions(-)
create mode 100644 raven/raven_integrations/__init__.py
create mode 100644 raven/raven_integrations/doctype/__init__.py
create mode 100644 raven/raven_integrations/doctype/raven_webhook/__init__.py
diff --git a/raven/api/raven_channel.py b/raven/api/raven_channel.py
index 44c64c2ed..0299db0e4 100644
--- a/raven/api/raven_channel.py
+++ b/raven/api/raven_channel.py
@@ -2,10 +2,6 @@
from frappe import _
from frappe.model.document import Document
-channel = frappe.qb.DocType("Raven Channel")
-channel_member = frappe.qb.DocType("Raven Channel Member")
-
-
@frappe.whitelist()
def get_all_channels(hide_archived=False):
'''
@@ -48,6 +44,8 @@ def get_all_channels(hide_archived=False):
def get_channel_list(hide_archived=False):
# get List of all channels where current user is a member (all includes public, private, open, and DM channels)
+ channel = frappe.qb.DocType("Raven Channel")
+ channel_member = frappe.qb.DocType("Raven Channel Member")
query = (frappe.qb.from_(channel)
.select(channel.name, channel.channel_name, channel.type, channel.channel_description, channel.is_archived, channel.is_direct_message, channel.is_self_message, channel.creation, channel.owner).distinct()
.left_join(channel_member)
diff --git a/raven/api/raven_message.py b/raven/api/raven_message.py
index 040c15d4c..674d50abc 100644
--- a/raven/api/raven_message.py
+++ b/raven/api/raven_message.py
@@ -6,11 +6,6 @@
from frappe.query_builder import Case, Order, JoinType
import json
from raven.api.raven_channel import get_peer_user_id
-channel = frappe.qb.DocType("Raven Channel")
-channel_member = frappe.qb.DocType("Raven Channel Member")
-message = frappe.qb.DocType('Raven Message')
-user = frappe.qb.DocType("User")
-
def track_visit(channel_id, commit=False):
'''
@@ -224,6 +219,11 @@ def get_unread_count_for_channels():
@frappe.whitelist()
def get_timeline_message_content(doctype, docname):
+ channel = frappe.qb.DocType("Raven Channel")
+ channel_member = frappe.qb.DocType("Raven Channel Member")
+ message = frappe.qb.DocType('Raven Message')
+ user = frappe.qb.DocType("User")
+
query = (frappe.qb.from_(message)
.select(message.creation, message.owner, message.name, message.text, message.file, channel.name.as_('channel_id'), channel.channel_name, channel.type, channel.is_direct_message, user.full_name, channel.is_self_message)
.join(channel).on(message.channel_id == channel.name)
diff --git a/raven/modules.txt b/raven/modules.txt
index c5387266a..57c00890c 100644
--- a/raven/modules.txt
+++ b/raven/modules.txt
@@ -1,3 +1,4 @@
Raven
Raven Messaging
-Raven Channel Management
\ No newline at end of file
+Raven Channel Management
+Raven Integrations
\ No newline at end of file
diff --git a/raven/raven_integrations/__init__.py b/raven/raven_integrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_integrations/doctype/__init__.py b/raven/raven_integrations/doctype/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/raven/raven_integrations/doctype/raven_webhook/__init__.py b/raven/raven_integrations/doctype/raven_webhook/__init__.py
new file mode 100644
index 000000000..e69de29bb
From 028b0ad0ef8ac24bbbcaf261ba1a0bb6d2b41e0c Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 23 Feb 2024 16:09:02 +0530
Subject: [PATCH 21/32] feat:Raven Webhook wrapper on Webhook
---
.../types/RavenIntegrations/RavenWebhook.ts | 47 ++++
.../doctype/raven_webhook/raven_webhook.js | 8 +
.../doctype/raven_webhook/raven_webhook.json | 220 ++++++++++++++++++
.../doctype/raven_webhook/raven_webhook.py | 209 +++++++++++++++++
.../raven_webhook/test_raven_webhook.py | 9 +
5 files changed, 493 insertions(+)
create mode 100644 raven-app/src/types/RavenIntegrations/RavenWebhook.ts
create mode 100644 raven/raven_integrations/doctype/raven_webhook/raven_webhook.js
create mode 100644 raven/raven_integrations/doctype/raven_webhook/raven_webhook.json
create mode 100644 raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
create mode 100644 raven/raven_integrations/doctype/raven_webhook/test_raven_webhook.py
diff --git a/raven-app/src/types/RavenIntegrations/RavenWebhook.ts b/raven-app/src/types/RavenIntegrations/RavenWebhook.ts
new file mode 100644
index 000000000..896e21f04
--- /dev/null
+++ b/raven-app/src/types/RavenIntegrations/RavenWebhook.ts
@@ -0,0 +1,47 @@
+import { WebhookHeader } from '../Integrations/WebhookHeader'
+import { WebhookData } from '../Integrations/WebhookData'
+
+export interface RavenWebhook{
+ creation: string
+ name: string
+ modified: string
+ owner: string
+ modified_by: string
+ docstatus: 0 | 1 | 2
+ parent?: string
+ parentfield?: string
+ parenttype?: string
+ idx?: number
+ /** Enabled : Check */
+ enabled?: 0 | 1
+ /** Trigger Webhook on Condition : Check */
+ trigger_webhook_on_condition?: 0 | 1
+ /** Channel : Link - Raven Channel */
+ channel_id?: string
+ /** User : Link - Raven User */
+ user?: string
+ /** Channel Type : Select */
+ channel_type?: "" | "Public" | "Private" | "Open" | "DM" | "Self Message"
+ /** Condition : Small Text - The webhook will be triggered if this expression is true */
+ condition?: string
+ /** Webhook Trigger : Select */
+ webhook_trigger: "Message Sent" | "Message Edited" | "Message Deleted" | "Message Reaction" | "Channel Created" | "Channel Deleted" | "Channel Member Added" | "Channel Member Deleted" | "Raven User Added" | "Raven User Deleted"
+ /** Conditions On : Select */
+ conditions_on?: "" | "Channel" | "User" | "Channel Type" | "Custom"
+ /** Request URL : Data */
+ request_url: string
+ /** Request Timeout : Int - The number of seconds until the request expires */
+ timeout: number
+ /** Is Dynamic URL? : Check - On checking this option, URL will be treated like a jinja template string */
+ is_dynamic_url?: 0 | 1
+ /** Enable Security : Check */
+ enable_security?: 0 | 1
+ /** Webhook Secret : Password */
+ webhook_secret?: string
+ /** Headers : Table - Webhook Header */
+ webhook_headers?: WebhookHeader[]
+ /** Data : Table - Webhook Data */
+ webhook_data?: WebhookData[]
+ /** Webhook : Link - Webhook */
+ webhook?: string
+}
\ No newline at end of file
diff --git a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.js b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.js
new file mode 100644
index 000000000..ef7657d47
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2024, The Commit Company and contributors
+// For license information, please see license.txt
+
+// frappe.ui.form.on("Raven Webhook", {
+// refresh(frm) {
+
+// },
+// });
diff --git a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.json b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.json
new file mode 100644
index 000000000..e2b2245c1
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.json
@@ -0,0 +1,220 @@
+{
+ "actions": [],
+ "autoname": "prompt",
+ "creation": "2024-02-23 13:21:14.329211",
+ "default_view": "List",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "sb_doc_events",
+ "enabled",
+ "trigger_webhook_on_condition",
+ "channel_id",
+ "user",
+ "channel_type",
+ "condition",
+ "cb_condition",
+ "webhook_trigger",
+ "conditions_on",
+ "html_condition",
+ "sb_webhook",
+ "request_url",
+ "timeout",
+ "is_dynamic_url",
+ "cb_webhook",
+ "sb_security",
+ "enable_security",
+ "webhook_secret",
+ "sb_webhook_headers",
+ "webhook_headers",
+ "sb_webhook_data",
+ "webhook_data",
+ "webhook"
+ ],
+ "fields": [
+ {
+ "fieldname": "sb_doc_events",
+ "fieldtype": "Section Break",
+ "label": "Doc Events"
+ },
+ {
+ "default": "1",
+ "fieldname": "enabled",
+ "fieldtype": "Check",
+ "label": "Enabled"
+ },
+ {
+ "depends_on": "eval:doc.conditions_on === 'Custom'",
+ "description": "The webhook will be triggered if this expression is true",
+ "fieldname": "condition",
+ "fieldtype": "Small Text",
+ "label": "Condition",
+ "mandatory_depends_on": "eval:doc.conditions_on === 'Custom'"
+ },
+ {
+ "fieldname": "cb_condition",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval:doc.conditions_on === 'Custom'",
+ "fieldname": "html_condition",
+ "fieldtype": "HTML",
+ "options": "Condition Examples:
\ndoc.status==\"Open\" doc.due_date==nowdate() doc.total > 40000\n "
+ },
+ {
+ "fieldname": "sb_webhook",
+ "fieldtype": "Section Break",
+ "label": "Webhook Request"
+ },
+ {
+ "fieldname": "request_url",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Request URL",
+ "reqd": 1
+ },
+ {
+ "default": "5",
+ "description": "The number of seconds until the request expires",
+ "fieldname": "timeout",
+ "fieldtype": "Int",
+ "label": "Request Timeout",
+ "reqd": 1
+ },
+ {
+ "default": "0",
+ "description": "On checking this option, URL will be treated like a jinja template string",
+ "fieldname": "is_dynamic_url",
+ "fieldtype": "Check",
+ "label": "Is Dynamic URL?"
+ },
+ {
+ "fieldname": "cb_webhook",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "sb_security",
+ "fieldtype": "Section Break",
+ "label": "Webhook Security"
+ },
+ {
+ "default": "0",
+ "fieldname": "enable_security",
+ "fieldtype": "Check",
+ "label": "Enable Security"
+ },
+ {
+ "depends_on": "eval:doc.enable_security == 1",
+ "fieldname": "webhook_secret",
+ "fieldtype": "Password",
+ "label": "Webhook Secret"
+ },
+ {
+ "fieldname": "sb_webhook_headers",
+ "fieldtype": "Section Break",
+ "label": "Webhook Headers"
+ },
+ {
+ "fieldname": "webhook_headers",
+ "fieldtype": "Table",
+ "label": "Headers",
+ "options": "Webhook Header"
+ },
+ {
+ "fieldname": "sb_webhook_data",
+ "fieldtype": "Section Break",
+ "label": "Webhook Data"
+ },
+ {
+ "depends_on": "eval: !doc.request_structure || doc.request_structure == \"Form URL-Encoded\"",
+ "fieldname": "webhook_data",
+ "fieldtype": "Table",
+ "label": "Data",
+ "options": "Webhook Data"
+ },
+ {
+ "fieldname": "webhook_trigger",
+ "fieldtype": "Select",
+ "label": "Webhook Trigger",
+ "options": "Message Sent\nMessage Edited\nMessage Deleted\nMessage Reaction\nChannel Created\nChannel Deleted\nChannel Member Added\nChannel Member Deleted\nRaven User Added\nRaven User Deleted",
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "trigger_webhook_on_condition",
+ "fieldtype": "Check",
+ "label": "Trigger Webhook on Condition"
+ },
+ {
+ "depends_on": "eval:doc.trigger_webhook_on_condition===1",
+ "fieldname": "conditions_on",
+ "fieldtype": "Select",
+ "label": "Conditions On",
+ "mandatory_depends_on": "eval:doc.trigger_webhook_on_condition===1",
+ "options": "\nChannel\nUser\nChannel Type\nCustom"
+ },
+ {
+ "depends_on": "eval:doc.conditions_on === 'Channel'",
+ "fieldname": "channel_id",
+ "fieldtype": "Link",
+ "label": "Channel",
+ "mandatory_depends_on": "eval:doc.conditions_on === 'Channel'",
+ "options": "Raven Channel"
+ },
+ {
+ "depends_on": "eval:doc.conditions_on === 'User'",
+ "fieldname": "user",
+ "fieldtype": "Link",
+ "label": "User",
+ "mandatory_depends_on": "eval:doc.conditions_on === 'User'",
+ "options": "Raven User"
+ },
+ {
+ "depends_on": "eval:doc.conditions_on ==='Channel Type'",
+ "fieldname": "channel_type",
+ "fieldtype": "Select",
+ "label": "Channel Type",
+ "mandatory_depends_on": "eval:doc.conditions_on ==='Channel Type'",
+ "options": "\nPublic\nPrivate\nOpen\nDM\nSelf Message"
+ },
+ {
+ "fieldname": "webhook",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "label": "Webhook",
+ "options": "Webhook"
+ }
+ ],
+ "links": [
+ {
+ "link_doctype": "Webhook Request Log",
+ "link_fieldname": "webhook"
+ }
+ ],
+ "modified": "2024-02-23 13:58:26.721842",
+ "modified_by": "Administrator",
+ "module": "Raven Integrations",
+ "name": "Raven Webhook",
+ "naming_rule": "Set by user",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
new file mode 100644
index 000000000..338585976
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
@@ -0,0 +1,209 @@
+# Copyright (c) 2024, The Commit Company and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe.model.document import Document
+
+
+class RavenWebhook(Document):
+ # begin: auto-generated types
+ # This code is auto-generated. Do not modify anything in this block.
+
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from frappe.integrations.doctype.webhook_data.webhook_data import WebhookData
+ from frappe.integrations.doctype.webhook_header.webhook_header import WebhookHeader
+ from frappe.types import DF
+
+ channel_id: DF.Link | None
+ channel_type: DF.Literal["", "Public",
+ "Private", "Open", "DM", "Self Message"]
+ condition: DF.SmallText | None
+ conditions_on: DF.Literal["", "Channel", "User", "Channel Type", "Custom"]
+ enable_security: DF.Check
+ enabled: DF.Check
+ is_dynamic_url: DF.Check
+ request_url: DF.Data
+ timeout: DF.Int
+ trigger_webhook_on_condition: DF.Check
+ user: DF.Link | None
+ webhook: DF.Link | None
+ webhook_data: DF.Table[WebhookData]
+ webhook_headers: DF.Table[WebhookHeader]
+ webhook_secret: DF.Password | None
+ webhook_trigger: DF.Literal["Message Sent", "Message Edited", "Message Deleted", "Message Reaction", "Channel Created",
+ "Channel Deleted", "Channel Member Added", "Channel Member Deleted", "Raven User Added", "Raven User Deleted"]
+ # end: auto-generated types
+
+ def validate(self):
+ # 1. Check if webhook name is unique
+ # 2. Check if webhook ID is exists
+ # 3. If exist then update the webhook
+ # 4. If not exist then create the webhook
+
+ # 1. Check if webhook name is unique
+ webhook = frappe.get_all('Raven Webhook', filters={
+ 'webhook_name': self.name})
+ if webhook:
+ frappe.throw('Webhook name already exists')
+
+ # 2. Check if webhook ID is exists
+ if self.webhook:
+ # Update the webhook
+ self.update_webhook()
+
+ else:
+ # Create the webhook
+ self.create_webhook()
+
+ def on_trash(self):
+ # Delete the webhook
+ if self.webhook:
+ frappe.delete_doc('Webhook', self.webhook)
+
+ def create_webhook(self):
+ # Create a new webhook
+
+ doctype, event = self.get_doctype_and_event()
+ conditions = self.get_conditions()
+ webhook_doc = frappe.new_doc('Webhook')
+ webhook_doc.name = self.name
+ webhook_doc.request_url = self.request_url
+ webhook_doc.is_dynamic_url = self.is_dynamic_url
+ webhook_doc.timeout = self.timeout
+ webhook_doc.enable_security = self.enable_security
+ webhook_doc.webhook_secret = self.webhook_secret
+ webhook_doc.request_method = 'POST'
+ webhook_doc.request_structure = 'Form URL-Encoded'
+ webhook_doc.webhook_headers = self.webhook_headers
+ webhook_doc.webhook_data = self.webhook_data
+ webhook_doc.webhook_doctype = doctype
+ webhook_doc.webhook_docevent = event
+ webhook_doc.condition = conditions
+ webhook_doc.save()
+ self.webhook = webhook_doc.name
+
+ def update_webhook(self):
+ # Update the webhook
+ conditions = self.get_conditions()
+ webhook_doc = frappe.get_doc('Webhook', self.webhook)
+ webhook_doc.request_url = self.request_url
+ webhook_doc.is_dynamic_url = self.is_dynamic_url
+ webhook_doc.timeout = self.timeout
+ webhook_doc.enable_security = self.enable_security
+ webhook_doc.webhook_secret = self.webhook_secret
+ webhook_doc.webhook_headers = self.webhook_headers
+ webhook_doc.webhook_data = self.webhook_data
+ webhook_doc.condition = conditions
+ webhook_doc.save()
+
+ def get_doctype_and_event(self):
+ doctypes_and_events = [
+ {
+ 'label': 'Message Sent',
+ 'doctype': 'Raven Message',
+ 'event': 'after_insert',
+ },
+ {
+
+ 'label': 'Message Edited',
+ 'doctype': 'Raven Message',
+ 'event': 'on_update'
+ },
+ {
+
+ 'label': 'Message Deleted',
+ 'doctype': 'Raven Message',
+ 'event': 'on_trash'
+ },
+ {
+
+ 'label': 'Message Reaction',
+ 'doctype': 'Raven Message Reaction',
+ 'event': 'after_insert'
+ },
+ {
+
+ 'label': 'Channel Created',
+ 'doctype': 'Raven Channel',
+ 'event': 'after_insert'
+ },
+ {
+
+ 'label': 'Channel Deleted',
+ 'doctype': 'Raven Channel',
+ 'event': 'on_trash'
+ },
+ {
+ 'label': 'Member Added to the Channel',
+ 'doctype': 'Raven Channel Member',
+ 'event': 'after_insert'
+ },
+ {
+ 'label': 'Member Deleted from the Channel',
+ 'doctype': 'Raven Channel Member',
+ 'event': 'on_trash'
+ },
+ {
+ 'label': 'Raven User Added',
+ 'doctype': 'Raven User',
+ 'event': 'after_insert'
+ },
+ {
+ 'label': 'Raven User Deleted',
+ 'doctype': 'Raven User',
+ 'event': 'on_trash'
+ }
+ ]
+ doctype, event = None, None
+ for doctype_and_event in doctypes_and_events:
+ if self.webhook_trigger == doctype_and_event['label']:
+ doctype = doctype_and_event['doctype']
+ event = doctype_and_event['event']
+ break
+ return doctype, event
+
+ def get_conditions(self):
+ # Get the conditions for the webhook
+ doctype, event = self.get_doctype_and_event()
+ if self.trigger_webhook_on_condition:
+ if self.conditions_on == 'Channel':
+ if doctype == 'Raven Channel':
+ # return 'doc.name == self.channel_id'
+ return f'doc.name == "{self.channel_id}"'
+ elif doctype == 'Raven Channel Member':
+ return f'doc.channel_id == "{self.channel_id}"'
+ elif doctype == 'Raven Message':
+ return f'doc.channel_id == "{self.channel_id}"'
+ elif doctype == 'Raven Message Reaction':
+ frappe.throw('Message Reaction cannot be triggered on Channel')
+ elif doctype == 'Raven User':
+ frappe.throw('Raven User cannot be triggered on Channel')
+
+ elif self.conditions_on == 'User':
+ if doctype == 'Raven Channel':
+ frappe.throw('Channel cannot be triggered on User')
+ elif doctype == 'Raven Channel Member':
+ return f'doc.user_id == "{self.user}"'
+ elif doctype == 'Raven Message':
+ return f'doc.owner == "{self.user}"'
+ elif doctype == 'Raven Message Reaction':
+ return f'doc.owner == "{self.user}"'
+
+ elif self.conditions_on == 'Channel Type':
+ if doctype == 'Raven Channel':
+ if self.channel_type in ['Public', 'Private', 'Open']:
+ return f'doc.type == "{self.channel_type}"'
+ elif self.channel_type == 'DM':
+ return f'doc.is_direct_message == 1'
+ elif self.channel_type == 'Self Message':
+ return f'doc.is_self_message == 1'
+ else:
+ frappe.throw('Invalid Channel Type')
+ else:
+ frappe.throw('Channel Type cannot be triggered on other doctypes')
+ elif self.conditions_on == 'Custom':
+ return self.condition
+
+ return None
diff --git a/raven/raven_integrations/doctype/raven_webhook/test_raven_webhook.py b/raven/raven_integrations/doctype/raven_webhook/test_raven_webhook.py
new file mode 100644
index 000000000..4c43f73d6
--- /dev/null
+++ b/raven/raven_integrations/doctype/raven_webhook/test_raven_webhook.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2024, The Commit Company and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestRavenWebhook(FrappeTestCase):
+ pass
From 406722cfdfd47f3af7b39a85242cab8c814223e8 Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 23 Feb 2024 16:09:22 +0530
Subject: [PATCH 22/32] feat:Raven Webhook Forms
---
.../integrations/webhooks/CreateWebhook.tsx | 14 +-
.../integrations/webhooks/ViewWebhook.tsx | 15 +-
.../integrations/webhooks/WebhookForm.tsx | 133 ++++++++++++------
.../integrations/webhooks/WebhookHeaders.tsx | 4 +-
.../integrations/webhooks/WebhookList.tsx | 7 +-
.../webhooks/WebhookReturnDataFieldTable.tsx | 28 ++--
.../feature/integrations/webhooks/utils.ts | 10 +-
7 files changed, 125 insertions(+), 86 deletions(-)
diff --git a/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx b/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx
index 9bf2f0b9b..5b3c22915 100644
--- a/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx
@@ -5,29 +5,25 @@ import { useNavigate } from 'react-router-dom'
import { Button, Flex, Separator, Text } from '@radix-ui/themes'
import { BiChevronLeft } from 'react-icons/bi'
import { ErrorBanner } from '@/components/layout/AlertBanner'
-import { WebhookForm, WebhookFormField } from './WebhookForm'
+import { WebhookForm } from './WebhookForm'
import { useToast } from '@/hooks/useToast'
+import { RavenWebhook } from '@/types/RavenIntegrations/RavenWebhook'
export const CreateWebhook = () => {
const navigate = useNavigate()
- const methods = useForm({
+ const methods = useForm({
defaultValues: {
- webhook_docevent: 'after_insert',
- timeout: 5,
- request_method: 'POST',
enabled: 1,
- request_structure: 'Form URL-Encoded',
- need_condition: false,
}
})
const { createDoc, loading, reset, error } = useFrappeCreateDoc()
const { toast } = useToast()
- const onSubmit = (data: Webhook) => {
- createDoc('Webhook', data)
+ const onSubmit = (data: RavenWebhook) => {
+ createDoc('Raven Webhook', data)
.then((doc) => {
reset()
methods.reset()
diff --git a/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
index 5095beb27..b4f057f09 100644
--- a/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
@@ -8,17 +8,18 @@ import { FieldValues, FormProvider, useForm } from "react-hook-form"
import { useParams } from "react-router-dom"
import { KeyedMutator } from 'swr'
import { BackToList } from "./CreateWebhook"
-import { WebhookForm, WebhookFormField } from "./WebhookForm"
+import { WebhookForm } from "./WebhookForm"
import { useToast } from "@/hooks/useToast"
import { BiDotsVerticalRounded } from "react-icons/bi"
import { useState } from "react"
import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog"
import { Loader } from "@/components/common/Loader"
+import { RavenWebhook } from "@/types/RavenIntegrations/RavenWebhook"
const ViewWebhook = () => {
const { ID } = useParams<{ ID: string }>()
- const { data, error, isLoading, mutate } = useFrappeGetDoc('Webhook', ID, undefined, {
+ const { data, error, isLoading, mutate } = useFrappeGetDoc('Raven Webhook', ID, undefined, {
shouldRetryOnError: false,
})
@@ -35,13 +36,12 @@ const ViewWebhook = () => {
export default ViewWebhook
ViewWebhook.displayName = 'ViewWebhook'
-export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc, mutate: KeyedMutator> }) => {
+export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc, mutate: KeyedMutator> }) => {
const formFields = removeFrappeFields(data)
- const methods = useForm({
+ const methods = useForm({
defaultValues: {
...formFields,
- need_condition: data.condition ? true : false,
docstatus: data.docstatus
}
})
@@ -53,7 +53,7 @@ export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc, mu
const isDirty = methods.formState.isDirty
const onSubmit = async (data: FieldValues) => {
- return updateDoc('Webhook', data.name, data)
+ return updateDoc('Raven Webhook', data.name, data)
.then((doc) => {
toast({
title: "Webhook updated successfully.",
@@ -65,7 +65,6 @@ export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc, mu
const formFields = removeFrappeFields(doc)
methods.reset({
...formFields,
- need_condition: doc.condition ? true : false,
docstatus: doc.docstatus
})
}
@@ -78,7 +77,7 @@ export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc, mu
}
const onUpdateEnabled = () => {
- updateDoc('Webhook', data.name, {
+ updateDoc('Raven Webhook', data.name, {
enabled: !data.enabled
}).then((doc) => {
toast({
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx
index a38fea33c..6fb78a60f 100644
--- a/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx
@@ -1,45 +1,22 @@
-import React, { useMemo } from 'react';
+import React, { useContext } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
-import { Webhook } from '@/types/Integrations/Webhook'
import { Box, Checkbox, Flex, TextFieldInput, Select, TextArea, Heading, Text } from '@radix-ui/themes';
import { ErrorText, HelperText, Label } from '@/components/common/Form';
import { WebhookData } from './WebhookReturnDataFieldTable';
import { WebhookHeaders } from './WebhookHeaders';
import { TriggerEvents } from './utils';
-
-export interface WebhookFormField extends Webhook {
- need_condition: boolean
-}
+import { RavenWebhook } from '@/types/RavenIntegrations/RavenWebhook';
+import { UserListContext } from '@/utils/users/UserListProvider';
+import { ChannelListContext, ChannelListContextType } from '@/utils/channel/ChannelListProvider';
export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
- const { register, formState: { errors }, control, setValue, watch } = useFormContext()
-
- const onTriggerEventChange = (value: string) => {
- if (value) {
- setValue('webhook_data', [])
- const field = TriggerEvents?.find(event => event.key === value)
- if (field) {
- setValue('webhook_doctype', field.doctype)
- setValue('webhook_docevent', field.event)
- }
- }
- }
-
- const webhookDoctype = watch('webhook_doctype')
-
- const webhookDocevent = watch('webhook_docevent')
+ const { register, formState: { errors }, control, setValue, watch } = useFormContext()
const security = watch('enable_security')
- const needCondition = watch('need_condition')
+ const needCondition = watch('trigger_webhook_on_condition')
- const triggerValue = useMemo(() => {
- if (webhookDoctype && webhookDocevent) {
- const field = TriggerEvents?.find(event => event.doctype === webhookDoctype && event.event === webhookDocevent)
- return field?.key
- }
- return ''
- }, [webhookDoctype, webhookDocevent])
+ const conditionOn = watch('conditions_on')
return (
@@ -102,25 +79,39 @@ export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
Trigger Event
-
-
-
-
- Trigger Events
- {
- TriggerEvents?.map((event, index) => (
- {event.label}
- ))
+ {
+ if (e.target.value) {
+ setValue('webhook_data', [])
+
}
-
-
-
+ }
+ }}
+ render={({ field }) => (
+
+
+
+
+ Trigger Events
+ {
+ TriggerEvents?.map((event, index) => (
+ {event.label}
+ ))
+ }
+
+
+
+ )}
+ />
(
field.onChange(!field.value)} />
@@ -134,6 +125,30 @@ export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
{errors?.enable_security && {errors.enable_security.message} }
{needCondition &&
+
+ Condition On
+ (
+
+
+
+
+ Condition On
+ Channel
+ User
+ Channel Type
+ Custom
+
+
+
+ )}
+ />
+ Field on which the condition will be applied
+
+ }
+ {conditionOn === 'Custom' ?
{
- }
+ : conditionOn === 'Channel' ?
+ Channel ID
+
+ Channel ID on which the condition will be applied
+ : conditionOn === 'User' ?
+ User ID
+
+ User ID on which the condition will be applied
+ : conditionOn === 'Channel Type' ?
+ Channel Type
+ (
+
+
+
+
+ Channel Type
+ Public
+ Private
+ Open
+ Direct Message
+ Self Message
+
+
+
+ )}
+ />
+ Channel Type on which the condition will be applied
+ : null}
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookHeaders.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookHeaders.tsx
index d7e040698..908f50e97 100644
--- a/raven-app/src/components/feature/integrations/webhooks/WebhookHeaders.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookHeaders.tsx
@@ -1,5 +1,5 @@
import { HelperText } from "@/components/common/Form";
-import { Webhook } from "@/types/Integrations/Webhook";
+import { RavenWebhook } from "@/types/RavenIntegrations/RavenWebhook";
import { Flex, Box, Heading, Table, TextFieldInput, IconButton, Button } from "@radix-ui/themes";
import { useState } from "react";
import { useFieldArray, useFormContext } from "react-hook-form";
@@ -7,7 +7,7 @@ import { BiMinusCircle } from "react-icons/bi";
import { BsPlus } from "react-icons/bs";
export const WebhookHeaders = () => {
- const { register } = useFormContext()
+ const { register } = useFormContext()
const { fields, append, remove } = useFieldArray({
name: 'webhook_headers'
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookList.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookList.tsx
index fee4779f9..126915fe0 100644
--- a/raven-app/src/components/feature/integrations/webhooks/WebhookList.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookList.tsx
@@ -4,6 +4,7 @@ import { ErrorBanner } from "@/components/layout/AlertBanner"
import { FullPageLoader } from "@/components/layout/Loaders"
import { useToast } from "@/hooks/useToast"
import { Webhook } from "@/types/Integrations/Webhook"
+import { RavenWebhook } from "@/types/RavenIntegrations/RavenWebhook"
import { DateMonthYear } from "@/utils/dateConversions"
import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog"
import { Flex, Separator, Button, Text, Badge, IconButton, AlertDialog } from "@radix-ui/themes"
@@ -14,7 +15,7 @@ import { Link, useNavigate } from "react-router-dom"
export const WebhookList = () => {
- const { data, error, isLoading, mutate } = useFrappeGetDocList('Webhook', {
+ const { data, error, isLoading, mutate } = useFrappeGetDocList('Raven Webhook', {
fields: ['name', 'request_url', 'enabled', 'owner', 'creation']
})
@@ -63,7 +64,7 @@ export const WebhookList = () => {
)
}
-export const WebhookItem = ({ webhook, mutate }: { webhook: Webhook, mutate: () => void }) => {
+export const WebhookItem = ({ webhook, mutate }: { webhook: RavenWebhook, mutate: () => void }) => {
const navigate = useNavigate()
@@ -135,7 +136,7 @@ export const DeleteWebhookAlertContent = ({ webhhookID, onClose, mutate }: { web
const { toast } = useToast()
const onDelete = () => {
- deleteDoc('Webhook', webhhookID).then(() => {
+ deleteDoc('Raven Webhook', webhhookID).then(() => {
mutate()
onClose()
toast({
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
index 041df5556..e21fa8d82 100644
--- a/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
@@ -5,31 +5,23 @@ import { useCallback, useMemo, useState } from "react";
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
import { BiInfoCircle, BiMinusCircle } from "react-icons/bi";
import { BsEye, BsPlus } from "react-icons/bs";
-import { FieldsData } from "./utils";
+import { FieldsData, TriggerEvents } from "./utils";
import { DoctypeFieldList } from './utils'
import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog";
+import { RavenWebhook } from "@/types/RavenIntegrations/RavenWebhook";
export const WebhookData = () => {
- const { register, control, watch, setValue, getValues } = useFormContext()
+ const { register, control, watch, setValue, getValues } = useFormContext()
const { fields, append, remove } = useFieldArray({
name: 'webhook_data'
});
- const webhookDoctype = watch('webhook_doctype')
+ const webhookTrigger = watch('webhook_trigger')
const webhookDataFieldName = useMemo(() => {
- return DoctypeFieldList?.find(field => field.doctype === webhookDoctype)?.fields || []
- }, [webhookDoctype])
-
- const getDoctypeField = useCallback((index: number) => {
- const webhookData = getValues('webhook_data')
- const fieldname = webhookData?.[index]?.fieldname
- if (fieldname) {
- return webhookDataFieldName?.find(field => field.fieldname === fieldname)
- }
- return undefined
- }, [webhookDataFieldName])
+ return DoctypeFieldList?.find(field => field.events.includes(webhookTrigger))?.fields || []
+ }, [webhookTrigger])
const [fieldIndex, setFieldIndex] = useState(null)
@@ -137,7 +129,7 @@ export const WebhookData = () => {
- {fieldIndex !== null && }
+ {fieldIndex !== null && }
@@ -167,13 +159,13 @@ export const WebhookData = () => {
)
}
-export const FieldInfoModal = ({ fieldIndex, doctype, onClose }: { fieldIndex: number, doctype: string, onClose: () => void }) => {
+export const FieldInfoModal = ({ fieldIndex, triggerEvent, onClose }: { fieldIndex: number, triggerEvent: string, onClose: () => void }) => {
const { watch } = useFormContext()
const fieldData = useMemo(() => {
const fieldname = watch(`webhook_data.${fieldIndex}.fieldname`)
- return DoctypeFieldList?.find(field => field.doctype === doctype)?.fields.find(field => field.fieldname === fieldname)
- }, [fieldIndex, doctype])
+ return DoctypeFieldList?.find(field => field.events.includes(triggerEvent))?.fields.find(field => field.fieldname === fieldname)
+ }, [fieldIndex, triggerEvent])
return (
diff --git a/raven-app/src/components/feature/integrations/webhooks/utils.ts b/raven-app/src/components/feature/integrations/webhooks/utils.ts
index bbe528b6c..24d4e89ce 100644
--- a/raven-app/src/components/feature/integrations/webhooks/utils.ts
+++ b/raven-app/src/components/feature/integrations/webhooks/utils.ts
@@ -45,13 +45,13 @@ export const TriggerEvents: TriggerEventField[] = [
},
{
key: 'channel_member_added',
- label: 'Member Added to the Channel',
+ label: 'Channel Member Added',
doctype: 'Raven Channel Member',
event: 'after_insert'
},
{
key: 'channel_member_deleted',
- label: 'Member Deleted from the Channel',
+ label: 'Channel Member Deleted',
doctype: 'Raven Channel Member',
event: 'on_trash'
},
@@ -116,10 +116,12 @@ const commonFields = [
export const DoctypeFieldList: {
doctype: 'Raven Message' | 'Raven Channel' | 'Raven Channel Member' | 'Raven User' | 'Raven Message Reaction',
+ events: string[]
fields: FieldsData[]
}[] = [
{
doctype: 'Raven Message',
+ events: ['Message Sent', 'Message Edited', 'Message Deleted'],
fields: [
{
fieldname: 'channel_id',
@@ -215,6 +217,7 @@ export const DoctypeFieldList: {
},
{
doctype: 'Raven Channel',
+ events: ['Channel Created', 'Channel Deleted'],
fields: [
{
fieldname: 'channel_name',
@@ -264,6 +267,7 @@ export const DoctypeFieldList: {
},
{
'doctype': 'Raven Channel Member',
+ events: ['Channel Member Added', 'Channel Member Deleted'],
fields: [
{
fieldname: 'channel_id',
@@ -298,6 +302,7 @@ export const DoctypeFieldList: {
},
{
doctype: 'Raven User',
+ events: ['Raven User Added', 'Raven User Deleted'],
fields: [
{
fieldname: 'user',
@@ -339,6 +344,7 @@ export const DoctypeFieldList: {
},
{
doctype: 'Raven Message Reaction',
+ events: ['Message Reaction'],
fields: [
{
fieldname: 'reaction',
From c551b8e45e1dc3332515712ce55177f99fb7748e Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 23 Feb 2024 18:39:27 +0530
Subject: [PATCH 23/32] feat:raven webhook controller methods
---
.../doctype/raven_webhook/raven_webhook.json | 4 +--
.../doctype/raven_webhook/raven_webhook.py | 34 +++++++++++++------
2 files changed, 25 insertions(+), 13 deletions(-)
diff --git a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.json b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.json
index e2b2245c1..2fda1d97e 100644
--- a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.json
+++ b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.json
@@ -137,7 +137,7 @@
"fieldname": "webhook_trigger",
"fieldtype": "Select",
"label": "Webhook Trigger",
- "options": "Message Sent\nMessage Edited\nMessage Deleted\nMessage Reaction\nChannel Created\nChannel Deleted\nChannel Member Added\nChannel Member Deleted\nRaven User Added\nRaven User Deleted",
+ "options": "Message Sent\nMessage Edited\nMessage Deleted\nMessage Reacted On\nChannel Created\nChannel Deleted\nChannel Member Added\nChannel Member Deleted\nUser Added\nUser Deleted",
"reqd": 1,
"set_only_once": 1
},
@@ -193,7 +193,7 @@
"link_fieldname": "webhook"
}
],
- "modified": "2024-02-23 13:58:26.721842",
+ "modified": "2024-02-23 18:29:18.230912",
"modified_by": "Administrator",
"module": "Raven Integrations",
"name": "Raven Webhook",
diff --git a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
index 338585976..2a2b30ac6 100644
--- a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
+++ b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
@@ -32,8 +32,8 @@ class RavenWebhook(Document):
webhook_data: DF.Table[WebhookData]
webhook_headers: DF.Table[WebhookHeader]
webhook_secret: DF.Password | None
- webhook_trigger: DF.Literal["Message Sent", "Message Edited", "Message Deleted", "Message Reaction", "Channel Created",
- "Channel Deleted", "Channel Member Added", "Channel Member Deleted", "Raven User Added", "Raven User Deleted"]
+ webhook_trigger: DF.Literal["Message Sent", "Message Edited", "Message Deleted", "Message Reacted On",
+ "Channel Created", "Channel Deleted", "Channel Member Added", "Channel Member Deleted", "User Added", "User Deleted"]
# end: auto-generated types
def validate(self):
@@ -60,7 +60,7 @@ def validate(self):
def on_trash(self):
# Delete the webhook
if self.webhook:
- frappe.delete_doc('Webhook', self.webhook)
+ frappe.db.delete('Webhook', self.webhook)
def create_webhook(self):
# Create a new webhook
@@ -76,16 +76,16 @@ def create_webhook(self):
webhook_doc.webhook_secret = self.webhook_secret
webhook_doc.request_method = 'POST'
webhook_doc.request_structure = 'Form URL-Encoded'
- webhook_doc.webhook_headers = self.webhook_headers
- webhook_doc.webhook_data = self.webhook_data
+ self.set_webhook_data_and_headers(webhook_doc)
webhook_doc.webhook_doctype = doctype
webhook_doc.webhook_docevent = event
webhook_doc.condition = conditions
- webhook_doc.save()
+ webhook_doc.insert()
self.webhook = webhook_doc.name
def update_webhook(self):
# Update the webhook
+
conditions = self.get_conditions()
webhook_doc = frappe.get_doc('Webhook', self.webhook)
webhook_doc.request_url = self.request_url
@@ -93,11 +93,23 @@ def update_webhook(self):
webhook_doc.timeout = self.timeout
webhook_doc.enable_security = self.enable_security
webhook_doc.webhook_secret = self.webhook_secret
- webhook_doc.webhook_headers = self.webhook_headers
- webhook_doc.webhook_data = self.webhook_data
webhook_doc.condition = conditions
+ self.set_webhook_data_and_headers(webhook_doc)
webhook_doc.save()
+ def set_webhook_data_and_headers(self, webhook_doc):
+ for data in self.webhook_data:
+ webhook_doc.append('webhook_data', {
+ 'key': data.key,
+ 'fieldname': data.fieldname,
+ })
+
+ for data in self.webhook_headers:
+ webhook_doc.append('webhook_headers', {
+ 'key': data.key,
+ 'value': data.value,
+ })
+
def get_doctype_and_event(self):
doctypes_and_events = [
{
@@ -119,7 +131,7 @@ def get_doctype_and_event(self):
},
{
- 'label': 'Message Reaction',
+ 'label': 'Message Reacted On',
'doctype': 'Raven Message Reaction',
'event': 'after_insert'
},
@@ -146,12 +158,12 @@ def get_doctype_and_event(self):
'event': 'on_trash'
},
{
- 'label': 'Raven User Added',
+ 'label': 'User Added',
'doctype': 'Raven User',
'event': 'after_insert'
},
{
- 'label': 'Raven User Deleted',
+ 'label': 'User Deleted',
'doctype': 'Raven User',
'event': 'on_trash'
}
From 150ff4a10f17bc5b7dc360e605bd5fdf2421fd69 Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 23 Feb 2024 18:39:56 +0530
Subject: [PATCH 24/32] feat:User,Channel Provider on Settings page
---
raven-app/src/App.tsx | 3 +-
raven-app/src/pages/MainPage.tsx | 59 +++++++------------
.../src/utils/channel/ChannelRedirect.tsx | 36 ++++++++++-
raven-app/src/utils/roles.ts | 4 ++
4 files changed, 61 insertions(+), 41 deletions(-)
diff --git a/raven-app/src/App.tsx b/raven-app/src/App.tsx
index c5ef1129b..2259fe5b0 100644
--- a/raven-app/src/App.tsx
+++ b/raven-app/src/App.tsx
@@ -20,7 +20,7 @@ const router = createBrowserRouter(
<>
import('@/pages/auth/Login')} />
}>
- } />
+ }>
} >
} />
import('./components/feature/saved-messages/SavedMessages')} />
@@ -34,6 +34,7 @@ const router = createBrowserRouter(
SS} />
+
>
), {
diff --git a/raven-app/src/pages/MainPage.tsx b/raven-app/src/pages/MainPage.tsx
index bb2866fcb..2107bd26c 100644
--- a/raven-app/src/pages/MainPage.tsx
+++ b/raven-app/src/pages/MainPage.tsx
@@ -1,51 +1,34 @@
import { Flex, Box } from '@radix-ui/themes'
import { Outlet } from 'react-router-dom'
-import { lazy, Suspense } from 'react'
+import { lazy } from 'react'
import { Sidebar } from '../components/layout/Sidebar/Sidebar'
import { VirtuosoRefProvider } from '../utils/message/VirtuosoRefProvider'
-import { ChannelListProvider } from '../utils/channel/ChannelListProvider'
-import { UserListProvider } from '@/utils/users/UserListProvider'
-import { ActiveUsersProvider } from '@/utils/users/ActiveUsersProvider'
-import { hasRavenUserRole } from '@/utils/roles'
-import { FullPageLoader } from '@/components/layout/Loaders'
import { MobileAppRedirectBanner } from '@/components/layout/AlertBanner'
import '../components/layout/AlertBanner/styles.css'
const AddRavenUsersPage = lazy(() => import('@/pages/AddRavenUsersPage'))
export const MainPage = () => {
- const isRavenUser = hasRavenUserRole()
- if (isRavenUser) {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
- } else {
- // If the user does not have the Raven User role, then show an error message if the user cannot add more people.
- // Else, show the page to add people to Raven
- return }>
-
-
- }
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
}
\ No newline at end of file
diff --git a/raven-app/src/utils/channel/ChannelRedirect.tsx b/raven-app/src/utils/channel/ChannelRedirect.tsx
index 47a3b23b8..77345a237 100644
--- a/raven-app/src/utils/channel/ChannelRedirect.tsx
+++ b/raven-app/src/utils/channel/ChannelRedirect.tsx
@@ -1,4 +1,11 @@
-import { Navigate } from 'react-router-dom'
+import { Suspense, useEffect } from 'react'
+import { Outlet, useLocation, useNavigate } from 'react-router-dom'
+import { hasRavenUserRole } from '../roles'
+import { FullPageLoader } from '@/components/layout/Loaders'
+import AddRavenUsersPage from '@/pages/AddRavenUsersPage'
+import { ActiveUsersProvider } from '../users/ActiveUsersProvider'
+import { UserListProvider } from '../users/UserListProvider'
+import { ChannelListProvider } from './ChannelListProvider'
/**
* Redirects to the last channel visited by the user
@@ -8,5 +15,30 @@ export const ChannelRedirect = () => {
const lastChannel = localStorage.getItem('ravenLastChannel') ?? 'general'
- return
+ const navigate = useNavigate()
+ const { pathname } = useLocation()
+
+ const isRavenUser = hasRavenUserRole()
+
+ useEffect(() => {
+ if (isRavenUser && (pathname === '/channel' || pathname === '/')) navigate(`/channel/${lastChannel}`, {
+ replace: true
+ })
+ }, [pathname, isRavenUser])
+
+ if (isRavenUser) {
+ return (
+
+
+
+
+
+
+
+ )
+
+ }
+ return }>
+
+
}
\ No newline at end of file
diff --git a/raven-app/src/utils/roles.ts b/raven-app/src/utils/roles.ts
index 958acc23e..395a446ef 100644
--- a/raven-app/src/utils/roles.ts
+++ b/raven-app/src/utils/roles.ts
@@ -1,6 +1,10 @@
export const hasRavenUserRole = () => {
+ if (import.meta.env.PROD) {
//@ts-expect-error
return (window?.frappe?.boot?.user?.roles ?? []).includes('Raven User');
+ } else {
+ return true
+ }
}
export const isSystemManager = () => {
From 337b80163f952c12c0265e14d1f6dd2499753037 Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 23 Feb 2024 18:40:24 +0530
Subject: [PATCH 25/32] feat:User, Channel and Channel Type list on Webhook
Form
---
.../feature/integrations/webhooks/CreateWebhook.tsx | 3 ++-
.../feature/integrations/webhooks/ViewWebhook.tsx | 2 +-
.../components/feature/integrations/webhooks/utils.ts | 10 +++++-----
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx b/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx
index 5b3c22915..a8184a54e 100644
--- a/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/CreateWebhook.tsx
@@ -16,6 +16,7 @@ export const CreateWebhook = () => {
const methods = useForm({
defaultValues: {
enabled: 1,
+ timeout: 5
}
})
const { createDoc, loading, reset, error } = useFrappeCreateDoc()
@@ -59,7 +60,7 @@ export const CreateWebhook = () => {
Create Webhook
diff --git a/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
index b4f057f09..950548fc6 100644
--- a/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
@@ -131,7 +131,7 @@ export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc
Save Webhook
diff --git a/raven-app/src/components/feature/integrations/webhooks/utils.ts b/raven-app/src/components/feature/integrations/webhooks/utils.ts
index 24d4e89ce..9a298ba71 100644
--- a/raven-app/src/components/feature/integrations/webhooks/utils.ts
+++ b/raven-app/src/components/feature/integrations/webhooks/utils.ts
@@ -27,7 +27,7 @@ export const TriggerEvents: TriggerEventField[] = [
},
{
key: 'emoji_reaction',
- label: 'Message Reaction',
+ label: 'Message Reactied On',
doctype: 'Raven Message Reaction',
event: 'after_insert'
},
@@ -57,13 +57,13 @@ export const TriggerEvents: TriggerEventField[] = [
},
{
key: 'raven_user_added',
- label: 'Raven User Added',
+ label: 'User Added',
doctype: 'Raven User',
event: 'after_insert'
},
{
key: 'raven_user_deleted',
- label: 'Raven User Deleted',
+ label: 'User Deleted',
doctype: 'Raven User',
event: 'on_trash'
}
@@ -302,7 +302,7 @@ export const DoctypeFieldList: {
},
{
doctype: 'Raven User',
- events: ['Raven User Added', 'Raven User Deleted'],
+ events: ['User Added', 'User Deleted'],
fields: [
{
fieldname: 'user',
@@ -344,7 +344,7 @@ export const DoctypeFieldList: {
},
{
doctype: 'Raven Message Reaction',
- events: ['Message Reaction'],
+ events: ['Message Reacted On'],
fields: [
{
fieldname: 'reaction',
From 24c9862437c73a798711d42f9753a3d97b4926be Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 23 Feb 2024 18:41:19 +0530
Subject: [PATCH 26/32] feat:User, Channel and Channel Type Select Dropdown
---
.../integrations/webhooks/WebhookForm.tsx | 184 ++++++++++++++----
1 file changed, 144 insertions(+), 40 deletions(-)
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx
index 6fb78a60f..49e5fd9a9 100644
--- a/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookForm.tsx
@@ -1,13 +1,17 @@
import React, { useContext } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
-import { Box, Checkbox, Flex, TextFieldInput, Select, TextArea, Heading, Text } from '@radix-ui/themes';
+import { Box, Checkbox, Flex, TextFieldInput, Select, TextArea, Heading, Text, } from '@radix-ui/themes';
import { ErrorText, HelperText, Label } from '@/components/common/Form';
import { WebhookData } from './WebhookReturnDataFieldTable';
import { WebhookHeaders } from './WebhookHeaders';
import { TriggerEvents } from './utils';
import { RavenWebhook } from '@/types/RavenIntegrations/RavenWebhook';
-import { UserListContext } from '@/utils/users/UserListProvider';
-import { ChannelListContext, ChannelListContextType } from '@/utils/channel/ChannelListProvider';
+import { UserFields, UserListContext } from '@/utils/users/UserListProvider';
+import { ChannelListContext, ChannelListContextType, ChannelListItem, } from '@/utils/channel/ChannelListProvider';
+import { UserAvatar } from '@/components/common/UserAvatar';
+import { SidebarIcon } from '@/components/layout/Sidebar';
+import { useGetUser } from '@/hooks/useGetUser';
+import { ChannelIcon } from '@/utils/layout/channelIcon';
export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
const { register, formState: { errors }, control, setValue, watch } = useFormContext()
@@ -18,6 +22,10 @@ export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
const conditionOn = watch('conditions_on')
+ const { users } = useContext(UserListContext)
+
+ const { channels } = useContext(ChannelListContext) as ChannelListContextType
+
return (
{isEdit === false ?
@@ -76,8 +84,9 @@ export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
})} />
{errors?.webhook_secret && {errors.webhook_secret.message} }
: null}
-
-
+
+
+
Trigger Event
{
{
+ if (!e.target.value) {
+ setValue('conditions_on', '')
+ setValue('condition', '')
+ setValue('channel_id', '')
+ setValue('user', '')
+ setValue('channel_type', '')
+ }
+ }
+ }}
render={({ field }) => (
field.onChange(!field.value)} />
@@ -125,17 +145,28 @@ export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
{errors?.enable_security && {errors.enable_security.message} }
{needCondition &&
-
- Condition On
+
+ Trigger On
{
+ if (e.target.value) {
+ setValue('condition', '')
+ setValue('channel_id', '')
+ setValue('user', '')
+ setValue('channel_type', '')
+ }
+ }
+ }}
render={({ field }) => (
- Condition On
+ Trigger On
Channel
User
Channel Type
@@ -150,14 +181,14 @@ export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
}
{conditionOn === 'Custom' ?
-
Condition
The webhook will be triggered if this expression is true
-
+
Condition Examples :
doc.channel_id == 'general'
@@ -166,38 +197,111 @@ export const WebhookForm = ({ isEdit = false }: { isEdit?: boolean }) => {
: conditionOn === 'Channel' ?
- Channel ID
-
- Channel ID on which the condition will be applied
+
+ Channel
+ (
+
+
+
+
+ Channel
+ {
+ channels.map((channel, index) => (
+
+
+
+ ))
+ }
+
+
+
+ )}
+ />
+ Channel ID on which the condition will be applied
+
: conditionOn === 'User' ?
- User ID
-
- User ID on which the condition will be applied
+
+ User
+ (
+
+
+
+
+ User
+ {
+ users.map((user, index) => (
+
+
+
+ ))
+ }
+
+
+
+ )}
+ />
+ User ID on which the condition will be applied
+
: conditionOn === 'Channel Type' ?
- Channel Type
- (
-
-
-
-
- Channel Type
- Public
- Private
- Open
- Direct Message
- Self Message
-
-
-
- )}
- />
- Channel Type on which the condition will be applied
+
+ Channel Type
+ (
+
+
+
+
+ Channel Type
+ Public
+ Private
+ Open
+ Direct Message
+ Self Message
+
+
+
+ )}
+ />
+ Channel Type on which the condition will be applied
+
: null}
-
-
+
+
+
+
)
-}
\ No newline at end of file
+}
+
+const DirectMessageItem = ({ user }: { user: UserFields }) => {
+
+ const userData = useGetUser(user?.name)
+
+ return
+
+
+
+
+
+ {userData?.full_name ?? user?.name}
+
+
+
+}
+
+const ChannelItem = ({ channel }: { channel: ChannelListItem }) => {
+ return
+
+
+ {channel.channel_name}
+
+
+}
From 45ab5151c4795beb07fa9edbda98228e9aab35e3 Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 23 Feb 2024 18:49:39 +0530
Subject: [PATCH 27/32] feat:unique key and header
---
.../doctype/raven_webhook/raven_webhook.py | 54 ++++++++++++++-----
1 file changed, 40 insertions(+), 14 deletions(-)
diff --git a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
index 2a2b30ac6..7a733c6dd 100644
--- a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
+++ b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
@@ -38,9 +38,11 @@ class RavenWebhook(Document):
def validate(self):
# 1. Check if webhook name is unique
- # 2. Check if webhook ID is exists
- # 3. If exist then update the webhook
- # 4. If not exist then create the webhook
+ # 2. Check if webhook_data and webhook_headers are unique
+ # 3. Check if webhook ID is exists
+ # 4. If exist then update the webhook
+ # 5. If not exist then create the webhook
+
# 1. Check if webhook name is unique
webhook = frappe.get_all('Raven Webhook', filters={
@@ -48,15 +50,24 @@ def validate(self):
if webhook:
frappe.throw('Webhook name already exists')
- # 2. Check if webhook ID is exists
+ # 2. Check if webhook_data and webhook_headers are unique
+ webhook_data_keys = [data.key for data in self.webhook_data]
+ webhook_header_keys = [data.key for data in self.webhook_headers]
+ if len(webhook_data_keys) != len(set(webhook_data_keys)):
+ frappe.throw('Webhook Data keys should be unique')
+ if len(webhook_header_keys) != len(set(webhook_header_keys)):
+ frappe.throw('Webhook Headers keys should be unique')
+
+ # 3. Check if webhook ID is exists
if self.webhook:
- # Update the webhook
+ # 4. Update the webhook
self.update_webhook()
else:
- # Create the webhook
+ # 5. Create the webhook
self.create_webhook()
+
def on_trash(self):
# Delete the webhook
if self.webhook:
@@ -98,17 +109,32 @@ def update_webhook(self):
webhook_doc.save()
def set_webhook_data_and_headers(self, webhook_doc):
+ # Set the webhook data and headers
+
+ # get the existing webhook data and headers
+ webhook_header = webhook_doc.get('webhook_headers', [])
+ webhook_data = webhook_doc.get('webhook_data', [])
+
+ # get the existing webhook data and headers keys
+ webhook_data_keys = [data.key for data in self.webhook_data]
+ webhook_header_keys = [data.key for data in self.webhook_headers]
+
+ # remove the existing webhook data and headers
+ # which are not in the current webhook data and headers
+ # and append the new webhook data and headers
for data in self.webhook_data:
- webhook_doc.append('webhook_data', {
- 'key': data.key,
- 'fieldname': data.fieldname,
- })
+ if data.key not in webhook_data_keys:
+ webhook_doc.append('webhook_data', {
+ 'key': data.key,
+ 'fieldname': data.fieldname,
+ })
for data in self.webhook_headers:
- webhook_doc.append('webhook_headers', {
- 'key': data.key,
- 'value': data.value,
- })
+ if data.key not in webhook_header_keys:
+ webhook_doc.append('webhook_headers', {
+ 'key': data.key,
+ 'value': data.value,
+ })
def get_doctype_and_event(self):
doctypes_and_events = [
From 8157da40c2a97b1bdcd11389a3f921702c23844f Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 23 Feb 2024 19:39:28 +0530
Subject: [PATCH 28/32] feat:Preview of Response
---
.../webhooks/WebhookReturnDataFieldTable.tsx | 105 +++--
.../feature/integrations/webhooks/utils.ts | 391 +++++++++++++++++-
.../doctype/raven_webhook/raven_webhook.py | 15 +-
3 files changed, 480 insertions(+), 31 deletions(-)
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
index e21fa8d82..b07fc2199 100644
--- a/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
@@ -1,11 +1,11 @@
-import { HelperText } from "@/components/common/Form";
+import { HelperText, Label } from "@/components/common/Form";
import { Webhook } from "@/types/Integrations/Webhook";
-import { Flex, Box, Heading, Table, TextFieldInput, IconButton, Button, Select, Text, Dialog, Badge } from "@radix-ui/themes";
-import { useCallback, useMemo, useState } from "react";
+import { Flex, Box, Heading, Table, TextFieldInput, IconButton, Button, Select, Dialog, Badge } from "@radix-ui/themes";
+import { useMemo, useState } from "react";
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
import { BiInfoCircle, BiMinusCircle } from "react-icons/bi";
import { BsEye, BsPlus } from "react-icons/bs";
-import { FieldsData, TriggerEvents } from "./utils";
+import { FieldsData, SampleData, } from "./utils";
import { DoctypeFieldList } from './utils'
import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog";
import { RavenWebhook } from "@/types/RavenIntegrations/RavenWebhook";
@@ -32,11 +32,11 @@ export const WebhookData = () => {
setFieldIndex(null)
}
- // const [previewOpen, setPreviewOpen] = useState(false);
+ const [previewOpen, setPreviewOpen] = useState(false);
- // const onPreviewClose = () => {
- // setPreviewOpen(false)
- // }
+ const onPreviewClose = () => {
+ setPreviewOpen(false)
+ }
return (
@@ -47,18 +47,18 @@ export const WebhookData = () => {
The fields you want to return in the webhook response.
- {/*
+
{ }} variant="ghost" color="gray" style={{
width: 'fit-content',
- }}>
+ }} disabled={fields.length === 0}>
Preview
-
+
- */}
+
append({ fieldname: '', key: '' })} variant="outline" style={{
width: 'fit-content',
}}>
@@ -180,16 +180,22 @@ export const FieldInfoModal = ({ fieldIndex, triggerEvent, onClose }: { fieldInd
{fieldData?.description}
-
- {fieldData?.example && <>
- Example:
-
-
-
-
- {JSON.parse(JSON.stringify(fieldData?.example || ''))}
-
- >}
+ {
+ fieldData?.example &&
+
+ Example
+
+
+
+
+ {JSON.stringify(fieldData?.example, null, 2)}
+
+
+
+
+ }
@@ -203,7 +209,29 @@ export const FieldInfoModal = ({ fieldIndex, triggerEvent, onClose }: { fieldInd
export const PreviewModal = ({ onClose }: { onClose: () => void }) => {
- const { register, watch } = useFormContext()
+ const { watch } = useFormContext()
+
+ const webhookTrigger = watch('webhook_trigger')
+
+ const webhookData = watch('webhook_data')
+
+ const [examples, setExamples] = useState('')
+
+ const exampleList = useMemo(() => {
+ return SampleData?.find(sample => sample.trigger_event.includes(webhookTrigger))?.examples?.map(example => example.name) || []
+ }, [webhookTrigger])
+
+ const jsonData = useMemo(() => {
+ const exampleData = SampleData?.find(sample => sample.trigger_event.includes(webhookTrigger))?.examples?.find(example => example.name === examples)
+
+ const webhookTriggerKeys = webhookData?.map(data => data.key)
+ const obj = {}
+ webhookTriggerKeys?.forEach(key => {
+ // @ts-ignore
+ obj[key as string] = exampleData?.fields?.find(field => field.field === key)?.value
+ })
+ return JSON.stringify(obj, null, 2)
+ }, [examples, webhookData, webhookTrigger])
return (
@@ -212,7 +240,36 @@ export const PreviewModal = ({ onClose }: { onClose: () => void }) => {
-
+
+
+ Example
+
+ setExamples(e)}>
+
+
+
+ Examples
+ {exampleList.map((example, index) => (
+ {example}
+ ))}
+
+
+
+
+
+
+
+ Response Data
+
+
+
+
+ {jsonData}
+
+
+
diff --git a/raven-app/src/components/feature/integrations/webhooks/utils.ts b/raven-app/src/components/feature/integrations/webhooks/utils.ts
index 9a298ba71..fb2c2504c 100644
--- a/raven-app/src/components/feature/integrations/webhooks/utils.ts
+++ b/raven-app/src/components/feature/integrations/webhooks/utils.ts
@@ -370,4 +370,393 @@ export const DoctypeFieldList: {
...commonFields
]
}
- ]
\ No newline at end of file
+ ]
+
+export const SampleData = [
+ {
+ trigger_event: ['Channel Created', 'Channel Deleted'],
+ examples: [
+ {
+ name: 'general',
+ fields: [
+ {
+ field: 'channel_name',
+ value: 'general'
+ },
+ {
+ field: 'channel_description',
+ value: 'General discussion'
+ },
+ {
+ field: 'type',
+ value: 'Public'
+ },
+ {
+ field: 'is_direct_message',
+ value: '0'
+ },
+ {
+ field: 'is_self_message',
+ value: '0'
+ },
+ {
+ field: 'is_archived',
+ value: '0'
+ }
+ ]
+ },
+ {
+ name: 'kings-landing',
+ fields: [
+ {
+ field: 'channel_name',
+ value: 'kings-landing'
+ },
+ {
+ field: 'channel_description',
+ value: 'The capital of Westeros and the Seven Kingdoms.'
+ },
+ {
+ field: 'type',
+ value: 'Public'
+ },
+ {
+ field: 'is_direct_message',
+ value: '0'
+ },
+ {
+ field: 'is_self_message',
+ value: '0'
+ },
+ {
+ field: 'is_archived',
+ value: '0'
+ }
+ ]
+ },
+ {
+ name: 'winterfell',
+ fields: [
+ {
+ field: 'channel_name',
+ value: 'winterfell'
+ },
+ {
+ field: 'channel_description',
+ value: 'The ancestral home of House Stark.'
+ },
+ {
+ field: 'type',
+ value: 'Public'
+ },
+ {
+ field: 'is_direct_message',
+ value: '0'
+ },
+ {
+ field: 'is_self_message',
+ value: '0'
+ },
+ {
+ field: 'is_archived',
+ value: '0'
+ }
+ ]
+ },
+ {
+ name: 'dragons-bay',
+ fields: [
+ {
+ field: 'channel_name',
+ value: 'dragons-bay'
+ },
+ {
+ field: 'channel_description',
+ value: 'The place where dragons are born.'
+ },
+ {
+ field: 'type',
+ value: 'Public'
+ },
+ {
+ field: 'is_direct_message',
+ value: '0'
+ },
+ {
+ field: 'is_self_message',
+ value: '0'
+ },
+ {
+ field: 'is_archived',
+ value: '0'
+ }
+ ]
+ },
+ {
+ name: 'white-walkers',
+ fields: [
+ {
+ field: 'channel_name',
+ value: 'white-walkers'
+ },
+ {
+ field: 'channel_description',
+ value: 'The army of the dead.'
+ },
+ {
+ field: 'type',
+ value: 'Private'
+ },
+ {
+ field: 'is_direct_message',
+ value: '0'
+ },
+ {
+ field: 'is_self_message',
+ value: '0'
+ },
+ {
+ field: 'is_archived',
+ value: '0'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ trigger_event: ['Message Sent', 'Message Edited', 'Message Deleted'],
+ examples: [
+ {
+ name: 'Hello, World!',
+ fields: [
+ {
+ field: 'channel_id',
+ value: 'general'
+ },
+ {
+ field: 'text',
+ value: 'Hello, World!'
+ },
+ {
+ field: 'message_type',
+ value: 'Text'
+ },
+ {
+ field: 'is_reply',
+ value: '0'
+ }
+ ]
+ },
+ {
+ name: 'The Iron Throne is mine!',
+ fields: [
+ {
+ field: 'channel_id',
+ value: 'kings-landing'
+ },
+ {
+ field: 'text',
+ value: 'The Iron Throne is mine!'
+ },
+ {
+ field: 'message_type',
+ value: 'Text'
+ },
+ {
+ field: 'is_reply',
+ value: '0'
+ }
+ ]
+ },
+ {
+ name: 'Winter is coming.',
+ fields: [
+ {
+ field: 'channel_id',
+ value: 'winterfell'
+ },
+ {
+ field: 'text',
+ value: 'Winter is coming.'
+ },
+ {
+ field: 'message_type',
+ value: 'Text'
+ },
+ {
+ field: 'is_reply',
+ value: '0'
+ }
+ ]
+ },
+ {
+ name: 'Dracarys!',
+ fields: [
+ {
+ field: 'channel_id',
+ value: 'dragons-bay'
+ },
+ {
+ field: 'text',
+ value: 'Dracarys!'
+ },
+ {
+ field: 'message_type',
+ value: 'Text'
+ },
+ {
+ field: 'is_reply',
+ value: '0'
+ }
+ ]
+ },
+ {
+ name: 'The Night King is coming.',
+ fields: [
+ {
+ field: 'channel_id',
+ value: 'white-walkers'
+ },
+ {
+ field: 'text',
+ value: 'The Night King is coming.'
+ },
+ {
+ field: 'message_type',
+ value: 'Text'
+ },
+ {
+ field: 'is_reply',
+ value: '0'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ trigger_event: ['Channel Member Added', 'Channel Member Deleted'],
+ examples: [
+ {
+ name: 'Jon snow',
+ fields: [
+ {
+ field: 'channel_id',
+ value: 'general'
+ },
+ {
+ field: 'user_id',
+ value: 'jon-snow'
+ },
+ {
+ field: 'is_admin',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'Daenerys Targaryen',
+ fields: [
+ {
+ field: 'channel_id',
+ value: 'kings-landing'
+ },
+ {
+ field: 'user_id',
+ value: 'daenerys-targaryen'
+ },
+ {
+ field: 'is_admin',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'Arya Stark',
+ fields: [
+ {
+ field: 'channel_id',
+ value: 'winterfell'
+ },
+ {
+ field: 'user_id',
+ value: 'arya-stark'
+ },
+ {
+ field: 'is_admin',
+ value: '1'
+ }
+ ]
+ },
+ ]
+ },
+ {
+ trigger_event: ['User Added', 'User Deleted'],
+ examples: [
+ {
+ name: 'Jon Snow',
+ fields: [
+ {
+ field: 'user',
+ value: 'jon-snow'
+ },
+ {
+ field: 'full_name',
+ value: 'Jon Snow'
+ },
+ {
+ field: 'first_name',
+ value: 'Jon'
+ },
+ {
+ field: 'enabled',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'Daenerys Targaryen',
+ fields: [
+ {
+ field: 'user',
+ value: 'daenerys-targaryen'
+ },
+ {
+ field: 'full_name',
+ value: 'Daenerys Targaryen'
+ },
+ {
+ field: 'first_name',
+ value: 'Daenerys'
+ },
+ {
+ field: 'enabled',
+ value: '1'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ trigger_event: ['Message Reacted On'],
+ examples: [
+ {
+ name: '👍',
+ fields: [
+ {
+ field: 'reaction',
+ value: '👍'
+ }
+ ]
+ },
+ {
+ name: '👎',
+ fields: [
+ {
+ field: 'reaction',
+ value: '👎'
+ }
+ ]
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
index 7a733c6dd..862a5c49a 100644
--- a/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
+++ b/raven/raven_integrations/doctype/raven_webhook/raven_webhook.py
@@ -39,9 +39,7 @@ class RavenWebhook(Document):
def validate(self):
# 1. Check if webhook name is unique
# 2. Check if webhook_data and webhook_headers are unique
- # 3. Check if webhook ID is exists
- # 4. If exist then update the webhook
- # 5. If not exist then create the webhook
+
# 1. Check if webhook name is unique
@@ -58,13 +56,18 @@ def validate(self):
if len(webhook_header_keys) != len(set(webhook_header_keys)):
frappe.throw('Webhook Headers keys should be unique')
- # 3. Check if webhook ID is exists
+ def before_save(self):
+ # 1. Check if webhook ID is exists
+ # 2. If exist then update the webhook
+ # 3. If not exist then create the webhook
+
+ # 1. Check if webhook ID is exists
if self.webhook:
- # 4. Update the webhook
+ # 2. Update the webhook
self.update_webhook()
else:
- # 5. Create the webhook
+ # 3. Create the webhook
self.create_webhook()
From 7d9a6a61948092f626253016340fb5c4df2e484f Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 1 Mar 2024 13:24:18 +0530
Subject: [PATCH 29/32] feat:removed unwanted files
---
.../integrations/webhooks/ViewWebhook.tsx | 8 +-
.../feature/settings/Integrations.tsx | 3 -
.../settings/api-events/APIEventsForm.tsx | 105 -------------
.../doctype-events/DocTypeEventsForm.tsx | 81 ----------
.../temporal-events/TemporalEventsForm.tsx | 139 ------------------
.../{Sidebar.tsx => SettingsSidebar.tsx} | 4 +-
...idebarBody.tsx => SettingsSidebarBody.tsx} | 0
...arHeader.tsx => SettingsSidebarHeader.tsx} | 0
.../ServerScripts/APIEvents/APIEvents.tsx | 75 ----------
.../APIEvents/CreateAPIEvents.tsx | 63 --------
.../ServerScripts/APIEvents/ViewAPIEvents.tsx | 127 ----------------
.../ServerScripts/CreateDocTypeEvent.tsx | 60 --------
.../settings/ServerScripts/DocTypeEvents.tsx | 73 ---------
.../TemporalEvents/CreateTemporalEvent.tsx | 65 --------
.../TemporalEvents/TemporalEvents.tsx | 75 ----------
.../TemporalEvents/ViewTemporalEvent.tsx | 130 ----------------
.../ServerScripts/ViewDocTypeEvent.tsx | 120 ---------------
raven-app/src/pages/settings/Settings.tsx | 2 +-
raven-app/src/utils/removeFrappeFields.ts | 14 --
19 files changed, 5 insertions(+), 1139 deletions(-)
delete mode 100644 raven-app/src/components/feature/settings/api-events/APIEventsForm.tsx
delete mode 100644 raven-app/src/components/feature/settings/doctype-events/DocTypeEventsForm.tsx
delete mode 100644 raven-app/src/components/feature/settings/temporal-events/TemporalEventsForm.tsx
rename raven-app/src/components/layout/Settings/{Sidebar.tsx => SettingsSidebar.tsx} (85%)
rename raven-app/src/components/layout/Settings/{SidebarBody.tsx => SettingsSidebarBody.tsx} (100%)
rename raven-app/src/components/layout/Settings/{SidebarHeader.tsx => SettingsSidebarHeader.tsx} (100%)
delete mode 100644 raven-app/src/pages/settings/ServerScripts/APIEvents/APIEvents.tsx
delete mode 100644 raven-app/src/pages/settings/ServerScripts/APIEvents/CreateAPIEvents.tsx
delete mode 100644 raven-app/src/pages/settings/ServerScripts/APIEvents/ViewAPIEvents.tsx
delete mode 100644 raven-app/src/pages/settings/ServerScripts/CreateDocTypeEvent.tsx
delete mode 100644 raven-app/src/pages/settings/ServerScripts/DocTypeEvents.tsx
delete mode 100644 raven-app/src/pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent.tsx
delete mode 100644 raven-app/src/pages/settings/ServerScripts/TemporalEvents/TemporalEvents.tsx
delete mode 100644 raven-app/src/pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent.tsx
delete mode 100644 raven-app/src/pages/settings/ServerScripts/ViewDocTypeEvent.tsx
delete mode 100644 raven-app/src/utils/removeFrappeFields.ts
diff --git a/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
index 950548fc6..045c2112c 100644
--- a/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/ViewWebhook.tsx
@@ -1,7 +1,5 @@
import { ErrorBanner } from "@/components/layout/AlertBanner"
import { FullPageLoader } from "@/components/layout/Loaders"
-import { Webhook } from "@/types/Integrations/Webhook"
-import { removeFrappeFields } from "@/utils/removeFrappeFields"
import { AlertDialog, Badge, Box, Button, DropdownMenu, Flex, IconButton, Separator, Text } from "@radix-ui/themes"
import { FrappeDoc, useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
import { FieldValues, FormProvider, useForm } from "react-hook-form"
@@ -38,10 +36,9 @@ ViewWebhook.displayName = 'ViewWebhook'
export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc, mutate: KeyedMutator> }) => {
- const formFields = removeFrappeFields(data)
const methods = useForm({
defaultValues: {
- ...formFields,
+ ...data,
docstatus: data.docstatus
}
})
@@ -62,9 +59,8 @@ export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc {
-
-
-
diff --git a/raven-app/src/components/feature/settings/api-events/APIEventsForm.tsx b/raven-app/src/components/feature/settings/api-events/APIEventsForm.tsx
deleted file mode 100644
index cbd8624b7..000000000
--- a/raven-app/src/components/feature/settings/api-events/APIEventsForm.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import { Label, HelperText } from "@/components/common/Form"
-import { Flex, Box, TextField, TextArea, Checkbox, Grid, Code } from "@radix-ui/themes"
-import { Controller, useFormContext } from "react-hook-form"
-
-export interface Props {
- edit?: boolean
-}
-
-export const APIEventsForm = ({ edit = false }: Props) => {
-
- const { register, watch, control } = useFormContext()
-
- const enableRateLimit = watch('enable_rate_limit')
-
- return (
-
- {!edit &&
- Name
-
- }
-
-
- API Method
-
- The API endpoint for which this event will be triggered (automatically prefixed with /api/method
).
-
-
-
- (
-
- field.onChange(!field.value)} />
- Allow anyone to use without logging in
-
- )}
- />
-
-
-
- {/* TODO: Add a script editor here (maybe use Monaco Editor) */}
- Script
-
- Your custom script to be be called via this API event.
-
-
-
- (
-
- field.onChange(!field.value)} />
- Enable Rate Limit
-
- )}
- />
-
-
- {enableRateLimit ?
-
- Rate Limit Count
-
- Number of requests allowed in the specified time.
-
-
-
- Rate Limit Seconds
-
- Time period in seconds.
-
- : null}
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/components/feature/settings/doctype-events/DocTypeEventsForm.tsx b/raven-app/src/components/feature/settings/doctype-events/DocTypeEventsForm.tsx
deleted file mode 100644
index 88267c433..000000000
--- a/raven-app/src/components/feature/settings/doctype-events/DocTypeEventsForm.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import { HelperText, Label } from "@/components/common/Form"
-import { Box, Flex, Grid, Link, Select, TextArea, TextField } from "@radix-ui/themes"
-import { useFormContext, Controller } from "react-hook-form"
-
-export interface Props {
- edit?: boolean
-}
-
-export const DocTypeEventsForm = ({ edit = false }: Props) => {
-
- const { register, control } = useFormContext()
-
- return (
-
- {!edit &&
- Name
-
- }
-
-
-
- Reference Document Type
-
- The event will be triggered for this DocType.
-
-
-
- DocType Event
- (
- field.onChange(value)} defaultValue="Before Insert">
-
-
-
- DocType Event
- Before Insert
- Before Validate
- Before Save
- After Insert
- After Save
- Before Submit
- After Submit
- Before Cancel
- After Cancel
- Before Save (Submitted Documents)
- After Save (Submitted Documents)
- On Payment Authorization
-
-
-
- )}
- />
- The event on which you want to run this script. Learn more
-
-
-
-
- {/* TODO: Add a script editor here (maybe use Monaco Editor) */}
- Script
-
- Your custom script to be be called via this document event.
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/components/feature/settings/temporal-events/TemporalEventsForm.tsx b/raven-app/src/components/feature/settings/temporal-events/TemporalEventsForm.tsx
deleted file mode 100644
index d7c5fa722..000000000
--- a/raven-app/src/components/feature/settings/temporal-events/TemporalEventsForm.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import { Label, HelperText } from "@/components/common/Form"
-import { Flex, Box, TextField, TextArea, Link, Select } from "@radix-ui/themes"
-import { Controller, useFormContext } from "react-hook-form"
-
-
-export interface Props {
- edit?: boolean
-}
-
-export const TemporalEventsForm = ({ edit = false }: Props) => {
-
- const { register, watch, control } = useFormContext()
-
- const frequency = watch('event_frequency')
-
- return (
-
- {!edit &&
- Name
-
- }
-
-
- How often should this event be triggered?
- (
- field.onChange(value)} defaultValue="Daily">
-
-
-
- Temporal Events
- Daily
- Weekly
- Monthly
- Yearly
- Daily Long
- Weekly Long
- Monthly Long
- Yearly Long
- Custom
-
-
-
- )}
- />
- The frequency at which this event will be triggered.
-
-
- {frequency === 'Cron' &&
- Cron Format
- {/* */}
-
- {/* //FIXME: Needs work - unstable component */}
-
- Starts with minute, hour, day of month, month, day of week. Learn more about cron format
- }
-
-
- {/* TODO: Add a script editor here (maybe use Monaco Editor) */}
- Script
-
- Your custom script to be be called via this API event.
-
-
- )
-}
-
-
-
-/**
- * AdvancedCronInput
- * This component is used to input cron format in a more advanced way
- * @param {string} name
- * */
-const AdvancedCronInput = ({ name, label, ...props }: { name: string; label: string }) => {
-
- const { register, setValue, watch, formState } = useFormContext();
-
- const error = formState.errors[name];
- const hasError = Boolean(error);
-
- const cronParts = ['minute', 'hour', 'day', 'month', 'dayOfWeek'];
-
- return (
-
-
- {cronParts.map((part, index) => (
- setValue(`${name}.${part}`, event.target.value)}
- style={{
- width: '40px',
- height: '40px',
- textAlign: 'center',
- fontSize: '16px',
- border: `1px solid ${hasError ? 'red' : 'gray'}`,
- borderRadius: '4px',
- borderColor: hasError ? 'red' : 'blue',
- }}
- />
- ))}
-
- {hasError && (
-
- {String(error?.message)}
-
- )}
-
- );
-};
-
-export default AdvancedCronInput;
diff --git a/raven-app/src/components/layout/Settings/Sidebar.tsx b/raven-app/src/components/layout/Settings/SettingsSidebar.tsx
similarity index 85%
rename from raven-app/src/components/layout/Settings/Sidebar.tsx
rename to raven-app/src/components/layout/Settings/SettingsSidebar.tsx
index 4cd74e0a8..0f59ff613 100644
--- a/raven-app/src/components/layout/Settings/Sidebar.tsx
+++ b/raven-app/src/components/layout/Settings/SettingsSidebar.tsx
@@ -1,7 +1,7 @@
import { Flex, Box, Separator } from "@radix-ui/themes"
-import { SidebarBody } from "./SidebarBody"
+import { SidebarBody } from "./SettingsSidebarBody"
import { SidebarFooter } from "../Sidebar/SidebarFooter"
-import { SidebarHeader } from "./SidebarHeader"
+import { SidebarHeader } from "./SettingsSidebarHeader"
export interface Props { }
diff --git a/raven-app/src/components/layout/Settings/SidebarBody.tsx b/raven-app/src/components/layout/Settings/SettingsSidebarBody.tsx
similarity index 100%
rename from raven-app/src/components/layout/Settings/SidebarBody.tsx
rename to raven-app/src/components/layout/Settings/SettingsSidebarBody.tsx
diff --git a/raven-app/src/components/layout/Settings/SidebarHeader.tsx b/raven-app/src/components/layout/Settings/SettingsSidebarHeader.tsx
similarity index 100%
rename from raven-app/src/components/layout/Settings/SidebarHeader.tsx
rename to raven-app/src/components/layout/Settings/SettingsSidebarHeader.tsx
diff --git a/raven-app/src/pages/settings/ServerScripts/APIEvents/APIEvents.tsx b/raven-app/src/pages/settings/ServerScripts/APIEvents/APIEvents.tsx
deleted file mode 100644
index 8bb7746f7..000000000
--- a/raven-app/src/pages/settings/ServerScripts/APIEvents/APIEvents.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { DateMonthAtHourMinuteAmPm } from "@/utils/dateConversions"
-import { Badge, Blockquote, Box, Button, Card, Code, Flex, Heading, Section, Table, Text } from "@radix-ui/themes"
-import { useFrappeGetDocList } from "frappe-react-sdk"
-import { Link, useNavigate } from "react-router-dom"
-
-export interface Props { }
-
-export const APIEvents = (props: Props) => {
-
- const navigate = useNavigate()
-
- const { data, error } = useFrappeGetDocList('Server Script', {
- fields: ['name', 'script_type', 'api_method', 'creation', 'modified', 'disabled'],
- filters: [['script_type', '=', 'API']],
- })
-
- return (
-
-
- API Events
- navigate('create')}>Add Event
-
-
-
- Lets say you want to perform some actions when an API Endpoint (method) is called.
- API Events enables you to write custom scripts that are triggered by events in an API Endpoint.
-
-
-
- {error && }
- {data &&
}
-
-
- )
-}
-
-
-const List = ({ data }: { data: any }) => {
- return (
-
-
-
-
-
- ID
- Status
- Endpoint
- Last modified
-
-
-
-
- {data?.map((item: any) => (
-
-
- {item.name}
-
-
-
- {item.disabled ? "Disabled" : "Enabled"}
-
-
- {item.api_method}
-
-
-
-
- ))}
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/APIEvents/CreateAPIEvents.tsx b/raven-app/src/pages/settings/ServerScripts/APIEvents/CreateAPIEvents.tsx
deleted file mode 100644
index d13632a98..000000000
--- a/raven-app/src/pages/settings/ServerScripts/APIEvents/CreateAPIEvents.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { APIEventsForm } from "@/components/feature/settings/api-events/APIEventsForm"
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { useToast } from "@/hooks/useToast"
-import { Box, Button, Flex, Heading, Section } from "@radix-ui/themes"
-import { useFrappeCreateDoc } from "frappe-react-sdk"
-import { FormProvider, useForm } from "react-hook-form"
-import { FiArrowLeft } from "react-icons/fi"
-import { useNavigate } from "react-router-dom"
-
-export interface Props { }
-
-export const CreateAPIEvent = (props: Props) => {
-
- const navigate = useNavigate()
-
- const methods = useForm()
-
- const { createDoc, error } = useFrappeCreateDoc()
-
- const { toast } = useToast()
-
- const onSubmit = (data: any) => {
- createDoc('Server Script', {
- name: data.name,
- script_type: 'API',
- api_method: data.api_method,
- allow_guest: data.allow_guest ?? false,
- script: data.script,
- enable_rate_limit: data.enable_rate_limit ?? false,
- rate_limit_count: data.rate_limit_count,
- rate_limit_seconds: data.rate_limit_seconds,
- })
- .then((doc) => {
- if (doc) {
- navigate(`../${doc?.name}`)
- }
- toast({
- title: `API Event ${data.name} updated`,
- variant: 'success',
- })
- })
- }
- //TODO: Figure out a way to show _server_messages in the UI (especially the script editor might have some errors that we need to show to the user)
- return (
-
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/APIEvents/ViewAPIEvents.tsx b/raven-app/src/pages/settings/ServerScripts/APIEvents/ViewAPIEvents.tsx
deleted file mode 100644
index db572b290..000000000
--- a/raven-app/src/pages/settings/ServerScripts/APIEvents/ViewAPIEvents.tsx
+++ /dev/null
@@ -1,127 +0,0 @@
-import { APIEventsForm } from "@/components/feature/settings/api-events/APIEventsForm"
-import { DeleteAlert } from "@/components/feature/settings/common/DeleteAlert"
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { useToast } from "@/hooks/useToast"
-import { Badge, Box, Button, DropdownMenu, Flex, Heading, Section } from "@radix-ui/themes"
-import { useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
-import { useState } from "react"
-import { FormProvider, useForm } from "react-hook-form"
-import { FiArrowLeft } from "react-icons/fi"
-import { useNavigate, useParams } from "react-router-dom"
-
-export interface Props { }
-
-export const ViewAPIEvent = (props: Props) => {
-
- const { apiID } = useParams<{ apiID: string }>()
-
- const { data: docEventData, error, mutate } = useFrappeGetDoc('Server Script', apiID)
-
- return (
- <>
- {error && }
- {docEventData && }
- >
- )
-}
-
-
-const ViewAPIEventPage = ({ data, onUpdate }: { data: any, onUpdate: () => void }) => {
-
- const [isOpen, setIsOpen] = useState(false)
-
- const navigate = useNavigate()
-
- const methods = useForm({
- defaultValues: {
- name: data.name,
- api_method: data.api_method,
- allow_guest: data.allow_guest ?? false,
- script: data.script,
- enable_rate_limit: data.enable_rate_limit,
- rate_limit_count: data.rate_limit_count ?? 5,
- rate_limit_seconds: data.rate_limit_seconds ?? 86400,
- }
- })
-
- const { updateDoc, error } = useFrappeUpdateDoc()
-
- const { toast } = useToast()
-
- const onSubmit = (data: any) => {
- console.log(data)
- updateDoc('Server Script', data.name, {
- api_method: data.api_method,
- allow_guest: data.allow_guest,
- script: data.script,
- enable_rate_limit: data.enable_rate_limit,
- rate_limit_count: data.rate_limit_count,
- rate_limit_seconds: data.rate_limit_seconds,
- })
- .then(() => {
- onUpdate()
- toast({
- title: `API Event ${data.name} updated`,
- variant: 'success',
- })
- })
- }
-
- const onStatusToggle = () => {
- updateDoc('Server Script', data.name, {
- disabled: !data.disabled
- })
- .then(() => {
- onUpdate()
- toast({
- title: `API Event ${data.name} ${data.disabled ? "enabled" : "disabled"}`,
- variant: 'success',
- })
- })
- }
-
- const onClose = () => {
- setIsOpen(false)
- }
-
- return (
-
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/CreateDocTypeEvent.tsx b/raven-app/src/pages/settings/ServerScripts/CreateDocTypeEvent.tsx
deleted file mode 100644
index 2c0466ba3..000000000
--- a/raven-app/src/pages/settings/ServerScripts/CreateDocTypeEvent.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { DocTypeEventsForm } from "@/components/feature/settings/doctype-events/DocTypeEventsForm"
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { useToast } from "@/hooks/useToast"
-import { Box, Button, Flex, Heading, Section } from "@radix-ui/themes"
-import { useFrappeCreateDoc } from "frappe-react-sdk"
-import { FormProvider, useForm } from "react-hook-form"
-import { FiArrowLeft } from "react-icons/fi"
-import { useNavigate } from "react-router-dom"
-
-export interface Props { }
-
-export const CreateDocTypeEvent = (props: Props) => {
-
- const navigate = useNavigate()
-
- const methods = useForm()
-
- const { createDoc, error } = useFrappeCreateDoc()
-
- const { toast } = useToast()
-
- const onSubmit = (data: any) => {
- createDoc('Server Script', {
- name: data.name,
- script_type: 'DocType Event',
- reference_doctype: data.reference_document_type,
- doctype_event: data.document_event,
- script: data.script,
- })
- .then((doc) => {
- if (doc) {
- navigate(`../${doc?.name}`)
- }
- toast({
- title: `DocType Event ${data.name} updated`,
- variant: 'success',
- })
- })
- }
- //TODO: Figure out a way to show _server_messages in the UI (especially the script editor might have some errors that we need to show to the user)
- return (
-
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/DocTypeEvents.tsx b/raven-app/src/pages/settings/ServerScripts/DocTypeEvents.tsx
deleted file mode 100644
index a562f10e4..000000000
--- a/raven-app/src/pages/settings/ServerScripts/DocTypeEvents.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { DateMonthAtHourMinuteAmPm } from "@/utils/dateConversions"
-import { Badge, Blockquote, Box, Button, Card, Flex, Heading, Section, Table, Text } from "@radix-ui/themes"
-import { useFrappeGetDocList } from "frappe-react-sdk"
-import { Link, useNavigate } from "react-router-dom"
-
-export interface Props { }
-
-export const DocTypeEvents = (props: Props) => {
-
- const navigate = useNavigate()
-
- const { data, error } = useFrappeGetDocList('Server Script', {
- fields: ['name', 'script_type', 'reference_doctype', 'creation', 'modified', 'disabled'],
- filters: [['script_type', '=', 'DocType Event']],
- })
-
- return (
-
-
- DocType Events
- navigate('create')}>Add Event
-
-
-
- You might want to perform some actions when a Sales Invoice is saved or a Purchase Order is submitted.
- DocType Events enables you to write custom scripts that are triggered by events in a DocType.
-
-
-
- {error && }
- {data &&
}
-
-
- )
-}
-
-
-const List = ({ data }: { data: any }) => {
- return (
-
-
-
-
-
- ID
- Status
- DocType
- Last modified
-
-
-
-
- {data?.map((item: any) => (
-
-
- {item.name}
-
-
-
- {item.disabled ? "Disabled" : "Enabled"}
-
- {item.reference_doctype}
-
-
-
- ))}
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent.tsx b/raven-app/src/pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent.tsx
deleted file mode 100644
index 4e7c5db52..000000000
--- a/raven-app/src/pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { APIEventsForm } from "@/components/feature/settings/api-events/APIEventsForm"
-import { TemporalEventsForm } from "@/components/feature/settings/temporal-events/TemporalEventsForm"
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { useToast } from "@/hooks/useToast"
-import { Box, Button, Flex, Heading, Section } from "@radix-ui/themes"
-import { useFrappeCreateDoc } from "frappe-react-sdk"
-import { FormProvider, useForm } from "react-hook-form"
-import { FiArrowLeft } from "react-icons/fi"
-import { useNavigate } from "react-router-dom"
-
-export interface Props { }
-
-export const CreateTemporalEvent = (props: Props) => {
-
- const navigate = useNavigate()
-
- const methods = useForm()
-
- const { createDoc, error } = useFrappeCreateDoc()
-
- const { toast } = useToast()
-
- const onSubmit = (data: any) => {
- let cronFormat = ''
- if (data.event_frequency === 'Cron') {
- cronFormat = data.cron_format?.minute + data.cron_format?.hour + data.cron_format?.day + data.cron_format?.month + data.cron_format?.dayOfWeek
- }
- createDoc('Server Script', {
- name: data.name,
- script_type: 'Scheduler Event',
- event_frequency: data.event_frequency,
- cron_format: cronFormat,
- script: data.script,
- })
- .then((doc) => {
- if (doc) {
- navigate(`../${doc?.name}`)
- }
- toast({
- title: `Temporal Event created`,
- variant: 'success',
- })
- })
- }
- //TODO: Figure out a way to show _server_messages in the UI (especially the script editor might have some errors that we need to show to the user)
- return (
-
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/TemporalEvents/TemporalEvents.tsx b/raven-app/src/pages/settings/ServerScripts/TemporalEvents/TemporalEvents.tsx
deleted file mode 100644
index ea2046b1e..000000000
--- a/raven-app/src/pages/settings/ServerScripts/TemporalEvents/TemporalEvents.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { DateMonthAtHourMinuteAmPm } from "@/utils/dateConversions"
-import { Box, Flex, Heading, Button, Section, Blockquote, Badge, Card, Table, Text, Code } from "@radix-ui/themes"
-import { useFrappeGetDocList } from "frappe-react-sdk"
-import { Link, useNavigate } from "react-router-dom"
-
-export interface Props { }
-
-export const TemporalEvents = (props: Props) => {
-
- const navigate = useNavigate()
-
- const { data, error } = useFrappeGetDocList('Server Script', {
- fields: ['name', 'script_type', 'event_frequency', 'creation', 'modified', 'disabled'],
- filters: [['script_type', '=', 'Scheduler Event']],
- })
-
- return (
-
-
- Temporal Events
- navigate('create')}>Add Event
-
-
-
- Lets say you want to pay your dues every month. You can write a script that runs every month and pays your dues.
- Temporal Events enables you to write custom scripts that are triggered by time or interval.
-
-
-
- {error && }
- {data &&
}
-
-
- )
-}
-
-
-const List = ({ data }: { data: any }) => {
- return (
-
-
-
-
-
- ID
- Status
- Frequency
- Last modified
-
-
-
-
- {data?.map((item: any) => (
-
-
- {item.name}
-
-
-
- {item.disabled ? "Disabled" : "Enabled"}
-
-
- {item.event_frequency}
-
-
-
-
- ))}
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent.tsx b/raven-app/src/pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent.tsx
deleted file mode 100644
index 25c6e8319..000000000
--- a/raven-app/src/pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import { DeleteAlert } from "@/components/feature/settings/common/DeleteAlert"
-import { TemporalEventsForm } from "@/components/feature/settings/temporal-events/TemporalEventsForm"
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { useToast } from "@/hooks/useToast"
-import { Badge, Box, Button, DropdownMenu, Flex, Heading, Section } from "@radix-ui/themes"
-import { useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
-import { useState } from "react"
-import { FormProvider, useForm } from "react-hook-form"
-import { FiArrowLeft } from "react-icons/fi"
-import { useNavigate, useParams } from "react-router-dom"
-
-export interface Props { }
-
-export const ViewTemporalEvent = (props: Props) => {
-
- const { scriptID } = useParams<{ scriptID: string }>()
-
- const { data: eventData, error, mutate } = useFrappeGetDoc('Server Script', scriptID)
-
- return (
- <>
- {error && }
- {eventData && }
- >
- )
-}
-
-
-const ViewTemporalEventPage = ({ data, onUpdate }: { data: any, onUpdate: () => void }) => {
-
- const [isOpen, setIsOpen] = useState(false)
-
- const navigate = useNavigate()
-
- const methods = useForm({
- defaultValues: {
- name: data.name,
- event_frequency: data.event_frequency,
- script: data.script,
- cron_format: {
- 'minute': data.cron_format?.slice(0, 2) ?? '',
- 'hour': data.cron_format?.slice(2, 4) ?? '',
- 'day': data.cron_format?.slice(4, 6) ?? '',
- 'month': data.cron_format?.slice(6, 8) ?? '',
- 'dayOfWeek': data.cron_format?.slice(8, 10) ?? '',
- }
- }
- })
-
- const { updateDoc, error } = useFrappeUpdateDoc()
-
- const { toast } = useToast()
-
- const onSubmit = (data: any) => {
- let cronFormat = ''
- if (data.event_frequency === 'Cron') {
- cronFormat = data.cron_format?.minute + data.cron_format?.hour + data.cron_format?.day + data.cron_format?.month + data.cron_format?.dayOfWeek
- }
- updateDoc('Server Script', data.name, {
- event_frequency: data.event_frequency,
- cron_format: cronFormat,
- script: data.script,
- })
- .then(() => {
- onUpdate()
- toast({
- title: `Temporal Event ${data.name} updated`,
- variant: 'success',
- })
- })
- }
-
- const onStatusToggle = () => {
- updateDoc('Server Script', data.name, {
- disabled: !data.disabled
- })
- .then(() => {
- onUpdate()
- toast({
- title: `Temporal Event ${data.name} ${data.disabled ? "enabled" : "disabled"}`,
- variant: 'success',
- })
- })
- }
-
- const onClose = () => {
- setIsOpen(false)
- }
-
- return (
-
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/ServerScripts/ViewDocTypeEvent.tsx b/raven-app/src/pages/settings/ServerScripts/ViewDocTypeEvent.tsx
deleted file mode 100644
index 8d939dc63..000000000
--- a/raven-app/src/pages/settings/ServerScripts/ViewDocTypeEvent.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import { DeleteAlert } from "@/components/feature/settings/common/DeleteAlert"
-import { DocTypeEventsForm } from "@/components/feature/settings/doctype-events/DocTypeEventsForm"
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { useToast } from "@/hooks/useToast"
-import { Badge, Box, Button, DropdownMenu, Flex, Heading, Section } from "@radix-ui/themes"
-import { useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk"
-import { useState } from "react"
-import { FormProvider, useForm } from "react-hook-form"
-import { FiArrowLeft } from "react-icons/fi"
-import { useNavigate, useParams } from "react-router-dom"
-
-export interface Props { }
-
-export const ViewDocTypeEvent = (props: Props) => {
-
- const { eventID } = useParams<{ eventID: string }>()
-
- const { data: docEventData, error, mutate } = useFrappeGetDoc('Server Script', eventID)
-
- return (
- <>
- {error && }
- {docEventData && }
- >
- )
-}
-
-
-const ViewDocTypeEventPage = ({ data, onUpdate }: { data: any, onUpdate: () => void }) => {
-
- const [isOpen, setIsOpen] = useState(false)
-
- const navigate = useNavigate()
-
- const methods = useForm({
- defaultValues: {
- name: data.name,
- reference_document_type: data.reference_doctype,
- document_event: data.doctype_event,
- script: data.script,
- }
- })
-
- const { updateDoc, error } = useFrappeUpdateDoc()
-
- const { toast } = useToast()
-
- const onSubmit = (data: any) => {
- updateDoc('Server Script', data.name, {
- reference_doctype: data.reference_document_type,
- doctype_event: data.document_event,
- script: data.script,
- })
- .then(() => {
- onUpdate()
- toast({
- title: `DocType Event ${data.name} updated`,
- variant: 'success',
- })
- })
- }
-
- const onStatusToggle = () => {
- updateDoc('Server Script', data.name, {
- disabled: !data.disabled
- })
- .then(() => {
- onUpdate()
- toast({
- title: `DocType Event ${data.name} ${data.disabled ? "enabled" : "disabled"}`,
- variant: 'success',
- })
- })
- }
-
- const onClose = () => {
- setIsOpen(false)
- }
-
- return (
-
-
-
- )
-}
\ No newline at end of file
diff --git a/raven-app/src/pages/settings/Settings.tsx b/raven-app/src/pages/settings/Settings.tsx
index d319c370e..3d3e649c4 100644
--- a/raven-app/src/pages/settings/Settings.tsx
+++ b/raven-app/src/pages/settings/Settings.tsx
@@ -1,4 +1,4 @@
-import { Sidebar } from "@/components/layout/Settings/Sidebar"
+import { Sidebar } from "@/components/layout/Settings/SettingsSidebar"
import { Flex, Box } from "@radix-ui/themes"
import { Outlet } from "react-router-dom"
diff --git a/raven-app/src/utils/removeFrappeFields.ts b/raven-app/src/utils/removeFrappeFields.ts
deleted file mode 100644
index 099cb188d..000000000
--- a/raven-app/src/utils/removeFrappeFields.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { FrappeDoc } from "frappe-react-sdk";
-
-type BaseFields = 'owner' | 'creation' | 'modified' | 'modified_by' | 'docstatus';
-type ChildFields = 'parent' | 'parenttype' | 'parentfield';
-export const removeFrappeFields = (data: FrappeDoc): Omit, BaseFields> => {
-
- try {
- const { owner, creation, modified, modified_by, docstatus, ...withoutBaseFields } = data;
- return withoutBaseFields;
- }
- catch (e) {
- return data
- }
-}
\ No newline at end of file
From a65f2dcb247464d141e9be118903c0cdde6f6a58 Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 1 Mar 2024 13:26:39 +0530
Subject: [PATCH 30/32] fix:remove Delete Alert
---
.../feature/settings/common/DeleteAlert.tsx | 109 ------------------
1 file changed, 109 deletions(-)
delete mode 100644 raven-app/src/components/feature/settings/common/DeleteAlert.tsx
diff --git a/raven-app/src/components/feature/settings/common/DeleteAlert.tsx b/raven-app/src/components/feature/settings/common/DeleteAlert.tsx
deleted file mode 100644
index 80c5c49b6..000000000
--- a/raven-app/src/components/feature/settings/common/DeleteAlert.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import { Loader } from "@/components/common/Loader"
-import { ErrorBanner } from "@/components/layout/AlertBanner"
-import { useToast } from "@/hooks/useToast"
-import { AlertDialog, Button, Callout, Checkbox, Flex, Text } from "@radix-ui/themes"
-import { useFrappeDeleteDoc } from "frappe-react-sdk"
-import { useState } from "react"
-import { FiAlertTriangle } from "react-icons/fi"
-import { useNavigate } from "react-router-dom"
-
-export interface Props {
- isOpen: boolean,
- onClose: () => void
- docname: string
- path: string
-}
-
-export const DeleteAlert = ({ isOpen, onClose, docname, path }: Props) => {
- return (
-
-
- {/* Hii */}
-
-
-
- )
-}
-
-
-type DeleteDocModalProps = {
- onClose: () => void,
- docname: string
- path: string
-}
-
-const AlertContent = ({ onClose, docname, path }: DeleteDocModalProps) => {
-
- const { deleteDoc, error, loading: deletingDoc, reset } = useFrappeDeleteDoc()
-
- const handleClose = () => {
- onClose()
- reset()
- }
-
- const { toast } = useToast()
- const navigate = useNavigate()
-
- const onSubmit = () => {
- if (docname) {
- deleteDoc('Server Script', docname)
- .then(() => {
- onClose()
- navigate(path)
- toast({
- title: `${docname} deleted`,
- variant: 'success',
- })
- })
- }
- }
-
- const [allowDelete, setAllowDelete] = useState(false)
-
- return (
- <>
-
- Delete {docname}?
-
-
-
-
-
-
-
-
-
- This action is permanent and cannot be undone.
-
-
- {/* When you delete a channel, all messages from this channel will be removed immediately. */}
- {/*
-
- All messages, including files and images will be removed
- You can archive this channel instead to preserve your messages
-
- */}
-
-
- setAllowDelete(!allowDelete)} color='red' />
- Yes, I understand, permanently delete this channel
-
-
-
-
-
-
-
- Cancel
-
-
-
-
- {deletingDoc && }
- {deletingDoc ? "Deleting" : "Delete"}
-
-
-
- >
- )
-}
\ No newline at end of file
From 02e69ff5938152f937d8e9913ca2ef34d86aff59 Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 1 Mar 2024 13:35:31 +0530
Subject: [PATCH 31/32] fix:imports
---
raven-app/src/App.tsx | 45 ++++++++++---------------------------------
1 file changed, 10 insertions(+), 35 deletions(-)
diff --git a/raven-app/src/App.tsx b/raven-app/src/App.tsx
index c2172cf2a..833a0a7d5 100644
--- a/raven-app/src/App.tsx
+++ b/raven-app/src/App.tsx
@@ -13,16 +13,6 @@ import { Settings } from './pages/settings/Settings'
import { CreateWebhook } from './components/feature/integrations/webhooks/CreateWebhook'
import ViewWebhook from './components/feature/integrations/webhooks/ViewWebhook'
import { WebhookList } from './components/feature/integrations/webhooks/WebhookList'
-import { DocTypeEvents } from './pages/settings/ServerScripts/DocTypeEvents'
-import { CreateDocTypeEvent } from './pages/settings/ServerScripts/CreateDocTypeEvent'
-import { ViewDocTypeEvent } from './pages/settings/ServerScripts/ViewDocTypeEvent'
-import { APIEvents } from './pages/settings/ServerScripts/APIEvents/APIEvents'
-import { CreateAPIEvent } from './pages/settings/ServerScripts/APIEvents/CreateAPIEvents'
-import { ViewAPIEvent } from './pages/settings/ServerScripts/APIEvents/ViewAPIEvents'
-import { TemporalEvents } from './pages/settings/ServerScripts/TemporalEvents/TemporalEvents'
-import { ViewTemporalEvent } from './pages/settings/ServerScripts/TemporalEvents/ViewTemporalEvent'
-import { CreateTemporalEvent } from './pages/settings/ServerScripts/TemporalEvents/CreateTemporalEvent'
-
const router = createBrowserRouter(
createRoutesFromElements(
@@ -30,34 +20,19 @@ const router = createBrowserRouter(
import('@/pages/auth/Login')} />
}>
}>
- } >
- } />
- import('./components/feature/saved-messages/SavedMessages')} />
- import('@/pages/ChatSpace')} />
-
- }>
-
- } />
- } />
- } />
- }>
- } />
- } />
- } />
-
- }>
- } />
- } />
- } />
-
- }>
- } />
- } />
- } />
+ } >
+ } />
+ import('./components/feature/saved-messages/SavedMessages')} />
+ import('@/pages/ChatSpace')} />
+
+ }>
+
+ } />
+ } />
+ } />
-
>
), {
From 5ca9a4b8ab7c0fab6e84afded70ea4324069ff4c Mon Sep 17 00:00:00 2001
From: Sumit Jain
Date: Fri, 1 Mar 2024 15:35:05 +0530
Subject: [PATCH 32/32] fix:code overflow ouside code bock
---
.../webhooks/WebhookReturnDataFieldTable.tsx | 9 +-
.../feature/integrations/webhooks/utils.ts | 231 ++++++++++++++++++
2 files changed, 237 insertions(+), 3 deletions(-)
diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
index b07fc2199..904d90211 100644
--- a/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
+++ b/raven-app/src/components/feature/integrations/webhooks/WebhookReturnDataFieldTable.tsx
@@ -226,10 +226,12 @@ export const PreviewModal = ({ onClose }: { onClose: () => void }) => {
const webhookTriggerKeys = webhookData?.map(data => data.key)
const obj = {}
- webhookTriggerKeys?.forEach(key => {
+ if (exampleData) {
+ webhookTriggerKeys?.forEach(key => {
// @ts-ignore
- obj[key as string] = exampleData?.fields?.find(field => field.field === key)?.value
+ obj[key as string] = exampleData?.fields?.find(field => field.field === key)?.value
})
+ }
return JSON.stringify(obj, null, 2)
}, [examples, webhookData, webhookTrigger])
@@ -263,7 +265,8 @@ export const PreviewModal = ({ onClose }: { onClose: () => void }) => {
{jsonData}
diff --git a/raven-app/src/components/feature/integrations/webhooks/utils.ts b/raven-app/src/components/feature/integrations/webhooks/utils.ts
index fb2c2504c..7fd6aab18 100644
--- a/raven-app/src/components/feature/integrations/webhooks/utils.ts
+++ b/raven-app/src/components/feature/integrations/webhooks/utils.ts
@@ -537,13 +537,52 @@ export const SampleData = [
field: 'text',
value: 'Hello, World!'
},
+ {
+ field: 'json',
+ value: `{
+ content: [
+ {
+ content: [
+ {
+ text: "Hello, World!",
+ type: "text"
+ }
+ ],
+ type: "paragraph"
+ }
+ ],
+ type: "doc"
+ }`
+ },
{
field: 'message_type',
value: 'Text'
},
+ {
+ field: 'file',
+ value: 'https://ravenapp.info/_astro/app-screenshot.e5f6e34e.png'
+ },
+ {
+ field: 'message_reactions',
+ value: `{
+ 'unicode_string 1':{
+ 'count': 1,
+ 'users':['user1'],
+ 'reaction': 'unicode_string 1'
+ }
+ }`
+ },
{
field: 'is_reply',
value: '0'
+ },
+ {
+ field: 'linked_message',
+ value: 'message-id'
+ },
+ {
+ field: 'content',
+ value: 'Hello, World!'
}
]
},
@@ -565,6 +604,45 @@ export const SampleData = [
{
field: 'is_reply',
value: '0'
+ },
+ {
+ field: 'linked_message',
+ value: 'message-id'
+ },
+ {
+ field: 'content',
+ value: 'The Iron Throne is mine!'
+ },
+ {
+ field: 'message_reactions',
+ value: `{
+ 'unicode_string 1':{
+ 'count': 1,
+ 'users':['user1'],
+ 'reaction': 'unicode_string 1'
+ }
+ }`
+ },
+ {
+ field: 'file',
+ value: 'https://ravenapp.info/_astro/app-screenshot.e5f6e34e.png'
+ },
+ {
+ field: 'json',
+ value: `{
+ content: [
+ {
+ content: [
+ {
+ text: "The Iron Throne is mine!",
+ type: "text"
+ }
+ ],
+ type: "paragraph"
+ }
+ ],
+ type: "doc"
+ }`
}
]
},
@@ -586,6 +664,45 @@ export const SampleData = [
{
field: 'is_reply',
value: '0'
+ },
+ {
+ field: 'linked_message',
+ value: 'message-id'
+ },
+ {
+ field: 'content',
+ value: 'Winter is coming.'
+ },
+ {
+ field: 'message_reactions',
+ value: `{
+ 'unicode_string 1':{
+ 'count': 1,
+ 'users':['user1'],
+ 'reaction': 'unicode_string 1'
+ }
+ }`
+ },
+ {
+ field: 'file',
+ value: 'https://ravenapp.info/_astro/app-screenshot.e5f6e34e.png'
+ },
+ {
+ field: 'json',
+ value: `{
+ content: [
+ {
+ content: [
+ {
+ text: "Winter is coming.",
+ type: "text"
+ }
+ ],
+ type: "paragraph"
+ }
+ ],
+ type: "doc"
+ }`
}
]
},
@@ -607,6 +724,45 @@ export const SampleData = [
{
field: 'is_reply',
value: '0'
+ },
+ {
+ field: 'linked_message',
+ value: 'message-id'
+ },
+ {
+ field: 'content',
+ value: 'Dracarys!'
+ },
+ {
+ field: 'message_reactions',
+ value: `{
+ 'unicode_string 1':{
+ 'count': 1,
+ 'users':['user1'],
+ 'reaction': 'unicode_string 1'
+ }
+ }`
+ },
+ {
+ field: 'file',
+ value: 'https://ravenapp.info/_astro/app-screenshot.e5f6e34e.png'
+ },
+ {
+ field: 'json',
+ value: `{
+ content: [
+ {
+ content: [
+ {
+ text: "Dracarys!",
+ type: "text"
+ }
+ ],
+ type: "paragraph"
+ }
+ ],
+ type: "doc"
+ }`
}
]
},
@@ -628,6 +784,45 @@ export const SampleData = [
{
field: 'is_reply',
value: '0'
+ },
+ {
+ field: 'linked_message',
+ value: 'message-id'
+ },
+ {
+ field: 'content',
+ value: 'The Night King is coming.'
+ },
+ {
+ field: 'message_reactions',
+ value: `{
+ 'unicode_string 1':{
+ 'count': 1,
+ 'users':['user1'],
+ 'reaction': 'unicode_string 1'
+ }
+ }`
+ },
+ {
+ field: 'file',
+ value: 'https://ravenapp.info/_astro/app-screenshot.e5f6e34e.png'
+ },
+ {
+ field: 'json',
+ value: `{
+ content: [
+ {
+ content: [
+ {
+ text: "The Night King is coming.",
+ type: "text"
+ }
+ ],
+ type: "paragraph"
+ }
+ ],
+ type: "doc"
+ }`
}
]
}
@@ -650,6 +845,10 @@ export const SampleData = [
{
field: 'is_admin',
value: '1'
+ },
+ {
+ field: 'last_visit',
+ value: '2021-08-12 12:00:00'
}
]
},
@@ -667,6 +866,10 @@ export const SampleData = [
{
field: 'is_admin',
value: '1'
+ },
+ {
+ field: 'last_visit',
+ value: '2021-08-12 12:00:00'
}
]
},
@@ -684,6 +887,10 @@ export const SampleData = [
{
field: 'is_admin',
value: '1'
+ },
+ {
+ field: 'last_visit',
+ value: '2021-08-12 12:00:00'
}
]
},
@@ -710,6 +917,10 @@ export const SampleData = [
{
field: 'enabled',
value: '1'
+ },
+ {
+ field: 'user_image',
+ value: 'https://example.com/image.jpg'
}
]
},
@@ -731,6 +942,10 @@ export const SampleData = [
{
field: 'enabled',
value: '1'
+ },
+ {
+ field: 'user_image',
+ value: 'https://example.com/image.jpg'
}
]
}
@@ -745,6 +960,14 @@ export const SampleData = [
{
field: 'reaction',
value: '👍'
+ },
+ {
+ field: 'message',
+ value: 'message-id'
+ },
+ {
+ field: 'reaction_escaped',
+ value: '\\ud83d\\udc4d'
}
]
},
@@ -754,6 +977,14 @@ export const SampleData = [
{
field: 'reaction',
value: '👎'
+ },
+ {
+ field: 'message',
+ value: 'message-id'
+ },
+ {
+ field: 'reaction_escaped',
+ value: '\\ud83d\\udc4e'
}
]
}