Rules.vue 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391
  1. <template>
  2. <div>
  3. <Head :title="$page.component" />
  4. <h1 id="primary-heading" class="sr-only">
  5. {{ $page.component }}
  6. </h1>
  7. <div class="sm:flex sm:items-center mb-6">
  8. <div class="sm:flex-auto">
  9. <h1 class="text-2xl font-semibold text-grey-900">Rules</h1>
  10. <p class="mt-2 text-sm text-grey-700">
  11. A list of all the rules {{ search ? 'found for your search' : 'in your account' }}
  12. <button @click="moreInfoOpen = !moreInfoOpen">
  13. <InformationCircleIcon
  14. class="h-6 w-6 inline-block cursor-pointer text-grey-500"
  15. title="Click for more information"
  16. />
  17. </button>
  18. </p>
  19. </div>
  20. <div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
  21. <button
  22. type="button"
  23. @click="openCreateModal"
  24. class="inline-flex items-center justify-center rounded-md border border-transparent bg-cyan-400 hover:bg-cyan-300 text-cyan-900 px-4 py-2 font-bold shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 sm:w-auto"
  25. >
  26. Create Rule
  27. </button>
  28. </div>
  29. </div>
  30. <div v-if="rows.length" class="bg-white shadow">
  31. <div class="vgt-responsive">
  32. <table class="table-auto w-full">
  33. <thead class="border-b border-grey-100 text-grey-400">
  34. <tr>
  35. <th scope="col" class="p-3"></th>
  36. <th scope="col" class="p-3 text-left">Created</th>
  37. <th scope="col" class="p-3 text-left">Name</th>
  38. <th scope="col" class="p-3 text-left">Active</th>
  39. <th scope="col" class="p-3 text-left">
  40. Applied
  41. <span
  42. class="tooltip outline-none"
  43. data-tippy-content="This is the number of times that the rule has been applied. Hover over the count to see when it was last applied."
  44. >
  45. <icon name="info" class="inline-block w-4 h-4 text-grey-300 fill-current" />
  46. </span>
  47. </th>
  48. <th scope="col" class="p-3"></th>
  49. </tr>
  50. </thead>
  51. <draggable
  52. :component-data="{ type: 'transition', name: 'flip-list' }"
  53. v-model="rows"
  54. item-key="id"
  55. tag="tbody"
  56. handle=".handle"
  57. :group="{ name: 'description' }"
  58. ghost-class="ghost"
  59. @change="reorderRules"
  60. @update="debounceToolips"
  61. >
  62. <template #item="{ element }">
  63. <tr class="border-b border-grey-100 h-20">
  64. <td scope="row" class="p-3">
  65. <icon
  66. name="menu"
  67. class="handle block w-6 h-6 text-grey-300 fill-current cursor-pointer"
  68. />
  69. </td>
  70. <td scope="row" class="p-3">
  71. <span
  72. class="tooltip outline-none cursor-default text-sm text-grey-500"
  73. :data-tippy-content="$filters.formatDate(element.created_at)"
  74. >{{ $filters.timeAgo(element.created_at) }}
  75. </span>
  76. </td>
  77. <td scope="row" class="p-3">
  78. <span class="font-medium text-grey-700">{{ element.name }}</span>
  79. </td>
  80. <td scope="row" class="p-3">
  81. <Toggle
  82. v-model="element.active"
  83. @on="activateRule(element.id)"
  84. @off="deactivateRule(element.id)"
  85. />
  86. </td>
  87. <td scope="row" class="p-3">
  88. <span
  89. v-if="element.last_applied"
  90. class="tooltip outline-none cursor-default font-semibold text-indigo-800"
  91. :data-tippy-content="
  92. $filters.timeAgo(element.last_applied) +
  93. ' (' +
  94. $filters.formatDate(element.last_applied) +
  95. ')'
  96. "
  97. >{{ element.applied.toLocaleString() }}
  98. </span>
  99. <span v-else>{{ element.applied.toLocaleString() }} </span>
  100. </td>
  101. <td scope="row" class="p-3 text-right w-0 min-w-fit whitespace-nowrap">
  102. <button
  103. @click="openEditModal(element)"
  104. as="button"
  105. type="button"
  106. class="text-indigo-500 hover:text-indigo-800 font-medium"
  107. >
  108. Edit
  109. </button>
  110. <button
  111. @click="openDeleteModal(element.id)"
  112. as="button"
  113. type="button"
  114. class="text-indigo-500 hover:text-indigo-800 font-medium ml-4"
  115. >
  116. Delete
  117. </button>
  118. </td>
  119. </tr>
  120. </template>
  121. </draggable>
  122. </table>
  123. </div>
  124. </div>
  125. <div v-else-if="search" class="text-center">
  126. <FunnelIcon class="mx-auto h-16 w-16 text-grey-400" />
  127. <h3 class="mt-2 text-lg font-medium text-grey-900">No Rules found for that search</h3>
  128. <p class="mt-1 text-md text-grey-500">Try entering a different search term.</p>
  129. <div class="mt-6">
  130. <Link
  131. :href="route('rules.index')"
  132. type="button"
  133. class="inline-flex items-center rounded-md border border-transparent bg-cyan-400 hover:bg-cyan-300 text-cyan-900 px-4 py-2 text-sm font-medium shadow-sm focus:outline-none"
  134. >
  135. View All Rules
  136. </Link>
  137. </div>
  138. </div>
  139. <div v-else class="text-center">
  140. <FunnelIcon class="mx-auto h-16 w-16 text-grey-400" />
  141. <h3 class="mt-2 text-lg font-medium text-grey-900">No Rules</h3>
  142. <p class="mt-1 text-md text-grey-500">Get started by creating a new rule.</p>
  143. <div class="mt-6">
  144. <button
  145. @click="openCreateModal"
  146. type="button"
  147. class="inline-flex items-center rounded-md border border-transparent bg-cyan-400 hover:bg-cyan-300 text-cyan-900 px-4 py-2 text-sm font-medium shadow-sm focus:outline-none"
  148. >
  149. <PlusIcon class="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
  150. Create a Rule
  151. </button>
  152. </div>
  153. </div>
  154. <Modal
  155. :open="createRuleModalOpen"
  156. @close="createRuleModalOpen = false"
  157. max-width="md:max-w-3xl"
  158. >
  159. <template v-slot:title> Create new rule </template>
  160. <template v-slot:content>
  161. <p class="mt-4 text-grey-700">
  162. Rules work on all emails, including replies and also send froms. New conditions and
  163. actions will be added over time.
  164. </p>
  165. <label for="rule_name" class="block font-medium leading-6 text-grey-600 text-sm my-2">
  166. Name
  167. </label>
  168. <p v-show="errors.ruleName" class="mb-3 text-red-500 text-sm">
  169. {{ errors.ruleName }}
  170. </p>
  171. <input
  172. v-model="createRuleObject.name"
  173. id="rule_name"
  174. type="text"
  175. class="block w-full rounded-md border-0 py-2 pr-10 ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-base sm:leading-6"
  176. :class="errors.ruleName ? 'ring-red-500' : ''"
  177. placeholder="Enter name"
  178. autofocus
  179. />
  180. <fieldset class="border border-cyan-400 p-4 my-4 rounded-sm">
  181. <legend class="px-2 leading-none text-sm">Conditions</legend>
  182. <!-- Loop for conditions -->
  183. <div v-for="(condition, key) in createRuleObject.conditions" :key="key">
  184. <!-- AND/OR operator -->
  185. <div v-if="key !== 0" class="flex justify-center my-2">
  186. <div class="relative">
  187. <select
  188. v-model="createRuleObject.operator"
  189. :id="`create_rule_operator_${key}`"
  190. class="block appearance-none w-full text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  191. required
  192. >
  193. <option value="AND">AND</option>
  194. <option value="OR">OR</option>
  195. </select>
  196. </div>
  197. </div>
  198. <div class="p-2 w-full bg-grey-100">
  199. <div class="flex">
  200. <div
  201. class="w-full flex flex-col sm:flex-row sm:items-center space-y-2 sm:space-y-0"
  202. >
  203. <span>If the</span>
  204. <span class="sm:ml-2">
  205. <div class="relative">
  206. <select
  207. v-model="createRuleObject.conditions[key].type"
  208. :id="`create_rule_condition_types_${key}`"
  209. class="block appearance-none w-full sm:w-32 text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  210. required
  211. >
  212. <option
  213. v-for="option in conditionTypeOptions"
  214. :key="option.value"
  215. :value="option.value"
  216. >
  217. {{ option.label }}
  218. </option>
  219. </select>
  220. </div>
  221. </span>
  222. <span
  223. v-if="conditionMatchOptions(createRuleObject, key).length"
  224. class="sm:ml-4 flex flex-col sm:flex-row space-y-2 sm:space-y-0"
  225. >
  226. <div class="relative sm:mr-4">
  227. <select
  228. v-model="createRuleObject.conditions[key].match"
  229. :id="`create_rule_condition_matches_${key}`"
  230. class="block appearance-none w-full sm:w-40 text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  231. required
  232. >
  233. <option
  234. v-for="option in conditionMatchOptions(createRuleObject, key)"
  235. :key="option"
  236. :value="option"
  237. >
  238. {{ option }}
  239. </option>
  240. </select>
  241. </div>
  242. <div class="flex">
  243. <input
  244. v-model="createRuleObject.conditions[key].currentConditionValue"
  245. @keyup.enter="addValueToCondition(createRuleObect, key)"
  246. type="text"
  247. class="w-full appearance-none bg-white border border-transparent rounded-l text-grey-700 focus:outline-none p-2"
  248. :class="errors.ruleConditions ? 'border-red-500' : ''"
  249. placeholder="Enter value"
  250. autofocus
  251. />
  252. <button
  253. @click="addValueToCondition(createRuleObject, key)"
  254. class="p-2 bg-grey-200 rounded-r text-grey-600"
  255. >
  256. Insert
  257. </button>
  258. </div>
  259. </span>
  260. </div>
  261. <div class="flex items-center">
  262. <!-- delete button -->
  263. <icon
  264. v-if="createRuleObject.conditions.length > 1"
  265. name="trash"
  266. class="block ml-4 w-6 h-6 text-grey-300 fill-current cursor-pointer"
  267. @click="deleteCondition(createRuleObject, key)"
  268. />
  269. </div>
  270. </div>
  271. <div class="mt-2 text-left">
  272. <span
  273. v-for="(value, index) in createRuleObject.conditions[key].values"
  274. :key="index"
  275. >
  276. <span class="bg-green-200 text-sm font-semibold rounded-sm pl-1 text-nowrap">
  277. {{ value }}
  278. <icon
  279. name="close"
  280. class="inline-block w-4 h-4 text-grey-900 fill-current cursor-pointer"
  281. @click="createRuleObject.conditions[key].values.splice(index, 1)"
  282. />
  283. </span>
  284. <span
  285. class="mx-1"
  286. v-if="index + 1 !== createRuleObject.conditions[key].values.length"
  287. >
  288. or
  289. </span>
  290. </span>
  291. </div>
  292. </div>
  293. </div>
  294. <!-- add condition button -->
  295. <button
  296. @click="addCondition(createRuleObject)"
  297. class="mt-4 p-2 text-grey-800 bg-white hover:bg-grey-50 border border-grey-100 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
  298. >
  299. Add condition
  300. </button>
  301. <p v-show="errors.ruleConditions" class="mt-2 text-red-500 text-sm">
  302. {{ errors.ruleConditions }}
  303. </p>
  304. </fieldset>
  305. <fieldset class="border border-cyan-400 p-4 my-4 rounded-sm">
  306. <legend class="px-2 leading-none text-sm">Actions</legend>
  307. <!-- Loop for actions -->
  308. <div v-for="(action, key) in createRuleObject.actions" :key="key">
  309. <!-- AND/OR operator -->
  310. <div v-if="key !== 0" class="flex justify-center my-2">
  311. <div class="relative">AND</div>
  312. </div>
  313. <div class="p-2 w-full bg-grey-100">
  314. <div class="flex">
  315. <div
  316. class="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:items-center w-full"
  317. >
  318. <span>Then</span>
  319. <span class="sm:ml-2">
  320. <div class="relative">
  321. <select
  322. v-model="createRuleObject.actions[key].type"
  323. @change="ruleActionChange(createRuleObject.actions[key])"
  324. :id="`rule_action_types_${key}`"
  325. class="w-full block appearance-none text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  326. required
  327. >
  328. <option
  329. v-for="option in actionTypeOptions"
  330. :key="option.value"
  331. :value="option.value"
  332. >
  333. {{ option.label }}
  334. </option>
  335. </select>
  336. </div>
  337. </span>
  338. <span
  339. v-if="
  340. createRuleObject.actions[key].type === 'subject' ||
  341. createRuleObject.actions[key].type === 'displayFrom'
  342. "
  343. class="sm:ml-4 flex"
  344. >
  345. <div class="flex w-full">
  346. <input
  347. v-model="createRuleObject.actions[key].value"
  348. type="text"
  349. class="w-full appearance-none bg-white border border-transparent rounded text-grey-700 focus:outline-none p-2"
  350. :class="errors.ruleActions ? 'border-red-500' : ''"
  351. placeholder="Enter value"
  352. autofocus
  353. />
  354. </div>
  355. </span>
  356. <multiselect
  357. v-if="createRuleObject.actions[key].type === 'forwardTo'"
  358. class="sm:!ml-4 flex"
  359. v-model="createRuleObject.actions[key].value"
  360. :options="recipientOptions"
  361. mode="single"
  362. value-prop="id"
  363. :close-on-select="true"
  364. :clear-on-select="false"
  365. :searchable="false"
  366. :allow-empty="true"
  367. placeholder="Select recipient"
  368. label="email"
  369. track-by="email"
  370. >
  371. </multiselect>
  372. <span
  373. v-else-if="createRuleObject.actions[key].type === 'banner'"
  374. class="sm:ml-4 flex"
  375. >
  376. <div class="relative sm:mr-4 w-full">
  377. <select
  378. v-model="createRuleObject.actions[key].value"
  379. :id="`create_rule_action_banner_${key}`"
  380. class="w-full block appearance-none sm:w-40 text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  381. required
  382. >
  383. <option value="top">Top</option>
  384. <option value="bottom">Bottom</option>
  385. <option value="off">Off</option>
  386. </select>
  387. </div>
  388. </span>
  389. </div>
  390. <div class="flex items-center">
  391. <!-- delete button -->
  392. <icon
  393. v-if="createRuleObject.actions.length > 1"
  394. name="trash"
  395. class="block ml-4 w-6 h-6 text-grey-300 fill-current cursor-pointer"
  396. @click="deleteAction(createRuleObject, key)"
  397. />
  398. </div>
  399. </div>
  400. </div>
  401. </div>
  402. <!-- add action button -->
  403. <button
  404. @click="addAction(createRuleObject)"
  405. class="mt-4 p-2 text-grey-800 bg-white hover:bg-grey-50 border border-grey-100 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
  406. >
  407. Add action
  408. </button>
  409. <p v-show="errors.ruleActions" class="mt-2 text-red-500 text-sm">
  410. {{ errors.ruleActions }}
  411. </p>
  412. </fieldset>
  413. <fieldset class="border border-cyan-400 p-4 my-4 rounded-sm">
  414. <legend class="px-2 leading-none text-sm">Apply rule on</legend>
  415. <div class="w-full flex">
  416. <div class="relative flex items-center">
  417. <input
  418. v-model="createRuleObject.forwards"
  419. id="forwards"
  420. name="forwards"
  421. type="checkbox"
  422. class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-grey-300 rounded"
  423. />
  424. <label for="forwards" class="ml-2 text-sm text-grey-700">Forwards</label>
  425. </div>
  426. <div class="relative flex items-center mx-4">
  427. <input
  428. v-model="createRuleObject.replies"
  429. id="replies"
  430. name="replies"
  431. type="checkbox"
  432. class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-grey-300 rounded"
  433. />
  434. <label for="replies" class="ml-2 text-sm text-grey-700">Replies</label>
  435. </div>
  436. <div class="relative flex items-center">
  437. <input
  438. v-model="createRuleObject.sends"
  439. id="sends"
  440. name="sends"
  441. type="checkbox"
  442. class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-grey-300 rounded"
  443. />
  444. <label for="sends" class="ml-2 text-sm text-grey-700">Sends</label>
  445. </div>
  446. </div>
  447. </fieldset>
  448. <div class="mt-6 flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
  449. <button
  450. @click="createNewRule"
  451. class="bg-cyan-400 hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:cursor-not-allowed"
  452. :disabled="createRuleLoading"
  453. >
  454. Create Rule
  455. <loader v-if="createRuleLoading" />
  456. </button>
  457. <button
  458. @click="createRuleModalOpen = false"
  459. class="px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
  460. >
  461. Cancel
  462. </button>
  463. </div>
  464. </template>
  465. </Modal>
  466. <Modal :open="editRuleModalOpen" @close="closeEditModal" max-width="md:max-w-3xl">
  467. <template v-slot:title> Edit rule </template>
  468. <template v-slot:content>
  469. <p class="mt-4 text-grey-700">
  470. Rules work on all emails, including replies and also send froms. New conditions and
  471. actions will be added over time.
  472. </p>
  473. <label for="edit_rule_name" class="block font-medium leading-6 text-grey-600 text-sm my-2">
  474. Name
  475. </label>
  476. <p v-show="errors.ruleName" class="mb-3 text-red-500 text-sm">
  477. {{ errors.ruleName }}
  478. </p>
  479. <input
  480. v-model="editRuleObject.name"
  481. id="edit_rule_name"
  482. type="text"
  483. class="block w-full rounded-md border-0 py-2 pr-10 ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-base sm:leading-6"
  484. :class="errors.ruleName ? 'ring-red-500' : ''"
  485. placeholder="Enter name"
  486. autofocus
  487. />
  488. <fieldset class="border border-cyan-400 p-4 my-4 rounded-sm">
  489. <legend class="px-2 leading-none text-sm">Conditions</legend>
  490. <!-- Loop for conditions -->
  491. <div v-for="(condition, key) in editRuleObject.conditions" :key="key">
  492. <!-- AND/OR operator -->
  493. <div v-if="key !== 0" class="flex justify-center my-2">
  494. <div class="relative">
  495. <select
  496. v-model="editRuleObject.operator"
  497. :id="`edit_rule_operator_${key}`"
  498. class="block appearance-none w-full text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  499. required
  500. >
  501. <option value="AND">AND</option>
  502. <option value="OR">OR</option>
  503. </select>
  504. </div>
  505. </div>
  506. <div class="p-2 w-full bg-grey-100">
  507. <div class="flex">
  508. <div
  509. class="w-full flex flex-col sm:flex-row sm:items-center space-y-2 sm:space-y-0"
  510. >
  511. <span>If the</span>
  512. <span class="sm:ml-2">
  513. <div class="relative">
  514. <select
  515. v-model="editRuleObject.conditions[key].type"
  516. :id="`edit_rule_condition_types_${key}`"
  517. class="block appearance-none w-full sm:w-32 text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  518. required
  519. >
  520. <option
  521. v-for="option in conditionTypeOptions"
  522. :key="option.value"
  523. :value="option.value"
  524. >
  525. {{ option.label }}
  526. </option>
  527. </select>
  528. </div>
  529. </span>
  530. <span
  531. v-if="conditionMatchOptions(editRuleObject, key).length"
  532. class="sm:ml-4 flex flex-col sm:flex-row space-y-2 sm:space-y-0"
  533. >
  534. <div class="relative sm:mr-4">
  535. <select
  536. v-model="editRuleObject.conditions[key].match"
  537. :id="`edit_rule_condition_matches_${key}`"
  538. class="block appearance-none w-full sm:w-40 text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  539. required
  540. >
  541. <option
  542. v-for="option in conditionMatchOptions(editRuleObject, key)"
  543. :key="option"
  544. :value="option"
  545. >
  546. {{ option }}
  547. </option>
  548. </select>
  549. </div>
  550. <div class="flex">
  551. <input
  552. v-model="editRuleObject.conditions[key].currentConditionValue"
  553. @keyup.enter="addValueToCondition(editRuleObect, key)"
  554. type="text"
  555. class="w-full appearance-none bg-white border border-transparent rounded-l text-grey-700 focus:outline-none p-2"
  556. :class="errors.ruleConditions ? 'border-red-500' : ''"
  557. placeholder="Enter value"
  558. autofocus
  559. />
  560. <button
  561. @click="addValueToCondition(editRuleObject, key)"
  562. class="p-2 bg-grey-200 rounded-r text-grey-600"
  563. >
  564. Insert
  565. </button>
  566. </div>
  567. </span>
  568. </div>
  569. <div class="flex items-center">
  570. <!-- delete button -->
  571. <icon
  572. v-if="editRuleObject.conditions.length > 1"
  573. name="trash"
  574. class="block ml-4 w-6 h-6 text-grey-300 fill-current cursor-pointer"
  575. @click="deleteCondition(editRuleObject, key)"
  576. />
  577. </div>
  578. </div>
  579. <div class="mt-2 text-left">
  580. <span v-for="(value, index) in editRuleObject.conditions[key].values" :key="index">
  581. <span class="bg-green-200 text-sm font-semibold rounded-sm pl-1 text-nowrap">
  582. {{ value }}
  583. <icon
  584. name="close"
  585. class="inline-block w-4 h-4 text-grey-900 fill-current cursor-pointer"
  586. @click="editRuleObject.conditions[key].values.splice(index, 1)"
  587. />
  588. </span>
  589. <span
  590. class="mx-1"
  591. v-if="index + 1 !== editRuleObject.conditions[key].values.length"
  592. >
  593. or
  594. </span>
  595. </span>
  596. </div>
  597. </div>
  598. </div>
  599. <!-- add condition button -->
  600. <button
  601. @click="addCondition(editRuleObject)"
  602. class="mt-4 p-2 text-grey-800 bg-white hover:bg-grey-50 border border-grey-100 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
  603. >
  604. Add condition
  605. </button>
  606. <p v-show="errors.ruleConditions" class="mt-2 text-red-500 text-sm">
  607. {{ errors.ruleConditions }}
  608. </p>
  609. </fieldset>
  610. <fieldset class="border border-cyan-400 p-4 my-4 rounded-sm">
  611. <legend class="px-2 leading-none text-sm">Actions</legend>
  612. <!-- Loop for actions -->
  613. <div v-for="(action, key) in editRuleObject.actions" :key="key">
  614. <!-- AND/OR operator -->
  615. <div v-if="key !== 0" class="flex justify-center my-2">
  616. <div class="relative">AND</div>
  617. </div>
  618. <div class="p-2 w-full bg-grey-100">
  619. <div class="flex">
  620. <div
  621. class="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:items-center w-full"
  622. >
  623. <span>Then</span>
  624. <span class="sm:ml-2">
  625. <div class="relative">
  626. <select
  627. v-model="editRuleObject.actions[key].type"
  628. @change="ruleActionChange(editRuleObject.actions[key])"
  629. :id="`rule_action_types_${key}`"
  630. class="w-full block appearance-none text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  631. required
  632. >
  633. <option
  634. v-for="option in actionTypeOptions"
  635. :key="option.value"
  636. :value="option.value"
  637. >
  638. {{ option.label }}
  639. </option>
  640. </select>
  641. </div>
  642. </span>
  643. <span
  644. v-if="
  645. editRuleObject.actions[key].type === 'subject' ||
  646. editRuleObject.actions[key].type === 'displayFrom'
  647. "
  648. class="sm:ml-4 flex"
  649. >
  650. <div class="flex w-full">
  651. <input
  652. v-model="editRuleObject.actions[key].value"
  653. type="text"
  654. class="w-full appearance-none bg-white border border-transparent rounded text-grey-700 focus:outline-none p-2"
  655. :class="errors.ruleActions ? 'border-red-500' : ''"
  656. placeholder="Enter value"
  657. autofocus
  658. />
  659. </div>
  660. </span>
  661. <multiselect
  662. v-if="editRuleObject.actions[key].type === 'forwardTo'"
  663. class="sm:!ml-4 flex"
  664. v-model="editRuleObject.actions[key].value"
  665. :options="recipientOptions"
  666. mode="single"
  667. value-prop="id"
  668. :close-on-select="true"
  669. :clear-on-select="false"
  670. :searchable="false"
  671. :allow-empty="true"
  672. placeholder="Select recipient"
  673. label="email"
  674. track-by="email"
  675. >
  676. </multiselect>
  677. <span
  678. v-else-if="editRuleObject.actions[key].type === 'banner'"
  679. class="sm:ml-4 flex"
  680. >
  681. <div class="relative sm:mr-4 w-full">
  682. <select
  683. v-model="editRuleObject.actions[key].value"
  684. :id="`edit_rule_action_banner_${key}`"
  685. class="w-full block appearance-none sm:w-40 text-grey-700 bg-white p-2 pr-8 rounded shadow focus:ring"
  686. required
  687. >
  688. <option value="top">Top</option>
  689. <option value="bottom">Bottom</option>
  690. <option value="off">Off</option>
  691. </select>
  692. </div>
  693. </span>
  694. </div>
  695. <div class="flex items-center">
  696. <!-- delete button -->
  697. <icon
  698. v-if="editRuleObject.actions.length > 1"
  699. name="trash"
  700. class="block ml-4 w-6 h-6 text-grey-300 fill-current cursor-pointer"
  701. @click="deleteAction(editRuleObject, key)"
  702. />
  703. </div>
  704. </div>
  705. </div>
  706. </div>
  707. <!-- add action button -->
  708. <button
  709. @click="addAction(editRuleObject)"
  710. class="mt-4 p-2 text-grey-800 bg-white hover:bg-grey-50 border border-grey-100 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
  711. >
  712. Add action
  713. </button>
  714. <p v-show="errors.ruleActions" class="mt-2 text-red-500 text-sm">
  715. {{ errors.ruleActions }}
  716. </p>
  717. </fieldset>
  718. <fieldset class="border border-cyan-400 p-4 my-4 rounded-sm">
  719. <legend class="px-2 leading-none text-sm">Apply rule on</legend>
  720. <div class="w-full flex">
  721. <div class="relative flex items-center">
  722. <input
  723. v-model="editRuleObject.forwards"
  724. id="forwards"
  725. name="forwards"
  726. type="checkbox"
  727. class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-grey-300 rounded"
  728. />
  729. <label for="forwards" class="ml-2 text-sm text-grey-700">Forwards</label>
  730. </div>
  731. <div class="relative flex items-center mx-4">
  732. <input
  733. v-model="editRuleObject.replies"
  734. id="replies"
  735. name="replies"
  736. type="checkbox"
  737. class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-grey-300 rounded"
  738. />
  739. <label for="replies" class="ml-2 text-sm text-grey-700">Replies</label>
  740. </div>
  741. <div class="relative flex items-center">
  742. <input
  743. v-model="editRuleObject.sends"
  744. id="sends"
  745. name="sends"
  746. type="checkbox"
  747. class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-grey-300 rounded"
  748. />
  749. <label for="sends" class="ml-2 text-sm text-grey-700">Sends</label>
  750. </div>
  751. </div>
  752. </fieldset>
  753. <div class="mt-6 flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
  754. <button
  755. @click="editRule"
  756. class="bg-cyan-400 hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:cursor-not-allowed"
  757. :disabled="editRuleLoading"
  758. >
  759. Save
  760. <loader v-if="editRuleLoading" />
  761. </button>
  762. <button
  763. @click="closeEditModal"
  764. class="px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
  765. >
  766. Cancel
  767. </button>
  768. </div>
  769. </template>
  770. </Modal>
  771. <Modal :open="deleteRuleModalOpen" @close="closeDeleteModal">
  772. <template v-slot:title> Delete rule </template>
  773. <template v-slot:content>
  774. <p class="mt-4 text-grey-700">Are you sure you want to delete this rule?</p>
  775. <div class="mt-6 flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
  776. <button
  777. type="button"
  778. @click="deleteRule(ruleIdToDelete)"
  779. class="px-4 py-3 text-white font-semibold bg-red-500 hover:bg-red-600 border border-transparent rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:cursor-not-allowed"
  780. :disabled="deleteRuleLoading"
  781. >
  782. Delete rule
  783. <loader v-if="deleteRuleLoading" />
  784. </button>
  785. <button
  786. @click="closeDeleteModal"
  787. class="px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
  788. >
  789. Cancel
  790. </button>
  791. </div>
  792. </template>
  793. </Modal>
  794. <Modal :open="moreInfoOpen" @close="moreInfoOpen = false">
  795. <template v-slot:title> More information </template>
  796. <template v-slot:content>
  797. <p class="mt-4 text-grey-700">
  798. Rules can be used to perform different actions if certain conditions are met.
  799. </p>
  800. <p class="mt-4 text-grey-700">
  801. For example you could create a rule that checks if the alias is for your custom domain and
  802. if so then to replace the email subject.
  803. </p>
  804. <p class="mt-4 text-grey-700">
  805. You can choose to apply rules on forwards, replies and/or sends.
  806. </p>
  807. <p class="mt-4 text-grey-700">
  808. Rules are applied in the order displayed on this page from top to bottom. You can re-order
  809. your rules by dragging them using the icon on the left of each row.
  810. </p>
  811. <div class="mt-6 flex flex-col">
  812. <button
  813. @click="moreInfoOpen = false"
  814. class="px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
  815. >
  816. Close
  817. </button>
  818. </div>
  819. </template>
  820. </Modal>
  821. </div>
  822. </template>
  823. <script setup>
  824. import { onMounted, ref, computed } from 'vue'
  825. import { Head, Link } from '@inertiajs/vue3'
  826. import Modal from '../Components/Modal.vue'
  827. import Toggle from '../Components/Toggle.vue'
  828. import { roundArrow } from 'tippy.js'
  829. import tippy from 'tippy.js'
  830. import draggable from 'vuedraggable'
  831. import Multiselect from '@vueform/multiselect'
  832. import { notify } from '@kyvg/vue3-notification'
  833. import { InformationCircleIcon, FunnelIcon } from '@heroicons/vue/24/outline'
  834. import { PlusIcon } from '@heroicons/vue/20/solid'
  835. const props = defineProps({
  836. initialRows: {
  837. type: Array,
  838. required: true,
  839. },
  840. recipientOptions: {
  841. type: Array,
  842. required: true,
  843. },
  844. search: {
  845. type: String,
  846. },
  847. })
  848. const rows = ref(props.initialRows)
  849. const editRuleObject = ref({})
  850. const ruleIdToDelete = ref('')
  851. const deleteRuleLoading = ref(false)
  852. const deleteRuleModalOpen = ref(false)
  853. const createRuleModalOpen = ref(false)
  854. const editRuleModalOpen = ref(false)
  855. const moreInfoOpen = ref(false)
  856. const createRuleLoading = ref(false)
  857. const editRuleLoading = ref(false)
  858. const createRuleObject = ref({
  859. name: '',
  860. conditions: [
  861. {
  862. type: 'select',
  863. match: 'contains',
  864. values: [],
  865. },
  866. ],
  867. actions: [
  868. {
  869. type: 'select',
  870. value: '',
  871. },
  872. ],
  873. operator: 'AND',
  874. forwards: false,
  875. replies: false,
  876. sends: false,
  877. })
  878. const tippyInstance = ref(null)
  879. const errors = ref({})
  880. const conditionTypeOptions = [
  881. {
  882. value: 'select',
  883. label: 'Select',
  884. },
  885. {
  886. value: 'sender',
  887. label: 'sender email',
  888. },
  889. {
  890. value: 'subject',
  891. label: 'subject',
  892. },
  893. {
  894. value: 'alias',
  895. label: 'alias email',
  896. },
  897. {
  898. value: 'alias_description',
  899. label: 'alias description',
  900. },
  901. ]
  902. const actionTypeOptions = [
  903. {
  904. value: 'select',
  905. label: 'Select',
  906. },
  907. {
  908. value: 'subject',
  909. label: 'replace the subject with',
  910. },
  911. {
  912. value: 'displayFrom',
  913. label: 'replace the "from name" with',
  914. },
  915. {
  916. value: 'encryption',
  917. label: 'turn PGP encryption off',
  918. },
  919. {
  920. value: 'banner',
  921. label: 'set the banner information location to',
  922. },
  923. {
  924. value: 'block',
  925. label: 'block the email',
  926. },
  927. {
  928. value: 'removeAttachments',
  929. label: 'remove attachments',
  930. },
  931. {
  932. value: 'forwardTo',
  933. label: 'forward to',
  934. },
  935. ]
  936. const indexToHuman = {
  937. 0: 'first',
  938. 1: 'second',
  939. 2: 'third',
  940. 3: 'forth',
  941. 4: 'fifth',
  942. }
  943. onMounted(() => {
  944. addTooltips()
  945. })
  946. const activeRules = () => {
  947. return _.filter(rows.value, rule => rule.active)
  948. }
  949. const rowsIds = computed(() => {
  950. return _.map(rows.value, row => row.id)
  951. })
  952. const addTooltips = () => {
  953. if (tippyInstance.value) {
  954. _.each(tippyInstance.value, instance => instance.destroy())
  955. }
  956. tippyInstance.value = tippy('.tooltip', {
  957. arrow: roundArrow,
  958. allowHTML: true,
  959. })
  960. }
  961. const debounceToolips = _.debounce(function () {
  962. addTooltips()
  963. }, 50)
  964. const createNewRule = () => {
  965. errors.value = {}
  966. if (!createRuleObject.value.name.length) {
  967. return (errors.value.ruleName = 'Please enter a rule name')
  968. }
  969. if (createRuleObject.value.name.length > 50) {
  970. return (errors.value.ruleName = 'Rule name cannot exceed 50 characters')
  971. }
  972. Object.entries(createRuleObject.value.conditions).forEach(([key, condition]) => {
  973. if (!condition.values.length) {
  974. return (errors.value.ruleConditions = `You must add some values for the ${indexToHuman[key]} condition, make sure to click "Insert"`)
  975. }
  976. })
  977. if (errors.value.ruleConditions) {
  978. return
  979. }
  980. Object.entries(createRuleObject.value.actions).forEach(([key, action]) => {
  981. if (!action.value && action.value !== false) {
  982. return (errors.value.ruleActions = `You must add a value for the ${indexToHuman[key]} action`)
  983. }
  984. })
  985. if (errors.value.ruleActions) {
  986. return
  987. }
  988. createRuleLoading.value = true
  989. axios
  990. .post(
  991. '/api/v1/rules',
  992. JSON.stringify({
  993. name: createRuleObject.value.name,
  994. conditions: createRuleObject.value.conditions,
  995. actions: createRuleObject.value.actions,
  996. operator: createRuleObject.value.operator,
  997. forwards: createRuleObject.value.forwards,
  998. replies: createRuleObject.value.replies,
  999. sends: createRuleObject.value.sends,
  1000. }),
  1001. {
  1002. headers: { 'Content-Type': 'application/json' },
  1003. },
  1004. )
  1005. .then(({ data }) => {
  1006. createRuleLoading.value = false
  1007. resetCreateRuleObject()
  1008. rows.value.push(data.data)
  1009. createRuleModalOpen.value = false
  1010. debounceToolips()
  1011. reorderRules(false)
  1012. successMessage('New rule created successfully')
  1013. })
  1014. .catch(error => {
  1015. createRuleLoading.value = false
  1016. if (error.response.status === 403) {
  1017. errorMessage(error.response.data)
  1018. } else if (error.response.data) {
  1019. errorMessage(Object.entries(error.response.data.errors)[0][1][0])
  1020. } else {
  1021. errorMessage()
  1022. }
  1023. })
  1024. }
  1025. const editRule = () => {
  1026. errors.value = {}
  1027. if (!editRuleObject.value.name.length) {
  1028. return (errors.value.ruleName = 'Please enter a rule name')
  1029. }
  1030. if (editRuleObject.value.name.length > 50) {
  1031. return (errors.value.ruleName = 'Rule name cannot exceed 50 characters')
  1032. }
  1033. Object.entries(editRuleObject.value.conditions).forEach(([key, condition]) => {
  1034. if (!condition.values.length) {
  1035. return (errors.value.ruleConditions = `You must add some values for the ${indexToHuman[key]} condition, make sure to click "Insert"`)
  1036. }
  1037. })
  1038. if (errors.value.ruleConditions) {
  1039. return
  1040. }
  1041. Object.entries(editRuleObject.value.actions).forEach(([key, action]) => {
  1042. if (!action.value && action.value !== false) {
  1043. return (errors.value.ruleActions = `You must add a value for the ${indexToHuman[key]} action`)
  1044. }
  1045. })
  1046. if (errors.value.ruleActions) {
  1047. return
  1048. }
  1049. editRuleLoading.value = true
  1050. axios
  1051. .patch(
  1052. `/api/v1/rules/${editRuleObject.value.id}`,
  1053. JSON.stringify({
  1054. name: editRuleObject.value.name,
  1055. conditions: editRuleObject.value.conditions,
  1056. actions: editRuleObject.value.actions,
  1057. operator: editRuleObject.value.operator,
  1058. forwards: editRuleObject.value.forwards,
  1059. replies: editRuleObject.value.replies,
  1060. sends: editRuleObject.value.sends,
  1061. }),
  1062. {
  1063. headers: { 'Content-Type': 'application/json' },
  1064. },
  1065. )
  1066. .then(response => {
  1067. let rule = _.find(rows.value, ['id', editRuleObject.value.id])
  1068. editRuleLoading.value = false
  1069. rule.name = editRuleObject.value.name
  1070. rule.conditions = editRuleObject.value.conditions
  1071. rule.actions = editRuleObject.value.actions
  1072. rule.operator = editRuleObject.value.operator
  1073. rule.forwards = editRuleObject.value.forwards
  1074. rule.replies = editRuleObject.value.replies
  1075. rule.sends = editRuleObject.value.sends
  1076. closeEditModal()
  1077. successMessage('Rule successfully updated')
  1078. })
  1079. .catch(error => {
  1080. editRuleLoading.value = false
  1081. if (error.response.data) {
  1082. errorMessage(Object.entries(error.response.data.errors)[0][1][0])
  1083. } else {
  1084. errorMessage()
  1085. }
  1086. })
  1087. }
  1088. const deleteRule = id => {
  1089. deleteRuleLoading.value = true
  1090. axios
  1091. .delete(`/api/v1/rules/${id}`)
  1092. .then(response => {
  1093. rows.value = _.reject(rows.value, rule => rule.id === id)
  1094. deleteRuleModalOpen.value = false
  1095. deleteRuleLoading.value = false
  1096. })
  1097. .catch(error => {
  1098. errorMessage()
  1099. deleteRuleModalOpen.value = false
  1100. deleteRuleLoading.value = false
  1101. })
  1102. }
  1103. const activateRule = id => {
  1104. axios
  1105. .post(
  1106. `/api/v1/active-rules`,
  1107. JSON.stringify({
  1108. id: id,
  1109. }),
  1110. {
  1111. headers: { 'Content-Type': 'application/json' },
  1112. },
  1113. )
  1114. .then(response => {
  1115. //
  1116. })
  1117. .catch(error => {
  1118. if (error.response !== undefined) {
  1119. errorMessage(error.response.data)
  1120. } else {
  1121. errorMessage()
  1122. }
  1123. })
  1124. }
  1125. const deactivateRule = id => {
  1126. axios
  1127. .delete(`/api/v1/active-rules/${id}`)
  1128. .then(response => {
  1129. //
  1130. })
  1131. .catch(error => {
  1132. if (error.response !== undefined) {
  1133. errorMessage(error.response.data)
  1134. } else {
  1135. errorMessage()
  1136. }
  1137. })
  1138. }
  1139. const reorderRules = (displaySuccess = true) => {
  1140. axios
  1141. .post(
  1142. `/api/v1/reorder-rules`,
  1143. JSON.stringify({
  1144. ids: rowsIds.value,
  1145. }),
  1146. {
  1147. headers: { 'Content-Type': 'application/json' },
  1148. },
  1149. )
  1150. .then(response => {
  1151. if (displaySuccess) {
  1152. successMessage('Rule order successfully updated')
  1153. }
  1154. })
  1155. .catch(error => {
  1156. if (error.response !== undefined) {
  1157. errorMessage(error.response.data)
  1158. } else {
  1159. errorMessage()
  1160. }
  1161. })
  1162. }
  1163. const conditionMatchOptions = (object, key) => {
  1164. if (
  1165. _.includes(['sender', 'subject', 'alias', 'alias_description'], object.conditions[key].type)
  1166. ) {
  1167. return [
  1168. 'contains',
  1169. 'does not contain',
  1170. 'is exactly',
  1171. 'is not',
  1172. 'starts with',
  1173. 'does not start with',
  1174. 'ends with',
  1175. 'does not end with',
  1176. ]
  1177. }
  1178. return []
  1179. }
  1180. const addCondition = object => {
  1181. if (object.conditions.length >= 5) {
  1182. return (errors.value.ruleConditions = `You cannot add more than 5 conditions per rule`)
  1183. }
  1184. object.conditions.push({
  1185. type: 'select',
  1186. match: 'contains',
  1187. values: [],
  1188. })
  1189. }
  1190. const deleteCondition = (object, key) => {
  1191. object.conditions.splice(key, 1)
  1192. }
  1193. const addValueToCondition = (object, key) => {
  1194. if (object.conditions[key].values.length >= 10) {
  1195. return (errors.value.ruleConditions = `You cannot add more than 10 values per condition`)
  1196. }
  1197. if (object.conditions[key].currentConditionValue) {
  1198. object.conditions[key].values.push(object.conditions[key].currentConditionValue)
  1199. }
  1200. // Reset current conditon value input
  1201. object.conditions[key].currentConditionValue = ''
  1202. }
  1203. const addAction = object => {
  1204. if (object.actions.length >= 5) {
  1205. return (errors.value.ruleActions = `You cannot add more than 5 actions per rule`)
  1206. }
  1207. object.actions.push({
  1208. type: 'select',
  1209. value: '',
  1210. })
  1211. }
  1212. const deleteAction = (object, key) => {
  1213. object.actions.splice(key, 1)
  1214. }
  1215. const resetCreateRuleObject = () => {
  1216. createRuleObject.value = {
  1217. name: '',
  1218. conditions: [
  1219. {
  1220. type: 'select',
  1221. match: 'contains',
  1222. values: [],
  1223. },
  1224. ],
  1225. actions: [
  1226. {
  1227. type: 'select',
  1228. value: '',
  1229. },
  1230. ],
  1231. operator: 'AND',
  1232. forwards: false,
  1233. replies: false,
  1234. sends: false,
  1235. }
  1236. }
  1237. const ruleActionChange = action => {
  1238. if (action.type === 'subject' || action.type === 'displayFrom' || action.type === 'select') {
  1239. action.value = ''
  1240. } else if (action.type === 'encryption') {
  1241. action.value = false
  1242. } else if (action.type === 'banner') {
  1243. action.value = 'top'
  1244. } else if (action.type === 'block') {
  1245. action.value = true
  1246. } else if (action.type === 'removeAttachments') {
  1247. action.value = true
  1248. } else if (action.type === 'forwardTo') {
  1249. action.value = ''
  1250. }
  1251. }
  1252. const openCreateModal = () => {
  1253. errors.value = {}
  1254. createRuleModalOpen.value = true
  1255. }
  1256. const openDeleteModal = id => {
  1257. deleteRuleModalOpen.value = true
  1258. ruleIdToDelete.value = id
  1259. }
  1260. const closeDeleteModal = () => {
  1261. deleteRuleModalOpen.value = false
  1262. _.delay(() => (ruleIdToDelete.value = ''), 300)
  1263. }
  1264. const openEditModal = rule => {
  1265. errors.value = {}
  1266. editRuleModalOpen.value = true
  1267. editRuleObject.value = _.cloneDeep(rule)
  1268. }
  1269. const closeEditModal = () => {
  1270. editRuleModalOpen.value = false
  1271. _.delay(() => (editRuleObject.value = {}), 300)
  1272. }
  1273. const successMessage = (text = '') => {
  1274. notify({
  1275. title: 'Success',
  1276. text: text,
  1277. type: 'success',
  1278. })
  1279. }
  1280. const errorMessage = (text = 'An error has occurred, please try again later') => {
  1281. notify({
  1282. title: 'Error',
  1283. text: text,
  1284. type: 'error',
  1285. })
  1286. }
  1287. </script>
  1288. <style>
  1289. .ghost {
  1290. opacity: 0.5;
  1291. background: #c8ebfb;
  1292. }
  1293. </style>