diff --git a/backend/Actions/MondayCom/MondayComController.php b/backend/Actions/MondayCom/MondayComController.php
new file mode 100644
index 000000000..9a3f01a77
--- /dev/null
+++ b/backend/Actions/MondayCom/MondayComController.php
@@ -0,0 +1,239 @@
+apiToken)) {
+ wp_send_json_error(__('API Token is empty', 'bit-integrations'), 400);
+ }
+
+ $query = 'query { me { id name email } }';
+ $response = self::request($requestParams->apiToken, $query);
+
+ if (!self::hasErrors($response) && isset($response->data->me->id)) {
+ wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200);
+ }
+
+ wp_send_json_error(self::errorMessage($response, __('Authentication failed', 'bit-integrations')), 400);
+ }
+
+ public function getBoards($requestParams)
+ {
+ self::validateToken($requestParams);
+
+ $query = <<<'GRAPHQL'
+ query ($limit: Int) {
+ boards (limit: $limit) {
+ id
+ name
+ }
+ }
+ GRAPHQL;
+ $response = self::request($requestParams->apiToken, $query, ['limit' => 100]);
+
+ if (self::hasErrors($response) || !isset($response->data->boards)) {
+ wp_send_json_error(self::errorMessage($response, __('Boards fetching failed', 'bit-integrations')), 400);
+ }
+
+ $boards = array_map(
+ fn ($b) => (object) ['id' => $b->id, 'name' => $b->name],
+ $response->data->boards
+ );
+
+ wp_send_json_success($boards, 200);
+ }
+
+ public function getGroups($requestParams)
+ {
+ self::validateToken($requestParams);
+ if (empty($requestParams->boardId)) {
+ wp_send_json_error(__('Board ID is empty', 'bit-integrations'), 400);
+ }
+
+ $query = <<<'GRAPHQL'
+ query ($boardIds: [ID!]) {
+ boards (ids: $boardIds) {
+ groups {
+ id
+ title
+ }
+ }
+ }
+ GRAPHQL;
+ $response = self::request($requestParams->apiToken, $query, ['boardIds' => [(string) $requestParams->boardId]]);
+
+ if (self::hasErrors($response) || !isset($response->data->boards[0]->groups)) {
+ wp_send_json_error(self::errorMessage($response, __('Groups fetching failed', 'bit-integrations')), 400);
+ }
+
+ $groups = array_map(
+ fn ($g) => (object) ['id' => $g->id, 'name' => $g->title],
+ $response->data->boards[0]->groups
+ );
+
+ wp_send_json_success($groups, 200);
+ }
+
+ public function getColumns($requestParams)
+ {
+ self::validateToken($requestParams);
+ if (empty($requestParams->boardId)) {
+ wp_send_json_error(__('Board ID is empty', 'bit-integrations'), 400);
+ }
+
+ $query = <<<'GRAPHQL'
+ query ($boardIds: [ID!]) {
+ boards (ids: $boardIds) {
+ columns {
+ id
+ title
+ type
+ }
+ }
+ }
+ GRAPHQL;
+ $response = self::request($requestParams->apiToken, $query, ['boardIds' => [(string) $requestParams->boardId]]);
+
+ if (self::hasErrors($response) || !isset($response->data->boards[0]->columns)) {
+ wp_send_json_error(self::errorMessage($response, __('Columns fetching failed', 'bit-integrations')), 400);
+ }
+
+ $columns = [];
+ foreach (reset($response->data->boards)->columns as $column) {
+ if ($column->type !== 'file') {
+ $columns[] = (object) [
+ 'key' => $column->id,
+ 'label' => $column->title,
+ 'type' => $column->type,
+ 'required' => false,
+ ];
+ }
+ }
+
+ wp_send_json_success($columns, 200);
+ }
+
+ public function getItems($requestParams)
+ {
+ self::validateToken($requestParams);
+ if (empty($requestParams->boardId)) {
+ wp_send_json_error(__('Board ID is empty', 'bit-integrations'), 400);
+ }
+
+ $query = <<<'GRAPHQL'
+ query ($boardIds: [ID!], $limit: Int) {
+ boards (ids: $boardIds) {
+ items_page (limit: $limit) {
+ items {
+ id
+ name
+ }
+ }
+ }
+ }
+ GRAPHQL;
+ $response = self::request(
+ $requestParams->apiToken,
+ $query,
+ [
+ 'boardIds' => [(string) $requestParams->boardId],
+ 'limit' => 100,
+ ]
+ );
+
+ if (self::hasErrors($response) || !isset($response->data->boards[0]->items_page->items)) {
+ wp_send_json_error(self::errorMessage($response, __('Items fetching failed', 'bit-integrations')), 400);
+ }
+
+ $items = array_map(
+ fn ($i) => (object) ['id' => $i->id, 'name' => $i->name],
+ $response->data->boards[0]->items_page->items
+ );
+
+ wp_send_json_success($items, 200);
+ }
+
+ public function execute($integrationData, $fieldValues)
+ {
+ $integrationDetails = $integrationData->flow_details;
+ $integId = $integrationData->id;
+ $fieldMap = $integrationDetails->field_map ?? [];
+ $apiToken = $integrationDetails->apiToken ?? '';
+
+ if (empty($apiToken)) {
+ return new WP_Error('REQ_FIELD_EMPTY', __('API Token is required for Monday.com api', 'bit-integrations'));
+ }
+
+ $recordApiHelper = new RecordApiHelper($integrationDetails, $integId, $apiToken);
+ $response = $recordApiHelper->execute($fieldValues, $fieldMap);
+
+ if (is_wp_error($response)) {
+ return $response;
+ }
+
+ return $response;
+ }
+
+ private static function validateToken($requestParams)
+ {
+ if (empty($requestParams->apiToken)) {
+ wp_send_json_error(__('API Token is empty', 'bit-integrations'), 400);
+ }
+ }
+
+ private static function setHeaders($apiToken)
+ {
+ return [
+ 'Authorization' => $apiToken,
+ 'Content-Type' => 'application/json',
+ 'API-Version' => self::API_VERSION,
+ ];
+ }
+
+ private static function request($apiToken, $query, $variables = [])
+ {
+ $body = ['query' => $query];
+
+ if (!empty($variables)) {
+ $body['variables'] = $variables;
+ }
+
+ return HttpHelper::post(self::API_URL, wp_json_encode($body), self::setHeaders($apiToken));
+ }
+
+ private static function hasErrors($response)
+ {
+ return is_wp_error($response) || !empty($response->errors) || !empty($response->error);
+ }
+
+ private static function errorMessage($response, $fallback)
+ {
+ if (is_wp_error($response)) {
+ return $response->get_error_message();
+ }
+
+ if (!empty($response->errors[0]->message)) {
+ return $response->errors[0]->message;
+ }
+
+ if (!empty($response->error)) {
+ return \is_string($response->error) ? $response->error : wp_json_encode($response->error);
+ }
+
+ return $fallback;
+ }
+}
diff --git a/backend/Actions/MondayCom/RecordApiHelper.php b/backend/Actions/MondayCom/RecordApiHelper.php
new file mode 100644
index 000000000..560bc0548
--- /dev/null
+++ b/backend/Actions/MondayCom/RecordApiHelper.php
@@ -0,0 +1,151 @@
+integrationDetails = $integrationDetails;
+ $this->integrationId = $integId;
+ $this->apiToken = $apiToken;
+ }
+
+ public function handleFilterResponse($response)
+ {
+ if ($response) {
+ return $response;
+ }
+
+ // translators: %s: Placeholder value
+ return (object) ['error' => wp_sprintf(__('%s plugin is not installed or activated', 'bit-integrations'), 'Bit Integrations Pro')];
+ }
+
+ public function generateReqDataFromFieldMap($fieldValues, $fieldMap)
+ {
+ $dataFinal = [];
+ foreach ($fieldMap as $item) {
+ $triggerValue = $item->formField ?? '';
+ $actionValue = $item->mondayComField ?? '';
+
+ if (empty($actionValue)) {
+ continue;
+ }
+
+ if ($triggerValue === 'custom') {
+ $dataFinal[$actionValue] = Common::replaceFieldWithValue($item->customValue ?? '', $fieldValues);
+ } elseif (!empty($triggerValue)) {
+ $dataFinal[$actionValue] = $fieldValues[$triggerValue] ?? '';
+ }
+ }
+
+ return $dataFinal;
+ }
+
+ public function execute($fieldValues, $fieldMap)
+ {
+ $fieldData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap);
+ $mainAction = $this->integrationDetails->mainAction ?? '';
+ $apiResponse = null;
+ $responseType = null;
+
+ $this->type = 'item';
+ $this->typeName = $mainAction;
+
+ switch ($mainAction) {
+ case 'create_item':
+ $this->type = 'item';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_create_item'), false, $fieldData, $this->integrationDetails, $this->apiToken);
+
+ break;
+ case 'update_item':
+ $this->type = 'item';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_update_item'), false, $fieldData, $this->integrationDetails, $this->apiToken);
+
+ break;
+ case 'create_subitem':
+ $this->type = 'subitem';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_create_subitem'), false, $fieldData, $this->integrationDetails, $this->apiToken);
+
+ break;
+ case 'move_item_to_group':
+ $this->type = 'item';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_move_item_to_group'), false, $fieldData, $this->apiToken);
+
+ break;
+ case 'archive_item':
+ $this->type = 'item';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_archive_item'), false, $fieldData, $this->apiToken);
+
+ break;
+ case 'delete_item':
+ $this->type = 'item';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_delete_item'), false, $fieldData, $this->apiToken);
+
+ break;
+ case 'archive_board':
+ $this->type = 'board';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_archive_board'), false, $fieldData, $this->apiToken);
+
+ break;
+ case 'archive_group':
+ $this->type = 'group';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_archive_group'), false, $fieldData, $this->integrationDetails, $this->apiToken);
+
+ break;
+ case 'delete_group':
+ $this->type = 'group';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_delete_group'), false, $fieldData, $this->integrationDetails, $this->apiToken);
+
+ break;
+ case 'create_group':
+ $this->type = 'group';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_create_group'), false, $fieldData, $this->integrationDetails, $this->apiToken);
+
+ break;
+ case 'duplicate_group':
+ $this->type = 'group';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_duplicate_group'), false, $fieldData, $this->integrationDetails, $this->apiToken);
+
+ break;
+ case 'create_column':
+ $this->type = 'column';
+ $apiResponse = Hooks::apply(Config::withPrefix('mondayCom_create_column'), false, $fieldData, $this->integrationDetails, $this->apiToken);
+
+ break;
+ default:
+ $apiResponse = null;
+ }
+
+ $apiResponse = $this->handleFilterResponse($apiResponse);
+ $responseType = $this->hasErrors($apiResponse) || !isset($apiResponse->data) ? 'error' : 'success';
+
+ LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), $responseType, wp_json_encode($apiResponse));
+
+ return $apiResponse;
+ }
+
+ private function hasErrors($response)
+ {
+ return is_wp_error($response) || !empty($response->errors) || !empty($response->error);
+ }
+}
diff --git a/backend/Actions/MondayCom/Routes.php b/backend/Actions/MondayCom/Routes.php
new file mode 100644
index 000000000..73fe8153c
--- /dev/null
+++ b/backend/Actions/MondayCom/Routes.php
@@ -0,0 +1,14 @@
+ import('./TheEventsCalendar/EditTheEven
const EditLMFWC = lazy(() => import('./LMFWC/EditLMFWC'))
const EditVoxel = lazy(() => import('./Voxel/EditVoxel'))
const EditSmartSuite = lazy(() => import('./SmartSuite/EditSmartSuite'))
+const EditMondayCom = lazy(() => import('./MondayCom/EditMondayCom'))
const EditBento = lazy(() => import('./Bento/EditBento'))
const EditLine = lazy(() => import('./Line/EditLine'))
const EditACPT = lazy(() => import('./ACPT/EditACPT'))
@@ -580,6 +581,8 @@ const IntegType = memo(({ allIntegURL, flow }) => {
return
case 'SmartSuite':
return
+ case 'Monday.Com':
+ return
case 'Bento':
return
case 'Line':
diff --git a/frontend/src/components/AllIntegrations/IntegInfo.jsx b/frontend/src/components/AllIntegrations/IntegInfo.jsx
index 03de88b5b..0a797372c 100644
--- a/frontend/src/components/AllIntegrations/IntegInfo.jsx
+++ b/frontend/src/components/AllIntegrations/IntegInfo.jsx
@@ -166,6 +166,7 @@ const TheEventsCalendarAuthorization = lazy(
const LMFWCAuthorization = lazy(() => import('./LMFWC/LMFWCAuthorization'))
const VoxelAuthorization = lazy(() => import('./Voxel/VoxelAuthorization'))
const SmartSuiteAuthorization = lazy(() => import('./SmartSuite/SmartSuiteAuthorization'))
+const MondayComAuthorization = lazy(() => import('./MondayCom/MondayComAuthorization'))
const BentoAuthorization = lazy(() => import('./Bento/BentoAuthorization'))
const LineAuthorization = lazy(() => import('./Line/LineAuthorization'))
const ACPTAuthorization = lazy(() => import('./ACPT/ACPTAuthorization'))
@@ -617,6 +618,8 @@ export default function IntegInfo() {
return
case 'SmartSuite':
return
+ case 'Monday.Com':
+ return
case 'Bento':
return
case 'Line':
diff --git a/frontend/src/components/AllIntegrations/MondayCom/EditMondayCom.jsx b/frontend/src/components/AllIntegrations/MondayCom/EditMondayCom.jsx
new file mode 100644
index 000000000..65a0ab4ff
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/MondayCom/EditMondayCom.jsx
@@ -0,0 +1,96 @@
+/* eslint-disable no-unused-vars */
+import { useState } from 'react'
+import { toast } from 'react-hot-toast'
+import { useNavigate } from 'react-router'
+import { useRecoilState, useRecoilValue } from 'recoil'
+import { $actionConf, $formFields, $newFlow } from '../../../GlobalStates'
+import { __ } from '../../../Utils/i18nwrap'
+import SnackMsg from '../../Utilities/SnackMsg'
+import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers'
+import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree'
+import SetEditIntegComponents from '../IntegrationHelpers/SetEditIntegComponents'
+import { checkMappedFields, handleInput } from './MondayComCommonFunc'
+import MondayComIntegLayout from './MondayComIntegLayout'
+import { needsBoard, needsItem } from './staticData'
+
+function EditMondayCom({ allIntegURL }) {
+ const navigate = useNavigate()
+ const [flow, setFlow] = useRecoilState($newFlow)
+ const [mondayComConf, setMondayComConf] = useRecoilState($actionConf)
+ const [isLoading, setIsLoading] = useState(false)
+ const [loading, setLoading] = useState({})
+ const [snack, setSnackbar] = useState({ show: false })
+ const formField = useRecoilValue($formFields)
+
+ const saveConfig = () => {
+ if (!checkMappedFields(mondayComConf)) {
+ setSnackbar({ show: true, msg: __('Please map mandatory fields', 'bit-integrations') })
+ return
+ }
+ const action = mondayComConf.mainAction
+ if (needsBoard.includes(action) && !mondayComConf.selectedBoard) {
+ toast.error(__('Please select a board', 'bit-integrations'))
+ return
+ }
+ if (needsItem.includes(action) && !mondayComConf.selectedItem) {
+ toast.error(__('Please select an item', 'bit-integrations'))
+ return
+ }
+ if (action === 'create_column' && !mondayComConf.columnType) {
+ toast.error(__('Please select a column type', 'bit-integrations'))
+ return
+ }
+
+ saveActionConf({
+ flow,
+ allIntegURL,
+ conf: mondayComConf,
+ navigate,
+ edit: 1,
+ setIsLoading,
+ setSnackbar
+ })
+ }
+
+ return (
+
+
+
+
+ {__('Integration Name:', 'bit-integrations')}
+ handleInput(e, mondayComConf, setMondayComConf)}
+ name="name"
+ value={mondayComConf.name}
+ type="text"
+ placeholder={__('Integration Name...', 'bit-integrations')}
+ />
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default EditMondayCom
diff --git a/frontend/src/components/AllIntegrations/MondayCom/MondayCom.jsx b/frontend/src/components/AllIntegrations/MondayCom/MondayCom.jsx
new file mode 100644
index 000000000..2f8c10c04
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/MondayCom/MondayCom.jsx
@@ -0,0 +1,142 @@
+/* eslint-disable no-console */
+/* eslint-disable no-unused-expressions */
+import { useState } from 'react'
+import toast from 'react-hot-toast'
+import 'react-multiple-select-dropdown-lite/dist/index.css'
+import { useNavigate } from 'react-router'
+import { __ } from '../../../Utils/i18nwrap'
+import SnackMsg from '../../Utilities/SnackMsg'
+import Steps from '../../Utilities/Steps'
+import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers'
+import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree'
+import MondayComAuthorization from './MondayComAuthorization'
+import { checkMappedFields, generateMappedField } from './MondayComCommonFunc'
+import MondayComIntegLayout from './MondayComIntegLayout'
+import { actionLists, needsBoard, needsItem } from './staticData'
+
+function MondayCom({ formFields, setFlow, flow, allIntegURL }) {
+ const navigate = useNavigate()
+ const [isLoading, setIsLoading] = useState(false)
+ const [loading, setLoading] = useState({})
+
+ const [step, setStep] = useState(1)
+ const [snack, setSnackbar] = useState({ show: false })
+
+ const mondayComFields = []
+ const [mondayComConf, setMondayComConf] = useState({
+ name: 'Monday.Com',
+ type: 'Monday.Com',
+ apiToken: '',
+ field_map: generateMappedField(mondayComFields),
+ mainAction: '',
+ mondayComFields,
+ actionLists
+ })
+
+ const saveConfig = () => {
+ setIsLoading(true)
+ const resp = saveIntegConfig(
+ flow,
+ setFlow,
+ allIntegURL,
+ mondayComConf,
+ navigate,
+ '',
+ '',
+ setIsLoading
+ )
+ resp.then(res => {
+ if (res.success) {
+ toast.success(res.data?.msg)
+ navigate(allIntegURL)
+ } else {
+ toast.error(res.data || res)
+ }
+ })
+ }
+
+ const nextPage = pageNo => {
+ setTimeout(() => {
+ document.getElementById('btcd-settings-wrp').scrollTop = 0
+ }, 300)
+
+ if (!checkMappedFields(mondayComConf)) {
+ toast.error(__('Please map mandatory fields', 'bit-integrations'))
+ return
+ }
+
+ const action = mondayComConf.mainAction
+ if (needsBoard.includes(action) && !mondayComConf.selectedBoard) {
+ toast.error(__('Please select a board', 'bit-integrations'))
+ return
+ }
+ if (needsItem.includes(action) && !mondayComConf.selectedItem) {
+ toast.error(__('Please select an item', 'bit-integrations'))
+ return
+ }
+ if (action === 'create_column' && !mondayComConf.columnType) {
+ toast.error(__('Please select a column type', 'bit-integrations'))
+ return
+ }
+ setStep(pageNo)
+ }
+
+ return (
+
+
+
+
+
+
+ {/* STEP 1 */}
+
+
+ {/* STEP 2 */}
+
+
+
+ {mondayComConf?.mainAction && (
+
nextPage(3)}
+ disabled={!checkMappedFields(mondayComConf)}
+ className="btn f-right btcd-btn-lg purple sh-sm flx"
+ type="button">
+ {__('Next', 'bit-integrations')}
+
+
+ )}
+
+
+ {/* STEP 3 */}
+ {mondayComConf?.mainAction && (
+
saveConfig()}
+ isLoading={isLoading}
+ dataConf={mondayComConf}
+ setDataConf={setMondayComConf}
+ formFields={formFields}
+ />
+ )}
+
+ )
+}
+
+export default MondayCom
diff --git a/frontend/src/components/AllIntegrations/MondayCom/MondayComActions.jsx b/frontend/src/components/AllIntegrations/MondayCom/MondayComActions.jsx
new file mode 100644
index 000000000..238bc2538
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/MondayCom/MondayComActions.jsx
@@ -0,0 +1,33 @@
+import { __ } from '../../../Utils/i18nwrap'
+import TableCheckBox from '../../Utilities/TableCheckBox'
+
+export default function MondayComActions({ mondayComConf, setMondayComConf }) {
+ const actionHandler = (val, typ) => {
+ const newConf = { ...mondayComConf }
+
+ if (typ === 'addToTop') {
+ if (val.target.checked) {
+ newConf.addToTop = true
+ } else {
+ delete newConf.addToTop
+ }
+ }
+
+ setMondayComConf({ ...newConf })
+ }
+
+ return (
+
+
+
actionHandler(e, 'addToTop')}
+ checked={!!mondayComConf?.addToTop}
+ className="wdt-200 mt-4 mr-2"
+ value="addToTop"
+ title={__('Add To Top', 'bit-integrations')}
+ subTitle={__('Duplicate selected group at the top of the board', 'bit-integrations')}
+ />
+
+
+ )
+}
diff --git a/frontend/src/components/AllIntegrations/MondayCom/MondayComAuthorization.jsx b/frontend/src/components/AllIntegrations/MondayCom/MondayComAuthorization.jsx
new file mode 100644
index 000000000..8fe6f8e3b
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/MondayCom/MondayComAuthorization.jsx
@@ -0,0 +1,126 @@
+/* eslint-disable jsx-a11y/anchor-is-valid */
+import { useState } from 'react'
+import { __ } from '../../../Utils/i18nwrap'
+import LoaderSm from '../../Loaders/LoaderSm'
+import Note from '../../Utilities/Note'
+import tutorialLinks from '../../../Utils/StaticData/tutorialLinks'
+import TutorialLink from '../../Utilities/TutorialLink'
+import { mondayComAuthentication } from './MondayComCommonFunc'
+import { create } from 'mutative'
+
+export default function MondayComAuthorization({
+ mondayComConf,
+ setMondayComConf,
+ step,
+ setStep,
+ loading,
+ setLoading,
+ isInfo
+}) {
+ const [isAuthorized, setIsAuthorized] = useState(false)
+ const [error, setError] = useState({ apiToken: '' })
+
+ const nextPage = () => {
+ setTimeout(() => {
+ document.getElementById('btcd-settings-wrp').scrollTop = 0
+ }, 300)
+ setStep(2)
+ }
+
+ const handleInput = e => {
+ const { name, value } = e.target
+ setError(err =>
+ create(err, draft => {
+ draft[name] = ''
+ })
+ )
+ setMondayComConf(prev =>
+ create(prev, draft => {
+ draft[name] = value
+ })
+ )
+ }
+
+ const ActiveInstructions = `
+ ${__('To Get Monday.com API Token', 'bit-integrations')}
+
+ ${__('Log in to your Monday.com account.', 'bit-integrations')}
+ ${__('Click on your avatar in the bottom left corner.', 'bit-integrations')}
+ ${__('Select Developers → API Token.', 'bit-integrations')}
+ ${__('Copy your personal API token (v2).', 'bit-integrations')}
+ `
+
+ return (
+
+
+
+
+ {__('Integration Name:', 'bit-integrations')}
+
+
+
+
+ {__('API Token:', 'bit-integrations')}
+
+
+
{error.apiToken}
+
+
+ {__('To Get API Token, Please Visit', 'bit-integrations')}
+
+
+ {__('Monday.com Developers', 'bit-integrations')}
+
+
+
+
+
+ {!isInfo && (
+
+
+ mondayComAuthentication(mondayComConf, setError, setIsAuthorized, loading, setLoading)
+ }
+ className="btn btcd-btn-lg purple sh-sm flx"
+ type="button"
+ disabled={isAuthorized || loading.auth}>
+ {isAuthorized ? __('Authorized ✔', 'bit-integrations') : __('Authorize', 'bit-integrations')}
+ {loading.auth && }
+
+
+
+ {__('Next', 'bit-integrations')}
+
+
+
+ )}
+
+
+ )
+}
diff --git a/frontend/src/components/AllIntegrations/MondayCom/MondayComCommonFunc.js b/frontend/src/components/AllIntegrations/MondayCom/MondayComCommonFunc.js
new file mode 100644
index 000000000..12fa97eb6
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/MondayCom/MondayComCommonFunc.js
@@ -0,0 +1,203 @@
+/* eslint-disable no-console */
+/* eslint-disable no-else-return */
+import toast from 'react-hot-toast'
+import bitsFetch from '../../../Utils/bitsFetch'
+import { __ } from '../../../Utils/i18nwrap'
+import { create } from 'mutative'
+import { staticFieldsMap, needsColumnMap } from './staticData'
+
+export const handleInput = (e, mondayComConf, setMondayComConf) => {
+ setMondayComConf(mondayComConf =>
+ create(mondayComConf, draftConf => {
+ const { name, value } = e.target
+ if (value !== '') {
+ draftConf[name] = value
+ } else {
+ delete draftConf[name]
+ }
+ })
+ )
+}
+
+export const generateMappedField = mondayComFields => {
+ const requiredFlds = mondayComFields?.filter(fld => fld.required === true) || []
+
+ if (requiredFlds.length > 0) {
+ return requiredFlds.map(field => ({ formField: '', mondayComField: field.key }))
+ }
+
+ if (!mondayComFields || mondayComFields.length === 0) {
+ return []
+ }
+
+ return [{ formField: '', mondayComField: '' }]
+}
+
+export const checkMappedFields = mondayComConf => {
+ const mappedFields = mondayComConf?.field_map
+ ? mondayComConf.field_map.filter(
+ mappedField =>
+ !mappedField.formField ||
+ !mappedField.mondayComField ||
+ (mappedField.formField === 'custom' && !mappedField.customValue)
+ )
+ : []
+ if (mappedFields.length > 0) {
+ return false
+ }
+ return true
+}
+
+export const mondayComAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => {
+ if (!confTmp.apiToken) {
+ setError({
+ apiToken: !confTmp.apiToken ? __("API Token can't be empty", 'bit-integrations') : ''
+ })
+ return
+ }
+
+ setError({})
+ setLoading({ ...loading, auth: true })
+
+ const requestParams = {
+ apiToken: confTmp.apiToken
+ }
+
+ bitsFetch(requestParams, 'mondayCom_authentication').then(result => {
+ if (result && result.success) {
+ setIsAuthorized(true)
+ setLoading({ ...loading, auth: false })
+ toast.success(__('Authorized Successfully', 'bit-integrations'))
+ return
+ }
+ setLoading({ ...loading, auth: false })
+ const message = typeof result?.data === 'string' ? result.data : result?.data?.message
+ const authErrorMessage = result?.message || result?.error || message
+ toast.error(
+ authErrorMessage
+ ? `${__('Authorization failed', 'bit-integrations')}: ${authErrorMessage}`
+ : __('Authorization failed', 'bit-integrations')
+ )
+ })
+}
+
+export const getAllBoards = (confTmp, setConf, setLoading) => {
+ setLoading(prev => ({ ...prev, board: true }))
+
+ const requestParams = {
+ apiToken: confTmp.apiToken
+ }
+
+ bitsFetch(requestParams, 'mondayCom_fetch_boards').then(result => {
+ if (result && result.success) {
+ if (result.data) {
+ setConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf.boards = result.data
+ })
+ )
+
+ setLoading(prev => ({ ...prev, board: false }))
+ toast.success(__('Boards fetched successfully', 'bit-integrations'))
+ return
+ }
+ setLoading(prev => ({ ...prev, board: false }))
+ toast.error(__('Boards Not Found!', 'bit-integrations'))
+ return
+ }
+ setLoading(prev => ({ ...prev, board: false }))
+ toast.error(__('Boards fetching failed', 'bit-integrations'))
+ })
+}
+
+export const getAllGroups = (confTmp, setConf, boardId, setLoading) => {
+ setLoading(prev => ({ ...prev, group: true }))
+
+ const requestParams = {
+ apiToken: confTmp.apiToken,
+ boardId
+ }
+
+ bitsFetch(requestParams, 'mondayCom_fetch_groups').then(result => {
+ if (result && result.success) {
+ if (result.data) {
+ setConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf.groups = result.data
+ })
+ )
+
+ setLoading(prev => ({ ...prev, group: false }))
+ return
+ }
+ setLoading(prev => ({ ...prev, group: false }))
+ toast.error(__('Groups Not Found!', 'bit-integrations'))
+ return
+ }
+ setLoading(prev => ({ ...prev, group: false }))
+ toast.error(__('Groups fetching failed', 'bit-integrations'))
+ })
+}
+
+export const getAllColumns = (confTmp, setConf, boardId, setLoading) => {
+ setLoading(prev => ({ ...prev, column: true }))
+
+ const requestParams = {
+ apiToken: confTmp.apiToken,
+ boardId
+ }
+
+ bitsFetch(requestParams, 'mondayCom_fetch_columns').then(result => {
+ if (result && result.success) {
+ if (result.data) {
+ setConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf.columns = result.data
+ if (needsColumnMap.includes(draftConf.mainAction)) {
+ const base = staticFieldsMap[draftConf.mainAction] || []
+ draftConf.mondayComFields = [...base, ...result.data]
+ draftConf.field_map = generateMappedField(draftConf.mondayComFields)
+ }
+ })
+ )
+
+ setLoading(prev => ({ ...prev, column: false }))
+ return
+ }
+ setLoading(prev => ({ ...prev, column: false }))
+ toast.error(__('Columns Not Found!', 'bit-integrations'))
+ return
+ }
+ setLoading(prev => ({ ...prev, column: false }))
+ toast.error(__('Columns fetching failed', 'bit-integrations'))
+ })
+}
+
+export const getAllItems = (confTmp, setConf, boardId, setLoading) => {
+ setLoading(prev => ({ ...prev, item: true }))
+
+ const requestParams = {
+ apiToken: confTmp.apiToken,
+ boardId
+ }
+
+ bitsFetch(requestParams, 'mondayCom_fetch_items').then(result => {
+ if (result && result.success) {
+ if (result.data) {
+ setConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf.items = result.data
+ })
+ )
+
+ setLoading(prev => ({ ...prev, item: false }))
+ return
+ }
+ setLoading(prev => ({ ...prev, item: false }))
+ toast.error(__('Items Not Found!', 'bit-integrations'))
+ return
+ }
+ setLoading(prev => ({ ...prev, item: false }))
+ toast.error(__('Items fetching failed', 'bit-integrations'))
+ })
+}
diff --git a/frontend/src/components/AllIntegrations/MondayCom/MondayComFieldMap.jsx b/frontend/src/components/AllIntegrations/MondayCom/MondayComFieldMap.jsx
new file mode 100644
index 000000000..1f7664352
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/MondayCom/MondayComFieldMap.jsx
@@ -0,0 +1,110 @@
+import { useRecoilValue } from 'recoil'
+import { $appConfigState } from '../../../GlobalStates'
+import { SmartTagField } from '../../../Utils/StaticData/SmartTagField'
+import { __, sprintf } from '../../../Utils/i18nwrap'
+import TagifyInput from '../../Utilities/TagifyInput'
+import { addFieldMap, delFieldMap, handleFieldMapping } from '../IntegrationHelpers/FieldMapHelper'
+import { handleCustomValue } from '../IntegrationHelpers/IntegrationHelpers'
+
+export default function MondayComFieldMap({
+ i,
+ formFields,
+ field,
+ mondayComConf,
+ setMondayComConf
+}) {
+ const { isPro } = useRecoilValue($appConfigState)
+
+ const availableFields = mondayComConf?.mondayComFields || []
+ const requiredFields = availableFields.filter(f => f.required === true)
+ const nonRequiredFields = availableFields.filter(f => f.required === false)
+
+ return (
+
+
+
+ handleFieldMapping(ev, i, mondayComConf, setMondayComConf)}>
+ {__('Select Field', 'bit-integrations')}
+
+ {formFields?.map(f => (
+
+ {f.label}
+
+ ))}
+
+ {__('Custom...', 'bit-integrations')}
+
+ {isPro &&
+ SmartTagField?.map(f => (
+
+ {f.label}
+
+ ))}
+
+
+
+ {field.formField === 'custom' && (
+ handleCustomValue(e, i, mondayComConf, setMondayComConf)}
+ label={__('Custom Value', 'bit-integrations')}
+ className="mr-2"
+ type="text"
+ value={field.customValue}
+ placeholder={__('Custom Value', 'bit-integrations')}
+ formFields={formFields}
+ />
+ )}
+
+ handleFieldMapping(ev, i, mondayComConf, setMondayComConf)}>
+ {__('Select Field', 'bit-integrations')}
+ {i < requiredFields.length ? (
+
+ {requiredFields[i].label}
+
+ ) : (
+ nonRequiredFields.map(({ key, label }) => (
+
+ {label}
+
+ ))
+ )}
+
+
+ {i >= requiredFields.length && (
+ <>
+
addFieldMap(i, mondayComConf, setMondayComConf)}
+ className="icn-btn sh-sm ml-2 mr-1"
+ type="button">
+ +
+
+
delFieldMap(i, mondayComConf, setMondayComConf)}
+ className="icn-btn sh-sm ml-1"
+ type="button"
+ aria-label={__('Remove field mapping', 'bit-integrations')}>
+
+
+ >
+ )}
+
+
+ )
+}
diff --git a/frontend/src/components/AllIntegrations/MondayCom/MondayComIntegLayout.jsx b/frontend/src/components/AllIntegrations/MondayCom/MondayComIntegLayout.jsx
new file mode 100644
index 000000000..3f9645125
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/MondayCom/MondayComIntegLayout.jsx
@@ -0,0 +1,325 @@
+/* eslint-disable no-unused-vars */
+import 'react-multiple-select-dropdown-lite/dist/index.css'
+import { useRecoilValue } from 'recoil'
+import { $appConfigState } from '../../../GlobalStates'
+import { __ } from '../../../Utils/i18nwrap'
+import Loader from '../../Loaders/Loader'
+import { addFieldMap } from '../IntegrationHelpers/FieldMapHelper'
+import MultiSelect from 'react-multiple-select-dropdown-lite'
+import {
+ getAllBoards,
+ getAllGroups,
+ getAllColumns,
+ getAllItems,
+ generateMappedField
+} from './MondayComCommonFunc'
+import MondayComFieldMap from './MondayComFieldMap'
+import {
+ staticFieldsMap,
+ columnTypeList,
+ needsBoard,
+ needsItem,
+ needsColumnMap
+} from './staticData'
+import { checkIsPro, getProLabel } from '../../Utilities/ProUtilHelpers'
+import { create } from 'mutative'
+import MondayComActions from './MondayComActions'
+
+export default function MondayComIntegLayout({
+ formFields,
+ mondayComConf,
+ setMondayComConf,
+ loading,
+ setLoading,
+ isLoading
+}) {
+ const btcbi = useRecoilValue($appConfigState)
+ const { isPro } = btcbi
+
+ const handleActionChange = (value, name) => {
+ const needBoard = needsBoard.includes(value)
+ setMondayComConf(prevConf =>
+ create(prevConf, draftConf => {
+ if (value !== '') {
+ draftConf[name] = value
+ } else {
+ delete draftConf[name]
+ }
+
+ delete draftConf.selectedBoard
+ delete draftConf.selectedGroup
+ delete draftConf.selectedItem
+ delete draftConf.columns
+ delete draftConf.groups
+ delete draftConf.items
+ delete draftConf.columnType
+ delete draftConf.addToTop
+
+ const base = staticFieldsMap[value] || []
+ draftConf.mondayComFields = base
+ draftConf.field_map = generateMappedField(base)
+ })
+ )
+
+ if (needBoard) {
+ getAllBoards(mondayComConf, setMondayComConf, setLoading)
+ }
+ }
+
+ const handleBoardChange = val => {
+ setMondayComConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf.selectedBoard = val
+ delete draftConf.selectedGroup
+ delete draftConf.selectedItem
+ delete draftConf.groups
+ delete draftConf.items
+ delete draftConf.columns
+ })
+ )
+
+ if (!val) return
+
+ const action = mondayComConf.mainAction
+
+ getAllGroups(mondayComConf, setMondayComConf, val, setLoading)
+
+ if (needsColumnMap.includes(action)) {
+ getAllColumns(mondayComConf, setMondayComConf, val, setLoading)
+ }
+ if (needsItem.includes(action)) {
+ getAllItems(mondayComConf, setMondayComConf, val, setLoading)
+ }
+ }
+
+ const handleSelectChange = (val, name) => {
+ setMondayComConf(prevConf =>
+ create(prevConf, draftConf => {
+ if (val === '' || val === null || val === undefined) {
+ delete draftConf[name]
+ } else {
+ draftConf[name] = val
+ }
+ })
+ )
+ }
+
+ const mainAction = mondayComConf?.mainAction
+ const hasFieldMap =
+ mainAction
+ && (staticFieldsMap[mainAction]?.length > 0 || needsColumnMap.includes(mainAction))
+
+ return (
+ <>
+
+
+ {__('Select Action:', 'bit-integrations')}
+ handleActionChange(val, 'mainAction')}
+ options={mondayComConf?.actionLists?.map(actionType => ({
+ label: checkIsPro(isPro, actionType.is_pro)
+ ? actionType.label
+ : getProLabel(actionType.label),
+ value: actionType.name,
+ disabled: !checkIsPro(isPro, actionType.is_pro)
+ }))}
+ singleSelect
+ closeOnSelect
+ />
+
+
+ {mainAction && needsBoard.includes(mainAction) && !loading.board && (
+ <>
+
+
+ {__('Select Board:', 'bit-integrations')}
+ ({ label: b.name, value: `${b.id}` })) || []
+ }
+ className="msl-wrp-options dropdown-custom-width"
+ defaultValue={mondayComConf?.selectedBoard}
+ onChange={handleBoardChange}
+ singleSelect
+ closeOnSelect
+ disabled={loading.board}
+ />
+ getAllBoards(mondayComConf, setMondayComConf, setLoading)}
+ className="icn-btn sh-sm ml-2 mr-2 tooltip"
+ style={{ '--tooltip-txt': `'${__('Refresh Boards', 'bit-integrations')}'` }}
+ type="button"
+ disabled={loading.board}>
+ ↻
+
+
+ >
+ )}
+
+ {mainAction
+ && !['create_column', 'delete_group', 'archive_group', 'duplicate_group', 'archive_item', 'delete_item', 'move_item_to_group'].includes(mainAction)
+ && mondayComConf?.selectedBoard
+ && !loading.group && (
+ <>
+
+
+ {__('Select Group:', 'bit-integrations')}
+ ({ label: g.name, value: `${g.id}` })) || []
+ }
+ className="msl-wrp-options dropdown-custom-width"
+ defaultValue={mondayComConf?.selectedGroup}
+ onChange={val => handleSelectChange(val, 'selectedGroup')}
+ singleSelect
+ closeOnSelect
+ />
+
+ getAllGroups(
+ mondayComConf,
+ setMondayComConf,
+ mondayComConf.selectedBoard,
+ setLoading
+ )
+ }
+ className="icn-btn sh-sm ml-2 mr-2 tooltip"
+ style={{ '--tooltip-txt': `'${__('Refresh Groups', 'bit-integrations')}'` }}
+ type="button"
+ disabled={loading.group}>
+ ↻
+
+
+ >
+ )}
+
+ {mainAction
+ && needsItem.includes(mainAction)
+ && mondayComConf?.selectedBoard
+ && !loading.item && (
+ <>
+
+
+ {__('Select Item:', 'bit-integrations')}
+ ({ label: it.name, value: `${it.id}` })) || []
+ }
+ className="msl-wrp-options dropdown-custom-width"
+ defaultValue={mondayComConf?.selectedItem}
+ onChange={val => handleSelectChange(val, 'selectedItem')}
+ singleSelect
+ closeOnSelect
+ />
+
+ getAllItems(
+ mondayComConf,
+ setMondayComConf,
+ mondayComConf.selectedBoard,
+ setLoading
+ )
+ }
+ className="icn-btn sh-sm ml-2 mr-2 tooltip"
+ style={{ '--tooltip-txt': `'${__('Refresh Items', 'bit-integrations')}'` }}
+ type="button"
+ disabled={loading.item}>
+ ↻
+
+
+ >
+ )}
+
+ {mainAction === 'create_column' && (
+ <>
+
+
+ {__('Column Type:', 'bit-integrations')}
+ handleSelectChange(val, 'columnType')}
+ singleSelect
+ closeOnSelect
+ />
+
+ >
+ )}
+
+ {(loading.board || loading.group || loading.column || loading.item) && (
+
+ )}
+
+ {mainAction && hasFieldMap && !isLoading && (
+
+
+
+ {__('Field Map', 'bit-integrations')}
+
+
+
+
+
+ {__('Form Fields', 'bit-integrations')}
+
+
+ {__('Monday.com Fields', 'bit-integrations')}
+
+
+ {mondayComConf?.field_map?.map((itm, i) => (
+
+ ))}
+ {needsColumnMap.includes(mainAction) && (
+
+
+ addFieldMap(
+ mondayComConf.field_map.length,
+ mondayComConf,
+ setMondayComConf,
+ false
+ )
+ }
+ className="icn-btn sh-sm"
+ type="button">
+ +
+
+
+ )}
+
+
+ )}
+
+ {mainAction === 'duplicate_group' && (
+ <>
+
+
+ {__('Utilities', 'bit-integrations')}
+
+
+
+ >
+ )}
+ >
+ )
+}
diff --git a/frontend/src/components/AllIntegrations/MondayCom/staticData.js b/frontend/src/components/AllIntegrations/MondayCom/staticData.js
new file mode 100644
index 000000000..96131d4ab
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/MondayCom/staticData.js
@@ -0,0 +1,83 @@
+import { __ } from '../../../Utils/i18nwrap'
+
+export const actionLists = [
+ { name: 'create_item', label: __('Create Item', 'bit-integrations'), is_pro: true },
+ { name: 'update_item', label: __('Update Item', 'bit-integrations'), is_pro: true },
+ { name: 'create_subitem', label: __('Create Subitem', 'bit-integrations'), is_pro: true },
+ { name: 'move_item_to_group', label: __('Move Item to Group', 'bit-integrations'), is_pro: true },
+ { name: 'archive_item', label: __('Archive Item', 'bit-integrations'), is_pro: true },
+ { name: 'delete_item', label: __('Delete Item', 'bit-integrations'), is_pro: true },
+ { name: 'archive_board', label: __('Archive Board', 'bit-integrations'), is_pro: true },
+ { name: 'create_group', label: __('Create Group', 'bit-integrations'), is_pro: true },
+ { name: 'duplicate_group', label: __('Duplicate Group', 'bit-integrations'), is_pro: true },
+ { name: 'archive_group', label: __('Archive Group', 'bit-integrations'), is_pro: true },
+ { name: 'delete_group', label: __('Delete Group', 'bit-integrations'), is_pro: true },
+ { name: 'create_column', label: __('Create Column', 'bit-integrations'), is_pro: true }
+]
+
+export const staticFieldsMap = {
+ create_item: [{ label: __('Item Name', 'bit-integrations'), key: 'item_name', required: true }],
+ update_item: [{ label: __('Item ID', 'bit-integrations'), key: 'item_id', required: true }],
+ create_subitem: [
+ { label: __('Subitem Name', 'bit-integrations'), key: 'subitem_name', required: true }
+ ],
+ move_item_to_group: [
+ { label: __('Group ID', 'bit-integrations'), key: 'group_id', required: true },
+ { label: __('Item ID', 'bit-integrations'), key: 'item_id', required: true }
+ ],
+ archive_item: [
+ { label: __('Item ID', 'bit-integrations'), key: 'item_id', required: true }
+ ],
+ delete_item: [
+ { label: __('Item ID', 'bit-integrations'), key: 'item_id', required: true }
+ ],
+ archive_board: [
+ { label: __('Board ID', 'bit-integrations'), key: 'board_id', required: true }
+ ],
+ create_group: [{ label: __('Group Name', 'bit-integrations'), key: 'group_name', required: true }],
+ duplicate_group: [
+ { label: __('Group ID', 'bit-integrations'), key: 'group_id', required: true },
+ { label: __('Group Title', 'bit-integrations'), key: 'group_title', required: true }
+ ],
+ archive_group: [
+ { label: __('Group ID', 'bit-integrations'), key: 'group_id', required: true }
+ ],
+ delete_group: [
+ { label: __('Group ID', 'bit-integrations'), key: 'group_id', required: true }
+ ],
+ create_column: [{ label: __('Column Title', 'bit-integrations'), key: 'column_title', required: true }]
+}
+
+export const columnTypeList = [
+ { label: __('Text', 'bit-integrations'), value: 'text' },
+ { label: __('Long Text', 'bit-integrations'), value: 'long_text' },
+ { label: __('Number', 'bit-integrations'), value: 'numbers' },
+ { label: __('Status', 'bit-integrations'), value: 'status' },
+ { label: __('Date', 'bit-integrations'), value: 'date' },
+ { label: __('Checkbox', 'bit-integrations'), value: 'checkbox' },
+ { label: __('Email', 'bit-integrations'), value: 'email' },
+ { label: __('Phone', 'bit-integrations'), value: 'phone' },
+ { label: __('Link', 'bit-integrations'), value: 'link' },
+ { label: __('Rating', 'bit-integrations'), value: 'rating' },
+ { label: __('Tags', 'bit-integrations'), value: 'tag' },
+ { label: __('People', 'bit-integrations'), value: 'people' },
+ { label: __('Timeline', 'bit-integrations'), value: 'timeline' }
+]
+
+export const needsBoard = [
+ 'create_item',
+ 'update_item',
+ 'create_subitem',
+ 'move_item_to_group',
+ 'archive_item',
+ 'delete_item',
+ 'archive_group',
+ 'delete_group',
+ 'create_group',
+ 'duplicate_group',
+ 'create_column'
+]
+
+export const needsItem = ['create_subitem']
+
+export const needsColumnMap = ['create_item', 'update_item']
diff --git a/frontend/src/components/AllIntegrations/NewInteg.jsx b/frontend/src/components/AllIntegrations/NewInteg.jsx
index 505955d35..59978c59c 100644
--- a/frontend/src/components/AllIntegrations/NewInteg.jsx
+++ b/frontend/src/components/AllIntegrations/NewInteg.jsx
@@ -164,6 +164,7 @@ const TheEventsCalendar = lazy(() => import('./TheEventsCalendar/TheEventsCalend
const LMFWC = lazy(() => import('./LMFWC/LMFWC'))
const Voxel = lazy(() => import('./Voxel/Voxel'))
const SmartSuite = lazy(() => import('./SmartSuite/SmartSuite'))
+const MondayCom = lazy(() => import('./MondayCom/MondayCom'))
const Bento = lazy(() => import('./Bento/Bento'))
const Line = lazy(() => import('./Line/Line'))
const ACPT = lazy(() => import('./ACPT/ACPT'))
@@ -1606,6 +1607,15 @@ export default function NewInteg({ allIntegURL }) {
setFlow={setFlow}
/>
)
+ case 'Monday.Com':
+ return (
+
+ )
case 'Bento':
return (