Browse Source

updated the code

Rohit Taware 1 year ago
parent
commit
d67d98a14d

+ 258 - 11
_includes/header.html

@@ -1,29 +1,276 @@
+<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
+<style>
+  .user-icon {
+    display: inline-block;
+    position: relative;
+  }
+
+  /* Style for the modal */
+  .modal {
+    display: none;
+    position: fixed;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    padding: 20px;
+    background-color: #FFC907;
+    border: 2px solid #ccc;
+    z-index: 2000;
+  }
+
+  .dropdown {
+    display: inline-block;
+    position: relative;
+    z-index: 1000;
+  }
+
+  .dropdown-content {
+    display: none;
+    position: absolute;
+    width: auto;
+    overflow: auto;
+    box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.4);
+    /* background-color: rgba(255, 255, 255, 0); */
+    background-color: white;
+  }
+
+  .dropdown.active .dropdown-content {
+    display: block;
+  }
+
+  .dropdown-content a {
+    display: block;
+    color: #000000;
+    padding: 5px;
+    text-decoration: none;
+  }
+
+  .dropdown-content a:hover {
+    color: #FFFFFF;
+    background-color: #FFC907;
+  }
+
+  .dropdown:hover {
+    /* Resetting styles for hover state */
+    border: none;
+    box-shadow: none;
+    display: block;
+    /* Or any other appropriate display value */
+    grid-template-columns: initial;
+    /* Or any other appropriate value */
+  }
+</style>
+
 <header class="site-header">
 
   <div class="wrapper">
 
     <a class="site-title" href="{{ site.baseurl }}/">{{ site.title }}</a>
     <img src="{% link assets/elements/icon_do.svg %}" alt="do-ocracy icon"
-         style="height:40px; padding: 5px 0 0 20px;" />
+      style="height:40px; padding: 5px 0 0 20px;" />
 
     <nav class="site-nav">
       <a href="#" class="menu-icon">
         <svg viewBox="0 0 18 15">
-          <path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/>
-          <path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/>
-          <path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/>
+          <path fill="#424242"
+            d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z" />
+          <path fill="#424242"
+            d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z" />
+          <path fill="#424242"
+            d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z" />
         </svg>
       </a>
 
-      <div class="trigger">
+      <div class="trigger" style="display: inline-block;">
         {% for my_page in site.pages %}
-          {% if my_page.title %}
-          <a class="page-link{% if page.url == my_page.url %} page-link-active{% endif %}" href="{{ my_page.url | prepend: site.baseurl }}">{{ my_page.title }}</a>
-          {% endif %}
+        {% if my_page.title %}
+        <a class="page-link{% if page.url == my_page.url %} page-link-active{% endif %}"
+          href="{{ my_page.url | prepend: site.baseurl }}">{{ my_page.title }}</a>
+        {% endif %}
         {% endfor %}
-      </div>
-    </nav>
+        <div class="user-icon">
+          <div id="loginLogoutButton">
+            <div id="login" style="height: 20px; width: 30px;">
+              <button onclick="showLoginModal()"
+                style="background-color: #FFC907; height: 30px; width: 35px; padding:0px; border-color: #FFC907;">
+                <img src="{% link assets/elements/loginIcon.png %}" alt="Login" style="width: 100%; height: 100%;">
+              </button>
+            </div>
 
-  </div>
 
+            <div id="logout" class="dropdown disable-hover"
+              style="background-color: #FFC907; height: 30px; width: 35px; ">
+              <button onclick="toggleDropdown()"
+                style="background-color: #FFC907; height: 30px; width: 32px; padding:0px;">
+                <img src="{% link assets/elements/loggedin.png %}" alt="Profile" style="width: 100%; height: 100%;">
+              </button>
+              <div class="dropdown-content">
+                <div id="emailText"
+                  style="padding: 10px; padding-top: 20px; margin-top: 1px; margin-bottom: 1px; height: 10 px"> </div>
+                <a href="#" style="padding: 10px; margin-top: 1px; margin-bottom: 1px; height: 10 px"
+                  @click="logout">Logout</a>
+              </div>
+
+            </div>
+          </div>
+        </div>
+
+        <div id="navBar" class="modal">
+          <div id="emailCode">
+            <label for="emailInput">Enter your email:</label>
+            <input type="email" id="emailInput" v-model="loginEmail" required>
+            <button @click="sendOTP" style="font-family: Arial, Helvetica, sans-serif;">Send Code</button>
+          </div>
+          <div id="emailCodeVerify" style="display: none;">
+            <label for="otpInput">Enter Code to Login:</label>
+            <input type="text" id="otpInput" v-model="otpCode" required>
+            <button @click="verifyOTP" style="font-family: Arial, Helvetica, sans-serif;">Verify Code</button>
+            <button @click="sendOTPAagin" style="font-family: Arial, Helvetica, sans-serif;"> Send Code again</button>
+          </div>
+          <button @click="closeModal" style="font-family: Arial;">Close</button>
+        </div>
+      </div>
+  </div>
+  </nav>
+  </div>
 </header>
