Merge pull request #207 from authorizerdev/feat/email-template-ui

feat: email template UI + subject
This commit is contained in:
Lakhan Samani 2022-08-07 11:10:44 +05:30 committed by GitHub
commit 8f69d5746e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2516 additions and 34 deletions

View File

@ -17,6 +17,9 @@
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.11",
"@types/react-router-dom": "^5.3.2", "@types/react-router-dom": "^5.3.2",
"dayjs": "^1.10.7", "dayjs": "^1.10.7",
"draft-js": "^0.11.7",
"draft-js-import-html": "^1.4.1",
"draftjs-to-html": "^0.9.1",
"esbuild": "^0.14.9", "esbuild": "^0.14.9",
"focus-visible": "^5.2.0", "focus-visible": "^5.2.0",
"framer-motion": "^5.5.5", "framer-motion": "^5.5.5",
@ -24,11 +27,16 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-draft-wysiwyg": "^1.15.0",
"react-dropzone": "^12.0.4", "react-dropzone": "^12.0.4",
"react-icons": "^4.3.1", "react-icons": "^4.3.1",
"react-router-dom": "^6.2.1", "react-router-dom": "^6.2.1",
"typescript": "^4.5.4", "typescript": "^4.5.4",
"urql": "^2.0.6" "urql": "^2.0.6"
},
"devDependencies": {
"@types/draftjs-to-html": "^0.8.1",
"@types/react-draft-wysiwyg": "^1.13.4"
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@ -1145,6 +1153,25 @@
"react-dom": "^16.8.0 || 17.x" "react-dom": "^16.8.0 || 17.x"
} }
}, },
"node_modules/@types/draft-js": {
"version": "0.11.9",
"resolved": "https://registry.npmjs.org/@types/draft-js/-/draft-js-0.11.9.tgz",
"integrity": "sha512-cQJBZjjIlGaPA1tOY+wGz2KhlPtAAZOIXpUvGPxPRw5uzZ2tcj8m6Yu1QDV9YgP36+cqE3cUvgkARBzgUiuI/Q==",
"dev": true,
"dependencies": {
"@types/react": "*",
"immutable": "~3.7.4"
}
},
"node_modules/@types/draftjs-to-html": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@types/draftjs-to-html/-/draftjs-to-html-0.8.1.tgz",
"integrity": "sha512-NBkphQs+qZ/sAz/j1pCUaxkPAOx00LTsE88aMSSfcvK+UfCpjHJDqIMCkm6wKotuJvY5w0BtdRazQ0sAaXzPdg==",
"dev": true,
"dependencies": {
"@types/draft-js": "*"
}
},
"node_modules/@types/history": { "node_modules/@types/history": {
"version": "4.7.9", "version": "4.7.9",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz",
@ -1191,6 +1218,16 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"node_modules/@types/react-draft-wysiwyg": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/@types/react-draft-wysiwyg/-/react-draft-wysiwyg-1.13.4.tgz",
"integrity": "sha512-wasD1t78JDmQvdPDRPf/mf5FSHMlncunW0F6KMOKB3awzi3Wi21yHMGsRAUOkfTr3R8F+yceG8fSLz0kYWu/QA==",
"dev": true,
"dependencies": {
"@types/draft-js": "*",
"@types/react": "*"
}
},
"node_modules/@types/react-router": { "node_modules/@types/react-router": {
"version": "5.1.17", "version": "5.1.17",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.17.tgz", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.17.tgz",
@ -1259,6 +1296,11 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
},
"node_modules/attr-accept": { "node_modules/attr-accept": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
@ -1306,6 +1348,11 @@
"node": ">=0.8.0" "node": ">=0.8.0"
} }
}, },
"node_modules/classnames": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -1340,6 +1387,16 @@
"toggle-selection": "^1.0.6" "toggle-selection": "^1.0.6"
} }
}, },
"node_modules/core-js": {
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz",
"integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==",
"hasInstallScript": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/cosmiconfig": { "node_modules/cosmiconfig": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
@ -1355,6 +1412,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/cross-fetch": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
"dependencies": {
"node-fetch": "2.6.7"
}
},
"node_modules/css-box-model": { "node_modules/css-box-model": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
@ -1383,6 +1448,68 @@
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
}, },
"node_modules/draft-js": {
"version": "0.11.7",
"resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.11.7.tgz",
"integrity": "sha512-ne7yFfN4sEL82QPQEn80xnADR8/Q6ALVworbC5UOSzOvjffmYfFsr3xSZtxbIirti14R7Y33EZC5rivpLgIbsg==",
"dependencies": {
"fbjs": "^2.0.0",
"immutable": "~3.7.4",
"object-assign": "^4.1.1"
},
"peerDependencies": {
"react": ">=0.14.0",
"react-dom": ">=0.14.0"
}
},
"node_modules/draft-js-import-element": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/draft-js-import-element/-/draft-js-import-element-1.4.0.tgz",
"integrity": "sha512-WmYT5PrCm47lGL5FkH6sRO3TTAcn7qNHsD3igiPqLG/RXrqyKrqN4+wBgbcT2lhna/yfWTRtgzAbQsSJoS1Meg==",
"dependencies": {
"draft-js-utils": "^1.4.0",
"synthetic-dom": "^1.4.0"
},
"peerDependencies": {
"draft-js": ">=0.10.0",
"immutable": "3.x.x"
}
},
"node_modules/draft-js-import-html": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/draft-js-import-html/-/draft-js-import-html-1.4.1.tgz",
"integrity": "sha512-KOZmtgxZriCDgg5Smr3Y09TjubvXe7rHPy/2fuLSsL+aSzwUDwH/aHDA/k47U+WfpmL4qgyg4oZhqx9TYJV0tg==",
"dependencies": {
"draft-js-import-element": "^1.4.0"
},
"peerDependencies": {
"draft-js": ">=0.10.0",
"immutable": "3.x.x"
}
},
"node_modules/draft-js-utils": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/draft-js-utils/-/draft-js-utils-1.4.1.tgz",
"integrity": "sha512-xE81Y+z/muC5D5z9qWmKfxEW1XyXfsBzSbSBk2JRsoD0yzMGGHQm/0MtuqHl/EUDkaBJJLjJ2EACycoDMY/OOg==",
"peerDependencies": {
"draft-js": ">=0.10.0",
"immutable": "3.x.x"
}
},
"node_modules/draftjs-to-html": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/draftjs-to-html/-/draftjs-to-html-0.9.1.tgz",
"integrity": "sha512-fFstE6+IayaVFBEvaFt/wN8vdj8FsTRzij7dy7LI9QIwf5LgfHFi9zSpvCg+feJ2tbYVqHxUkjcibwpsTpgFVQ=="
},
"node_modules/draftjs-utils": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz",
"integrity": "sha512-EstHqr3R3JVcilJrBaO/A+01GvwwKmC7e4TCjC7S94ZeMh4IVmf60OuQXtHHpwItK8C2JCi3iljgN5KHkJboUg==",
"peerDependencies": {
"draft-js": "^0.11.x",
"immutable": "3.x.x || 4.x.x"
}
},
"node_modules/error-ex": { "node_modules/error-ex": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@ -1647,6 +1774,26 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/fbjs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-2.0.0.tgz",
"integrity": "sha512-8XA8ny9ifxrAWlyhAbexXcs3rRMtxWcs3M0lctLfB49jRDHiaxj+Mo0XxbwE7nKZYzgCFoq64FS+WFd4IycPPQ==",
"dependencies": {
"core-js": "^3.6.4",
"cross-fetch": "^3.0.4",
"fbjs-css-vars": "^1.0.0",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"node_modules/fbjs-css-vars": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz",
"integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="
},
"node_modules/file-selector": { "node_modules/file-selector": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz", "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz",
@ -1802,6 +1949,23 @@
"react-is": "^16.7.0" "react-is": "^16.7.0"
} }
}, },
"node_modules/html-to-draftjs": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/html-to-draftjs/-/html-to-draftjs-1.5.0.tgz",
"integrity": "sha512-kggLXBNciKDwKf+KYsuE+V5gw4dZ7nHyGMX9m0wy7urzWjKGWyNFetmArRLvRV0VrxKN70WylFsJvMTJx02OBQ==",
"peerDependencies": {
"draft-js": "^0.10.x || ^0.11.x",
"immutable": "3.x.x || 4.x.x"
}
},
"node_modules/immutable": {
"version": "3.7.6",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
"integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -1856,6 +2020,14 @@
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
}, },
"node_modules/linkify-it": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
"integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
"dependencies": {
"uc.micro": "^1.0.1"
}
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@ -1877,6 +2049,25 @@
"loose-envify": "cli.js" "loose-envify": "cli.js"
} }
}, },
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/object-assign": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -1945,6 +2136,14 @@
"tslib": "^2.1.0" "tslib": "^2.1.0"
} }
}, },
"node_modules/promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"dependencies": {
"asap": "~2.0.3"
}
},
"node_modules/prop-types": { "node_modules/prop-types": {
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@ -1991,6 +2190,24 @@
"react": "17.0.2" "react": "17.0.2"
} }
}, },
"node_modules/react-draft-wysiwyg": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/react-draft-wysiwyg/-/react-draft-wysiwyg-1.15.0.tgz",
"integrity": "sha512-p1cYZcWc6/ALFBVksbFoCM3b29fGQDlZLIMrXng0TU/UElxIOF2/AWWo4L5auIYVhmqKTZ0NkNjnXOzGGuxyeA==",
"dependencies": {
"classnames": "^2.2.6",
"draftjs-utils": "^0.10.2",
"html-to-draftjs": "^1.5.0",
"linkify-it": "^2.2.0",
"prop-types": "^15.7.2"
},
"peerDependencies": {
"draft-js": "^0.10.x || ^0.11.x",
"immutable": "3.x.x || 4.x.x",
"react": "0.13.x || 0.14.x || ^15.0.0-0 || 15.x.x || ^16.0.0-0 || ^16.x.x || ^17.x.x || ^18.x.x",
"react-dom": "0.13.x || 0.14.x || ^15.0.0-0 || 15.x.x || ^16.0.0-0 || ^16.x.x || ^17.x.x || ^18.x.x"
}
},
"node_modules/react-dropzone": { "node_modules/react-dropzone": {
"version": "12.0.4", "version": "12.0.4",
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.4.tgz", "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.4.tgz",
@ -2207,6 +2424,11 @@
"object-assign": "^4.1.1" "object-assign": "^4.1.1"
} }
}, },
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
},
"node_modules/source-map": { "node_modules/source-map": {
"version": "0.5.7", "version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@ -2240,6 +2462,11 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/synthetic-dom": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/synthetic-dom/-/synthetic-dom-1.4.0.tgz",
"integrity": "sha512-mHv51ZsmZ+ShT/4s5kg+MGUIhY7Ltq4v03xpN1c8T1Krb5pScsh/lzEjyhrVD0soVDbThbd2e+4dD9vnDG4rhg=="
},
"node_modules/tiny-invariant": { "node_modules/tiny-invariant": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
@ -2258,6 +2485,11 @@
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
}, },
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
@ -2275,6 +2507,29 @@
"node": ">=4.2.0" "node": ">=4.2.0"
} }
}, },
"node_modules/ua-parser-js": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/ua-parser-js"
},
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
}
],
"engines": {
"node": "*"
}
},
"node_modules/uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"node_modules/urql": { "node_modules/urql": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/urql/-/urql-2.0.6.tgz", "resolved": "https://registry.npmjs.org/urql/-/urql-2.0.6.tgz",
@ -2333,6 +2588,20 @@
"loose-envify": "^1.0.0" "loose-envify": "^1.0.0"
} }
}, },
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wonka": { "node_modules/wonka": {
"version": "4.0.15", "version": "4.0.15",
"resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz", "resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz",
@ -2529,8 +2798,7 @@
"@chakra-ui/css-reset": { "@chakra-ui/css-reset": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-1.1.1.tgz",
"integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg==", "integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg=="
"requires": {}
}, },
"@chakra-ui/descendant": { "@chakra-ui/descendant": {
"version": "2.1.1", "version": "2.1.1",
@ -3134,8 +3402,7 @@
"@graphql-typed-document-node/core": { "@graphql-typed-document-node/core": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz",
"integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg=="
"requires": {}
}, },
"@popperjs/core": { "@popperjs/core": {
"version": "2.11.0", "version": "2.11.0",
@ -3172,6 +3439,25 @@
"tslib": "^2.1.0" "tslib": "^2.1.0"
} }
}, },
"@types/draft-js": {
"version": "0.11.9",
"resolved": "https://registry.npmjs.org/@types/draft-js/-/draft-js-0.11.9.tgz",
"integrity": "sha512-cQJBZjjIlGaPA1tOY+wGz2KhlPtAAZOIXpUvGPxPRw5uzZ2tcj8m6Yu1QDV9YgP36+cqE3cUvgkARBzgUiuI/Q==",
"dev": true,
"requires": {
"@types/react": "*",
"immutable": "~3.7.4"
}
},
"@types/draftjs-to-html": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@types/draftjs-to-html/-/draftjs-to-html-0.8.1.tgz",
"integrity": "sha512-NBkphQs+qZ/sAz/j1pCUaxkPAOx00LTsE88aMSSfcvK+UfCpjHJDqIMCkm6wKotuJvY5w0BtdRazQ0sAaXzPdg==",
"dev": true,
"requires": {
"@types/draft-js": "*"
}
},
"@types/history": { "@types/history": {
"version": "4.7.9", "version": "4.7.9",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz",
@ -3218,6 +3504,16 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/react-draft-wysiwyg": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/@types/react-draft-wysiwyg/-/react-draft-wysiwyg-1.13.4.tgz",
"integrity": "sha512-wasD1t78JDmQvdPDRPf/mf5FSHMlncunW0F6KMOKB3awzi3Wi21yHMGsRAUOkfTr3R8F+yceG8fSLz0kYWu/QA==",
"dev": true,
"requires": {
"@types/draft-js": "*",
"@types/react": "*"
}
},
"@types/react-router": { "@types/react-router": {
"version": "5.1.17", "version": "5.1.17",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.17.tgz", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.17.tgz",
@ -3279,6 +3575,11 @@
} }
} }
}, },
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
},
"attr-accept": { "attr-accept": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
@ -3316,6 +3617,11 @@
} }
} }
}, },
"classnames": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
},
"color-convert": { "color-convert": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -3350,6 +3656,11 @@
"toggle-selection": "^1.0.6" "toggle-selection": "^1.0.6"
} }
}, },
"core-js": {
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz",
"integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg=="
},
"cosmiconfig": { "cosmiconfig": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
@ -3362,6 +3673,14 @@
"yaml": "^1.7.2" "yaml": "^1.7.2"
} }
}, },
"cross-fetch": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
"requires": {
"node-fetch": "2.6.7"
}
},
"css-box-model": { "css-box-model": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
@ -3390,6 +3709,48 @@
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
}, },
"draft-js": {
"version": "0.11.7",
"resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.11.7.tgz",
"integrity": "sha512-ne7yFfN4sEL82QPQEn80xnADR8/Q6ALVworbC5UOSzOvjffmYfFsr3xSZtxbIirti14R7Y33EZC5rivpLgIbsg==",
"requires": {
"fbjs": "^2.0.0",
"immutable": "~3.7.4",
"object-assign": "^4.1.1"
}
},
"draft-js-import-element": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/draft-js-import-element/-/draft-js-import-element-1.4.0.tgz",
"integrity": "sha512-WmYT5PrCm47lGL5FkH6sRO3TTAcn7qNHsD3igiPqLG/RXrqyKrqN4+wBgbcT2lhna/yfWTRtgzAbQsSJoS1Meg==",
"requires": {
"draft-js-utils": "^1.4.0",
"synthetic-dom": "^1.4.0"
}
},
"draft-js-import-html": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/draft-js-import-html/-/draft-js-import-html-1.4.1.tgz",
"integrity": "sha512-KOZmtgxZriCDgg5Smr3Y09TjubvXe7rHPy/2fuLSsL+aSzwUDwH/aHDA/k47U+WfpmL4qgyg4oZhqx9TYJV0tg==",
"requires": {
"draft-js-import-element": "^1.4.0"
}
},
"draft-js-utils": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/draft-js-utils/-/draft-js-utils-1.4.1.tgz",
"integrity": "sha512-xE81Y+z/muC5D5z9qWmKfxEW1XyXfsBzSbSBk2JRsoD0yzMGGHQm/0MtuqHl/EUDkaBJJLjJ2EACycoDMY/OOg=="
},
"draftjs-to-html": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/draftjs-to-html/-/draftjs-to-html-0.9.1.tgz",
"integrity": "sha512-fFstE6+IayaVFBEvaFt/wN8vdj8FsTRzij7dy7LI9QIwf5LgfHFi9zSpvCg+feJ2tbYVqHxUkjcibwpsTpgFVQ=="
},
"draftjs-utils": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz",
"integrity": "sha512-EstHqr3R3JVcilJrBaO/A+01GvwwKmC7e4TCjC7S94ZeMh4IVmf60OuQXtHHpwItK8C2JCi3iljgN5KHkJboUg=="
},
"error-ex": { "error-ex": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@ -3536,6 +3897,26 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
}, },
"fbjs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-2.0.0.tgz",
"integrity": "sha512-8XA8ny9ifxrAWlyhAbexXcs3rRMtxWcs3M0lctLfB49jRDHiaxj+Mo0XxbwE7nKZYzgCFoq64FS+WFd4IycPPQ==",
"requires": {
"core-js": "^3.6.4",
"cross-fetch": "^3.0.4",
"fbjs-css-vars": "^1.0.0",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"fbjs-css-vars": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz",
"integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="
},
"file-selector": { "file-selector": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz", "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz",
@ -3659,6 +4040,16 @@
"react-is": "^16.7.0" "react-is": "^16.7.0"
} }
}, },
"html-to-draftjs": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/html-to-draftjs/-/html-to-draftjs-1.5.0.tgz",
"integrity": "sha512-kggLXBNciKDwKf+KYsuE+V5gw4dZ7nHyGMX9m0wy7urzWjKGWyNFetmArRLvRV0VrxKN70WylFsJvMTJx02OBQ=="
},
"immutable": {
"version": "3.7.6",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
"integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw=="
},
"import-fresh": { "import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -3704,6 +4095,14 @@
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
}, },
"linkify-it": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
"integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
"requires": {
"uc.micro": "^1.0.1"
}
},
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@ -3722,6 +4121,14 @@
"js-tokens": "^3.0.0 || ^4.0.0" "js-tokens": "^3.0.0 || ^4.0.0"
} }
}, },
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": {
"whatwg-url": "^5.0.0"
}
},
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -3777,6 +4184,14 @@
} }
} }
}, },
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "~2.0.3"
}
},
"prop-types": { "prop-types": {
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@ -3814,6 +4229,18 @@
"scheduler": "^0.20.2" "scheduler": "^0.20.2"
} }
}, },
"react-draft-wysiwyg": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/react-draft-wysiwyg/-/react-draft-wysiwyg-1.15.0.tgz",
"integrity": "sha512-p1cYZcWc6/ALFBVksbFoCM3b29fGQDlZLIMrXng0TU/UElxIOF2/AWWo4L5auIYVhmqKTZ0NkNjnXOzGGuxyeA==",
"requires": {
"classnames": "^2.2.6",
"draftjs-utils": "^0.10.2",
"html-to-draftjs": "^1.5.0",
"linkify-it": "^2.2.0",
"prop-types": "^15.7.2"
}
},
"react-dropzone": { "react-dropzone": {
"version": "12.0.4", "version": "12.0.4",
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.4.tgz", "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.4.tgz",
@ -3845,8 +4272,7 @@
"react-icons": { "react-icons": {
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz",
"integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==", "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ=="
"requires": {}
}, },
"react-is": { "react-is": {
"version": "16.13.1", "version": "16.13.1",
@ -3968,6 +4394,11 @@
"object-assign": "^4.1.1" "object-assign": "^4.1.1"
} }
}, },
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
},
"source-map": { "source-map": {
"version": "0.5.7", "version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@ -3995,6 +4426,11 @@
"has-flag": "^3.0.0" "has-flag": "^3.0.0"
} }
}, },
"synthetic-dom": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/synthetic-dom/-/synthetic-dom-1.4.0.tgz",
"integrity": "sha512-mHv51ZsmZ+ShT/4s5kg+MGUIhY7Ltq4v03xpN1c8T1Krb5pScsh/lzEjyhrVD0soVDbThbd2e+4dD9vnDG4rhg=="
},
"tiny-invariant": { "tiny-invariant": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
@ -4010,6 +4446,11 @@
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
}, },
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"tslib": { "tslib": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
@ -4020,6 +4461,16 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
"integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==" "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg=="
}, },
"ua-parser-js": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
},
"uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"urql": { "urql": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/urql/-/urql-2.0.6.tgz", "resolved": "https://registry.npmjs.org/urql/-/urql-2.0.6.tgz",
@ -4032,8 +4483,7 @@
"use-callback-ref": { "use-callback-ref": {
"version": "1.2.5", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz",
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==", "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg=="
"requires": {}
}, },
"use-sidecar": { "use-sidecar": {
"version": "1.0.5", "version": "1.0.5",
@ -4059,6 +4509,20 @@
"loose-envify": "^1.0.0" "loose-envify": "^1.0.0"
} }
}, },
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"wonka": { "wonka": {
"version": "4.0.15", "version": "4.0.15",
"resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz", "resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz",

View File

@ -19,6 +19,9 @@
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.11",
"@types/react-router-dom": "^5.3.2", "@types/react-router-dom": "^5.3.2",
"dayjs": "^1.10.7", "dayjs": "^1.10.7",
"draft-js": "^0.11.7",
"draft-js-import-html": "^1.4.1",
"draftjs-to-html": "^0.9.1",
"esbuild": "^0.14.9", "esbuild": "^0.14.9",
"focus-visible": "^5.2.0", "focus-visible": "^5.2.0",
"framer-motion": "^5.5.5", "framer-motion": "^5.5.5",
@ -26,10 +29,15 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-draft-wysiwyg": "^1.15.0",
"react-dropzone": "^12.0.4", "react-dropzone": "^12.0.4",
"react-icons": "^4.3.1", "react-icons": "^4.3.1",
"react-router-dom": "^6.2.1", "react-router-dom": "^6.2.1",
"typescript": "^4.5.4", "typescript": "^4.5.4",
"urql": "^2.0.6" "urql": "^2.0.6"
},
"devDependencies": {
"@types/draftjs-to-html": "^0.8.1",
"@types/react-draft-wysiwyg": "^1.13.4"
} }
} }