+
+<script>
+  function toggleDropdown() {
+    var dropdown = document.getElementById('logout');
+    dropdown.classList.toggle('active');
+    var myDiv = document.getElementById('emailText'); // Get the div element by ID
+    myDiv.textContent = localStorage.getItem('userEmail');
+  }
+  function showLoginModal() {
+    // Show the modal and overlay
+    document.getElementById('navBar').style.display = 'block';
+    document.getElementById('overlay').style.display = 'block';
+  }
+  function logout() {
+    // Remove email from local storage
+    localStorage.removeItem('userEmail');
+
+    // Hide the dropdown and overlay
+    document.getElementById('login').style.display = 'block';
+    document.getElementById('logout').style.display = 'none';
+  }
+
+  function showLoginModal() {
+    document.getElementById('navBar').style.display = 'block';
+    // document.getElementById('overlay').style.display = 'block';
+  }
+
+  function hideLoginModal() {
+    document.getElementById('navBar').style.display = 'none';
+    document.getElementById('overlay').style.display = 'none';
+  }
+
+  function isValidEmail(email) {
+    // Regular expression for a basic email validation
+    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+    return emailRegex.test(email);
+  }
+  new Vue({
+    el: '#loginLogoutButton',
+    data: {
+      email: localStorage.getItem('userEmail') || null,
+      showDropdown: false,
+    },
+    mounted() {
+      // Retrieve email from local storage on component mount
+      this.email = localStorage.getItem('userEmail');
+      if (this.email) {
+        document.getElementById('login').style.display = 'none';
+        document.getElementById('logout').style.display = 'block';
+      } else {
+        document.getElementById('login').style.display = 'block';
+        document.getElementById('logout').style.display = 'none';
+      }
+    },
+  });
+  new Vue({
+    el: '#navBar',
+    data: {
+      loginEmail: '',
+      email: localStorage.getItem('userEmail'),
+      otpCode: "",
+      backendUrl: "http://localhost:3000/api/send_otp"
+    },
+    methods: {
+
+      sendOTPAagin() {
+        document.getElementById('emailCode').style.display = 'block';
+        document.getElementById('emailCodeVerify').style.display = 'none';
+      },
+      sendOTP() {
+        const requestData = {
+          email: this.loginEmail
+        }
+        if (isValidEmail(this.loginEmail)) {
+          fetch(this.backendUrl, {
+            method: 'POST',
+            headers: {
+              'Content-Type': 'application/json',
+              'Access-Control-Allow-Methods': 'POST',
+              'Access-Control-Allow-Headers': 'Content-Type'
+              // Add any other headers you may need, such as authorization headers
+            },
+            body: JSON.stringify(requestData),
+          }).then(response => {
+            if (!response.ok) {
+              alert('Error while sending OTP');
+            } else {
+              alert(`Code sent to: ${this.loginEmail}`);
+              document.getElementById('emailCode').style.display = 'none';
+              document.getElementById('emailCodeVerify').style.display = 'block';
+            }
+
+          })
+        } else {
+          alert(`Please enter valid email address`);
+        }
+
+      },
+
+      verifyOTP() {
+
+        const requestData = {
+          email: this.loginEmail,
+          otp: this.otpCode
+        }
+        fetch(this.backendUrl, {
+          method: 'POST',
+          headers: {
+            'Content-Type': 'application/json',
+            'Access-Control-Allow-Methods': 'POST',
+            'Access-Control-Allow-Headers': 'Content-Type'
+            // Add any other headers you may need, such as authorization headers
+          },
+          body: JSON.stringify(requestData),
+        }).then(response => {
+          console.log(response)
+          if (response.status == 500) {
+            alert('Error while validating code');
+          } else if (response.status == 400) {
+            alert('Invalid code, please enter valid code');
+          } else {
+            alert('Logged in successfully');
+            localStorage.setItem('userEmail', this.loginEmail)
+            document.getElementById('login').style.display = 'none';
+            document.getElementById('logout').style.display = 'block';
+            document.getElementById('logout').classList.remove('active');
+            this.closeModal();
+          }
+        })
+
+      },
+
+      closeModal() {
+        document.getElementById('navBar').style.display = 'none';
+
+      },
+    },
+  });
+
+</script>

+ 2 - 1
_includes/vue-rule-script.html

@@ -242,7 +242,8 @@
         </div>
         <div class="btn-group" v-if="view && !legacy">
             <vue-button class="primary is-large" @click="clickPreview" :icon="icons.fork">Fork &amp; Edit</vue-button>
-            <vue-button class="primary is-large" @click="clickPreviewEdit" :icon="icons.fork">Edit </vue-button>
+            <vue-button class="primary is-large" v-if = "isOwner" @click="clickPreviewEdit" :icon="icons.fork">Edit </vue-button>
+            <vue-button class="primary is-large" v-if = "isAdmin" @click="clickDelete" :icon="icons.fork">Delete </vue-button>
             <vue-button class="is-large" @click="handleClickDownload" :icon="icons.download">Download</vue-button>
         </div>
     </div>

+ 1 - 1
_layouts/default.html

@@ -6,7 +6,7 @@
   <body>
 
     {% include header.html %}
-
+    
     <div class="page-content">
       <div class="wrapper">
         {{ content }}

BIN
assets/elements/loggedin.png


BIN
assets/elements/loginIcon.png


+ 160 - 119
assets/js/vue.rules.js

@@ -23,7 +23,7 @@ const app = Vue.createApp({
         name: "",
         lineage: "",
         summary: "",
-        config: {}, 
+        config: {},
         creator: {
           name: "",
           url: "",
@@ -36,12 +36,16 @@ const app = Vue.createApp({
       loading: false,
       publishing: false,
       edit: false,
+      isOwner: false,
+      isAdmin: false,
       view: (global.rule) ? true : false,
       preview: (global.rule) ? true : false,
       template: (global.rule) ? true : false,
       steinAPI: 'https://api.steinhq.com/v1/storages/5e8b937ab88d3d04ae0816a5',
-      backendUrl: 'http://localhost:3000/api/validate_otp',
+      backendUrl: 'http://localhost:3000/api/publish_rule',
       backendUrlrule: 'http://localhost:3000/api/get_rule',
+      backedDeleterule: 'http://localhost:3000/api/delete_rule',
+      adminMail: "communityrule@medlab.host",
       // TODO keep an array of past states for undo/redo
       history: [],
       // the data of the current module in the editor
@@ -109,7 +113,7 @@ const app = Vue.createApp({
         name: "",
         icon: "",
         summary: "",
-        config: {},  
+        config: {},
         type: "",
         modules: []
       },
@@ -193,7 +197,7 @@ const app = Vue.createApp({
           name: module.name,
           icon: (module.icon && !module.icon.includes('http')) ? global.url + module.icon : module.icon,
           summary: module.summary,
-          config: module.config,  
+          config: module.config,
           type: module.type,
           modules: (module.modules) ? module.modules.map(cleanModules) : [],
         }
@@ -251,28 +255,28 @@ const app = Vue.createApp({
     /**
      * @returns {Object} a new module object from the moduleTemplate
      */
-      newModule() {
-          return JSON.parse(JSON.stringify(this.moduleTemplate));
-      },
+    newModule() {
+      return JSON.parse(JSON.stringify(this.moduleTemplate));
+    },
     /**
      * spreads a source module into a new module from the moduleTemplate
      * @param {Object} sourceModule the module to copy
      * @param {Boolean} includeSubmodules whether to copy submodules or not
      * @returns 
      */
-    cloneModule(sourceModule,includeSubmodules) {
+    cloneModule(sourceModule, includeSubmodules) {
       let output = {
-          ...this.moduleTemplate,
-          ...sourceModule,
+        ...this.moduleTemplate,
+        ...sourceModule,
         //TODO: implement lineage pattern, same as the rule does
         source: sourceModule, // keep track of where this module came from
       };
       if (!includeSubmodules) output.modules = [];
       // clear configs
       document.getElementById("newConfigKey").value =
-            "Configuration";
+        "Configuration";
       document.getElementById("newConfigValue").value =
-            "Value";
+        "Value";
       // delete unnecessary properties
       delete output.content;
       delete output.readonly;
@@ -319,7 +323,7 @@ const app = Vue.createApp({
      * @param {Object} module to add
      * @param {Object} target a module or the rule Object where the module should be added as a submodule 
      */
-    addModule(module,target = this.rule) {
+    addModule(module, target = this.rule) {
       target.modules.push(module);
     },
     /**
@@ -330,7 +334,7 @@ const app = Vue.createApp({
      * @param {Object} target the module or rule to remove the module from (defaults to rule)
      */
     removeModule(module, target = this.rule) {
-      if (! this.moduleContains(module, target)) return; // if the module is not in the target, do nothing
+      if (!this.moduleContains(module, target)) return; // if the module is not in the target, do nothing
       // 
       target.modules.forEach((m, idx) => {
         if (m === module) {
@@ -360,8 +364,8 @@ const app = Vue.createApp({
       const module = targetModule.module;
 
       ev.dataTransfer.setDragImage(targetModule, ev.offsetX, ev.offsetY);
-      
-      this.holding = {module};
+
+      this.holding = { module };
     },
     /**
      * Handles the start drag event for a module 
@@ -395,7 +399,7 @@ const app = Vue.createApp({
      */
     // TODO: return the module location in the haystack (Maybe?)
     moduleContains(needle, haystack = this.rule) {
-      if (! haystack.modules ) return false; // does the haystack even have modules?
+      if (!haystack.modules) return false; // does the haystack even have modules?
       if (haystack.modules.includes(needle)) return true; // is the needle in the haystack?
       return haystack.modules.some(submodule => this.moduleContains(needle, submodule)); // is the needle in any of the submodules?
     },
@@ -425,7 +429,7 @@ const app = Vue.createApp({
         // if the module has a source, remove it from that source
         this.removeModule(holdingModule, this.holding.source);
       }
-      
+
       this.addModule(module, landingModule);
       this.editModule(module);
       this.endDragModule();
@@ -456,31 +460,31 @@ const app = Vue.createApp({
       const store = new SteinStore(
         this.steinAPI
       );
-      const backendUrlGetRule = this.backendUrlrule + "?ruleId="+id;
+      const backendUrlGetRule = this.backendUrlrule + "?ruleId=" + id;
 
       (async () => {
         var rule = [];
         // read values from all sheets
-        console.log(backendUrlGetRule)
+
         await fetch(backendUrlGetRule, {
-        method: 'GET',
-        headers: {
+          method: 'GET',
+          headers: {
             'Content-Type': 'application/json',
             // Add any other headers you may need, such as authorization headers
-        },
-      }).then(response => {
-        if (!response.ok) {
-          throw new Error('Network response was not ok');
-        }
-        return response.json(); // This returns another Promise
-      })
-      .then (data => {
+          },
+        }).then(response => {
+          if (!response.ok) {
+            throw new Error('Network response was not ok');
+          }
+          return response.json(); // This returns another Promise
+        })
+          .then(data => {
 
-        if (data.rules.length > 0) {
-          rule = JSON.parse(data.rules)[0];
-        }
+            if (data.rules.length > 0) {
+              rule = JSON.parse(data.rules)[0];
+            }
 
-      });
+          });
         // no rule found, exit
         // TODO: inform the user that the rule was not found
         if (!rule) {
@@ -506,7 +510,7 @@ const app = Vue.createApp({
           name: rule.name,
           lineage: rule.lineage,
           summary: rule.summary,
-          config: rule.config,  
+          config: rule.config,
           creator: {
             name: rule.creator_name,
             url: rule.creator_url,
@@ -515,12 +519,18 @@ const app = Vue.createApp({
           latest_version: rule.latest_version
         }
 
-        console.log(this.rule.latest_version)
+        if (rule.email == localStorage.getItem('userEmail')) {
+          this.isOwner = true;
+        }
+        if (localStorage.getItem('userEmail') == this.adminMail) {
+          this.isAdmin = true;
+        }
+
         /** Add name to <title> for v3+ rules */
         document.title = rule.name + " / CommunityRule"
-          
+
         this.loading = false;
-        
+
       })();
       /*fetch(backendUrlGetRule, {
         method: 'GET',
@@ -584,6 +594,35 @@ const app = Vue.createApp({
     },
 
     // editor methods =========================================================
+    clickDelete() {
+      (async () => {
+        // read values from all sheets
+        console.log("rule to delete " + this.rule.ruleID);
+        const requestData = {
+          ruleID: this.rule.ruleID
+        }
+        await fetch(this.backedDeleterule, {
+          method: 'DELETE',
+          headers: {
+            'Content-Type': 'application/json',
+            // Add any other headers you may need, such as authorization headers
+          },
+          body: JSON.stringify(requestData),
+        }).then(response => {
+          if (!response.ok) {
+            throw new Error('Network response was not ok');
+          }
+          alert("Error while deleting rule");
+          return response.json(); // This returns another Promise
+        }).then(data => {
+          alert('Rule deleted successfully');
+          window.open("/library", false);
+        });
+
+        this.loading = false;
+
+      })();
+    },
 
     /**
      * Adds a module to the editor
@@ -666,23 +705,23 @@ const app = Vue.createApp({
 
     // config methods =========================================================
 
-      /**
-       * Add custom config entry to the module
-       */
-      addConfig() {
-        const k = document.getElementById("newConfigKey").value;
-        const v = document.getElementById("newConfigValue").value;
-        this.editor.module.config[k] = v;
-        this.resetNewConfigInputs();
-      },
-      
-      /**
-       * Removes the config entry from the module
-       */
-      removeConfig(key) {
-          delete this.editor.module.config[key];
-      },
-      
+    /**
+     * Add custom config entry to the module
+     */
+    addConfig() {
+      const k = document.getElementById("newConfigKey").value;
+      const v = document.getElementById("newConfigValue").value;
+      this.editor.module.config[k] = v;
+      this.resetNewConfigInputs();
+    },
+
+    /**
+     * Removes the config entry from the module
+     */
+    removeConfig(key) {
+      delete this.editor.module.config[key];
+    },
+
     // custom module methods ==================================================
 
     /**
@@ -733,14 +772,18 @@ const app = Vue.createApp({
      */
     handleClickPublish() {
       // Confirm user knows what they're getting into
+      if (!localStorage.getItem('userEmail')) {
+        alert('Please login first to publish this rule')
+        return;
+      }
       if (!confirm("Publish to the public Library?")) return;
 
-      if ( !this.rule.name ) {
+      if (!this.rule.name) {
         alert("Please enter a name for this rule.");
         return;
       }
 
-      if ( this.rule.modules.length < 1 ) {
+      if (this.rule.modules.length < 1) {
         alert("Please add at least one module to this rule.");
         return;
       }
@@ -750,78 +793,76 @@ const app = Vue.createApp({
       const rule = this.ruleExport;
       var ruleID = new Date().getTime(); // TODO: allow for custom named IDs, check for uniqueness
       if (this.edit) {
-        console.log('asdfasdfasdfasdf' + this.rule.ruleID)
+
         ruleID = this.rule.ruleID;
-      } 
+      }
       var latest_ver = 1;
 
       if (this.edit) {
         latest_ver = parseInt(this.rule.latest_version) + 1;
-        console.log('Updated version' + latest_ver)
       }
-// ------------------ exisituing code ---------------
+      // ------------------ exisituing code ---------------
       // add to database
-    //   const store = new SteinStore(
-    //     this.steinAPI
-    //   );
-
-    //   store.append('rules', [{
-    //     ruleID: ruleID,
-    //     timestamp: rule.timestamp,
-    //     icon: rule.icon,
-    //     name: rule.name,
-    //     lineage: rule.lineage,
-    //     summary: rule.summary,
-    //     config: this.jsonify(rule.config),
-    //     modules: this.jsonify(rule.modules),
-    //     creatorName: rule.creator.name,
-    //     creatorUrl: rule.creator.url,
-    //     version: 3
-    //   }]).then(data => {
-    //     this.publishing = false;
-    //     window.open("/create/?r=" + ruleID, "_self", false);
-    //   });
-
-    // -=---------------------------- updated code -------------------
-
-    const requestData = {
-          ruleID: ruleID,
-          timestamp: rule.timestamp,
-          icon: rule.icon,
-          name: rule.name,
-          lineage: rule.lineage,
-          summary: rule.summary,
-          config: this.jsonify(rule.config),
-          modules: this.jsonify(rule.modules),
-          creatorName: rule.creator.name,
-          creatorUrl: rule.creator.url,
-          email: 'rota3015@colorado.edu',
-          edit: this.edit,
-          otp: 1234,
-          version: 3,
-          latest_version: latest_ver
-        }
-    
-    fetch(this.backendUrl, {
-          method: 'POST',
-          headers: {
-              'Content-Type': 'application/json',
-              'Access-Control-Allow-Methods': 'POST',
-              'Access-Control-Allow-Headers': 'Content-Type'
-              // Add any other headers you may need, such as authorization headers
-          },
-          body: JSON.stringify(requestData),
+      //   const store = new SteinStore(
+      //     this.steinAPI
+      //   );
+
+      //   store.append('rules', [{
+      //     ruleID: ruleID,
+      //     timestamp: rule.timestamp,
+      //     icon: rule.icon,
+      //     name: rule.name,
+      //     lineage: rule.lineage,
+      //     summary: rule.summary,
+      //     config: this.jsonify(rule.config),
+      //     modules: this.jsonify(rule.modules),
+      //     creatorName: rule.creator.name,
+      //     creatorUrl: rule.creator.url,
+      //     version: 3
+      //   }]).then(data => {
+      //     this.publishing = false;
+      //     window.open("/create/?r=" + ruleID, "_self", false);
+      //   });
+
+      // -=---------------------------- updated code -------------------
+
+      const requestData = {
+        ruleID: ruleID,
+        timestamp: rule.timestamp,
+        icon: rule.icon,
+        name: rule.name,
+        lineage: rule.lineage,
+        summary: rule.summary,
+        config: this.jsonify(rule.config),
+        modules: this.jsonify(rule.modules),
+        creatorName: rule.creator.name,
+        creatorUrl: rule.creator.url,
+        email: localStorage.getItem('userEmail'),
+        edit: this.edit,
+        version: 3,
+        latest_version: latest_ver
+      }
+
+      fetch(this.backendUrl, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+          'Access-Control-Allow-Methods': 'POST',
+          'Access-Control-Allow-Headers': 'Content-Type'
+          // Add any other headers you may need, such as authorization headers
+        },
+        body: JSON.stringify(requestData),
       }).then(response => {
         if (!response.ok) {
-            throw new Error(`HTTP error! Status: ${response.status}`);
+          throw new Error(`HTTP error! Status: ${response.status}`);
         }
         return response.json(); // or response.text() if expecting plain text
       }).then(data => {
         // Handle the data received from the backend
         console.log('Data from backend:', data);
       }).then(data => {
-              this.publishing = false;
-              window.open("/create/?r=" + ruleID, "_self", false);
+        this.publishing = false;
+        window.open("/create/?r=" + ruleID, "_self", false);
       }).catch(error => {
         // Handle errors that occurred during the fetch
         console.error('Fetch error:', error);
@@ -889,7 +930,7 @@ const app = Vue.createApp({
      * Handles the click event for activating the rule preview
      */
     clickPreview() {
-      if(this.template) this.rule.icon = ''; // TODO: find a less hacky way to reset template icons
+      if (this.template) this.rule.icon = ''; // TODO: find a less hacky way to reset template icons
       this.view = false;
       this.preview = !this.preview;
     },
@@ -897,10 +938,10 @@ const app = Vue.createApp({
      * Handles the click event for activating the rule preview for edit rule
      */
     clickPreviewEdit() {
-      if(this.template) this.rule.icon = ''; // TODO: find a less hacky way to reset template icons
-        this.view = false;
-        this.preview = !this.preview;
-        this.edit = true;
+      if (this.template) this.rule.icon = ''; // TODO: find a less hacky way to reset template icons
+      this.view = false;
+      this.preview = !this.preview;
+      this.edit = true;
     },
     /**
      * Filters module library based on the search term