View File

@ -0,0 +1,106 @@
import React from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaRegTrashAlt } from 'react-icons/fa';
import { DeleteEmailTemplate } from '../graphql/mutation';
import { capitalizeFirstLetter } from '../utils';
interface deleteEmailTemplateModalInputPropTypes {
emailTemplateId: string;
eventName: string;
fetchEmailTemplatesData: Function;
}
const DeleteEmailTemplateModal = ({
emailTemplateId,
eventName,
fetchEmailTemplatesData,
}: deleteEmailTemplateModalInputPropTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const deleteHandler = async () => {
const res = await client
.mutation(DeleteEmailTemplate, { params: { id: emailTemplateId } })
.toPromise();
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
});
return;
} else if (res.data?._delete_email_template) {
toast({
title: capitalizeFirstLetter(res.data?._delete_email_template.message),
isClosable: true,
status: 'success',
position: 'bottom-right',
});
}
onClose();
fetchEmailTemplatesData();
};
return (
<>
<MenuItem onClick={onOpen}>Delete</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Delete Email Template</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text fontSize="md">Are you sure?</Text>
<Flex
padding="5%"
marginTop="5%"
marginBottom="2%"
border="1px solid #ff7875"
borderRadius="5px"
flexDirection="column"
>
<Text fontSize="sm">
Email template for event <b>{eventName}</b> will be deleted
permanently!
</Text>
</Flex>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaRegTrashAlt />}
colorScheme="red"
variant="solid"
onClick={deleteHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Delete
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default DeleteEmailTemplateModal;

View File

@ -12,7 +12,6 @@ import {
Select, Select,
Textarea, Textarea,
Switch, Switch,
Code,
Text, Text,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {

View File

@ -31,6 +31,7 @@ import {
FiUsers, FiUsers,
FiChevronDown, FiChevronDown,
FiLink, FiLink,
FiFileText,
} from 'react-icons/fi'; } from 'react-icons/fi';
import { BiCustomize } from 'react-icons/bi'; import { BiCustomize } from 'react-icons/bi';
import { AiOutlineKey } from 'react-icons/ai'; import { AiOutlineKey } from 'react-icons/ai';
@ -113,6 +114,7 @@ const LinkItems: Array<LinkItemProps> = [
}, },
{ name: 'Users', icon: FiUsers, route: '/users' }, { name: 'Users', icon: FiUsers, route: '/users' },
{ name: 'Webhooks', icon: FiLink, route: '/webhooks' }, { name: 'Webhooks', icon: FiLink, route: '/webhooks' },
{ name: 'Email Templates', icon: FiFileText, route: '/email-templates' },
]; ];
interface SidebarProps extends BoxProps { interface SidebarProps extends BoxProps {

View File

@ -0,0 +1,375 @@
import React, { useEffect, useState } from 'react';
import {
Button,
Center,
Flex,
Input,
InputGroup,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Select,
Text,
useDisclosure,
useToast,
} from '@chakra-ui/react';
import { FaPlus } from 'react-icons/fa';
import { useClient } from 'urql';
import { Editor } from 'react-draft-wysiwyg';
import { EditorState, convertToRaw, Modifier } from 'draft-js';
import draftToHtml from 'draftjs-to-html';
import { stateFromHTML } from 'draft-js-import-html';
import {
UpdateModalViews,
EmailTemplateInputDataFields,
emailTemplateEventNames,
emailTemplateVariables,
} from '../constants';
import { capitalizeFirstLetter } from '../utils';
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
interface selectedEmailTemplateDataTypes {
[EmailTemplateInputDataFields.ID]: string;
[EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string;
[EmailTemplateInputDataFields.CREATED_AT]: number;
[EmailTemplateInputDataFields.TEMPLATE]: string;
}
interface UpdateEmailTemplateInputPropTypes {
view: UpdateModalViews;
selectedTemplate?: selectedEmailTemplateDataTypes;
fetchEmailTemplatesData: Function;
}
interface templateVariableDataTypes {
text: string;
value: string;
}
interface emailTemplateDataType {
[EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string;
}
interface validatorDataType {
[EmailTemplateInputDataFields.SUBJECT]: boolean;
}
const initTemplateData: emailTemplateDataType = {
[EmailTemplateInputDataFields.EVENT_NAME]:
emailTemplateEventNames.BASIC_AUTH_SIGNUP,
[EmailTemplateInputDataFields.SUBJECT]: '',
};
const initTemplateValidatorData: validatorDataType = {
[EmailTemplateInputDataFields.SUBJECT]: true,
};
const UpdateEmailTemplate = ({
view,
selectedTemplate,
fetchEmailTemplatesData,
}: UpdateEmailTemplateInputPropTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [loading, setLoading] = useState<boolean>(false);
const [editorState, setEditorState] = React.useState<EditorState>(
EditorState.createEmpty()
);
const [templateVariables, setTemplateVariables] = useState<
templateVariableDataTypes[]
>([]);
const [templateData, setTemplateData] = useState<emailTemplateDataType>({
...initTemplateData,
});
const [validator, setValidator] = useState<validatorDataType>({
...initTemplateValidatorData,
});
const onEditorStateChange = (editorState: EditorState) => {
setEditorState(editorState);
};
const inputChangehandler = (inputType: string, value: any) => {
if (inputType !== EmailTemplateInputDataFields.EVENT_NAME) {
setValidator({
...validator,
[inputType]: value?.trim().length,
});
}
setTemplateData({ ...templateData, [inputType]: value });
};
const validateData = () => {
const rawData: string = draftToHtml(
convertToRaw(editorState.getCurrentContent())
).trim();
return (
!loading &&
rawData &&
rawData !== '<p></p>' &&
rawData !== '<h1></h1>' &&
templateData[EmailTemplateInputDataFields.EVENT_NAME].length > 0 &&
templateData[EmailTemplateInputDataFields.SUBJECT].length > 0 &&
validator[EmailTemplateInputDataFields.SUBJECT]
);
};
const saveData = async () => {
if (!validateData()) return;
setLoading(true);
const params = {
[EmailTemplateInputDataFields.EVENT_NAME]:
templateData[EmailTemplateInputDataFields.EVENT_NAME],
[EmailTemplateInputDataFields.SUBJECT]:
templateData[EmailTemplateInputDataFields.SUBJECT],
[EmailTemplateInputDataFields.TEMPLATE]: draftToHtml(
convertToRaw(editorState.getCurrentContent())
).trim(),
};
let res: any = {};
if (
view === UpdateModalViews.Edit &&
selectedTemplate?.[EmailTemplateInputDataFields.ID]
) {
res = await client
.mutation(EditEmailTemplate, {
params: {
...params,
id: selectedTemplate[EmailTemplateInputDataFields.ID],
},
})
.toPromise();
} else {
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
}
setLoading(false);
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
});
} else if (
res.data?._add_email_template ||
res.data?._update_email_template
) {
toast({
title: capitalizeFirstLetter(
res.data?._add_email_template?.message ||
res.data?._update_email_template?.message
),
isClosable: true,
status: 'success',
position: 'bottom-right',
});
setTemplateData({
...initTemplateData,
});
setValidator({ ...initTemplateValidatorData });
fetchEmailTemplatesData();
}
view === UpdateModalViews.ADD && onClose();
};
const resetData = () => {
if (selectedTemplate) {
setTemplateData(selectedTemplate);
setEditorState(
EditorState.createWithContent(stateFromHTML(selectedTemplate.template))
);
} else {
setTemplateData({ ...initTemplateData });
setEditorState(EditorState.createEmpty());
}
};
useEffect(() => {
if (
isOpen &&
view === UpdateModalViews.Edit &&
selectedTemplate &&
Object.keys(selectedTemplate || {}).length
) {
const { id, created_at, template, ...rest } = selectedTemplate;
setTemplateData(rest);
setEditorState(EditorState.createWithContent(stateFromHTML(template)));
}
}, [isOpen]);
useEffect(() => {
const updatedTemplateVariables = Object.entries(
emailTemplateVariables
).reduce((acc, varData): any => {
if (
(templateData[EmailTemplateInputDataFields.EVENT_NAME] !==
emailTemplateEventNames.VERIFY_OTP &&
varData[1] === emailTemplateVariables.otp) ||
(templateData[EmailTemplateInputDataFields.EVENT_NAME] ===
emailTemplateEventNames.VERIFY_OTP &&
varData[1] === emailTemplateVariables.verification_url)
) {
return acc;
}
return [
...acc,
{
text: varData[0],
value: varData[1],
},
];
}, []);
setTemplateVariables(updatedTemplateVariables);
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
return (
<>
{view === UpdateModalViews.ADD ? (
<Button
leftIcon={<FaPlus />}
colorScheme="blue"
variant="solid"
onClick={onOpen}
isDisabled={false}
size="sm"
>
<Center h="100%">Add Template</Center>{' '}
</Button>
) : (
<MenuItem onClick={onOpen}>Edit</MenuItem>
)}
<Modal isOpen={isOpen} onClose={onClose} size="3xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>
{view === UpdateModalViews.ADD
? 'Add New Email Template'
: 'Edit Email Template'}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex
flexDirection="column"
border="1px"
borderRadius="md"
borderColor="gray.200"
p="5"
>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<Flex flex="1">Event Name</Flex>
<Flex flex="3">
<Select
size="md"
value={
templateData[EmailTemplateInputDataFields.EVENT_NAME]
}
onChange={(e) =>
inputChangehandler(
EmailTemplateInputDataFields.EVENT_NAME,
e.currentTarget.value
)
}
>
{Object.entries(emailTemplateEventNames).map(
([key, value]: any) => (
<option value={value} key={key}>
{key}
</option>
)
)}
</Select>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="5%"
>
<Flex flex="1">Subject</Flex>
<Flex flex="3">
<InputGroup size="md">
<Input
pr="4.5rem"
type="text"
placeholder="Subject Line"
value={templateData[EmailTemplateInputDataFields.SUBJECT]}
isInvalid={
!validator[EmailTemplateInputDataFields.SUBJECT]
}
onChange={(e) =>
inputChangehandler(
EmailTemplateInputDataFields.SUBJECT,
e.currentTarget.value
)
}
/>
</InputGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<Flex>Template Body</Flex>
<Text
style={{
fontSize: 14,
}}
color="gray.400"
>{`To select dynamic variables open curly braces "{"`}</Text>
</Flex>
<Editor
editorState={editorState}
onEditorStateChange={onEditorStateChange}
editorStyle={{
border: '1px solid #d9d9d9',
borderRadius: '5px',
marginTop: '2%',
height: '35vh',
}}
mention={{
separator: ' ',
trigger: '{',
suggestions: templateVariables,
}}
/>
</Flex>
</ModalBody>
<ModalFooter>
<Button
variant="outline"
onClick={resetData}
isDisabled={loading}
marginRight="5"
>
Reset
</Button>
<Button
colorScheme="blue"
variant="solid"
isLoading={loading}
onClick={saveData}
isDisabled={!validateData()}
>
<Center h="100%" pt="5%">
Save
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default UpdateEmailTemplate;

View File

@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react';
import { import {
Button, Button,
Center, Center,
Code,
Collapse,
Flex, Flex,
Input, Input,
InputGroup, InputGroup,
@ -20,19 +22,29 @@ import {
useDisclosure, useDisclosure,
useToast, useToast,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { FaMinusCircle, FaPlus } from 'react-icons/fa'; import {
FaAngleDown,
FaAngleUp,
FaMinusCircle,
FaPlus,
FaRegClone,
} from 'react-icons/fa';
import { useClient } from 'urql'; import { useClient } from 'urql';
import { import {
webhookEventNames, webhookEventNames,
ArrayInputOperations, ArrayInputOperations,
WebhookInputDataFields, WebhookInputDataFields,
WebhookInputHeaderFields, WebhookInputHeaderFields,
UpdateWebhookModalViews, UpdateModalViews,
webhookVerifiedStatus, webhookVerifiedStatus,
webhookPayloadExample,
} from '../constants'; } from '../constants';
import { capitalizeFirstLetter, validateURI } from '../utils'; import {
capitalizeFirstLetter,
copyTextToClipboard,
validateURI,
} from '../utils';
import { AddWebhook, EditWebhook, TestEndpoint } from '../graphql/mutation'; import { AddWebhook, EditWebhook, TestEndpoint } from '../graphql/mutation';
import { rest } from 'lodash';
import { BiCheckCircle, BiError, BiErrorCircle } from 'react-icons/bi'; import { BiCheckCircle, BiError, BiErrorCircle } from 'react-icons/bi';
interface headersDataType { interface headersDataType {
@ -54,7 +66,7 @@ interface selecetdWebhookDataTypes {
} }
interface UpdateWebhookModalInputPropTypes { interface UpdateWebhookModalInputPropTypes {
view: UpdateWebhookModalViews; view: UpdateModalViews;
selectedWebhook?: selecetdWebhookDataTypes; selectedWebhook?: selecetdWebhookDataTypes;
fetchWebookData: Function; fetchWebookData: Function;
} }
@ -103,6 +115,7 @@ const UpdateWebhookModal = ({
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [verifyingEndpoint, setVerifyingEndpoint] = useState<boolean>(false); const [verifyingEndpoint, setVerifyingEndpoint] = useState<boolean>(false);
const [isShowingPayload, setIsShowingPayload] = useState<boolean>(false);
const [webhook, setWebhook] = useState<webhookDataType>({ const [webhook, setWebhook] = useState<webhookDataType>({
...initWebhookData, ...initWebhookData,
}); });
@ -254,7 +267,7 @@ const UpdateWebhookModal = ({
const params = getParams(); const params = getParams();
let res: any = {}; let res: any = {};
if ( if (
view === UpdateWebhookModalViews.Edit && view === UpdateModalViews.Edit &&
selectedWebhook?.[WebhookInputDataFields.ID] selectedWebhook?.[WebhookInputDataFields.ID]
) { ) {
res = await client res = await client
@ -292,12 +305,12 @@ const UpdateWebhookModal = ({
setValidator({ ...initWebhookValidatorData }); setValidator({ ...initWebhookValidatorData });
fetchWebookData(); fetchWebookData();
} }
view === UpdateWebhookModalViews.ADD && onClose(); view === UpdateModalViews.ADD && onClose();
}; };
useEffect(() => { useEffect(() => {
if ( if (
isOpen && isOpen &&
view === UpdateWebhookModalViews.Edit && view === UpdateModalViews.Edit &&
selectedWebhook && selectedWebhook &&
Object.keys(selectedWebhook || {}).length Object.keys(selectedWebhook || {}).length
) { ) {
@ -347,7 +360,7 @@ const UpdateWebhookModal = ({
}; };
return ( return (
<> <>
{view === UpdateWebhookModalViews.ADD ? ( {view === UpdateModalViews.ADD ? (
<Button <Button
leftIcon={<FaPlus />} leftIcon={<FaPlus />}
colorScheme="blue" colorScheme="blue"
@ -365,9 +378,7 @@ const UpdateWebhookModal = ({
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent>
<ModalHeader> <ModalHeader>
{view === UpdateWebhookModalViews.ADD {view === UpdateModalViews.ADD ? 'Add New Webhook' : 'Edit Webhook'}
? 'Add New Webhook'
: 'Edit Webhook'}
</ModalHeader> </ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
@ -457,6 +468,63 @@ const UpdateWebhookModal = ({
</Text> </Text>
</Flex> </Flex>
</Flex> </Flex>
<Flex
width="100%"
justifyContent="center"
alignItems="center"
marginBottom="5%"
flexDirection="column"
>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
>
<Flex>
Payload
<Text color="gray.500" ml={1}>
(example)
</Text>
</Flex>
<Button
onClick={() => setIsShowingPayload(!isShowingPayload)}
variant="ghost"
>
{isShowingPayload ? <FaAngleUp /> : <FaAngleDown />}
</Button>
</Flex>
<Collapse
style={{
marginTop: 10,
width: '100%',
}}
in={isShowingPayload}
>
<Code
width="inherit"
borderRadius={5}
padding={2}
position="relative"
>
<pre style={{ overflow: 'auto' }}>
{webhookPayloadExample}
</pre>
{isShowingPayload && (
<Flex
position="absolute"
top={4}
right={4}
cursor="pointer"
onClick={() =>
copyTextToClipboard(webhookPayloadExample)
}
>
<FaRegClone color="#bfbfbf" />
</Flex>
)}
</Code>
</Collapse>
</Flex>
<Flex <Flex
width="100%" width="100%"
justifyContent="space-between" justifyContent="space-between"

View File

@ -162,12 +162,20 @@ export enum WebhookInputDataFields {
HEADERS = 'headers', HEADERS = 'headers',
} }
export enum EmailTemplateInputDataFields {
ID = 'id',
EVENT_NAME = 'event_name',
SUBJECT = 'subject',
CREATED_AT = 'created_at',
TEMPLATE = 'template',
}
export enum WebhookInputHeaderFields { export enum WebhookInputHeaderFields {
KEY = 'key', KEY = 'key',
VALUE = 'value', VALUE = 'value',
} }
export enum UpdateWebhookModalViews { export enum UpdateModalViews {
ADD = 'add', ADD = 'add',
Edit = 'edit', Edit = 'edit',
} }
@ -183,8 +191,67 @@ export const webhookEventNames = {
USER_ACCESS_REVOKED: 'user.access_revoked', USER_ACCESS_REVOKED: 'user.access_revoked',
}; };
export const emailTemplateEventNames = {
BASIC_AUTH_SIGNUP: 'basic_auth_signup',
MAGIC_LINK_LOGIN: 'magic_link_login',
UPDATE_EMAIL: 'update_email',
FORGOT_PASSWORD: 'forgot_password',
VERIFY_OTP: 'verify_otp',
};
export enum webhookVerifiedStatus { export enum webhookVerifiedStatus {
VERIFIED = 'verified', VERIFIED = 'verified',
NOT_VERIFIED = 'not_verified', NOT_VERIFIED = 'not_verified',
PENDING = 'verification_pending', PENDING = 'verification_pending',
} }
export const emailTemplateVariables = {
'user.id': '{user.id}}',
'user.email': '{user.email}}',
'user.given_name': '{user.given_name}}',
'user.family_name': '{user.family_name}}',
'user.signup_methods': '{user.signup_methods}}',
'user.email_verified': '{user.email_verified}}',
'user.picture': '{user.picture}}',
'user.roles': '{user.roles}}',
'user.middle_name': '{user.middle_name}}',
'user.nickname': '{user.nickname}}',
'user.preferred_username': '{user.preferred_username}}',
'user.gender': '{user.gender}}',
'user.birthdate': '{user.birthdate}}',
'user.phone_number': '{user.phone_number}}',
'user.phone_number_verified': '{user.phone_number_verified}}',
'user.created_at': '{user.created_at}}',
'user.updated_at': '{user.updated_at}}',
'organization.name': '{organization.name}}',
'organization.logo': '{organization.logo}}',
verification_url: '{verification_url}}',
otp: '{otp}}',
};
export const webhookPayloadExample: string = `{
"event_name":"user.login",
"user":{
"birthdate":null,
"created_at":1657524721,
"email":"lakhan.m.samani@gmail.com",
"email_verified":true,
"family_name":"Samani",
"gender":null,
"given_name":"Lakhan",
"id":"466d0b31-1b87-420e-bea5-09d05d79c586",
"middle_name":null,
"nickname":null,
"phone_number":null,
"phone_number_verified":false,
"picture":"https://lh3.googleusercontent.com/a-/AFdZucppvU6a2zIDkX0wvhhapVjT0ZMKDlYCkQDi3NxcUg=s96-c",
"preferred_username":"lakhan.m.samani@gmail.com",
"revoked_timestamp":null,
"roles":[
"user"
],
"signup_methods":"google",
"updated_at":1657526492
},
"auth_recipe":"google"
}`;

View File

@ -112,3 +112,27 @@ export const TestEndpoint = `
} }
} }
`; `;
export const AddEmailTemplate = `
mutation addEmailTemplate($params: AddEmailTemplateRequest!) {
_add_email_template(params: $params) {
message
}
}
`;
export const EditEmailTemplate = `
mutation editEmailTemplate($params: UpdateEmailTemplateRequest!) {
_update_email_template(params: $params) {
message
}
}
`;
export const DeleteEmailTemplate = `
mutation deleteEmailTemplate($params: DeleteEmailTemplateRequest!) {
_delete_email_template(params: $params) {
message
}
}
`;

View File

@ -122,6 +122,26 @@ export const WebhooksDataQuery = `
} }
`; `;
export const EmailTemplatesQuery = `
query getEmailTemplates($params: PaginatedInput!) {
_email_templates(params: $params) {
EmailTemplates {
id
event_name
subject
created_at
template
}
pagination {
limit
page
offset
total
}
}
}
`;
export const WebhookLogsQuery = ` export const WebhookLogsQuery = `
query getWebhookLogs($params: ListWebhookLogRequest!) { query getWebhookLogs($params: ListWebhookLogRequest!) {
_webhook_logs(params: $params) { _webhook_logs(params: $params) {

View File

@ -0,0 +1,347 @@
import React, { useEffect, useState } from 'react';
import { useClient } from 'urql';
import {
Box,
Button,
Center,
Flex,
IconButton,
Menu,
MenuButton,
MenuList,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
Spinner,
Table,
TableCaption,
Tbody,
Td,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import {
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaAngleDown,
FaAngleLeft,
FaAngleRight,
FaExclamationCircle,
} from 'react-icons/fa';
import UpdateEmailTemplateModal from '../components/UpdateEmailTemplateModal';
import {
pageLimits,
UpdateModalViews,
EmailTemplateInputDataFields,
} from '../constants';
import { EmailTemplatesQuery, WebhooksDataQuery } from '../graphql/queries';
import dayjs from 'dayjs';
import DeleteEmailTemplateModal from '../components/DeleteEmailTemplateModal';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface EmailTemplateDataType {
[EmailTemplateInputDataFields.ID]: string;
[EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string;
[EmailTemplateInputDataFields.CREATED_AT]: number;
[EmailTemplateInputDataFields.TEMPLATE]: string;
}
const EmailTemplates = () => {
const client = useClient();
const [loading, setLoading] = useState<boolean>(false);
const [emailTemplatesData, setEmailTemplatesData] = useState<
EmailTemplateDataType[]
>([]);
const [paginationProps, setPaginationProps] = useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const fetchEmailTemplatesData = async () => {
setLoading(true);
const res = await client
.query(EmailTemplatesQuery, {
params: {
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (res.data?._email_templates) {
const { pagination, EmailTemplates: emailTemplates } =
res.data?._email_templates;
const maxPages = getMaxPages(pagination);
if (emailTemplates?.length) {
setEmailTemplatesData(emailTemplates);
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
useEffect(() => {
fetchEmailTemplatesData();
}, [paginationProps.page, paginationProps.limit]);
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
<Text fontSize="md" fontWeight="bold">
Email Templates
</Text>
<UpdateEmailTemplateModal
view={UpdateModalViews.ADD}
fetchEmailTemplatesData={fetchEmailTemplatesData}
/>
</Flex>
{!loading ? (
emailTemplatesData.length ? (
<Table variant="simple">
<Thead>
<Tr>
<Th>Event Name</Th>
<Th>Subject</Th>
<Th>Created At</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{emailTemplatesData.map((templateData: EmailTemplateDataType) => (
<Tr
key={templateData[EmailTemplateInputDataFields.ID]}
style={{ fontSize: 14 }}
>
<Td maxW="300">
{templateData[EmailTemplateInputDataFields.EVENT_NAME]}
</Td>
<Td>{templateData[EmailTemplateInputDataFields.SUBJECT]}</Td>
<Td>
{dayjs(templateData.created_at * 1000).format(
'MMM DD, YYYY'
)}
</Td>
<Td>
<Menu>
<MenuButton as={Button} variant="unstyled" size="sm">
<Flex
justifyContent="space-between"
alignItems="center"
>
<Text fontSize="sm" fontWeight="light">
Menu
</Text>
<FaAngleDown style={{ marginLeft: 10 }} />
</Flex>
</MenuButton>
<MenuList>
<UpdateEmailTemplateModal
view={UpdateModalViews.Edit}
selectedTemplate={templateData}
fetchEmailTemplatesData={fetchEmailTemplatesData}
/>
<DeleteEmailTemplateModal
emailTemplateId={
templateData[EmailTemplateInputDataFields.ID]
}
eventName={
templateData[
EmailTemplateInputDataFields.EVENT_NAME
]
}
fetchEmailTemplatesData={fetchEmailTemplatesData}
/>
</MenuList>
</Menu>
</Td>
</Tr>
))}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{pageLimits.map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Box>
);
};
export default EmailTemplates;

View File

@ -8,7 +8,6 @@ import {
IconButton, IconButton,
Menu, Menu,
MenuButton, MenuButton,
MenuItem,
MenuList, MenuList,
NumberDecrementStepper, NumberDecrementStepper,
NumberIncrementStepper, NumberIncrementStepper,
@ -40,7 +39,7 @@ import UpdateWebhookModal from '../components/UpdateWebhookModal';
import { import {
pageLimits, pageLimits,
WebhookInputDataFields, WebhookInputDataFields,
UpdateWebhookModalViews, UpdateModalViews,
} from '../constants'; } from '../constants';
import { WebhooksDataQuery } from '../graphql/queries'; import { WebhooksDataQuery } from '../graphql/queries';
import DeleteWebhookModal from '../components/DeleteWebhookModal'; import DeleteWebhookModal from '../components/DeleteWebhookModal';
@ -125,7 +124,7 @@ const Webhooks = () => {
Webhooks Webhooks
</Text> </Text>
<UpdateWebhookModal <UpdateWebhookModal
view={UpdateWebhookModalViews.ADD} view={UpdateModalViews.ADD}
fetchWebookData={fetchWebookData} fetchWebookData={fetchWebookData}
/> />
</Flex> </Flex>
@ -196,7 +195,7 @@ const Webhooks = () => {
</MenuButton> </MenuButton>
<MenuList> <MenuList>
<UpdateWebhookModal <UpdateWebhookModal
view={UpdateWebhookModalViews.Edit} view={UpdateModalViews.Edit}
selectedWebhook={webhook} selectedWebhook={webhook}
fetchWebookData={fetchWebookData} fetchWebookData={fetchWebookData}
/> />

View File

@ -3,6 +3,7 @@ import { Outlet, Route, Routes } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext'; import { useAuthContext } from '../contexts/AuthContext';
import { DashboardLayout } from '../layouts/DashboardLayout'; import { DashboardLayout } from '../layouts/DashboardLayout';
import EmailTemplates from '../pages/EmailTemplates';
const Auth = lazy(() => import('../pages/Auth')); const Auth = lazy(() => import('../pages/Auth'));
const Environment = lazy(() => import('../pages/Environment')); const Environment = lazy(() => import('../pages/Environment'));
@ -31,6 +32,7 @@ export const AppRoutes = () => {
</Route> </Route>
<Route path="users" element={<Users />} /> <Route path="users" element={<Users />} />
<Route path="webhooks" element={<Webhooks />} /> <Route path="webhooks" element={<Webhooks />} />
<Route path="email-templates" element={<EmailTemplates />} />
<Route path="*" element={<Home />} /> <Route path="*" element={<Home />} />
</Route> </Route>
</Routes> </Routes>

View File

@ -12,6 +12,7 @@ type EmailTemplate struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"` ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"` EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
Subject string `gorm:"type:text" json:"subject" bson:"subject" cql:"subject"`
Template string `gorm:"type:text" json:"template" bson:"template" cql:"template"` Template string `gorm:"type:text" json:"template" bson:"template" cql:"template"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"` CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"` UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
@ -26,6 +27,7 @@ func (e *EmailTemplate) AsAPIEmailTemplate() *model.EmailTemplate {
return &model.EmailTemplate{ return &model.EmailTemplate{
ID: id, ID: id,
EventName: e.EventName, EventName: e.EventName,
Subject: e.Subject,
Template: e.Template, Template: e.Template,
CreatedAt: refs.NewInt64Ref(e.CreatedAt), CreatedAt: refs.NewInt64Ref(e.CreatedAt),
UpdatedAt: refs.NewInt64Ref(e.UpdatedAt), UpdatedAt: refs.NewInt64Ref(e.UpdatedAt),

View File

@ -29,7 +29,7 @@ func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate models.Em
return nil, fmt.Errorf("Email template with %s event_name already exists", emailTemplate.EventName) return nil, fmt.Errorf("Email template with %s event_name already exists", emailTemplate.EventName)
} }
insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_name, template, created_at, updated_at) VALUES ('%s', '%s', '%s', %d, %d)", KeySpace+"."+models.Collections.EmailTemplate, emailTemplate.ID, emailTemplate.EventName, emailTemplate.Template, emailTemplate.CreatedAt, emailTemplate.UpdatedAt) insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_name, subject, template, created_at, updated_at) VALUES ('%s', '%s', '%s','%s', %d, %d)", KeySpace+"."+models.Collections.EmailTemplate, emailTemplate.ID, emailTemplate.EventName, emailTemplate.Subject, emailTemplate.Template, emailTemplate.CreatedAt, emailTemplate.UpdatedAt)
err := p.db.Query(insertQuery).Exec() err := p.db.Query(insertQuery).Exec()
if err != nil { if err != nil {
return nil, err return nil, err
@ -103,14 +103,14 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagin
// there is no offset in cassandra // there is no offset in cassandra
// so we fetch till limit + offset // so we fetch till limit + offset
// and return the results from offset to limit // and return the results from offset to limit
query := fmt.Sprintf("SELECT id, event_name, template, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.EmailTemplate, pagination.Limit+pagination.Offset) query := fmt.Sprintf("SELECT id, event_name, subject, template, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.EmailTemplate, pagination.Limit+pagination.Offset)
scanner := p.db.Query(query).Iter().Scanner() scanner := p.db.Query(query).Iter().Scanner()
counter := int64(0) counter := int64(0)
for scanner.Next() { for scanner.Next() {
if counter >= pagination.Offset { if counter >= pagination.Offset {
var emailTemplate models.EmailTemplate var emailTemplate models.EmailTemplate
err := scanner.Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) err := scanner.Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -128,8 +128,8 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagin
// GetEmailTemplateByID to get EmailTemplate by id // GetEmailTemplateByID to get EmailTemplate by id
func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) { func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) {
var emailTemplate models.EmailTemplate var emailTemplate models.EmailTemplate
query := fmt.Sprintf(`SELECT id, event_name, template, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.EmailTemplate, emailTemplateID) query := fmt.Sprintf(`SELECT id, event_name, subject, template, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.EmailTemplate, emailTemplateID)
err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -139,8 +139,8 @@ func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID str
// GetEmailTemplateByEventName to get EmailTemplate by event_name // GetEmailTemplateByEventName to get EmailTemplate by event_name
func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) { func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) {
var emailTemplate models.EmailTemplate var emailTemplate models.EmailTemplate
query := fmt.Sprintf(`SELECT id, event_name, template, created_at, updated_at FROM %s WHERE event_name = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.EmailTemplate, eventName) query := fmt.Sprintf(`SELECT id, event_name, subject, template, created_at, updated_at FROM %s WHERE event_name = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.EmailTemplate, eventName)
err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -214,6 +214,12 @@ func NewProvider() (*provider, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// add subject on email_templates table
emailTemplateAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD subject text;`, KeySpace, models.Collections.EmailTemplate)
err = session.Query(emailTemplateAlterQuery).Exec()
if err != nil {
return nil, err
}
return &provider{ return &provider{
db: session, db: session,

View File

@ -56,6 +56,7 @@ type ComplexityRoot struct {
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
EventName func(childComplexity int) int EventName func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
Subject func(childComplexity int) int
Template func(childComplexity int) int Template func(childComplexity int) int
UpdatedAt func(childComplexity int) int UpdatedAt func(childComplexity int) int
} }
@ -403,6 +404,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.EmailTemplate.ID(childComplexity), true return e.complexity.EmailTemplate.ID(childComplexity), true
case "EmailTemplate.subject":
if e.complexity.EmailTemplate.Subject == nil {
break
}
return e.complexity.EmailTemplate.Subject(childComplexity), true
case "EmailTemplate.template": case "EmailTemplate.template":
if e.complexity.EmailTemplate.Template == nil { if e.complexity.EmailTemplate.Template == nil {
break break
@ -1978,6 +1986,7 @@ type EmailTemplate {
id: ID! id: ID!
event_name: String! event_name: String!
template: String! template: String!
subject: String!
created_at: Int64 created_at: Int64
updated_at: Int64 updated_at: Int64
} }
@ -2193,6 +2202,7 @@ input TestEndpointRequest {
input AddEmailTemplateRequest { input AddEmailTemplateRequest {
event_name: String! event_name: String!
subject: String!
template: String! template: String!
} }
@ -2200,6 +2210,7 @@ input UpdateEmailTemplateRequest {
id: ID! id: ID!
event_name: String event_name: String
template: String template: String
subject: String
} }
input DeleteEmailTemplateRequest { input DeleteEmailTemplateRequest {
@ -3108,6 +3119,41 @@ func (ec *executionContext) _EmailTemplate_template(ctx context.Context, field g
return ec.marshalNString2string(ctx, field.Selections, res) return ec.marshalNString2string(ctx, field.Selections, res)
} }
func (ec *executionContext) _EmailTemplate_subject(ctx context.Context, field graphql.CollectedField, obj *model.EmailTemplate) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "EmailTemplate",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Subject, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _EmailTemplate_created_at(ctx context.Context, field graphql.CollectedField, obj *model.EmailTemplate) (ret graphql.Marshaler) { func (ec *executionContext) _EmailTemplate_created_at(ctx context.Context, field graphql.CollectedField, obj *model.EmailTemplate) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -10087,6 +10133,14 @@ func (ec *executionContext) unmarshalInputAddEmailTemplateRequest(ctx context.Co
if err != nil { if err != nil {
return it, err return it, err
} }
case "subject":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("subject"))
it.Subject, err = ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
case "template": case "template":
var err error var err error
@ -10866,6 +10920,14 @@ func (ec *executionContext) unmarshalInputUpdateEmailTemplateRequest(ctx context
if err != nil { if err != nil {
return it, err return it, err
} }
case "subject":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("subject"))
it.Subject, err = ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
} }
} }
@ -11632,6 +11694,11 @@ func (ec *executionContext) _EmailTemplate(ctx context.Context, sel ast.Selectio
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "subject":
out.Values[i] = ec._EmailTemplate_subject(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "created_at": case "created_at":
out.Values[i] = ec._EmailTemplate_created_at(ctx, field, obj) out.Values[i] = ec._EmailTemplate_created_at(ctx, field, obj)
case "updated_at": case "updated_at":

View File

@ -4,6 +4,7 @@ package model
type AddEmailTemplateRequest struct { type AddEmailTemplateRequest struct {
EventName string `json:"event_name"` EventName string `json:"event_name"`
Subject string `json:"subject"`
Template string `json:"template"` Template string `json:"template"`
} }
@ -43,6 +44,7 @@ type EmailTemplate struct {
ID string `json:"id"` ID string `json:"id"`
EventName string `json:"event_name"` EventName string `json:"event_name"`
Template string `json:"template"` Template string `json:"template"`
Subject string `json:"subject"`
CreatedAt *int64 `json:"created_at"` CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"` UpdatedAt *int64 `json:"updated_at"`
} }
@ -240,6 +242,7 @@ type UpdateEmailTemplateRequest struct {
ID string `json:"id"` ID string `json:"id"`
EventName *string `json:"event_name"` EventName *string `json:"event_name"`
Template *string `json:"template"` Template *string `json:"template"`
Subject *string `json:"subject"`
} }
type UpdateEnvInput struct { type UpdateEnvInput struct {

View File

@ -189,6 +189,7 @@ type EmailTemplate {
id: ID! id: ID!
event_name: String! event_name: String!
template: String! template: String!
subject: String!
created_at: Int64 created_at: Int64
updated_at: Int64 updated_at: Int64
} }
@ -404,6 +405,7 @@ input TestEndpointRequest {
input AddEmailTemplateRequest { input AddEmailTemplateRequest {
event_name: String! event_name: String!
subject: String!
template: String! template: String!
} }
@ -411,6 +413,7 @@ input UpdateEmailTemplateRequest {
id: ID! id: ID!
event_name: String event_name: String
template: String template: String
subject: String
} }
input DeleteEmailTemplateRequest { input DeleteEmailTemplateRequest {

View File

@ -34,6 +34,10 @@ func AddEmailTemplateResolver(ctx context.Context, params model.AddEmailTemplate
return nil, fmt.Errorf("invalid event name %s", params.EventName) return nil, fmt.Errorf("invalid event name %s", params.EventName)
} }
if strings.TrimSpace(params.Subject) == "" {
return nil, fmt.Errorf("empty subject not allowed")
}
if strings.TrimSpace(params.Template) == "" { if strings.TrimSpace(params.Template) == "" {
return nil, fmt.Errorf("empty template not allowed") return nil, fmt.Errorf("empty template not allowed")
} }
@ -41,6 +45,7 @@ func AddEmailTemplateResolver(ctx context.Context, params model.AddEmailTemplate
_, err = db.Provider.AddEmailTemplate(ctx, models.EmailTemplate{ _, err = db.Provider.AddEmailTemplate(ctx, models.EmailTemplate{
EventName: params.EventName, EventName: params.EventName,
Template: params.Template, Template: params.Template,
Subject: params.Subject,
}) })
if err != nil { if err != nil {
log.Debug("Failed to add email template: ", err) log.Debug("Failed to add email template: ", err)

View File

@ -51,6 +51,14 @@ func UpdateEmailTemplateResolver(ctx context.Context, params model.UpdateEmailTe
emailTemplateDetails.EventName = refs.StringValue(params.EventName) emailTemplateDetails.EventName = refs.StringValue(params.EventName)
} }
if params.Subject != nil && emailTemplateDetails.Subject != refs.StringValue(params.Subject) {
if strings.TrimSpace(refs.StringValue(params.Subject)) == "" {
log.Debug("empty subject not allowed")
return nil, fmt.Errorf("empty subject not allowed")
}
emailTemplateDetails.Subject = refs.StringValue(params.Subject)
}
if params.Template != nil && emailTemplateDetails.Template != refs.StringValue(params.Template) { if params.Template != nil && emailTemplateDetails.Template != refs.StringValue(params.Template) {
if strings.TrimSpace(refs.StringValue(params.Template)) == "" { if strings.TrimSpace(refs.StringValue(params.Template)) == "" {
log.Debug("empty template not allowed") log.Debug("empty template not allowed")

View File

@ -31,10 +31,21 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
assert.Nil(t, emailTemplate) assert.Nil(t, emailTemplate)
}) })
t.Run("should not add email template for empty template", func(t *testing.T) {
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
EventName: s.TestInfo.TestEmailTemplateEventTypes[0],
Template: " test ",
Subject: " ",
})
assert.Error(t, err)
assert.Nil(t, emailTemplate)
})
t.Run("should not add email template for empty template", func(t *testing.T) { t.Run("should not add email template for empty template", func(t *testing.T) {
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{ emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
EventName: s.TestInfo.TestEmailTemplateEventTypes[0], EventName: s.TestInfo.TestEmailTemplateEventTypes[0],
Template: " ", Template: " ",
Subject: "test",
}) })
assert.Error(t, err) assert.Error(t, err)
assert.Nil(t, emailTemplate) assert.Nil(t, emailTemplate)
@ -43,7 +54,8 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
t.Run("should add email template for "+eventType, func(t *testing.T) { t.Run("should add email template for "+eventType, func(t *testing.T) {
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{ emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
EventName: eventType, EventName: eventType,
Template: `Test email`, Template: "Test email",
Subject: "Test email",
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, emailTemplate) assert.NotNil(t, emailTemplate)
@ -52,6 +64,7 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
et, err := db.Provider.GetEmailTemplateByEventName(ctx, eventType) et, err := db.Provider.GetEmailTemplateByEventName(ctx, eventType)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, et.EventName, eventType) assert.Equal(t, et.EventName, eventType)
assert.Equal(t, "Test email", et.Subject)
}) })
} }
}) })

View File

@ -31,6 +31,7 @@ func updateEmailTemplateTest(t *testing.T, s TestSetup) {
res, err := resolvers.UpdateEmailTemplateResolver(ctx, model.UpdateEmailTemplateRequest{ res, err := resolvers.UpdateEmailTemplateResolver(ctx, model.UpdateEmailTemplateRequest{
ID: emailTemplate.ID, ID: emailTemplate.ID,
Template: refs.NewStringRef("Updated test template"), Template: refs.NewStringRef("Updated test template"),
Subject: refs.NewStringRef("Updated subject"),
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -42,5 +43,6 @@ func updateEmailTemplateTest(t *testing.T, s TestSetup) {
assert.NotNil(t, updatedEmailTemplate) assert.NotNil(t, updatedEmailTemplate)
assert.Equal(t, emailTemplate.ID, updatedEmailTemplate.ID) assert.Equal(t, emailTemplate.ID, updatedEmailTemplate.ID)
assert.Equal(t, updatedEmailTemplate.Template, "Updated test template") assert.Equal(t, updatedEmailTemplate.Template, "Updated test template")
assert.Equal(t, updatedEmailTemplate.Subject, "Updated subject")
}) })
} }

View File

@ -12,9 +12,901 @@
<script> <script>
window.__authorizer__ = {{.data}} window.__authorizer__ = {{.data}}
</script> </script>
<style>
.rdw-option-wrapper {
border: 1px solid #F1F1F1;
padding: 5px;
min-width: 25px;
height: 20px;
border-radius: 2px;
margin: 0 4px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
background: white;
text-transform: capitalize;
}
.rdw-option-wrapper:hover {
box-shadow: 1px 1px 0px #BFBDBD;
}
.rdw-option-wrapper:active {
box-shadow: 1px 1px 0px #BFBDBD inset;
}
.rdw-option-active {
box-shadow: 1px 1px 0px #BFBDBD inset;
}
.rdw-option-disabled {
opacity: 0.3;
cursor: default;
}
.rdw-dropdown-wrapper {
height: 30px;
background: white;
cursor: pointer;
border: 1px solid #F1F1F1;
border-radius: 2px;
margin: 0 3px;
text-transform: capitalize;
background: white;
}
.rdw-dropdown-wrapper:focus {
outline: none;
}
.rdw-dropdown-wrapper:hover {
box-shadow: 1px 1px 0px #BFBDBD;
background-color: #FFFFFF;
}
.rdw-dropdown-wrapper:active {
box-shadow: 1px 1px 0px #BFBDBD inset;
}
.rdw-dropdown-carettoopen {
height: 0px;
width: 0px;
position: absolute;
top: 35%;
right: 10%;
border-top: 6px solid black;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
.rdw-dropdown-carettoclose {
height: 0px;
width: 0px;
position: absolute;
top: 35%;
right: 10%;
border-bottom: 6px solid black;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
.rdw-dropdown-selectedtext {
display: flex;
position: relative;
height: 100%;
align-items: center;
padding: 0 5px;
}
.rdw-dropdown-optionwrapper {
z-index: 100;
position: relative;
border: 1px solid #F1F1F1;
width: 98%;
background: white;
border-radius: 2px;
margin: 0;
padding: 0;
max-height: 250px;
overflow-y: scroll;
}
.rdw-dropdown-optionwrapper:hover {
box-shadow: 1px 1px 0px #BFBDBD;
background-color: #FFFFFF;
}
.rdw-dropdownoption-default {
min-height: 25px;
display: flex;
align-items: center;
padding: 0 5px;
}
.rdw-dropdownoption-highlighted {
background: #F1F1F1;
}
.rdw-dropdownoption-active {
background: #f5f5f5;
}
.rdw-dropdownoption-disabled {
opacity: 0.3;
cursor: default;
}
.rdw-inline-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
flex-wrap: wrap
}
.rdw-inline-dropdown {
width: 50px;
}
.rdw-inline-dropdownoption {
height: 40px;
display: flex;
justify-content: center;
}
.rdw-block-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
flex-wrap: wrap
}
.rdw-block-dropdown {
width: 110px;
}
.rdw-fontsize-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
flex-wrap: wrap
}
.rdw-fontsize-dropdown {
min-width: 40px;
}
.rdw-fontsize-option {
display: flex;
justify-content: center;
}
.rdw-fontfamily-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
flex-wrap: wrap
}
.rdw-fontfamily-dropdown {
width: 115px;
}
.rdw-fontfamily-placeholder {
white-space: nowrap;
max-width: 90px;
overflow: hidden;
text-overflow: ellipsis;
}
.rdw-fontfamily-optionwrapper {
width: 140px;
}
.rdw-list-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
flex-wrap: wrap
}
.rdw-list-dropdown {
width: 50px;
z-index: 90;
}
.rdw-list-dropdownOption {
height: 40px;
display: flex;
justify-content: center;
}
.rdw-text-align-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
flex-wrap: wrap
}
.rdw-text-align-dropdown {
width: 50px;
z-index: 90;
}
.rdw-text-align-dropdownOption {
height: 40px;
display: flex;
justify-content: center;
}
.rdw-right-aligned-block {
text-align: right;
}
.rdw-left-aligned-block {
text-align: left !important;
}
.rdw-center-aligned-block {
text-align: center !important;
}
.rdw-justify-aligned-block {
text-align: justify !important;
}
.rdw-right-aligned-block > div {
display: inline-block;
}
.rdw-left-aligned-block > div {
display: inline-block;
}
.rdw-center-aligned-block > div {
display: inline-block;
}
.rdw-justify-aligned-block > div {
display: inline-block;
}
.rdw-colorpicker-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
position: relative;
flex-wrap: wrap
}
.rdw-colorpicker-modal {
position: absolute;
top: 35px;
left: 5px;
display: flex;
flex-direction: column;
width: 175px;
height: 175px;
border: 1px solid #F1F1F1;
padding: 15px;
border-radius: 2px;
z-index: 100;
background: white;
box-shadow: 3px 3px 5px #BFBDBD;
}
.rdw-colorpicker-modal-header {
display: flex;
padding-bottom: 5px;
}
.rdw-colorpicker-modal-style-label {
font-size: 15px;
width: 50%;
text-align: center;
cursor: pointer;
padding: 0 10px 5px;
}
.rdw-colorpicker-modal-style-label-active {
border-bottom: 2px solid #0a66b7;
}
.rdw-colorpicker-modal-options {
margin: 5px auto;
display: flex;
width: 100%;
height: 100%;
flex-wrap: wrap;
overflow: scroll;
}
.rdw-colorpicker-cube {
width: 22px;
height: 22px;
border: 1px solid #F1F1F1;
}
.rdw-colorpicker-option {
margin: 3px;
padding: 0;
min-height: 20px;
border: none;
width: 22px;
height: 22px;
min-width: 22px;
box-shadow: 1px 2px 1px #BFBDBD inset;
}
.rdw-colorpicker-option:hover {
box-shadow: 1px 2px 1px #BFBDBD;
}
.rdw-colorpicker-option:active {
box-shadow: -1px -2px 1px #BFBDBD;
}
.rdw-colorpicker-option-active {
box-shadow: 0px 0px 2px 2px #BFBDBD;
}
.rdw-link-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
position: relative;
flex-wrap: wrap
}
.rdw-link-dropdown {
width: 50px;
}
.rdw-link-dropdownOption {
height: 40px;
display: flex;
justify-content: center;
}
.rdw-link-dropdownPlaceholder {
margin-left: 8px;
}
.rdw-link-modal {
position: absolute;
top: 35px;
left: 5px;
display: flex;
flex-direction: column;
width: 235px;
height: 205px;
border: 1px solid #F1F1F1;
padding: 15px;
border-radius: 2px;
z-index: 100;
background: white;
box-shadow: 3px 3px 5px #BFBDBD;
}
.rdw-link-modal-label {
font-size: 15px;
}
.rdw-link-modal-input {
margin-top: 5px;
border-radius: 2px;
border: 1px solid #F1F1F1;
height: 25px;
margin-bottom: 15px;
padding: 0 5px;
}
.rdw-link-modal-input:focus {
outline: none;
}
.rdw-link-modal-buttonsection {
margin: 0 auto;
}
.rdw-link-modal-target-option {
margin-bottom: 20px;
}
.rdw-link-modal-target-option > span {
margin-left: 5px;
}
.rdw-link-modal-btn {
margin-left: 10px;
width: 75px;
height: 30px;
border: 1px solid #F1F1F1;
border-radius: 2px;
cursor: pointer;
background: white;
text-transform: capitalize;
}
.rdw-link-modal-btn:hover {
box-shadow: 1px 1px 0px #BFBDBD;
}
.rdw-link-modal-btn:active {
box-shadow: 1px 1px 0px #BFBDBD inset;
}
.rdw-link-modal-btn:focus {
outline: none !important;
}
.rdw-link-modal-btn:disabled {
background: #ece9e9;
}
.rdw-link-dropdownoption {
height: 40px;
display: flex;
justify-content: center;
}
.rdw-history-dropdown {
width: 50px;
}
.rdw-embedded-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
position: relative;
flex-wrap: wrap
}
.rdw-embedded-modal {
position: absolute;
top: 35px;
left: 5px;
display: flex;
flex-direction: column;
width: 235px;
height: 180px;
border: 1px solid #F1F1F1;
padding: 15px;
border-radius: 2px;
z-index: 100;
background: white;
justify-content: space-between;
box-shadow: 3px 3px 5px #BFBDBD;
}
.rdw-embedded-modal-header {
font-size: 15px;
display: flex;
}
.rdw-embedded-modal-header-option {
width: 50%;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.rdw-embedded-modal-header-label {
width: 95px;
border: 1px solid #f1f1f1;
margin-top: 5px;
background: #6EB8D4;
border-bottom: 2px solid #0a66b7;
}
.rdw-embedded-modal-link-section {
display: flex;
flex-direction: column;
}
.rdw-embedded-modal-link-input {
width: 88%;
height: 35px;
margin: 10px 0;
border: 1px solid #F1F1F1;
border-radius: 2px;
font-size: 15px;
padding: 0 5px;
}
.rdw-embedded-modal-link-input-wrapper {
display: flex;
align-items: center;
}
.rdw-embedded-modal-link-input:focus {
outline: none;
}
.rdw-embedded-modal-btn-section {
display: flex;
justify-content: center;
}
.rdw-embedded-modal-btn {
margin: 0 3px;
width: 75px;
height: 30px;
border: 1px solid #F1F1F1;
border-radius: 2px;
cursor: pointer;
background: white;
text-transform: capitalize;
}
.rdw-embedded-modal-btn:hover {
box-shadow: 1px 1px 0px #BFBDBD;
}
.rdw-embedded-modal-btn:active {
box-shadow: 1px 1px 0px #BFBDBD inset;
}
.rdw-embedded-modal-btn:focus {
outline: none !important;
}
.rdw-embedded-modal-btn:disabled {
background: #ece9e9;
}
.rdw-embedded-modal-size {
align-items: center;
display: flex;
margin: 8px 0;
justify-content: space-between;
}
.rdw-embedded-modal-size-input {
width: 80%;
height: 20px;
border: 1px solid #F1F1F1;
border-radius: 2px;
font-size: 12px;
}
.rdw-embedded-modal-size-input:focus {
outline: none;
}
.rdw-emoji-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
position: relative;
flex-wrap: wrap
}
.rdw-emoji-modal {
overflow: auto;
position: absolute;
top: 35px;
left: 5px;
display: flex;
flex-wrap: wrap;
width: 235px;
height: 180px;
border: 1px solid #F1F1F1;
padding: 15px;
border-radius: 2px;
z-index: 100;
background: white;
box-shadow: 3px 3px 5px #BFBDBD;
}
.rdw-emoji-icon {
margin: 2.5px;
height: 24px;
width: 24px;
cursor: pointer;
font-size: 22px;
display: flex;
justify-content: center;
align-items: center;
}
.rdw-spinner {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
}
.rdw-spinner > div {
width: 12px;
height: 12px;
background-color: #333;
border-radius: 100%;
display: inline-block;
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
}
.rdw-spinner .rdw-bounce1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.rdw-spinner .rdw-bounce2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
@-webkit-keyframes sk-bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes sk-bouncedelay {
0%, 80%, 100% {
-webkit-transform: scale(0);
transform: scale(0);
} 40% {
-webkit-transform: scale(1.0);
transform: scale(1.0);
}
}
.rdw-image-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
position: relative;
flex-wrap: wrap
}
.rdw-image-modal {
position: absolute;
top: 35px;
left: 5px;
display: flex;
flex-direction: column;
width: 235px;
border: 1px solid #F1F1F1;
padding: 15px;
border-radius: 2px;
z-index: 100;
background: white;
box-shadow: 3px 3px 5px #BFBDBD;
}
.rdw-image-modal-header {
font-size: 15px;
margin: 10px 0;
display: flex;
}
.rdw-image-modal-header-option {
width: 50%;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.rdw-image-modal-header-label {
width: 80px;
background: #f1f1f1;
border: 1px solid #f1f1f1;
margin-top: 5px;
}
.rdw-image-modal-header-label-highlighted {
background: #6EB8D4;
border-bottom: 2px solid #0a66b7;
}
.rdw-image-modal-upload-option {
width: 100%;
color: gray;
cursor: pointer;
display: flex;
border: none;
font-size: 15px;
align-items: center;
justify-content: center;
background-color: #f1f1f1;
outline: 2px dashed gray;
outline-offset: -10px;
margin: 10px 0;
padding: 9px 0;
}
.rdw-image-modal-upload-option-highlighted {
outline: 2px dashed #0a66b7;
}
.rdw-image-modal-upload-option-label {
cursor: pointer;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: 15px;
}
.rdw-image-modal-upload-option-label span{
padding: 0 20px;
}
.rdw-image-modal-upload-option-image-preview {
max-width: 100%;
max-height: 200px;
}
.rdw-image-modal-upload-option-input {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
.rdw-image-modal-url-section {
display: flex;
align-items: center;
}
.rdw-image-modal-url-input {
width: 90%;
height: 35px;
margin: 15px 0 12px;
border: 1px solid #F1F1F1;
border-radius: 2px;
font-size: 15px;
padding: 0 5px;
}
.rdw-image-modal-btn-section {
margin: 10px auto 0;
}
.rdw-image-modal-url-input:focus {
outline: none;
}
.rdw-image-modal-btn {
margin: 0 5px;
width: 75px;
height: 30px;
border: 1px solid #F1F1F1;
border-radius: 2px;
cursor: pointer;
background: white;
text-transform: capitalize;
}
.rdw-image-modal-btn:hover {
box-shadow: 1px 1px 0px #BFBDBD;
}
.rdw-image-modal-btn:active {
box-shadow: 1px 1px 0px #BFBDBD inset;
}
.rdw-image-modal-btn:focus {
outline: none !important;
}
.rdw-image-modal-btn:disabled {
background: #ece9e9;
}
.rdw-image-modal-spinner {
position: absolute;
top: -3px;
left: 0;
width: 100%;
height: 100%;
opacity: 0.5;
}
.rdw-image-modal-alt-input {
width: 70%;
height: 20px;
border: 1px solid #F1F1F1;
border-radius: 2px;
font-size: 12px;
margin-left: 5px;
}
.rdw-image-modal-alt-input:focus {
outline: none;
}
.rdw-image-modal-alt-lbl {
font-size: 12px;
}
.rdw-image-modal-size {
align-items: center;
display: flex;
margin: 8px 0;
justify-content: space-between;
}
.rdw-image-modal-size-input {
width: 40%;
height: 20px;
border: 1px solid #F1F1F1;
border-radius: 2px;
font-size: 12px;
}
.rdw-image-modal-size-input:focus {
outline: none;
}
.rdw-image-mandatory-sign {
color: red;
margin-left: 3px;
margin-right: 3px;
}
.rdw-remove-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
position: relative;
flex-wrap: wrap
}
.rdw-history-wrapper {
display: flex;
align-items: center;
margin-bottom: 6px;
flex-wrap: wrap
}
.rdw-history-dropdownoption {
height: 40px;
display: flex;
justify-content: center;
}
.rdw-history-dropdown {
width: 50px;
}
.rdw-link-decorator-wrapper {
position: relative;
}
.rdw-link-decorator-icon {
position: absolute;
left: 40%;
top: 0;
cursor: pointer;
background-color: white;
}
.rdw-mention-link {
text-decoration: none;
color: #1236ff;
background-color: #f0fbff;
padding: 1px 2px;
border-radius: 2px;
}
.rdw-suggestion-wrapper {
position: relative;
}
.rdw-suggestion-dropdown {
position: absolute;
display: flex;
flex-direction: column;
border: 1px solid #F1F1F1;
min-width: 100px;
max-height: 150px;
overflow: auto;
background: white;
z-index: 100;
}
.rdw-suggestion-option {
padding: 7px 5px;
border-bottom: 1px solid #f1f1f1;
}
.rdw-suggestion-option-active {
background-color: #F1F1F1;
}
.rdw-hashtag-link {
text-decoration: none;
color: #1236ff;
background-color: #f0fbff;
padding: 1px 2px;
border-radius: 2px;
}
.rdw-image-alignment-options-popup {
position: absolute;
background: white;
display: flex;
padding: 5px 2px;
border-radius: 2px;
border: 1px solid #F1F1F1;
width: 105px;
cursor: pointer;
z-index: 100;
}
.rdw-alignment-option-left {
justify-content: flex-start;
}
.rdw-image-alignment-option {
height: 15px;
width: 15px;
min-width: 15px;
}
.rdw-image-alignment {
position: relative;
}
.rdw-image-imagewrapper {
position: relative;
}
.rdw-image-center {
display: flex;
justify-content: center;
}
.rdw-image-left {
display: flex;
}
.rdw-image-right {
display: flex;
justify-content: flex-end;
}
.rdw-image-alignment-options-popup-right {
right: 0;
}
.rdw-editor-main {
height: 100%;
overflow: auto;
box-sizing: border-box;
}
.rdw-editor-toolbar {
padding: 6px 5px 0;
border-radius: 2px;
border: 1px solid #F1F1F1;
display: flex;
justify-content: flex-start;
background: white;
flex-wrap: wrap;
font-size: 15px;
margin-bottom: 5px;
user-select: none;
}
.public-DraftStyleDefault-block {
margin: 1em 0;
}
.rdw-editor-wrapper:focus {
outline: none;
}
.rdw-editor-wrapper {
box-sizing: content-box;
}
.rdw-editor-main blockquote {
border-left: 5px solid #f1f1f1;
padding-left: 5px;
}
.rdw-editor-main pre {
background: #f1f1f1;
border-radius: 3px;
padding: 1px 10px;
}
/**
* Draft v0.9.1
*
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
.DraftEditor-editorContainer,.DraftEditor-root,.public-DraftEditor-content{height:inherit;text-align:initial}.public-DraftEditor-content[contenteditable=true]{-webkit-user-modify:read-write-plaintext-only}.DraftEditor-root{position:relative}.DraftEditor-editorContainer{background-color:rgba(255,255,255,0);border-left:.1px solid transparent;position:relative;z-index:1}.public-DraftEditor-block{position:relative}.DraftEditor-alignLeft .public-DraftStyleDefault-block{text-align:left}.DraftEditor-alignLeft .public-DraftEditorPlaceholder-root{left:0;text-align:left}.DraftEditor-alignCenter .public-DraftStyleDefault-block{text-align:center}.DraftEditor-alignCenter .public-DraftEditorPlaceholder-root{margin:0 auto;text-align:center;width:100%}.DraftEditor-alignRight .public-DraftStyleDefault-block{text-align:right}.DraftEditor-alignRight .public-DraftEditorPlaceholder-root{right:0;text-align:right}.public-DraftEditorPlaceholder-root{color:#9197a3;position:absolute;z-index:0}.public-DraftEditorPlaceholder-hasFocus{color:#bdc1c9}.DraftEditorPlaceholder-hidden{display:none}.public-DraftStyleDefault-block{position:relative;white-space:pre-wrap}.public-DraftStyleDefault-ltr{direction:ltr;text-align:left}.public-DraftStyleDefault-rtl{direction:rtl;text-align:right}.public-DraftStyleDefault-listLTR{direction:ltr}.public-DraftStyleDefault-listRTL{direction:rtl}.public-DraftStyleDefault-ol,.public-DraftStyleDefault-ul{margin:16px 0;padding:0}.public-DraftStyleDefault-depth0.public-DraftStyleDefault-listLTR{margin-left:1.5em}.public-DraftStyleDefault-depth0.public-DraftStyleDefault-listRTL{margin-right:1.5em}.public-DraftStyleDefault-depth1.public-DraftStyleDefault-listLTR{margin-left:3em}.public-DraftStyleDefault-depth1.public-DraftStyleDefault-listRTL{margin-right:3em}.public-DraftStyleDefault-depth2.public-DraftStyleDefault-listLTR{margin-left:4.5em}.public-DraftStyleDefault-depth2.public-DraftStyleDefault-listRTL{margin-right:4.5em}.public-DraftStyleDefault-depth3.public-DraftStyleDefault-listLTR{margin-left:6em}.public-DraftStyleDefault-depth3.public-DraftStyleDefault-listRTL{margin-right:6em}.public-DraftStyleDefault-depth4.public-DraftStyleDefault-listLTR{margin-left:7.5em}.public-DraftStyleDefault-depth4.public-DraftStyleDefault-listRTL{margin-right:7.5em}.public-DraftStyleDefault-unorderedListItem{list-style-type:square;position:relative}.public-DraftStyleDefault-unorderedListItem.public-DraftStyleDefault-depth0{list-style-type:disc}.public-DraftStyleDefault-unorderedListItem.public-DraftStyleDefault-depth1{list-style-type:circle}.public-DraftStyleDefault-orderedListItem{list-style-type:none;position:relative}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-listLTR:before{left:-36px;position:absolute;text-align:right;width:30px}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-listRTL:before{position:absolute;right:-36px;text-align:left;width:30px}.public-DraftStyleDefault-orderedListItem:before{content:counter(ol0) ". ";counter-increment:ol0}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth1:before{content:counter(ol1) ". ";counter-increment:ol1}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth2:before{content:counter(ol2) ". ";counter-increment:ol2}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth3:before{content:counter(ol3) ". ";counter-increment:ol3}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth4:before{content:counter(ol4) ". ";counter-increment:ol4}.public-DraftStyleDefault-depth0.public-DraftStyleDefault-reset{counter-reset:ol0}.public-DraftStyleDefault-depth1.public-DraftStyleDefault-reset{counter-reset:ol1}.public-DraftStyleDefault-depth2.public-DraftStyleDefault-reset{counter-reset:ol2}.public-DraftStyleDefault-depth3.public-DraftStyleDefault-reset{counter-reset:ol3}.public-DraftStyleDefault-depth4.public-DraftStyleDefault-reset{counter-reset:ol4}
/*# sourceMappingURL=react-draft-wysiwyg.css.map*/
</style>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/dashboard/build/index.js"></script> <script type="module" src="/dashboard/build/index.js"></script>
<script>var global = global || window;</script>
</body> </body>
</html> </html>