├── .meteor ├── .gitignore ├── release ├── platforms ├── .id ├── .finished-upgraders ├── packages └── versions ├── server ├── configurations.js ├── migrations.js ├── startup.js ├── registrations.js └── cron.js ├── client ├── layouts │ ├── pages.html │ ├── auth.html │ └── panel.html ├── styles │ ├── paper-dashboard │ │ ├── cards │ │ │ ├── _card-map.scss │ │ │ ├── _card-plain.scss │ │ │ ├── _card-chart.scss │ │ │ ├── _card-stats.scss │ │ │ └── _card-user.scss │ │ ├── mixins │ │ │ ├── _cards.scss │ │ │ ├── _transparency.scss │ │ │ ├── _page-header.scss │ │ │ ├── _dropdown.scss │ │ │ └── _buttons.scss │ │ ├── _images.scss │ │ ├── _mixins.scss │ │ ├── _footers.scss │ │ ├── _animated-buttons.scss │ │ ├── _alerts.scss │ │ ├── _page-header.scss │ │ ├── _misc.scss │ │ ├── _cards.scss │ │ ├── _tables.scss │ │ ├── _typography.scss │ │ ├── _checkboxes-radio.scss │ │ └── plugins │ │ │ └── _plugin-animate-bootstrap-notify.scss │ └── paper-dashboard.scss ├── partials │ ├── preloader.html │ ├── checkbox.html │ ├── panel │ │ ├── modalAddNewContact.js │ │ ├── footer.js │ │ ├── sidebar.js │ │ ├── footer.html │ │ ├── header.html │ │ ├── sidebar.html │ │ └── modalAddNewContact.html │ ├── emailPreview.js │ ├── buttonsForPromising.html │ ├── buttonsForCampains.html │ ├── buttonsForCrm.html │ ├── buttonsForCrm.js │ ├── buttonsForCampains.js │ ├── buttonsForPromising.js │ └── emailPreview.html ├── helpers │ ├── ui.js │ ├── loader.js │ ├── debug-log.js │ ├── tooltips.js │ ├── sessions.js │ ├── server-method.js │ └── helpers.js ├── pages │ ├── panel │ │ ├── campaigns │ │ │ ├── noCampaigns.js │ │ │ ├── noCampaigns.html │ │ │ ├── previewCampain.html │ │ │ ├── previewCampain.js │ │ │ ├── campaigns.html │ │ │ ├── newCampain.html │ │ │ ├── campaigns.js │ │ │ └── newCampain.js │ │ ├── integrations │ │ │ ├── integrations.js │ │ │ └── integrations.html │ │ ├── all-sended-emails │ │ │ ├── all-sended-emails.js │ │ │ └── all-sended-emails.html │ │ ├── promising-prospects │ │ │ ├── promising-prospects.html │ │ │ └── promising-prospects.js │ │ ├── crm │ │ │ └── crm.html │ │ ├── member │ │ │ └── add.html │ │ ├── gdpr │ │ │ ├── gdpr.js │ │ │ └── gdpr.html │ │ ├── settings │ │ │ ├── settings.js │ │ │ └── settings.html │ │ └── dashboard │ │ │ ├── dashboard.js │ │ │ └── dashboard.html │ └── auth │ │ ├── password-reset │ │ ├── password-reset.html │ │ └── password-reset.js │ │ ├── password-set │ │ ├── password-set.html │ │ └── password-set.js │ │ ├── login │ │ ├── login.html │ │ └── login.js │ │ └── register │ │ ├── register.html │ │ └── register.js ├── client.html └── client.js ├── public ├── favicon.ico ├── apple-icon.png ├── images │ ├── cib.png │ └── img │ │ ├── 1.png │ │ └── cib.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-96x96.png ├── ms-icon-144x144.png ├── ms-icon-150x150.png ├── ms-icon-310x310.png ├── ms-icon-70x70.png ├── apple-icon-57x57.png ├── apple-icon-60x60.png ├── apple-icon-72x72.png ├── apple-icon-76x76.png ├── android-icon-144x144.png ├── android-icon-192x192.png ├── android-icon-36x36.png ├── android-icon-48x48.png ├── android-icon-72x72.png ├── android-icon-96x96.png ├── apple-icon-114x114.png ├── apple-icon-120x120.png ├── apple-icon-144x144.png ├── apple-icon-152x152.png ├── apple-icon-180x180.png ├── fonts │ ├── nucleo-icons.eot │ ├── nucleo-icons.ttf │ ├── nucleo-icons.woff │ └── nucleo-icons.woff2 ├── apple-icon-precomposed.png ├── placeholder │ └── icons │ │ ├── play.svg │ │ ├── facebook-f.svg │ │ ├── white │ │ ├── facebook-f.svg │ │ ├── google.svg │ │ ├── clock-o.svg │ │ └── twitter.svg │ │ ├── chevron-left.svg │ │ ├── chevron-right.svg │ │ ├── level-up.svg │ │ ├── level-down.svg │ │ ├── map-marker.svg │ │ ├── check.svg │ │ ├── facebook-official.svg │ │ ├── folder-o.svg │ │ ├── close.svg │ │ ├── chevron-circle-down.svg │ │ ├── folder-open.svg │ │ ├── google.svg │ │ ├── plane.svg │ │ ├── share-alt.svg │ │ ├── clock-o.svg │ │ ├── play-circle-o.svg │ │ ├── twitter.svg │ │ ├── apple.svg │ │ ├── calendar-o.svg │ │ ├── chrome.svg │ │ ├── envelope.svg │ │ ├── linkedin-in.svg │ │ ├── heart-o.svg │ │ ├── quote-left.svg │ │ ├── file-text-o.svg │ │ ├── envelope-o.svg │ │ ├── phone.svg │ │ ├── internet-explorer.svg │ │ ├── firefox.svg │ │ ├── users.svg │ │ ├── instagram.svg │ │ ├── safari.svg │ │ └── unicorn.svg ├── browserconfig.xml └── manifest.json ├── lib ├── collections │ ├── _config.js │ ├── disallowed-domains.js │ ├── disallowed-emails.js │ ├── statistics.js │ ├── settings.js │ ├── users.js │ └── sent.js ├── helpers.js └── routes │ └── server.js ├── settings.json ├── .github └── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md ├── .gitignore ├── project-tap.i18n ├── imports ├── checkboxes.js └── dc.js ├── private └── README.md ├── .eslintrc.json ├── LICENSE ├── package.json └── CONTRIBUTING.md /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /server/configurations.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@2.6 2 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /client/layouts/pages.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/cards/_card-map.scss: -------------------------------------------------------------------------------- 1 | .map{ 2 | height: 500px; 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon.png -------------------------------------------------------------------------------- /public/images/cib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/images/cib.png -------------------------------------------------------------------------------- /server/migrations.js: -------------------------------------------------------------------------------- 1 | Meteor.startup(function () { 2 | Migrations.migrateTo("latest"); 3 | }); 4 | -------------------------------------------------------------------------------- /public/images/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/images/img/1.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/favicon-96x96.png -------------------------------------------------------------------------------- /public/images/img/cib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/images/img/cib.png -------------------------------------------------------------------------------- /public/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/ms-icon-144x144.png -------------------------------------------------------------------------------- /public/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/ms-icon-150x150.png -------------------------------------------------------------------------------- /public/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/ms-icon-310x310.png -------------------------------------------------------------------------------- /public/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/ms-icon-70x70.png -------------------------------------------------------------------------------- /public/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-57x57.png -------------------------------------------------------------------------------- /public/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-60x60.png -------------------------------------------------------------------------------- /public/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-72x72.png -------------------------------------------------------------------------------- /public/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-76x76.png -------------------------------------------------------------------------------- /client/partials/preloader.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/collections/_config.js: -------------------------------------------------------------------------------- 1 | import SimpleSchema from 'simpl-schema'; 2 | 3 | SimpleSchema.extendOptions(['denyInsert']); -------------------------------------------------------------------------------- /public/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/android-icon-144x144.png -------------------------------------------------------------------------------- /public/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/android-icon-192x192.png -------------------------------------------------------------------------------- /public/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/android-icon-36x36.png -------------------------------------------------------------------------------- /public/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/android-icon-48x48.png -------------------------------------------------------------------------------- /public/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/android-icon-72x72.png -------------------------------------------------------------------------------- /public/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/android-icon-96x96.png -------------------------------------------------------------------------------- /public/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-114x114.png -------------------------------------------------------------------------------- /public/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-120x120.png -------------------------------------------------------------------------------- /public/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-144x144.png -------------------------------------------------------------------------------- /public/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-152x152.png -------------------------------------------------------------------------------- /public/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-180x180.png -------------------------------------------------------------------------------- /public/fonts/nucleo-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/fonts/nucleo-icons.eot -------------------------------------------------------------------------------- /public/fonts/nucleo-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/fonts/nucleo-icons.ttf -------------------------------------------------------------------------------- /public/fonts/nucleo-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/fonts/nucleo-icons.woff -------------------------------------------------------------------------------- /public/fonts/nucleo-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/fonts/nucleo-icons.woff2 -------------------------------------------------------------------------------- /server/startup.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | import { Meteor } from "meteor/meteor"; 3 | 4 | Meteor.startup(function () {}); 5 | -------------------------------------------------------------------------------- /client/partials/checkbox.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catin-black/meteor-emails/HEAD/public/apple-icon-precomposed.png -------------------------------------------------------------------------------- /lib/helpers.js: -------------------------------------------------------------------------------- 1 | export function capitalizeFirstLetter(string) { 2 | return string.charAt(0).toUpperCase() + string.slice(1); 3 | } -------------------------------------------------------------------------------- /settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": 3 | { 4 | 5 | }, 6 | "private": 7 | { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/layouts/auth.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/mixins/_cards.scss: -------------------------------------------------------------------------------- 1 | @mixin icon-color($color) { 2 | box-shadow: 0px 9px 30px -6px $color; 3 | color: $color; 4 | } 5 | -------------------------------------------------------------------------------- /client/helpers/ui.js: -------------------------------------------------------------------------------- 1 | UI.registerHelper("isActiveRoute", function (params) { 2 | return Router.current().route.getName() === params ? "active" : ""; 3 | }); 4 | -------------------------------------------------------------------------------- /client/pages/panel/campaigns/noCampaigns.js: -------------------------------------------------------------------------------- 1 | Template.noCampaigns.helpers({ 2 | campaigns: function () { 3 | return SendInformation.find().fetch(); 4 | }, 5 | }); 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | /meteor-emails.sublime-project 4 | /meteor-emails.sublime-workspace 5 | /emails.sublime-project 6 | /emails.sublime-workspace 7 | .vscode/ -------------------------------------------------------------------------------- /project-tap.i18n: -------------------------------------------------------------------------------- 1 | { 2 | "helper_name": "_", 3 | "supported_languages": ["en"], 4 | "i18n_files_route": "/i18n", 5 | "cdn_path": null, 6 | "preloaded_langs": ["en"] 7 | } -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_images.scss: -------------------------------------------------------------------------------- 1 | img{ 2 | max-width: 100%; 3 | border-radius: $border-radius-small; 4 | } 5 | .img-raised{ 6 | box-shadow: $box-shadow-raised; 7 | } 8 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/mixins/_transparency.scss: -------------------------------------------------------------------------------- 1 | // Opacity 2 | 3 | @mixin opacity($opacity) { 4 | opacity: $opacity; 5 | // IE8 filter 6 | $opacity-ie: ($opacity * 100); 7 | filter: #{alpha(opacity=$opacity-ie)}; 8 | } 9 | -------------------------------------------------------------------------------- /public/placeholder/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_mixins.scss: -------------------------------------------------------------------------------- 1 | //Components 2 | @import "mixins/buttons"; 3 | @import "mixins/vendor-prefixes"; 4 | @import "mixins/inputs"; 5 | @import "mixins/page-header"; 6 | @import "mixins/dropdown"; 7 | @import "mixins/cards"; 8 | @import "mixins/transparency"; 9 | -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /client/partials/panel/modalAddNewContact.js: -------------------------------------------------------------------------------- 1 | Template.modalAddNewContact.onRendered(function() { 2 | 3 | }); 4 | 5 | Template.modalAddNewContact.helpers({ 6 | 7 | }); 8 | 9 | Template.modalAddNewContact.events({ 10 | 11 | }); 12 | 13 | Template.modalAddNewContact.onDestroyed(function() { 14 | 15 | }); -------------------------------------------------------------------------------- /imports/checkboxes.js: -------------------------------------------------------------------------------- 1 | function checkTable(event) { 2 | const checkbox = $(event.target); 3 | const table = checkbox.parents("table").first(); 4 | const checkboxes = table.find("tbody tr td input[type=checkbox]"); 5 | checkboxes.prop("checked", checkbox.is(":checked")); 6 | } 7 | 8 | export { checkTable }; 9 | -------------------------------------------------------------------------------- /public/placeholder/icons/facebook-f.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/white/facebook-f.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/partials/panel/footer.js: -------------------------------------------------------------------------------- 1 | Template.partialPanelFooter.onRendered(function () {}); 2 | 3 | Template.partialPanelFooter.helpers({ 4 | currentYear: function () { 5 | return new Date().getFullYear(); 6 | }, 7 | }); 8 | 9 | Template.partialPanelFooter.events({}); 10 | 11 | Template.partialPanelFooter.onDestroyed(function () {}); 12 | -------------------------------------------------------------------------------- /public/placeholder/icons/level-up.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | z0i5uzyw9viv6.wy0jktoufgcp 8 | -------------------------------------------------------------------------------- /public/placeholder/icons/level-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /private/README.md: -------------------------------------------------------------------------------- 1 | **private folder** 2 | 3 | All files inside a top-level directory called `private/` are only accessible from server code and can be loaded via the [`Assets`](http://docs.meteor.com/#/full/assets_getText) API. This can be used for private data files and any files that are in your project directory that you don't want to be accessible from the outside. 4 | -------------------------------------------------------------------------------- /public/placeholder/icons/map-marker.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /client/helpers/loader.js: -------------------------------------------------------------------------------- 1 | import { SessionDataIsLoading } from "/client/helpers/sessions"; 2 | 3 | export class Loader { 4 | constructor() {} 5 | 6 | show() { 7 | $("body").append('
'); 8 | SessionDataIsLoading.set(true); 9 | } 10 | 11 | hide() { 12 | $("#preloader").remove(); 13 | SessionDataIsLoading.set(false); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/layouts/panel.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/placeholder/icons/facebook-official.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/partials/emailPreview.js: -------------------------------------------------------------------------------- 1 | Template.emailPreview.helpers({ 2 | subject: function () { 3 | return Messages.findOne({ _id: Template.instance().data.item._id }).subject; 4 | }, 5 | }); 6 | Template.emailPreview.events({}); 7 | 8 | Template.emailPreview.onRendered(function () {}); 9 | 10 | Template.emailPreview.onCreated(function () {}); 11 | 12 | Template.emailPreview.onDestroyed(function () {}); 13 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/cards/_card-plain.scss: -------------------------------------------------------------------------------- 1 | 2 | .card-plain{ 3 | background: transparent; 4 | box-shadow: none; 5 | 6 | .card-header, 7 | .card-footer{ 8 | margin-left: 0; 9 | margin-right: 0; 10 | background-color: transparent; 11 | } 12 | 13 | &:not(.card-subcategories).card-body{ 14 | padding-left: 0; 15 | padding-right: 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /public/placeholder/icons/folder-o.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/pages/panel/integrations/integrations.js: -------------------------------------------------------------------------------- 1 | import { Meteor } from "meteor/meteor"; 2 | 3 | Template.pagePanelIntegrations.onRendered(function () {}); 4 | 5 | Template.pagePanelIntegrations.helpers({ 6 | userSettings: function () { 7 | return Settings.findOne({ 8 | userId: Meteor.userId(), 9 | }); 10 | }, 11 | }); 12 | 13 | Template.pagePanelIntegrations.events({}); 14 | 15 | Template.pagePanelIntegrations.onDestroyed(function () {}); 16 | -------------------------------------------------------------------------------- /client/pages/panel/all-sended-emails/all-sended-emails.js: -------------------------------------------------------------------------------- 1 | import { tooltips } from "/client/helpers/tooltips"; 2 | Template.pagePanelAllSendedEmails.helpers({}); 3 | 4 | Template.pagePanelAllSendedEmails.events({}); 5 | Template.pagePanelAllSendedEmails.onRendered(function () { 6 | $(".table thead").addClass("text-primary"); 7 | const template = this; 8 | tooltips(template); 9 | }); 10 | 11 | Template.pagePanelAllSendedEmails.onDestroyed(function () {}); 12 | -------------------------------------------------------------------------------- /public/placeholder/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /public/placeholder/icons/chevron-circle-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/folder-open.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/google.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/plane.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/white/google.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/share-alt.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/helpers/debug-log.js: -------------------------------------------------------------------------------- 1 | export function startedDebugLog(textToLog) { 2 | debugLog("➡️ - " + textToLog, "color: #f1c40f"); 3 | } 4 | 5 | export function successDebugLog(textToLog) { 6 | debugLog("✅ - " + textToLog, "color: #2ecc71"); 7 | } 8 | 9 | export function errorDebugLog(textToLog) { 10 | debugLog("❌ - " + textToLog, "color: #e74c3c"); 11 | } 12 | 13 | function debugLog(textToLog, style = "color: #9d9d9d") { 14 | if (Meteor.isDevelopment) { 15 | console.log("%c" + textToLog, style); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/collections/disallowed-domains.js: -------------------------------------------------------------------------------- 1 | import { Mongo } from 'meteor/mongo'; 2 | import SimpleSchema from 'simpl-schema'; 3 | 4 | DisallowedDomains = new Mongo.Collection('DisallowedDomains'); 5 | 6 | let DisallowedDomainsSchema = {}; 7 | 8 | DisallowedDomainsSchema.NotAllowed = new SimpleSchema({ 9 | userId: { 10 | type: String, 11 | }, 12 | domain: { 13 | type: String, 14 | // optional: false, 15 | } 16 | }); 17 | 18 | DisallowedDomains.attachSchema(DisallowedDomainsSchema.NotAllowed); -------------------------------------------------------------------------------- /public/placeholder/icons/clock-o.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/collections/disallowed-emails.js: -------------------------------------------------------------------------------- 1 | import { Mongo } from 'meteor/mongo'; 2 | import SimpleSchema from 'simpl-schema'; 3 | 4 | DisallowedEmails = new Mongo.Collection('DisallowedEmails'); 5 | 6 | let DisallowedEmailsSchema = {}; 7 | 8 | DisallowedEmailsSchema.NotAllowed = new SimpleSchema({ 9 | userId: { 10 | type: String, 11 | }, 12 | email: { 13 | type: String, 14 | regEx: SimpleSchema.RegEx.Email 15 | } 16 | }); 17 | 18 | DisallowedEmails.attachSchema(DisallowedEmailsSchema.NotAllowed); -------------------------------------------------------------------------------- /public/placeholder/icons/play-circle-o.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/mixins/_page-header.scss: -------------------------------------------------------------------------------- 1 | @mixin linear-gradient($color1, $color2){ 2 | background: $color1; /* For browsers that do not support gradients */ 3 | background: -webkit-linear-gradient(90deg, $color1 , $color2); /* For Safari 5.1 to 6.0 */ 4 | background: -o-linear-gradient(90deg, $color1, $color2); /* For Opera 11.1 to 12.0 */ 5 | background: -moz-linear-gradient(90deg, $color1, $color2); /* For Firefox 3.6 to 15 */ 6 | background: linear-gradient(0deg, $color1 , $color2); /* Standard syntax */ 7 | } 8 | -------------------------------------------------------------------------------- /lib/routes/server.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | Router.route('/images/1/:img', function() { 3 | const self = this; 4 | const file = fs.readFileSync(Assets.absoluteFilePath('1.png')); 5 | var headers = { 6 | 'Content-type': 'image/png', 7 | 'Content-Disposition': "attachment; filename=" + "1.png" 8 | }; 9 | 10 | self.response.writeHead(200, headers); 11 | Meteor.call('trackOpens', self.params.query.track, function(e, r) { 12 | return self.response.end(file); 13 | }); 14 | }, { where: "server" }); -------------------------------------------------------------------------------- /public/placeholder/icons/white/clock-o.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/white/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/apple.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/calendar-o.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/chrome.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/envelope.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["meteor"], 3 | "extends": ["eslint:recommended", "plugin:meteor/recommended"], 4 | "env": { 5 | "meteor": true, 6 | "browser": true 7 | }, 8 | "globals": { 9 | "TAPi18n": "writable", 10 | "DisallowedDomains": "writable", 11 | "Settings": "writable", 12 | "Members": "writable", 13 | "AutoForm": "writable", 14 | "SendInformation": "writable", 15 | "DisallowedEmails": "writable", 16 | "Migrations": "writable", 17 | "Statistics": "writable", 18 | "SyncedCron": "writable", 19 | "Messages": "writable" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /public/placeholder/icons/linkedin-in.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/pages/panel/campaigns/noCampaigns.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/placeholder/icons/heart-o.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/quote-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/mixins/_dropdown.scss: -------------------------------------------------------------------------------- 1 | @mixin dropdown-colors($brand-color, $dropdown-header-color, $dropdown-color, $background-color ) { 2 | background-color: $brand-color; 3 | 4 | &:before{ 5 | color: $brand-color; 6 | } 7 | 8 | .dropdown-header:not([href]):not([tabindex]){ 9 | color: $dropdown-header-color; 10 | } 11 | 12 | .dropdown-item{ 13 | color: $dropdown-color; 14 | 15 | &:hover, 16 | &:focus{ 17 | background-color: $background-color; 18 | } 19 | } 20 | 21 | .dropdown-divider{ 22 | background-color: $background-color; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /public/placeholder/icons/file-text-o.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/pages/panel/campaigns/previewCampain.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | 1.3.0-split-minifiers-package 14 | 1.4.0-remove-old-dev-bundle-link 15 | 1.4.1-add-shell-server-package 16 | 1.4.3-split-account-service-packages 17 | 1.5-add-dynamic-import-package 18 | 1.7-split-underscore-from-meteor-base 19 | 1.8.3-split-jquery-from-blaze 20 | -------------------------------------------------------------------------------- /public/placeholder/icons/envelope-o.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/partials/buttonsForPromising.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/partials/buttonsForCampains.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/placeholder/icons/phone.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature request]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /client/pages/panel/all-sended-emails/all-sended-emails.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/pages/panel/campaigns/previewCampain.js: -------------------------------------------------------------------------------- 1 | import { tooltips } from "/client/helpers/tooltips"; 2 | 3 | Template.previewCampaign.helpers({ 4 | info: function () { 5 | return Template.instance().sendInfo.get(); 6 | }, 7 | selector: function () { 8 | return { 9 | campaignId: Router.current().params._id, 10 | }; 11 | }, 12 | }); 13 | 14 | Template.previewCampaign.events({}); 15 | Template.previewCampaign.onRendered(function () { 16 | $(".table thead").addClass("text-primary"); 17 | const template = this; 18 | tooltips(template); 19 | }); 20 | 21 | Template.previewCampaign.onCreated(function () { 22 | this.sendInfo = new ReactiveVar(SendInformation.findOne()); 23 | }); 24 | 25 | Template.previewCampaign.onDestroyed(function () {}); 26 | -------------------------------------------------------------------------------- /public/placeholder/icons/internet-explorer.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/placeholder/icons/firefox.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/helpers/tooltips.js: -------------------------------------------------------------------------------- 1 | export function tooltips(template) { 2 | $(".dataTables_wrapper") 3 | .find("table") 4 | .on("length.dt", function () { 5 | ckTool(template); 6 | }) 7 | .on("order.dt", function () { 8 | ckTool(template); 9 | }) 10 | .on("search.dt", function () { 11 | ckTool(template); 12 | }) 13 | .on("page.dt", function () { 14 | ckTool(template); 15 | }); 16 | } 17 | 18 | function ckTool(template) { 19 | template.$(".text-truncate span").tooltip({ 20 | placement: "left", 21 | }); 22 | template.$(".mail span").tooltip({ 23 | placement: "top", 24 | }); 25 | $(".dataTables_wrapper").find("table th:last-child").addClass("text-right"); 26 | $(".dataTables_wrapper") 27 | .find("table tr td button.preview") 28 | .addClass("pull-right"); 29 | } 30 | -------------------------------------------------------------------------------- /client/partials/panel/sidebar.js: -------------------------------------------------------------------------------- 1 | import { 2 | showConfirmDialog, 3 | showSuccessNotification, 4 | } from "../../helpers/helpers"; 5 | 6 | Template.partialPanelSidebar.onRendered(function () {}); 7 | 8 | Template.partialPanelSidebar.helpers({}); 9 | 10 | Template.partialPanelSidebar.events({ 11 | "click .logout": function (event) { 12 | event.preventDefault(); 13 | 14 | showConfirmDialog({ 15 | title: TAPi18n.__("logout.txt"), 16 | confirmCallback: function () { 17 | Meteor.logout(function () { 18 | showSuccessNotification(TAPi18n.__("logout.done")); 19 | Router.go("authLogin"); 20 | }); 21 | }, 22 | cancelCallback: function () { 23 | // this.redirect(window.history.go(-1)); 24 | }, 25 | }); 26 | }, 27 | }); 28 | 29 | Template.partialPanelSidebar.onDestroyed(function () {}); 30 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/cards/_card-chart.scss: -------------------------------------------------------------------------------- 1 | .card-chart { 2 | .card-header{ 3 | .card-title{ 4 | margin-top: 10px; 5 | margin-bottom: 0; 6 | } 7 | .card-category{ 8 | margin-bottom: 5px; 9 | } 10 | } 11 | 12 | .table{ 13 | margin-bottom: 0; 14 | 15 | td{ 16 | border-top: none; 17 | border-bottom: 1px solid #e9ecef; 18 | } 19 | } 20 | 21 | .card-progress { 22 | margin-top: 30px; 23 | } 24 | 25 | .chart-area { 26 | height: 190px; 27 | width: calc(100% + 30px); 28 | margin-left: -15px; 29 | margin-right: -15px; 30 | } 31 | .card-footer { 32 | margin-top: 15px; 33 | 34 | .stats{ 35 | color: $dark-gray; 36 | } 37 | } 38 | 39 | .dropdown{ 40 | position: absolute; 41 | right: 20px; 42 | top: 20px; 43 | 44 | .btn{ 45 | margin: 0; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /client/pages/auth/password-reset/password-reset.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/pages/auth/password-set/password-set.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/partials/buttonsForCrm.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/placeholder/icons/users.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/pages/auth/password-reset/password-reset.js: -------------------------------------------------------------------------------- 1 | import { callServerMethod } from "/client/helpers/server-method"; 2 | import { 3 | showDangerNotification, 4 | showSuccessNotification, 5 | } from "/client/helpers/helpers"; 6 | Template.pageAuthPasswordReset.onRendered(function () {}); 7 | 8 | Template.pageAuthPasswordReset.helpers({}); 9 | 10 | Template.pageAuthPasswordReset.events({ 11 | "submit #password-reset-form": function (event) { 12 | event.preventDefault(); 13 | 14 | callServerMethod({ 15 | methodName: "passwordReset", 16 | params: { 17 | email: $(event.currentTarget).find("#email").val(), 18 | }, 19 | resultCallback: function (result) { 20 | showSuccessNotification( 21 | TAPi18n.__("resetPassword.notification.success") 22 | ); 23 | }, 24 | errorCallback: function (error) { 25 | showDangerNotification(error.reason); 26 | }, 27 | }); 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /imports/dc.js: -------------------------------------------------------------------------------- 1 | function displayConfirmation(title, text, callback) { 2 | /** 3 | * By specifying an object for buttons, 4 | * you can set exactly as many buttons as you like, 5 | * and specify the value that they resolve to when they're clicked! 6 | * @type {Object} 7 | */ 8 | const buttons = { 9 | cancel: "Cancel", 10 | ok: { 11 | text: "Ok", 12 | value: "ok", 13 | }, 14 | }; 15 | swal({ 16 | title: title, 17 | icon: "info", 18 | text: text, 19 | html: true, 20 | buttons: buttons, 21 | }).then(function (value) { 22 | switch (value) { 23 | case "ok": 24 | $("body").append( 25 | '
Please wait. I am doing my job./span>
' 26 | ); 27 | callback(); 28 | break; 29 | default: 30 | break; 31 | } 32 | }); 33 | } 34 | 35 | export { displayConfirmation }; 36 | -------------------------------------------------------------------------------- /public/placeholder/icons/instagram.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/pages/auth/password-set/password-set.js: -------------------------------------------------------------------------------- 1 | import { 2 | showDangerNotification, 3 | showSuccessNotification, 4 | } from "/client/helpers/helpers"; 5 | import { SessionAuthorizationPasswordToken } from "../../../helpers/sessions"; 6 | Template.pageAuthPasswordSet.onRendered(function () {}); 7 | 8 | Template.pageAuthPasswordSet.helpers({}); 9 | 10 | Template.pageAuthPasswordSet.events({ 11 | "submit #password-set-form": function (event) { 12 | event.preventDefault(); 13 | 14 | const password = $(event.currentTarget).find("#password").val(); 15 | const token = SessionAuthorizationPasswordToken.get(); 16 | 17 | if (token && password) { 18 | Accounts.resetPassword(token, password, function (err) { 19 | showDangerNotification(err); 20 | }); 21 | showSuccessNotification(TAPi18n.__("setPassword.notification.success")); 22 | } else { 23 | showDangerNotification(TAPi18n.__("setPassword.notification.error")); 24 | } 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/cards/_card-stats.scss: -------------------------------------------------------------------------------- 1 | %card-stats{ 2 | hr{ 3 | margin: 5px 15px; 4 | } 5 | } 6 | 7 | 8 | .card-stats{ 9 | .card-body{ 10 | padding: 15px 15px 0px; 11 | 12 | .numbers{ 13 | text-align: right; 14 | font-size: 2em; 15 | 16 | p{ 17 | margin-bottom: 0; 18 | } 19 | .card-category { 20 | color: $dark-gray; 21 | font-size: 16px; 22 | line-height: 1.4em; 23 | } 24 | } 25 | } 26 | .card-footer{ 27 | padding: 0px 15px 15px; 28 | 29 | .stats{ 30 | color: $dark-gray; 31 | } 32 | 33 | hr{ 34 | margin-top: 10px; 35 | margin-bottom: 15px; 36 | } 37 | } 38 | .icon-big { 39 | font-size: 3em; 40 | min-height: 64px; 41 | 42 | i{ 43 | line-height: 59px; 44 | } 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /client/partials/buttonsForCrm.js: -------------------------------------------------------------------------------- 1 | import { callServerMethod } from "/client/helpers/server-method"; 2 | import { 3 | showConfirmDialog, 4 | showSuccessNotification, 5 | } from "/client/helpers/helpers"; 6 | 7 | Template.buttonsForCrm.helpers({}); 8 | 9 | Template.buttonsForCrm.events({ 10 | "click button.deleteSingleMember": function (event, templateInstance) { 11 | showConfirmDialog({ 12 | title: TAPi18n.__("confirm.remove"), 13 | confirmCallback: function () { 14 | callServerMethod({ 15 | methodName: "removeMembers", 16 | params: { 17 | members: [templateInstance.data.item._id], 18 | }, 19 | resultCallback: function () { 20 | showSuccessNotification(TAPi18n.__("alerts.done")); 21 | }, 22 | }); 23 | }, 24 | cancelCallback: function () {}, 25 | }); 26 | }, 27 | }); 28 | 29 | Template.buttonsForCrm.onRendered(function () {}); 30 | 31 | Template.buttonsForCrm.onDestroyed(function () {}); 32 | -------------------------------------------------------------------------------- /client/partials/buttonsForCampains.js: -------------------------------------------------------------------------------- 1 | import { callServerMethod } from "/client/helpers/server-method"; 2 | import { 3 | showConfirmDialog, 4 | showSuccessNotification, 5 | } from "/client/helpers/helpers"; 6 | 7 | Template.buttonsForCampains.helpers({}); 8 | Template.buttonsForCampains.events({ 9 | "click button.deleteSingleCampain": function (event, template) { 10 | showConfirmDialog({ 11 | title: TAPi18n.__("confirm.remove"), 12 | confirmCallback: function () { 13 | callServerMethod({ 14 | methodName: "removeCampaigns", 15 | params: { 16 | campaigns: [template.data.item._id], 17 | }, 18 | resultCallback: function () { 19 | showSuccessNotification(TAPi18n.__("alerts.done")); 20 | }, 21 | }); 22 | }, 23 | cancelCallback: function () {}, 24 | }); 25 | }, 26 | }); 27 | 28 | Template.buttonsForCampains.onRendered(function () {}); 29 | 30 | Template.buttonsForCampains.onDestroyed(function () {}); 31 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/cards/_card-user.scss: -------------------------------------------------------------------------------- 1 | .card-user{ 2 | .image{ 3 | height: 130px; 4 | 5 | img { 6 | border-radius: 12px; 7 | } 8 | } 9 | 10 | .author{ 11 | text-align: center; 12 | text-transform: none; 13 | margin-top: -77px; 14 | 15 | a + p.description{ 16 | margin-top: -7px; 17 | } 18 | } 19 | 20 | .avatar{ 21 | width: 124px; 22 | height: 124px; 23 | border: 1px solid $white-color; 24 | position: relative; 25 | } 26 | 27 | .card-body{ 28 | min-height: 240px; 29 | } 30 | 31 | hr{ 32 | margin: 5px 15px 15px; 33 | } 34 | 35 | .card-body + .card-footer { 36 | padding-top: 0; 37 | } 38 | 39 | .card-footer { 40 | h5 { 41 | font-size: 1.25em; 42 | margin-bottom: 0; 43 | } 44 | } 45 | 46 | .button-container{ 47 | margin-bottom: 6px; 48 | text-align: center; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: catin-black 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /client/partials/buttonsForPromising.js: -------------------------------------------------------------------------------- 1 | import { callServerMethod } from "/client/helpers/server-method"; 2 | import { 3 | showConfirmDialog, 4 | showSuccessNotification, 5 | } from "/client/helpers/helpers"; 6 | 7 | Template.buttonsForPromising.helpers({}); 8 | 9 | Template.buttonsForPromising.events({ 10 | "click button.markAsUsed": function (event, templateInstance) { 11 | showConfirmDialog({ 12 | title: TAPi18n.__("confirm.markAsUsed"), 13 | confirmCallback: function () { 14 | callServerMethod({ 15 | methodName: "markAsUsed", 16 | params: { 17 | messages: [templateInstance.data.item.memberId], 18 | }, 19 | resultCallback: function () { 20 | showSuccessNotification(TAPi18n.__("alerts.done")); 21 | }, 22 | }); 23 | }, 24 | cancelCallback: function () {}, 25 | }); 26 | }, 27 | }); 28 | 29 | Template.buttonsForPromising.onRendered(function () {}); 30 | 31 | Template.buttonsForPromising.onDestroyed(function () {}); 32 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_footers.scss: -------------------------------------------------------------------------------- 1 | .footer{ 2 | padding: 24px 0; 3 | 4 | &.footer-default{ 5 | background-color: #f2f2f2; 6 | } 7 | 8 | nav{ 9 | display: inline-block; 10 | float: left; 11 | padding-left: 0; 12 | } 13 | 14 | ul{ 15 | margin-bottom: 0; 16 | padding: 0; 17 | list-style: none; 18 | 19 | li{ 20 | display: inline-block; 21 | 22 | a{ 23 | color: inherit; 24 | padding: $padding-base-vertical; 25 | font-size: $font-size-small; 26 | text-transform: uppercase; 27 | text-decoration: none; 28 | 29 | &:hover{ 30 | text-decoration: none; 31 | } 32 | } 33 | } 34 | } 35 | 36 | .copyright{ 37 | font-size: $font-size-small; 38 | line-height: 1.8; 39 | } 40 | 41 | &:after{ 42 | display: table; 43 | clear: both; 44 | content: " "; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client/pages/panel/campaigns/campaigns.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/pages/panel/promising-prospects/promising-prospects.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/partials/emailPreview.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Adam Trojańczyk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /client/pages/auth/login/login.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/registrations.js: -------------------------------------------------------------------------------- 1 | Accounts.onCreateUser(function(options, user) { 2 | 3 | if (user.services.facebook) { 4 | user.emails = [{ 5 | "address": user.services.facebook.email, 6 | "verified": true 7 | }]; 8 | user.username = user.services.facebook.email; 9 | 10 | } else if (user.services.google) { 11 | user.emails = [{ 12 | "address": user.services.google.email, 13 | "verified": true 14 | }]; 15 | user.username = user.services.google.email; 16 | 17 | } else if (user.services.twitter) { 18 | user.emails = [{ 19 | "address": user.services.twitter.email, 20 | "verified": true 21 | }]; 22 | user.username = user.services.twitter.email; 23 | 24 | } 25 | 26 | user.profile = {}; 27 | Settings.insert({ 28 | userId: user._id, 29 | APIkey: {}, 30 | billingInformation: {}, 31 | additionalEmail: user.emails[0].address 32 | }); 33 | Statistics.insert({ 34 | userId: user._id, 35 | APIkey: {}, 36 | billingInformation: {} 37 | }); 38 | return user; 39 | }); -------------------------------------------------------------------------------- /server/cron.js: -------------------------------------------------------------------------------- 1 | SyncedCron.config({ 2 | // Log job run details to console 3 | log: true, 4 | // Use a custom logger function (defaults to Meteor's logging package) 5 | logger: null, 6 | // Name of collection to use for synchronisation and logging 7 | collectionName: "cronHistory", 8 | // Default to using localTime 9 | utc: false, 10 | /* 11 | TTL in seconds for history records in collection to expire 12 | NOTE: Unset to remove expiry but ensure you remove the index from 13 | mongo by hand 14 | ALSO: SyncedCron can't use the `_ensureIndex` command to modify 15 | the TTL index. The best way to modify the default value of 16 | `collectionTTL` is to remove the index by hand (in the mongo shell 17 | run `db.cronHistory.dropIndex({startedAt: 1})`) and re-run your 18 | project. SyncedCron will recreate the index with the updated TTL. 19 | */ 20 | collectionTTL: 172800, 21 | }); 22 | 23 | SyncedCron.add({ 24 | name: "Check new statistics", 25 | schedule: function (parser) { 26 | return parser.text("every 30 minutes"); 27 | }, 28 | job: function () { 29 | // TODO: Get stats for all users 30 | return; 31 | }, 32 | }); 33 | 34 | SyncedCron.start(); 35 | -------------------------------------------------------------------------------- /public/placeholder/icons/safari.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_animated-buttons.scss: -------------------------------------------------------------------------------- 1 | //animations 2 | 3 | .icon-property{ 4 | @include transition($slow-transition-time, $transition-bezier); 5 | position: relative; 6 | display: inline-block; 7 | } 8 | 9 | #animated-buttons{ 10 | .btn{ 11 | i{ 12 | position: relative; 13 | top: 3px; 14 | margin-top: -3px; 15 | } 16 | } 17 | } 18 | 19 | .btn-rotate{ 20 | i{ 21 | @extend .icon-property; 22 | } 23 | 24 | &:hover, 25 | &:focus{ 26 | i{ 27 | @include rotate-53(); 28 | } 29 | } 30 | } 31 | 32 | .btn-magnify{ 33 | i{ 34 | @extend .icon-property; 35 | } 36 | 37 | &:hover, 38 | &:focus{ 39 | i{ 40 | @include transform-scale(1.22); 41 | } 42 | } 43 | } 44 | 45 | .btn-move-left{ 46 | i{ 47 | @extend .icon-property; 48 | margin-right: 0; 49 | } 50 | 51 | &:hover, 52 | &:focus{ 53 | i{ 54 | @include transform-translate-x(-5px); 55 | } 56 | } 57 | } 58 | 59 | .btn-move-right{ 60 | i{ 61 | @extend .icon-property; 62 | margin-right: 0; 63 | } 64 | 65 | &:hover, 66 | &:focus{ 67 | i{ 68 | @include transform-translate-x(5px); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /client/helpers/sessions.js: -------------------------------------------------------------------------------- 1 | import { Session } from "meteor/session"; 2 | 3 | class SessionObject { 4 | constructor(key) { 5 | check(key, String); 6 | 7 | this.key = key; 8 | } 9 | 10 | get() { 11 | return Session.get(this.key); 12 | } 13 | 14 | set(value) { 15 | Session.set(this.key, value); 16 | } 17 | 18 | nullify() { 19 | Session.set(this.key, null); 20 | } 21 | } 22 | 23 | export const SessionDataIsLoading = new SessionObject("data.status.loading"); 24 | export const SessionDataCsvColumns = new SessionObject("data.csv.columns"); 25 | export const SessionDataCsvColumnsMapped = new SessionObject( 26 | "data.csv.columns.mapped" 27 | ); 28 | export const SessionDataCsvContents = new SessionObject("data.csv.contents"); 29 | export const SessionDataCsvFilename = new SessionObject("data.csv.filename"); 30 | export const SessionDataCsvStatusUploaded = new SessionObject( 31 | "data.csv.status.uploaded" 32 | ); 33 | export const SessionDataCsvStatusParsed = new SessionObject( 34 | "data.csv.status.parsed" 35 | ); 36 | export const SessionDataCsvUploadReport = new SessionObject( 37 | "data.csv.upload.report" 38 | ); 39 | export const SessionSelectedMembers = new SessionObject("members.selected"); 40 | export const SessionSelectedCampaigns = new SessionObject("campaigns.selected"); 41 | export const SessionAuthorizationPasswordToken = new SessionObject( 42 | "authorization.password.token" 43 | ); 44 | -------------------------------------------------------------------------------- /client/pages/auth/login/login.js: -------------------------------------------------------------------------------- 1 | import { Meteor } from "meteor/meteor"; 2 | import { 3 | showDangerNotification, 4 | showSuccessNotification, 5 | } from "../../../helpers/helpers"; 6 | 7 | Template.pageAuthLogin.events({ 8 | /* 9 | * Login with standard credentials 10 | * */ 11 | "submit #login-form": function (event) { 12 | event.preventDefault(); 13 | 14 | const user = $(event.currentTarget).find("#email").val(); 15 | const password = $(event.currentTarget).find("#password").val(); 16 | 17 | Meteor.loginWithPassword(user, password, function (error) { 18 | actionAfterLogin(error); 19 | }); 20 | }, 21 | }); 22 | 23 | function actionAfterLogin(error) { 24 | if (error) { 25 | switch (error.reason) { 26 | case "User not found": 27 | case "Incorrect password": 28 | showDangerNotification( 29 | TAPi18n.__("login.notification.wrongCredentials") 30 | ); 31 | break; 32 | default: 33 | if (error.error === "too-many-requests") { 34 | showDangerNotification( 35 | TAPi18n.__("login.notification.tooManyRequests") 36 | ); 37 | } else { 38 | showDangerNotification( 39 | TAPi18n.__("login.notification.unhandledError") 40 | ); 41 | console.log(error); 42 | } 43 | } 44 | } else { 45 | Router.go("panelDashboard"); 46 | showSuccessNotification(TAPi18n.__("login.notification.loginSuccessfull")); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/pages/panel/integrations/integrations.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/partials/panel/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_alerts.scss: -------------------------------------------------------------------------------- 1 | .alert{ 2 | border: 0; 3 | border-radius: $border-radius-small; 4 | color: $white-color; 5 | padding-top: .9rem; 6 | padding-bottom: .9rem; 7 | position: relative; 8 | 9 | &.alert-success{ 10 | background-color: lighten($success-color, 5%); 11 | } 12 | 13 | &.alert-danger{ 14 | background-color: lighten($danger-color, 5%); 15 | } 16 | 17 | &.alert-warning{ 18 | background-color: lighten($warning-color, 5%); 19 | } 20 | 21 | &.alert-info{ 22 | background-color: lighten($info-color, 5%); 23 | } 24 | 25 | &.alert-primary{ 26 | background-color: lighten($primary-color, 5%); 27 | } 28 | 29 | .close{ 30 | color: $white-color; 31 | opacity: .9; 32 | text-shadow: none; 33 | line-height: 0; 34 | outline: 0; 35 | 36 | i.fa, 37 | i.nc-icon{ 38 | font-size: 14px !important; 39 | } 40 | 41 | &:hover, 42 | &:focus { 43 | opacity: 1; 44 | } 45 | } 46 | 47 | span[data-notify="icon"]{ 48 | font-size: 27px; 49 | display: block; 50 | left: 19px; 51 | position: absolute; 52 | top: 50%; 53 | margin-top: -11px; 54 | } 55 | 56 | button.close{ 57 | position: absolute; 58 | right: 10px; 59 | top: 50%; 60 | margin-top: -13px; 61 | width: 25px; 62 | height: 25px; 63 | padding: 3px; 64 | } 65 | 66 | .close ~ span{ 67 | display: block; 68 | max-width: 89%; 69 | } 70 | 71 | &.alert-with-icon{ 72 | padding-left: 65px; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /client/client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | .meteoremails app 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /client/pages/auth/register/register.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base@1.5.1 # Packages every Meteor app needs to have 8 | mongo@1.14.0 # The database Meteor supports right now 9 | blaze-html-templates # Compile .html files into Meteor Blaze views 10 | reactive-var@1.0.11 # Reactive variable for tracker 11 | tracker@1.2.0 # Meteor's client-side reactive programming library 12 | 13 | standard-minifier-css@1.7.4 # CSS minifier run for production mode 14 | standard-minifier-js@2.8.0 # JS minifier run for production mode 15 | es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers 16 | ecmascript@0.16.1 # Enable ECMAScript2015+ syntax in app code 17 | shell-server@0.5.0 # Server-side component of the `meteor shell` command 18 | 19 | meteorhacks:ssr 20 | email@2.2.0 21 | percolate:synced-cron@1.3.2 22 | momentjs:moment 23 | iron:router@1.1.2 24 | check@1.3.1 25 | fortawesome:fontawesome 26 | aldeed:autoform@6.3.0 27 | percolate:migrations 28 | ongoworks:security 29 | thebakery:collection-csv 30 | lfergon:exportcsv 31 | aldeed:delete-button 32 | http@1.4.2 33 | random@1.2.0 34 | aldeed:tabular 35 | loredanacirstea:meteor-tabular-filter 36 | jabbslad:basic-auth 37 | reywood:publish-composite 38 | underscore@1.0.10 39 | accounts-password@2.2.0 40 | accounts-facebook@1.3.3 41 | accounts-google@1.4.0 42 | service-configuration@1.3.0 43 | fourseven:scss 44 | aldeed:collection2@3.0.0 45 | aldeed:schema-index 46 | ostrio:cookies 47 | dburles:collection-helpers 48 | jquery 49 | rocketchat:tap-i18n 50 | dxrx:unblock 51 | -------------------------------------------------------------------------------- /client/helpers/server-method.js: -------------------------------------------------------------------------------- 1 | import { Loader } from "/client/helpers/loader"; 2 | import { 3 | errorDebugLog, 4 | startedDebugLog, 5 | successDebugLog, 6 | } from "/client/helpers/debug-log"; 7 | import { 8 | showSuccessNotification, 9 | showDangerNotification, 10 | } from "/client/helpers/helpers"; 11 | 12 | export function callServerMethod(args) { 13 | startedDebugLog("======================="); 14 | startedDebugLog(`Method::start():\t ${args.methodName}`); 15 | 16 | const startTime = new Date(); 17 | const loader = new Loader(); 18 | let submitButton = {}; 19 | 20 | loader.show(); 21 | 22 | if (args.form) { 23 | submitButton = args.form.querySelector("[type=submit]"); 24 | 25 | submitButton.setAttribute("disabled", "disabled"); 26 | submitButton.classList.add("disabled"); 27 | } 28 | 29 | Meteor.call(args.methodName, args.params, function (error, result) { 30 | if (error) { 31 | console.log(error); 32 | errorDebugLog(`Method::error():\t ${args.methodName} - ${error.reason}`); 33 | if (args.errorCallback) { 34 | args.errorCallback(error); 35 | } else { 36 | showDangerNotification(error.details); 37 | loader.hide(); 38 | console.log(error); 39 | } 40 | } else { 41 | if (args.resultCallback) { 42 | args.resultCallback(result); 43 | } else { 44 | showSuccessNotification(TAPi18n.__("alerts.done")); 45 | } 46 | } 47 | 48 | if (args.always) args.always(); 49 | successDebugLog( 50 | `Method::stop():\t ${args.methodName} :\t lasts for ${Math.abs( 51 | new Date().getTime() - startTime.getTime() 52 | )}ms` 53 | ); 54 | 55 | if (args.form) { 56 | submitButton.removeAttribute("disabled"); 57 | submitButton.classList.remove("disabled"); 58 | } 59 | loader.hide(); 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | ========================================================= 4 | * Paper Dashboard 2 - v2.0.0 5 | ========================================================= 6 | 7 | * Product Page: https://www.creative-tim.com/product/paper-dashboard-2 8 | * Copyright 2018 Creative Tim (http://www.creative-tim.com) 9 | 10 | * Designed by www.invisionapp.com Coded by www.creative-tim.com 11 | 12 | ========================================================= 13 | 14 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 15 | 16 | */ 17 | #ckPanel { 18 | @import './paper-dashboard/variables'; 19 | @import './paper-dashboard/mixins'; 20 | 21 | 22 | 23 | // Core CSS 24 | @import "./paper-dashboard/buttons"; 25 | @import "./paper-dashboard/inputs"; 26 | @import "./paper-dashboard/typography"; 27 | @import "./paper-dashboard/misc"; 28 | @import "./paper-dashboard/checkboxes-radio"; 29 | 30 | 31 | // components 32 | @import "./paper-dashboard/navbar"; 33 | @import "./paper-dashboard/page-header"; 34 | @import "./paper-dashboard/dropdown"; 35 | @import "./paper-dashboard/alerts"; 36 | @import "./paper-dashboard/images"; 37 | @import "./paper-dashboard/tables"; 38 | @import "./paper-dashboard/sidebar-and-main-panel"; 39 | @import "./paper-dashboard/footers"; 40 | @import "./paper-dashboard/fixed-plugin"; 41 | 42 | // cards 43 | @import "./paper-dashboard/cards"; 44 | @import "./paper-dashboard/cards/card-plain"; 45 | @import "./paper-dashboard/cards/card-chart"; 46 | @import "./paper-dashboard/cards/card-user"; 47 | @import "./paper-dashboard/cards/card-map"; 48 | @import "./paper-dashboard/cards/card-stats"; 49 | 50 | @import "./paper-dashboard/responsive"; 51 | } 52 | 53 | @import "./paper-dashboard/nucleo-outline"; 54 | @import "./paper-dashboard/plugins/plugin-animate-bootstrap-notify"; 55 | @import "./paper-dashboard/plugins/plugin-perfect-scrollbar"; 56 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_page-header.scss: -------------------------------------------------------------------------------- 1 | .page-header{ 2 | min-height: 100vh; 3 | max-height: 1000px; 4 | padding: 0; 5 | color: $white-color; 6 | position: relative; 7 | 8 | .page-header-image{ 9 | position: absolute; 10 | background-size: cover; 11 | background-position: center center; 12 | width: 100%; 13 | height: 100%; 14 | z-index: -1; 15 | } 16 | 17 | .content-center{ 18 | position: absolute; 19 | top: 50%; 20 | left: 50%; 21 | z-index: 2; 22 | -ms-transform: translate(-50%, -50%); 23 | -webkit-transform: translate(-50%, -50%); 24 | transform: translate(-50%, -50%); 25 | text-align: center; 26 | color: #FFFFFF; 27 | padding: 0 15px; 28 | width: 100%; 29 | max-width: 880px; 30 | 31 | } 32 | 33 | footer{ 34 | position: absolute; 35 | bottom: 0; 36 | width: 100%; 37 | } 38 | 39 | .container{ 40 | height: 100%; 41 | z-index: 1; 42 | } 43 | 44 | .category, 45 | .description{ 46 | color: $opacity-8; 47 | } 48 | 49 | &.page-header-small{ 50 | min-height: 60vh; 51 | max-height: 440px; 52 | } 53 | 54 | &.page-header-mini{ 55 | min-height: 40vh; 56 | max-height: 340px; 57 | } 58 | 59 | .title{ 60 | margin-bottom: 15px; 61 | } 62 | .title + h4{ 63 | margin-top: 10px; 64 | } 65 | 66 | &:after, 67 | &:before{ 68 | position: absolute; 69 | z-index: 0; 70 | width: 100%; 71 | height: 100%; 72 | display: block; 73 | left: 0; 74 | top: 0; 75 | content: ""; 76 | } 77 | 78 | &:before{ 79 | background-color: rgba(0,0,0,.3); 80 | } 81 | 82 | &[filter-color="orange"]{ 83 | @include linear-gradient(rgba($black-color,.20), rgba(224, 23, 3, 0.6)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /client/client.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | import "jquery/dist/jquery.js"; 3 | import "bootstrap/dist/js/bootstrap.js"; 4 | import "summernote/dist/summernote-bs4.js"; 5 | import "summernote/dist/summernote-bs4.css"; 6 | import "summernote-fontawesome"; 7 | import { SessionAuthorizationPasswordToken } from "./helpers/sessions"; 8 | import dataTablesBootstrap from "datatables.net-bs4"; 9 | import icons from "glyphicons"; 10 | import SmoothScroll from "smooth-scroll"; 11 | import { 12 | showWarningNotification, 13 | showSuccessNotification, 14 | } from "/client/helpers/helpers"; 15 | 16 | Meteor.startup(function () { 17 | TAPi18n.setLanguage("en"); 18 | }); 19 | 20 | Accounts.onResetPasswordLink(function (token, done) { 21 | SessionAuthorizationPasswordToken.set(token); 22 | Router.go("authPasswordSet"); 23 | }); 24 | dataTablesBootstrap(window, $); 25 | 26 | new SmoothScroll('a.scrollme[href*="#"]', { 27 | speed: 600, 28 | }); 29 | 30 | const hooksObject = { 31 | onSubmit: function (insertDoc, updateDoc, currentDoc) { 32 | this.done(); 33 | }, 34 | onSuccess: function (formType, result) { 35 | if (result) { 36 | showSuccessNotification("Success"); 37 | Router.go("panelCrm"); 38 | return false; 39 | } 40 | }, 41 | onError: function (formType, error) { 42 | if (error) { 43 | showWarningNotification("Check your form"); 44 | return false; 45 | } 46 | }, 47 | }; 48 | 49 | const integrationsHooks = { 50 | onSubmit: function (insertDoc, updateDoc, currentDoc) { 51 | this.done(); 52 | }, 53 | onSuccess: function (formType, result) { 54 | if (result) { 55 | showSuccessNotification("Success"); 56 | return false; 57 | } 58 | }, 59 | onError: function (formType, error) { 60 | if (error) { 61 | showWarningNotification("Check your form"); 62 | return false; 63 | } 64 | }, 65 | }; 66 | 67 | AutoForm.addHooks(["memberAdd"], hooksObject); 68 | AutoForm.addHooks( 69 | [ 70 | "integrationsUpdateSalesFlareAPI", 71 | "integrationsUpdateHubSpotAPI", 72 | "integrationsUpdateInfusionSoftAPI", 73 | "settingsUpdateProfile", 74 | ], 75 | integrationsHooks 76 | ); 77 | -------------------------------------------------------------------------------- /client/pages/panel/campaigns/newCampain.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/collections/statistics.js: -------------------------------------------------------------------------------- 1 | import { Mongo } from 'meteor/mongo'; 2 | import SimpleSchema from 'simpl-schema'; 3 | 4 | Statistics = new Mongo.Collection('Statistics'); 5 | 6 | let StatisticsSchemas = {}; 7 | 8 | StatisticsSchemas.Statistics = new SimpleSchema({ 9 | userId: { 10 | type: String, 11 | }, 12 | blocks: { 13 | type: Number, 14 | optional: true, 15 | defaultValue: 0 16 | }, 17 | bounce_drops: { 18 | type: Number, 19 | optional: true, 20 | defaultValue: 0 21 | }, 22 | bounces: { 23 | type: Number, 24 | optional: true, 25 | defaultValue: 0 26 | }, 27 | clicks: { 28 | type: Number, 29 | optional: true, 30 | defaultValue: 0 31 | }, 32 | deferred: { 33 | type: Number, 34 | optional: true, 35 | defaultValue: 0 36 | }, 37 | delivered: { 38 | type: Number, 39 | optional: true, 40 | defaultValue: 0 41 | }, 42 | invalid_emails: { 43 | type: Number, 44 | optional: true, 45 | defaultValue: 0 46 | }, 47 | opens: { 48 | type: Number, 49 | optional: true, 50 | defaultValue: 0 51 | }, 52 | processed: { 53 | type: Number, 54 | optional: true, 55 | defaultValue: 0 56 | }, 57 | requests: { 58 | type: Number, 59 | optional: true, 60 | defaultValue: 0 61 | }, 62 | spam_report_drops: { 63 | type: Number, 64 | optional: true, 65 | defaultValue: 0 66 | }, 67 | spam_reports: { 68 | type: Number, 69 | optional: true, 70 | defaultValue: 0 71 | }, 72 | unique_clicks: { 73 | type: Number, 74 | optional: true, 75 | defaultValue: 0 76 | }, 77 | unique_opens: { 78 | type: Number, 79 | optional: true, 80 | defaultValue: 0 81 | }, 82 | unsubscribe_drops: { 83 | type: Number, 84 | optional: true, 85 | defaultValue: 0 86 | }, 87 | unsubscribes: { 88 | type: Number, 89 | optional: true, 90 | defaultValue: 0 91 | }, 92 | }); 93 | 94 | Statistics.attachSchema(StatisticsSchemas.Statistics); -------------------------------------------------------------------------------- /client/pages/auth/register/register.js: -------------------------------------------------------------------------------- 1 | import { callServerMethod } from "/client//helpers/server-method"; 2 | import { 3 | showDangerNotification, 4 | showSuccessNotification, 5 | } from "/client/helpers/helpers"; 6 | import validate from "jquery-validation"; 7 | 8 | Template.pageAuthRegister.onRendered(function () { 9 | $.validator.addMethod( 10 | "validate_email", 11 | function (value, element) { 12 | if ( 13 | /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,12})+$/.test( 14 | value 15 | ) 16 | ) { 17 | return true; 18 | } else { 19 | return false; 20 | } 21 | }, 22 | TAPi18n.__("register.pleasProvideProperEmail") 23 | ); 24 | this.$("form").validate({ 25 | errorPlacement: function (error, element) { 26 | if (element.is(":checkbox")) { 27 | error.insertBefore(element); 28 | } else { 29 | error.insertAfter(element); 30 | } 31 | }, 32 | rules: { 33 | email: { 34 | required: true, 35 | email: true, 36 | validate_email: true, 37 | }, 38 | password: { 39 | required: true, 40 | }, 41 | sendgridAPI: { 42 | required: true, 43 | }, 44 | agreement: { 45 | required: true, 46 | }, 47 | }, 48 | }); 49 | }); 50 | 51 | Template.pageAuthRegister.helpers({}); 52 | 53 | Template.pageAuthRegister.events({ 54 | "submit #register-form": function (event) { 55 | event.preventDefault(); 56 | const email = $(event.currentTarget).find("#email"); 57 | const password = $(event.currentTarget).find("#password"); 58 | const sendgridAPI = $(event.currentTarget).find("#sendgridAPI"); 59 | const agreement = $(event.currentTarget).find("#agreement"); 60 | callServerMethod({ 61 | methodName: "registerUser", 62 | params: { 63 | email: email.val(), 64 | password: password.val(), 65 | sendgridAPI: sendgridAPI.val(), 66 | agreement: agreement.is(":checked"), 67 | }, 68 | resultCallback: function (result) { 69 | showSuccessNotification(TAPi18n.__("register.registered")); 70 | Router.go("authLogin"); 71 | }, 72 | errorCallback: function (error) { 73 | showDangerNotification(error.details); 74 | }, 75 | }); 76 | }, 77 | }); 78 | -------------------------------------------------------------------------------- /client/partials/panel/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/pages/panel/crm/crm.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/pages/panel/member/add.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meteor-emails", 3 | "version": "0.0.14", 4 | "private": true, 5 | "keywords": [ 6 | "meteor", 7 | "open-source", 8 | "email", 9 | "sendgrid" 10 | ], 11 | "homepage": "https://meteoremails.com", 12 | "author": { 13 | "name": "Adam Trojanczyk", 14 | "email": "adam.trojanczyk@gmail.com" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/catin-black/meteor-emails" 19 | }, 20 | "scripts": { 21 | "start": "meteor npm install && meteor --settings settings.json", 22 | "visualize": "meteor --production --extra-packages bundle-visualizer --settings settings.json", 23 | "lint": "eslint .", 24 | "pretest": "npm run lint --silent" 25 | }, 26 | "eslintConfig": { 27 | "extends": "@meteorjs/eslint-config-meteor" 28 | }, 29 | "dependencies": { 30 | "@babel/runtime": "^7.17.8", 31 | "@sendgrid/client": "^7.6.2", 32 | "@sendgrid/mail": "^7.6.2", 33 | "bcrypt": "^5.0.1", 34 | "bootstrap": "^4.6.0", 35 | "bootstrap-notify": "^3.1.3", 36 | "chart.js": "^3.7.1", 37 | "core-js": "^3.21.1", 38 | "datatables.net-bs": "^1.11.5", 39 | "datatables.net-bs4": "^1.11.5", 40 | "datatables.net-buttons": "^2.2.2", 41 | "datatables.net-buttons-bs": "^2.2.2", 42 | "fibers": "^5.0.1", 43 | "glyphicons": "^0.2.0", 44 | "jquery": "^3.6.0", 45 | "jquery-validation": "^1.19.3", 46 | "lodash": "^4.17.21", 47 | "meteor-node-stubs": "^1.2.1", 48 | "node-sass": "^7.0.1", 49 | "nodelastic": "^1.0.2", 50 | "nodemailer": "^6.7.3", 51 | "now-ui-kit": "^1.3.0", 52 | "papaparse": "^5.3.2", 53 | "paypal-checkout": "^4.0.334", 54 | "perfect-scrollbar": "^1.5.5", 55 | "popper.js": "^1.16.1", 56 | "prompt-confirm": "^2.0.4", 57 | "pxl-for-emails": "0.0.3", 58 | "request": "^2.88.2", 59 | "request-promise": "^4.2.6", 60 | "request-promise-native": "^1.0.9", 61 | "reset-css": "^5.0.1", 62 | "simpl-schema": "^1.12.0", 63 | "smooth-scroll": "^16.1.3", 64 | "summernote": "0.8.18", 65 | "summernote-fontawesome": "^0.1.0", 66 | "sweetalert2": "^11.4.7", 67 | "tablesorter": "^2.31.3", 68 | "urllib": "^2.38.0" 69 | }, 70 | "devDependencies": { 71 | "@meteorjs/eslint-config-meteor": "^1.0.5", 72 | "babel-eslint": "^10.1.0", 73 | "eslint": "^8.12.0", 74 | "eslint-config-airbnb": "^19.0.4", 75 | "eslint-import-resolver-meteor": "^0.4.0", 76 | "eslint-plugin-import": "^2.25.4", 77 | "eslint-plugin-jsx-a11y": "^6.5.1", 78 | "eslint-plugin-meteor": "^7.3.0", 79 | "eslint-plugin-react": "^7.29.4" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/collections/settings.js: -------------------------------------------------------------------------------- 1 | import { Mongo } from 'meteor/mongo'; 2 | import SimpleSchema from 'simpl-schema'; 3 | 4 | Settings = new Mongo.Collection('Settings'); 5 | let SettingsSchema = {}; 6 | SettingsSchema.Settings = new SimpleSchema({ 7 | userId: { 8 | type: String, 9 | }, 10 | additionalEmail: { 11 | type: String, 12 | regEx: SimpleSchema.RegEx.Email, 13 | optional: true, 14 | label: "Send emails from address" 15 | }, 16 | howManyEmails: { 17 | type: Number, 18 | optional: true, 19 | defaultValue: 100 20 | }, 21 | sendStatsError: { 22 | type: Boolean, 23 | optional: true, 24 | defaultValue: false 25 | }, 26 | APIkey: { 27 | type: Object, 28 | label: "API Keys" 29 | }, 30 | 31 | 'APIkey.sendGrid': { 32 | type: String, 33 | optional: true, 34 | label: "Send Grid API Key" 35 | }, 36 | 37 | 'APIkey.salesFlare': { 38 | type: String, 39 | optional: true, 40 | }, 41 | 42 | 'APIkey.salesFlareUserId': { 43 | type: String, 44 | optional: true, 45 | }, 46 | 47 | 'APIkey.hubSpot': { 48 | type: String, 49 | optional: true, 50 | }, 51 | 52 | 'APIkey.infusionSoft': { 53 | type: String, 54 | optional: true, 55 | }, 56 | 57 | billingInformation: { 58 | type: String, 59 | optional: true, 60 | type: Object, 61 | }, 62 | 63 | 'billingInformation.firstName': { 64 | type: String, 65 | optional: true, 66 | }, 67 | 68 | 'billingInformation.lastName': { 69 | type: String, 70 | optional: true, 71 | }, 72 | 73 | 'billingInformation.address': { 74 | type: String, 75 | optional: true, 76 | }, 77 | 78 | 'billingInformation.city': { 79 | type: String, 80 | optional: true, 81 | }, 82 | 83 | 'billingInformation.country': { 84 | type: String, 85 | optional: true, 86 | }, 87 | 88 | 'billingInformation.postalCode': { 89 | type: String, 90 | optional: true, 91 | }, 92 | 93 | 'billingInformation.additionalInformation': { 94 | type: String, 95 | optional: true, 96 | } 97 | }); 98 | 99 | Settings.attachSchema(SettingsSchema.Settings); 100 | 101 | Settings.allow({ 102 | update: function(userId, doc, fields, modifier) { 103 | if (fields.indexOf("howManyEmails") < 0) { 104 | if (userId && doc.userId === userId) { 105 | return true; 106 | } 107 | } else return false; 108 | } 109 | }); -------------------------------------------------------------------------------- /lib/collections/users.js: -------------------------------------------------------------------------------- 1 | import SimpleSchema from 'simpl-schema'; 2 | 3 | let UserSchema = {}; 4 | 5 | UserSchema.User = new SimpleSchema({ 6 | username: { 7 | type: String, 8 | max: 800, 9 | // For accounts-password, either emails or username is required, but not both. It is OK to make this 10 | // optional here because the accounts-password package does its own validation. 11 | // Third-party login packages may not require either. Adjust this schema as necessary for your usage. 12 | optional: true 13 | }, 14 | emails: { 15 | type: Array, 16 | // For accounts-password, either emails or username is required, but not both. It is OK to make this 17 | // optional here because the accounts-password package does its own validation. 18 | // Third-party login packages may not require either. Adjust this schema as necessary for your usage. 19 | optional: true 20 | }, 21 | "emails.$": { 22 | type: Object 23 | }, 24 | "emails.$.address": { 25 | type: String, 26 | max: 800, 27 | regEx: SimpleSchema.RegEx.Email 28 | }, 29 | "emails.$.verified": { 30 | type: Boolean 31 | }, 32 | createdAt: { 33 | type: Date 34 | }, 35 | profile: { 36 | type: Object, 37 | blackbox: true, 38 | optional: true 39 | }, 40 | agreement: { 41 | type: Boolean, 42 | defaultValue: false 43 | }, 44 | // Make sure this services field is in your schema if you're using any of the accounts packages 45 | services: { 46 | type: Object, 47 | optional: true, 48 | blackbox: true 49 | }, 50 | // Add `roles` to your schema if you use the meteor-roles package. 51 | // Option 1: Object type 52 | // If you specify that type as Object, you must also specify the 53 | // `Roles.GLOBAL_GROUP` group whenever you add a user to a role. 54 | // Example: 55 | // Roles.addUsersToRoles(userId, ["admin"], Roles.GLOBAL_GROUP); 56 | // You can't mix and match adding with and without a group since 57 | // you will fail validation in some cases. 58 | roles: { 59 | type: Object, 60 | optional: true, 61 | blackbox: true 62 | }, 63 | // Option 2: [String] type 64 | // If you are sure you will never need to use role groups, then 65 | // you can specify [String] as the type 66 | roles: { 67 | type: Array, 68 | optional: true 69 | }, 70 | 'roles.$': { 71 | type: String, 72 | optional: true 73 | }, 74 | // In order to avoid an 'Exception in setInterval callback' from Meteor 75 | heartbeat: { 76 | type: Date, 77 | optional: true 78 | } 79 | }); 80 | 81 | Meteor.users.attachSchema(UserSchema.User); 82 | -------------------------------------------------------------------------------- /client/pages/panel/gdpr/gdpr.js: -------------------------------------------------------------------------------- 1 | import { callServerMethod } from "/client/helpers/server-method"; 2 | import { 3 | showSuccessNotification, 4 | showDangerNotification, 5 | } from "/client/helpers/helpers"; 6 | 7 | Template.pagePanelGdpr.onRendered(function () {}); 8 | 9 | Template.pagePanelGdpr.helpers({ 10 | disallowedDomains: function () { 11 | const domains = DisallowedDomains.find({ 12 | userId: Meteor.userId(), 13 | }); 14 | 15 | return domains.count() > 0 ? domains : []; 16 | }, 17 | disallowedEmails: function () { 18 | const emails = DisallowedEmails.find({ 19 | userId: Meteor.userId(), 20 | }); 21 | return emails.count() > 0 ? emails : []; 22 | }, 23 | }); 24 | 25 | Template.pagePanelGdpr.events({ 26 | "submit #add-disallowed-email": function (event) { 27 | event.preventDefault(); 28 | 29 | callServerMethod({ 30 | methodName: "badEmail", 31 | params: { 32 | email: $(event.currentTarget).find("[type=email]").val(), 33 | }, 34 | resultCallback: function () { 35 | showSuccessNotification(TAPi18n.__("gdpr.notification.correct")); 36 | }, 37 | }); 38 | }, 39 | 40 | "submit #add-disallowed-domain": function (event) { 41 | event.preventDefault(); 42 | 43 | let domain = $(event.currentTarget).find("[type=text]").val(); 44 | let correctDomain = domain.match( 45 | /^((?:(?:(?:\w[\.\-\+]?)*)\w)+)((?:(?:(?:\w[\.\-\+]?){0,62})\w)+)\.(\w{2,10})$/ 46 | ); 47 | 48 | if (correctDomain) { 49 | callServerMethod({ 50 | methodName: "addDisallowedDomain", 51 | params: { 52 | domain: domain, 53 | }, 54 | resultCallback: function () { 55 | showSuccessNotification(TAPi18n.__("gdpr.notification.correct")); 56 | }, 57 | }); 58 | } else { 59 | showDangerNotification(TAPi18n.__("gdpr.notification.wrongDomainName")); 60 | } 61 | }, 62 | 63 | "click .remove-email": function (event) { 64 | event.preventDefault(); 65 | 66 | callServerMethod({ 67 | methodName: "removeDisallowedEmail", 68 | params: { 69 | emailId: event.currentTarget.getAttribute("data-id"), 70 | }, 71 | resultCallback: function () { 72 | showSuccessNotification(TAPi18n.__("gdpr.notification.deletedEmail")); 73 | }, 74 | }); 75 | }, 76 | 77 | "click .remove-domain": function (event) { 78 | event.preventDefault(); 79 | 80 | callServerMethod({ 81 | methodName: "removeDisallowedDomain", 82 | params: { 83 | domainId: event.currentTarget.getAttribute("data-id"), 84 | }, 85 | resultCallback: function () { 86 | showSuccessNotification("Domena została usunięta!"); 87 | }, 88 | }); 89 | }, 90 | }); 91 | 92 | Template.pagePanelGdpr.onDestroyed(function () {}); 93 | -------------------------------------------------------------------------------- /client/partials/panel/sidebar.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/helpers/helpers.js: -------------------------------------------------------------------------------- 1 | import notify from "bootstrap-notify"; 2 | import Swal from "sweetalert2"; 3 | 4 | export function closeModal() { 5 | const modal = $(".modal.show"); 6 | 7 | if (modal.length > 0) { 8 | modal.modal("hide"); 9 | } 10 | } 11 | 12 | export function showModal(id) { 13 | $(id).modal("show"); 14 | } 15 | 16 | export function showDangerAlert(title, text) { 17 | showAlert({ 18 | type: "error", 19 | title: title, 20 | text: text, 21 | }); 22 | } 23 | 24 | export function showConfirmDialog(args) { 25 | return Swal.fire({ 26 | title: args.title ? args.title : "", 27 | text: args.text ? args.text : "", 28 | type: "warning", 29 | showCancelButton: true, 30 | confirmButtonColor: "#3085d6", 31 | cancelButtonColor: "#d33", 32 | reverseButtons: true, 33 | confirmButtonText: args.confirmButtonText ? args.confirmButtonText : "Yes", 34 | }).then((result) => { 35 | if (result.value) { 36 | if (args.confirmCallback) args.confirmCallback(); 37 | } else { 38 | if (args.cancelCallback) args.cancelCallback(); 39 | } 40 | }); 41 | } 42 | 43 | export function showSuccessAlert(title, text) { 44 | showAlert({ 45 | type: "success", 46 | title: title, 47 | text: text, 48 | }); 49 | } 50 | export function showWarningAlert(title, text) { 51 | showAlert({ 52 | type: "warning", 53 | title: title, 54 | text: text, 55 | }); 56 | } 57 | export function showInfoAlert(title, text) { 58 | showAlert({ 59 | type: "info", 60 | title: title, 61 | text: text, 62 | }); 63 | } 64 | export function showQuestionAlert(title, text) { 65 | showAlert({ 66 | type: "question", 67 | title: title, 68 | text: text, 69 | }); 70 | } 71 | 72 | export function showBasicAlert(args) { 73 | return Swal.fire(args); 74 | } 75 | 76 | function showAlert(args) { 77 | return Swal.fire({ 78 | icon: args.type, 79 | title: args.title, 80 | text: args.text, 81 | }); 82 | } 83 | 84 | export function showSuccessNotification(text) { 85 | check(text, String); 86 | 87 | showNotification({ 88 | text: text, 89 | type: "success", 90 | }); 91 | } 92 | 93 | export function showInfoNotification(text) { 94 | check(text, String); 95 | 96 | showNotification({ 97 | text: text, 98 | type: "info", 99 | }); 100 | } 101 | 102 | export function showWarningNotification(text) { 103 | check(text, String); 104 | 105 | showNotification({ 106 | text: text, 107 | type: "warning", 108 | }); 109 | } 110 | 111 | export function showDangerNotification(text) { 112 | check(text, String); 113 | 114 | showNotification({ 115 | text: text, 116 | type: "danger", 117 | }); 118 | } 119 | 120 | export function clearPreviousNotifications() { 121 | $("[data-notify]").empty(); 122 | } 123 | 124 | function showNotification(args) { 125 | check(args.text, String); 126 | check(args.type, String); 127 | 128 | $.notify( 129 | { 130 | icon: "now-ui-icons ui-1_bell-53", 131 | message: args.text, 132 | }, 133 | { 134 | type: args.type, 135 | timer: 4000, 136 | placement: { 137 | from: "top", 138 | align: "right", 139 | }, 140 | } 141 | ); 142 | } 143 | -------------------------------------------------------------------------------- /client/pages/panel/settings/settings.js: -------------------------------------------------------------------------------- 1 | import { Meteor } from "meteor/meteor"; 2 | import { callServerMethod } from "../../../helpers/server-method"; 3 | import { 4 | showSuccessNotification, 5 | showDangerNotification, 6 | } from "../../../helpers/helpers"; 7 | 8 | Template.pagePanelSettings.helpers({ 9 | userSettings: function () { 10 | return Settings.findOne({ 11 | userId: Meteor.userId(), 12 | }); 13 | }, 14 | }); 15 | 16 | Template.pagePanelSettings.onRendered(function () { 17 | $.validator.addMethod( 18 | "validate_email", 19 | function (value, element) { 20 | if ( 21 | /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,12})+$/.test( 22 | value 23 | ) 24 | ) { 25 | return true; 26 | } else { 27 | return false; 28 | } 29 | }, 30 | TAPi18n.__("register.pleasProvideProperEmail") 31 | ); 32 | this.$("form#emailForm").validate({ 33 | errorPlacement: function (error, element) { 34 | if (element.is(":checkbox")) { 35 | error.insertBefore(element); 36 | } else { 37 | error.insertAfter(element); 38 | } 39 | }, 40 | rules: { 41 | email: { 42 | required: true, 43 | email: true, 44 | validate_email: true, 45 | }, 46 | }, 47 | }); 48 | }); 49 | 50 | Template.pagePanelSettings.events({ 51 | "submit #settingsChangePassword": function (event) { 52 | event.preventDefault(); 53 | const oldPassword = $(event.currentTarget).find("#oldPassword").val(), 54 | newPassword = $(event.currentTarget).find("#newPassword").val(), 55 | newPasswordRepeat = $(event.currentTarget) 56 | .find("#newPasswordRepeat") 57 | .val(); 58 | if (oldPassword && newPassword && newPasswordRepeat) { 59 | if (newPassword === newPasswordRepeat) { 60 | Accounts.changePassword(oldPassword, newPassword, function (err) { 61 | if (err) { 62 | showDangerNotification(err); 63 | } else { 64 | showSuccessNotification( 65 | TAPi18n.__("settings.notification.success") 66 | ); 67 | $(event.currentTarget).find(".clear").val(""); 68 | } 69 | }); 70 | } else { 71 | showDangerNotification(TAPi18n.__("settings.notification.mismatch")); 72 | } 73 | } else { 74 | showDangerNotification(TAPi18n.__("settings.notification.error")); 75 | } 76 | }, 77 | "submit #emailForm": function (event) { 78 | event.preventDefault(); 79 | const email = $(event.currentTarget).find("#email").val(); 80 | if (email) { 81 | callServerMethod({ 82 | methodName: "additionalEmail", 83 | params: { 84 | email: email, 85 | }, 86 | resultCallback: function (result) { 87 | showSuccessNotification( 88 | TAPi18n.__("resetPassword.notification.success") 89 | ); 90 | }, 91 | errorCallback: function (error) { 92 | showDangerNotification(error.reason); 93 | }, 94 | }); 95 | } else { 96 | showDangerNotification(TAPi18n.__("settings.notification.error")); 97 | } 98 | }, 99 | }); 100 | 101 | Template.pagePanelSettings.onDestroyed(function () {}); 102 | -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@2.2.1 2 | accounts-facebook@1.3.3 3 | accounts-google@1.4.0 4 | accounts-oauth@1.4.0 5 | accounts-password@2.2.0 6 | aldeed:autoform@6.3.0 7 | aldeed:collection2@3.2.2 8 | aldeed:delete-button@2.0.0 9 | aldeed:schema-index@3.0.0 10 | aldeed:simple-schema@1.5.4 11 | aldeed:tabular@2.1.2 12 | allow-deny@1.1.1 13 | autoupdate@1.8.0 14 | babel-compiler@7.8.0 15 | babel-runtime@1.5.0 16 | base64@1.0.12 17 | binary-heap@1.0.11 18 | blaze@2.5.0 19 | blaze-html-templates@1.2.1 20 | blaze-tools@1.1.2 21 | boilerplate-generator@1.7.1 22 | caching-compiler@1.2.2 23 | caching-html-compiler@1.2.1 24 | callback-hook@1.4.0 25 | cfs:http-methods@0.0.32 26 | check@1.3.1 27 | coffeescript@1.0.17 28 | dburles:collection-helpers@1.1.0 29 | ddp@1.4.0 30 | ddp-client@2.5.0 31 | ddp-common@1.4.0 32 | ddp-rate-limiter@1.1.0 33 | ddp-server@2.5.0 34 | deps@1.0.12 35 | diff-sequence@1.1.1 36 | dxrx:unblock@1.0.0 37 | dynamic-import@0.7.2 38 | ecmascript@0.16.1 39 | ecmascript-runtime@0.8.0 40 | ecmascript-runtime-client@0.12.1 41 | ecmascript-runtime-server@0.11.0 42 | ejson@1.1.1 43 | email@2.2.0 44 | es5-shim@4.8.0 45 | facebook-oauth@1.10.0 46 | fetch@0.1.1 47 | fortawesome:fontawesome@4.7.0 48 | fourseven:scss@4.15.0 49 | geojson-utils@1.0.10 50 | google-oauth@1.4.1 51 | hot-code-push@1.0.4 52 | html-tools@1.1.2 53 | htmljs@1.1.1 54 | http@1.4.4 55 | id-map@1.1.1 56 | inter-process-messaging@0.1.1 57 | iron:controller@1.1.0 58 | iron:core@1.0.11 59 | iron:dynamic-template@1.1.0 60 | iron:layout@1.0.12 61 | iron:location@1.1.0 62 | iron:middleware-stack@1.1.0 63 | iron:router@1.2.0 64 | iron:url@1.1.0 65 | jabbslad:basic-auth@0.2.2 66 | jquery@1.11.11 67 | lfergon:exportcsv@0.0.8 68 | livedata@1.0.18 69 | localstorage@1.2.0 70 | logging@1.3.1 71 | loredanacirstea:meteor-tabular-filter@0.2.2 72 | mdg:validation-error@0.5.1 73 | meteor@1.10.0 74 | meteor-base@1.5.1 75 | meteorhacks:ssr@2.2.0 76 | meteorspark:util@0.2.0 77 | minifier-css@1.6.0 78 | minifier-js@2.7.3 79 | minimongo@1.8.0 80 | modern-browsers@0.1.7 81 | modules@0.18.0 82 | modules-runtime@0.12.0 83 | momentjs:moment@2.29.1 84 | mongo@1.14.4 85 | mongo-decimal@0.1.2 86 | mongo-dev-server@1.1.0 87 | mongo-id@1.0.8 88 | montiapm:meteorx@2.2.0 89 | npm-mongo@4.3.1 90 | oauth@2.1.1 91 | oauth2@1.3.1 92 | observe-sequence@1.0.19 93 | ongoworks:security@2.1.0 94 | ordered-dict@1.1.0 95 | ostrio:cookies@2.7.0 96 | percolate:migrations@1.0.3 97 | percolate:synced-cron@1.3.2 98 | promise@0.12.0 99 | raix:eventemitter@0.1.3 100 | random@1.2.0 101 | rate-limit@1.0.9 102 | react-fast-refresh@0.2.2 103 | reactive-dict@1.3.0 104 | reactive-var@1.0.11 105 | reload@1.3.1 106 | retry@1.1.0 107 | reywood:publish-composite@1.7.3 108 | rocketchat:tap-i18n@1.9.1 109 | routepolicy@1.1.1 110 | service-configuration@1.3.0 111 | session@1.2.0 112 | sha@1.0.9 113 | shell-server@0.5.0 114 | socket-stream-client@0.4.0 115 | spacebars@1.2.0 116 | spacebars-compiler@1.3.0 117 | standard-minifier-css@1.7.4 118 | standard-minifier-js@2.8.0 119 | templating@1.4.1 120 | templating-compiler@1.4.1 121 | templating-runtime@1.5.0 122 | templating-tools@1.2.1 123 | thebakery:collection-csv@0.1.0 124 | tmeasday:check-npm-versions@1.0.2 125 | tracker@1.2.0 126 | typescript@4.4.1 127 | ui@1.0.13 128 | underscore@1.0.10 129 | url@1.3.2 130 | webapp@1.13.0 131 | webapp-hashing@1.1.0 132 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_misc.scss: -------------------------------------------------------------------------------- 1 | body{ 2 | color: $black-color; 3 | font-size: $font-size-base; 4 | font-family: $sans-serif-family; 5 | -moz-osx-font-smoothing: grayscale; 6 | -webkit-font-smoothing: antialiased; 7 | } 8 | 9 | .main{ 10 | position: relative; 11 | background: $white-color; 12 | } 13 | /* Animations */ 14 | .nav-pills .nav-link, 15 | .navbar, 16 | .nav-tabs .nav-link, 17 | .sidebar .nav a, 18 | .sidebar .nav a i, 19 | .animation-transition-general, 20 | .tag, 21 | .tag [data-role="remove"], 22 | .animation-transition-general{ 23 | @include transition($general-transition-time, $transition-ease); 24 | } 25 | 26 | //transition for dropdown caret 27 | .dropdown-toggle:after, 28 | .bootstrap-switch-label:before, 29 | .caret{ 30 | @include transition($fast-transition-time, $transition-ease); 31 | } 32 | 33 | .dropdown-toggle[aria-expanded="true"]:after, 34 | a[data-toggle="collapse"][aria-expanded="true"] .caret, 35 | .card-collapse .card a[data-toggle="collapse"][aria-expanded="true"] i, 36 | .card-collapse .card a[data-toggle="collapse"].expanded i{ 37 | @include rotate-180(); 38 | } 39 | 40 | .button-bar{ 41 | display: block; 42 | position: relative; 43 | width: 22px; 44 | height: 1px; 45 | border-radius: 1px; 46 | background: $white-bg; 47 | 48 | & + .button-bar{ 49 | margin-top: 7px; 50 | } 51 | 52 | &:nth-child(2){ 53 | width: 17px; 54 | } 55 | } 56 | 57 | .caret{ 58 | display: inline-block; 59 | width: 0; 60 | height: 0; 61 | margin-left: 2px; 62 | vertical-align: middle; 63 | border-top: 4px dashed; 64 | border-top: 4px solid\9; 65 | border-right: 4px solid transparent; 66 | border-left: 4px solid transparent; 67 | } 68 | 69 | .pull-left{ 70 | float: left; 71 | } 72 | .pull-right{ 73 | float: right; 74 | } 75 | 76 | 77 | .offline-doc { 78 | .navbar.navbar-transparent{ 79 | padding-top: 25px; 80 | border-bottom: none; 81 | 82 | .navbar-minimize { 83 | display: none; 84 | } 85 | .navbar-brand, 86 | .collapse .navbar-nav .nav-link { 87 | color: $white-color !important; 88 | } 89 | } 90 | .footer { 91 | z-index: 3 !important; 92 | } 93 | .page-header{ 94 | .container { 95 | z-index: 3; 96 | } 97 | &:after { 98 | background-color: rgba(0, 0, 0, 0.5); 99 | content: ""; 100 | display: block; 101 | height: 100%; 102 | left: 0; 103 | position: absolute; 104 | top: 0; 105 | width: 100%; 106 | z-index: 2; 107 | } 108 | } 109 | } 110 | 111 | .fixed-plugin { 112 | .dropdown-menu li { 113 | padding: 2px !important; 114 | } 115 | } 116 | 117 | // badge color 118 | 119 | .badge{ 120 | &.badge-default{ 121 | @include badge-color($default-color); 122 | } 123 | &.badge-primary{ 124 | @include badge-color($primary-color); 125 | } 126 | &.badge-info{ 127 | @include badge-color($info-color); 128 | } 129 | &.badge-success{ 130 | @include badge-color($success-color); 131 | } 132 | &.badge-warning{ 133 | @include badge-color($warning-color); 134 | } 135 | &.badge-danger{ 136 | @include badge-color($danger-color); 137 | } 138 | &.badge-neutral{ 139 | @include badge-color($white-color); 140 | color: inherit; 141 | } 142 | } 143 | 144 | .card-user { 145 | form { 146 | .form-group { 147 | margin-bottom: 20px; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /client/pages/panel/settings/settings.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/pages/panel/dashboard/dashboard.js: -------------------------------------------------------------------------------- 1 | import { callServerMethod } from "/client/helpers/server-method"; 2 | import { 3 | showSuccessNotification, 4 | showWarningAlert, 5 | } from "/client/helpers/helpers"; 6 | import Chart from "chart.js"; 7 | 8 | Template.pagePanelDashboard.helpers({ 9 | allMails: function () { 10 | const instance = Template.instance(); 11 | return Messages.find({ 12 | sentAt: { $gte: instance.startOfMonth, $lte: instance.endOfMonth }, 13 | }).count(); 14 | }, 15 | opened: function () { 16 | const instance = Template.instance(); 17 | return Messages.find({ 18 | opens: { $gt: 0 }, 19 | sentAt: { $gte: instance.startOfMonth, $lte: instance.endOfMonth }, 20 | }).count(); 21 | }, 22 | promising: function () { 23 | const instance = Template.instance(); 24 | return Messages.find({ 25 | sentAt: { $gte: instance.startOfMonth, $lte: instance.endOfMonth }, 26 | $or: [{ opens: { $gt: 1 } }, { clicks: { $gt: 0 } }], 27 | }).count(); 28 | }, 29 | sendGridError: function () { 30 | const settings = Settings.findOne(); 31 | return settings && settings.sendStatsError ? true : false; 32 | }, 33 | }); 34 | 35 | Template.pagePanelDashboard.events({ 36 | "click #stats, click #stats2": function (event, template) { 37 | event.preventDefault(); 38 | callServerMethod({ 39 | methodName: "stats", 40 | resultCallback: function () { 41 | showSuccessNotification(TAPi18n.__("alerts.done")); 42 | }, 43 | }); 44 | }, 45 | "click #showStatsError": function (event, template) { 46 | event.preventDefault(); 47 | return showWarningAlert( 48 | TAPi18n.__("dashboard.sendGridErrorTitle"), 49 | TAPi18n.__("dashboard.sendGridError") 50 | ); 51 | }, 52 | }); 53 | 54 | Template.pagePanelDashboard.onCreated(function () { 55 | this.startOfMonth = moment().startOf("month").toDate(); 56 | this.endOfMonth = moment().endOf("month").toDate(); 57 | }); 58 | 59 | Template.pagePanelDashboard.onRendered(function () { 60 | const ctx = document.getElementById("chartEmail").getContext("2d"); 61 | const processed = Messages.find({ status: "processed" }).count(); 62 | const delivered = Messages.find({ status: "delivered" }).count(); 63 | const not_delivered = Messages.find({ status: "not_delivered" }).count(); 64 | const myChart = new Chart(ctx, { 65 | type: "bar", 66 | data: { 67 | labels: [ 68 | TAPi18n.__("stats.processed"), 69 | TAPi18n.__("stats.delivered"), 70 | TAPi18n.__("stats.not_delivered"), 71 | ], 72 | datasets: [ 73 | { 74 | label: TAPi18n.__("stats.emails"), 75 | data: [processed, delivered, not_delivered], 76 | backgroundColor: [ 77 | "rgba(255, 99, 132, 0.2)", 78 | "rgba(54, 162, 235, 0.2)", 79 | "rgba(255, 206, 86, 0.2)", 80 | ], 81 | borderColor: [ 82 | "rgba(255,99,132,1)", 83 | "rgba(54, 162, 235, 1)", 84 | "rgba(255, 206, 86, 1)", 85 | ], 86 | borderWidth: 1, 87 | }, 88 | ], 89 | }, 90 | options: { 91 | responsive: true, 92 | maintainAspectRatio: false, 93 | scales: { 94 | yAxes: [ 95 | { 96 | ticks: { 97 | beginAtZero: true, 98 | precision: 0, 99 | }, 100 | }, 101 | ], 102 | }, 103 | }, 104 | }); 105 | }); 106 | 107 | Template.pagePanelDashboard.onDestroyed(function () {}); 108 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_cards.scss: -------------------------------------------------------------------------------- 1 | .card{ 2 | border-radius: $border-radius-extreme; 3 | box-shadow: 0 6px 10px -4px rgba(0, 0, 0, 0.15); 4 | background-color: #FFFFFF; 5 | color: $card-black-color; 6 | margin-bottom: 20px; 7 | position: relative; 8 | border: 0 none; 9 | 10 | -webkit-transition: transform 300ms cubic-bezier(0.34, 2, 0.6, 1), box-shadow 200ms ease; 11 | -moz-transition: transform 300ms cubic-bezier(0.34, 2, 0.6, 1), box-shadow 200ms ease; 12 | -o-transition: transform 300ms cubic-bezier(0.34, 2, 0.6, 1), box-shadow 200ms ease; 13 | -ms-transition: transform 300ms cubic-bezier(0.34, 2, 0.6, 1), box-shadow 200ms ease; 14 | transition: transform 300ms cubic-bezier(0.34, 2, 0.6, 1), box-shadow 200ms ease; 15 | 16 | .card-body{ 17 | padding: 15px 15px 10px 15px; 18 | 19 | &.table-full-width{ 20 | padding-left: 0; 21 | padding-right: 0; 22 | } 23 | } 24 | 25 | .card-header{ 26 | &:not([data-background-color]){ 27 | background-color: transparent; 28 | } 29 | padding: 15px 15px 0; 30 | border: 0; 31 | 32 | .card-title{ 33 | margin-top: 10px; 34 | } 35 | } 36 | 37 | .map{ 38 | border-radius: $border-radius-small; 39 | 40 | &.map-big{ 41 | height: 400px; 42 | } 43 | } 44 | 45 | &[data-background-color="orange"]{ 46 | background-color: $primary-color; 47 | 48 | .card-header{ 49 | background-color: $primary-color; 50 | } 51 | 52 | .card-footer{ 53 | .stats{ 54 | color: $white-color; 55 | } 56 | } 57 | } 58 | 59 | &[data-background-color="red"]{ 60 | background-color: $danger-color; 61 | } 62 | 63 | &[data-background-color="yellow"]{ 64 | background-color: $warning-color; 65 | } 66 | 67 | &[data-background-color="blue"]{ 68 | background-color: $info-color; 69 | } 70 | 71 | &[data-background-color="green"]{ 72 | background-color: $success-color; 73 | } 74 | 75 | .image{ 76 | overflow: hidden; 77 | height: 200px; 78 | position: relative; 79 | } 80 | 81 | .avatar{ 82 | width: 30px; 83 | height: 30px; 84 | overflow: hidden; 85 | border-radius: 50%; 86 | margin-bottom: 15px; 87 | } 88 | 89 | .numbers { 90 | font-size: 2em; 91 | } 92 | 93 | .big-title { 94 | font-size: 12px; 95 | text-align: center; 96 | font-weight: 500; 97 | padding-bottom: 15px; 98 | } 99 | 100 | label{ 101 | font-size: $font-size-small; 102 | margin-bottom: 5px; 103 | color: $dark-gray; 104 | } 105 | 106 | .card-footer{ 107 | background-color: transparent; 108 | border: 0; 109 | 110 | 111 | .stats{ 112 | i{ 113 | margin-right: 5px; 114 | position: relative; 115 | top: 0px; 116 | color: $default-color; 117 | } 118 | } 119 | 120 | .btn{ 121 | margin: 0; 122 | } 123 | } 124 | 125 | &.card-plain{ 126 | background-color: transparent; 127 | box-shadow: none; 128 | border-radius: 0; 129 | 130 | 131 | .card-body{ 132 | padding-left: 5px; 133 | padding-right: 5px; 134 | } 135 | 136 | img{ 137 | border-radius: $border-radius-extreme; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /public/placeholder/icons/unicorn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /client/partials/panel/modalAddNewContact.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_tables.scss: -------------------------------------------------------------------------------- 1 | .table{ 2 | 3 | .img-wrapper{ 4 | width: 40px; 5 | height: 40px; 6 | border-radius: 50%; 7 | overflow: hidden; 8 | margin: 0 auto; 9 | } 10 | 11 | .img-row{ 12 | max-width: 60px; 13 | width: 60px; 14 | } 15 | 16 | .form-check{ 17 | margin: 0; 18 | 19 | & label .form-check-sign::before, 20 | & label .form-check-sign::after{ 21 | top: -17px; 22 | left: 4px; 23 | } 24 | } 25 | 26 | .btn{ 27 | margin: 0; 28 | } 29 | 30 | small,.small{ 31 | font-weight: 300; 32 | } 33 | 34 | .card-tasks .card-body &{ 35 | margin-bottom: 0; 36 | 37 | > thead > tr > th, 38 | > tbody > tr > th, 39 | > tfoot > tr > th, 40 | > thead > tr > td, 41 | > tbody > tr > td, 42 | > tfoot > tr > td{ 43 | padding-top: 0; 44 | padding-bottom: 0; 45 | } 46 | } 47 | 48 | > thead > tr > th{ 49 | font-size: 14px; 50 | font-weight: $font-weight-bold; 51 | padding-bottom: 0; 52 | text-transform: uppercase; 53 | border: 0; 54 | } 55 | 56 | .radio, 57 | .checkbox{ 58 | margin-top: 0; 59 | margin-bottom: 0; 60 | padding: 0; 61 | width: 15px; 62 | 63 | .icons{ 64 | position: relative; 65 | } 66 | 67 | label{ 68 | &:after, 69 | &:before{ 70 | top: -17px; 71 | left: -3px; 72 | } 73 | } 74 | } 75 | > thead > tr > th, 76 | > tbody > tr > th, 77 | > tfoot > tr > th, 78 | > thead > tr > td, 79 | > tbody > tr > td, 80 | > tfoot > tr > td{ 81 | padding: 12px 7px; 82 | vertical-align: middle; 83 | } 84 | 85 | .th-description{ 86 | max-width: 150px; 87 | } 88 | .td-price{ 89 | font-size: 26px; 90 | font-weight: $font-weight-light; 91 | margin-top: 5px; 92 | position: relative; 93 | top: 4px; 94 | text-align: right; 95 | } 96 | .td-total{ 97 | font-weight: $font-weight-bold; 98 | font-size: $font-size-h5; 99 | padding-top: 20px; 100 | text-align: right; 101 | } 102 | 103 | .td-actions .btn{ 104 | margin: 0px; 105 | } 106 | 107 | > tbody > tr{ 108 | position: relative; 109 | } 110 | } 111 | 112 | .table-shopping{ 113 | > thead > tr > th{ 114 | font-size: $font-size-h6; 115 | text-transform: uppercase; 116 | } 117 | > tbody > tr > td{ 118 | font-size: $font-paragraph; 119 | 120 | b{ 121 | display: block; 122 | margin-bottom: 5px; 123 | } 124 | } 125 | .td-name{ 126 | font-weight: $font-weight-normal; 127 | font-size: 1.5em; 128 | small{ 129 | color: $dark-gray; 130 | font-size: 0.75em; 131 | font-weight: $font-weight-light; 132 | } 133 | } 134 | .td-number{ 135 | font-weight: $font-weight-light; 136 | font-size: $font-size-h4; 137 | } 138 | .td-name{ 139 | min-width: 200px; 140 | } 141 | .td-number{ 142 | text-align: right; 143 | min-width: 170px; 144 | 145 | small{ 146 | margin-right: 3px; 147 | } 148 | } 149 | 150 | .img-container{ 151 | width: 120px; 152 | max-height: 160px; 153 | overflow: hidden; 154 | display: block; 155 | 156 | img{ 157 | width: 100%; 158 | } 159 | } 160 | } 161 | 162 | .table-responsive{ 163 | overflow: scroll; 164 | padding-bottom: 10px; 165 | } 166 | 167 | #tables .table-responsive{ 168 | margin-bottom: 30px; 169 | } 170 | 171 | .table-hover>tbody>tr:hover{ 172 | background-color: #f5f5f5; 173 | } 174 | -------------------------------------------------------------------------------- /lib/collections/sent.js: -------------------------------------------------------------------------------- 1 | import { Mongo } from 'meteor/mongo'; 2 | import SimpleSchema from 'simpl-schema'; 3 | SendInformation = new Mongo.Collection('SentInformation'); 4 | import Tabular from 'meteor/aldeed:tabular'; 5 | 6 | let SendInformationSchemas = {}; 7 | SendInformationSchemas.SendInformation = new SimpleSchema({ 8 | userId: { 9 | type: String, 10 | }, 11 | name: { 12 | type: String, 13 | }, 14 | howMany: { 15 | type: Number 16 | }, 17 | opens: { 18 | type: Number, 19 | optional: true, 20 | defaultValue: 0 21 | }, 22 | clicks: { 23 | type: Number, 24 | optional: true, 25 | defaultValue: 0 26 | }, 27 | sent: { 28 | type: Boolean 29 | }, 30 | sentAt: { 31 | type: Date, 32 | label: "Server date of creation", 33 | autoValue: function() { 34 | if (this.isInsert) { 35 | return new Date; 36 | } else if (this.isUpsert) { 37 | return { 38 | $setOnInsert: new Date 39 | }; 40 | } else { 41 | return this.unset(); 42 | } 43 | } 44 | } 45 | }); 46 | SendInformation.attachSchema(SendInformationSchemas.SendInformation); 47 | 48 | SendInformation.helpers({ 49 | opens() { 50 | const messages = Messages.find({ campaignId: this._id }).fetch(); 51 | let opens = 0; 52 | messages.forEach( function(msg) { 53 | opens = opens + msg.opens; 54 | }); 55 | return opens; 56 | }, 57 | clicks() { 58 | const messages = Messages.find({ campaignId: this._id }).fetch(); 59 | let clicks = 0; 60 | messages.forEach( function(msg) { 61 | clicks = clicks + msg.clicks; 62 | }); 63 | return clicks; 64 | }, 65 | }); 66 | 67 | new Tabular.Table({ 68 | name: "SendInformation", 69 | collection: SendInformation, 70 | pub: "tabular_Campaigns", 71 | responsive: false, 72 | autoWidth: true, 73 | columns: [{ 74 | tmpl: Meteor.isClient && Template.checkbox, 75 | tmplContext(rowData) { 76 | return { 77 | item: rowData, 78 | }; 79 | } 80 | }, 81 | { 82 | data: "name", 83 | render: function(val, type, doc) { 84 | return val ? '' + val + '' : '-'; 85 | }, 86 | titleFn: function() { 87 | return TAPi18n.__('campaign.table.name'); 88 | } 89 | },{ 90 | data: "sentAt", 91 | titleFn: function() { 92 | return TAPi18n.__('campaign.table.sendDate'); 93 | }, 94 | render: function(val, type, doc) { 95 | return moment(val).format('YYYY MMMM DD, HH:MM:SS') 96 | }, 97 | },{ 98 | data: "howMany", 99 | titleFn: function() { 100 | return TAPi18n.__('campaign.table.howMany'); 101 | } 102 | },{ 103 | data: "opens()", 104 | titleFn: function() { 105 | return TAPi18n.__('campaign.table.opens'); 106 | } 107 | },{ 108 | data: "clicks()", 109 | titleFn: function() { 110 | return TAPi18n.__('campaign.table.clicks'); 111 | } 112 | }, 113 | { 114 | 115 | tmpl: Meteor.isClient && Template.buttonsForCrm, 116 | tmplContext(rowData) { 117 | return { 118 | item: rowData, 119 | disableEdit: true 120 | }; 121 | } 122 | }, 123 | ], 124 | selector(userId) { 125 | return { userId: userId, sent: true }; 126 | } 127 | }); -------------------------------------------------------------------------------- /client/pages/panel/campaigns/campaigns.js: -------------------------------------------------------------------------------- 1 | import { 2 | showConfirmDialog, 3 | showWarningAlert, 4 | showSuccessNotification, 5 | } from "/client/helpers/helpers"; 6 | import { callServerMethod } from "/client/helpers/server-method"; 7 | import { checkTable } from "/imports/checkboxes.js"; 8 | 9 | Template.pagePanelCampaigns.helpers({ 10 | selected: function () { 11 | return Template.instance().selectedCampaigns.get().length; 12 | }, 13 | }); 14 | 15 | Template.pagePanelCampaigns.events({ 16 | "change .table th input[type=checkbox]": function (event, template) { 17 | checkTable(event); 18 | $("table.table tbody tr td input[type=checkbox]").each(function () { 19 | const checked = $(this).is(":checked"); 20 | const campaign = $(this).attr("data-id"); 21 | campaignArray(checked, campaign, template); 22 | }); 23 | }, 24 | "change .table tr td input[type=checkbox]": function (event, template) { 25 | const checked = $(event.target).is(":checked"); 26 | const campaign = $(event.target).attr("data-id"); 27 | campaignArray(checked, campaign, template); 28 | }, 29 | "click #deleteSelected": function (event, template) { 30 | if (!template.selectedCampaigns.get().length) 31 | showWarningAlert( 32 | TAPi18n.__("alerts.warning"), 33 | TAPi18n.__("alerts.noSelected") 34 | ); 35 | else { 36 | showConfirmDialog({ 37 | title: TAPi18n.__("confirm.remove"), 38 | confirmCallback: function () { 39 | callServerMethod({ 40 | methodName: "removeCampaigns", 41 | params: { 42 | campaigns: template.selectedCampaigns.get(), 43 | }, 44 | resultCallback: function () { 45 | template.selectedCampaigns.set([]); 46 | showSuccessNotification(TAPi18n.__("alerts.done")); 47 | }, 48 | }); 49 | }, 50 | cancelCallback: function () {}, 51 | }); 52 | } 53 | }, 54 | }); 55 | Template.pagePanelCampaigns.onRendered(function () { 56 | const template = this; 57 | $(".table thead").addClass("text-primary"); 58 | $(".table thead").each(function () { 59 | $(this).find("th").first().append(''); 60 | }); 61 | $(".dataTables_wrapper") 62 | .find("table") 63 | .on("length.dt", function () { 64 | selectCampaigns(template); 65 | }) 66 | .on("order.dt", function () { 67 | selectCampaigns(template); 68 | }) 69 | .on("search.dt", function () { 70 | selectCampaigns(template); 71 | }) 72 | .on("page.dt", function () { 73 | selectCampaigns(template); 74 | }); 75 | }); 76 | 77 | Template.pagePanelCampaigns.onCreated(function () { 78 | this.selectedCampaigns = new ReactiveVar([]); 79 | }); 80 | 81 | Template.pagePanelCampaigns.onDestroyed(function () {}); 82 | 83 | function campaignArray(checked, campaign, template) { 84 | let campaignsArray = template.selectedCampaigns.get(); 85 | const index = campaignsArray.indexOf(campaign); 86 | if (campaign && checked) { 87 | //If do not exist 88 | if (index < 0) { 89 | campaignsArray.push(campaign); 90 | template.selectedCampaigns.set(campaignsArray); 91 | } 92 | } else if (campaign && !checked) { 93 | //If exist 94 | if (index > -1) { 95 | campaignsArray.splice(index, 1); 96 | } 97 | template.selectedCampaigns.set(campaignsArray); 98 | } else { 99 | return true; 100 | } 101 | } 102 | 103 | function selectCampaigns(template) { 104 | const campaignsArray = template.selectedCampaigns.get(); 105 | template.$("table tr td input[type=checkbox]").each(function () { 106 | const member = $(this).attr("data-id"); 107 | const index = campaignsArray.indexOf(member); 108 | if (index > -1) $(this).prop("checked", true); 109 | }); 110 | $(".text-truncate span").tooltip({ 111 | placement: "left", 112 | }); 113 | $(".mail span").tooltip({ 114 | placement: "top", 115 | }); 116 | } 117 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_typography.scss: -------------------------------------------------------------------------------- 1 | button, 2 | input, 3 | optgroup, 4 | select, 5 | textarea{ 6 | font-family: $sans-serif-family; 7 | } 8 | h1,h2,h3,h4,h5,h6{ 9 | font-weight: $font-weight-normal; 10 | } 11 | 12 | a{ 13 | color: $primary-color; 14 | &:hover, 15 | &:focus{ 16 | color: $primary-color; 17 | } 18 | } 19 | h1, .h1 { 20 | font-size: $font-size-h1; 21 | line-height: 1.15; 22 | margin-bottom: $margin-base-vertical * 2; 23 | 24 | small{ 25 | font-weight: $font-weight-bold; 26 | text-transform: uppercase; 27 | opacity: .8; 28 | } 29 | } 30 | h2, .h2{ 31 | font-size: $font-size-h2; 32 | margin-bottom: $margin-base-vertical * 2; 33 | } 34 | h3, .h3{ 35 | font-size: $font-size-h3; 36 | margin-bottom: $margin-base-vertical * 2; 37 | line-height: 1.4em; 38 | } 39 | h4, .h4{ 40 | font-size: $font-size-h4; 41 | line-height: 1.45em; 42 | margin-top: $margin-base-vertical * 2; 43 | margin-bottom: $margin-base-vertical; 44 | 45 | & + .category, 46 | &.title + .category{ 47 | margin-top: -10px; 48 | } 49 | } 50 | h5, .h5 { 51 | font-size: $font-size-h5; 52 | line-height: 1.4em; 53 | margin-bottom: 15px; 54 | } 55 | h6, .h6{ 56 | font-size: $font-size-h6; 57 | font-weight: $font-weight-bold; 58 | text-transform: uppercase; 59 | } 60 | p{ 61 | &.description{ 62 | font-size: 1.14em; 63 | } 64 | } 65 | 66 | // i.fa{ 67 | // font-size: 18px; 68 | // position: relative; 69 | // top: 1px; 70 | // } 71 | 72 | .title{ 73 | font-weight: $font-weight-bold; 74 | 75 | &.title-up{ 76 | text-transform: uppercase; 77 | 78 | a{ 79 | color: $black-color; 80 | text-decoration: none; 81 | } 82 | } 83 | & + .category{ 84 | margin-top: -10px; 85 | } 86 | } 87 | 88 | .description, 89 | .card-description, 90 | .footer-big p, 91 | .card .footer .stats{ 92 | color: $dark-gray; 93 | font-weight: $font-weight-light; 94 | } 95 | .category, 96 | .card-category{ 97 | text-transform: capitalize; 98 | font-weight: $font-weight-normal; 99 | color: $dark-gray; 100 | font-size: $font-size-mini; 101 | } 102 | 103 | .card-category{ 104 | font-size: $font-size-h6; 105 | } 106 | 107 | .text-primary, 108 | a.text-primary:focus, a.text-primary:hover { 109 | color: $brand-primary !important; 110 | } 111 | .text-info, 112 | a.text-info:focus, a.text-info:hover { 113 | color: $brand-info !important; 114 | } 115 | .text-success, 116 | a.text-success:focus, a.text-success:hover { 117 | color: $brand-success !important; 118 | } 119 | .text-warning, 120 | a.text-warning:focus, a.text-warning:hover { 121 | color: $brand-warning !important; 122 | } 123 | .text-danger, 124 | a.text-danger:focus, a.text-danger:hover { 125 | color: $brand-danger !important; 126 | } 127 | 128 | .text-gray, 129 | a.text-gray:focus, a.text-gray:hover{ 130 | color: $light-gray !important; 131 | } 132 | 133 | 134 | .blockquote{ 135 | border-left: none; 136 | border: 1px solid $default-color; 137 | padding: 20px; 138 | font-size: $font-size-blockquote; 139 | line-height: 1.8; 140 | 141 | small{ 142 | color: $default-color; 143 | font-size: $font-size-small; 144 | text-transform: uppercase; 145 | } 146 | 147 | &.blockquote-primary{ 148 | border-color: $primary-color; 149 | color: $primary-color; 150 | 151 | small{ 152 | color: $primary-color; 153 | } 154 | } 155 | 156 | &.blockquote-danger{ 157 | border-color: $danger-color; 158 | color: $danger-color; 159 | 160 | small{ 161 | color: $danger-color; 162 | } 163 | } 164 | 165 | &.blockquote-white{ 166 | border-color: $opacity-8; 167 | color: $white-color; 168 | 169 | small{ 170 | color: $opacity-8; 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/_checkboxes-radio.scss: -------------------------------------------------------------------------------- 1 | .from-check, 2 | .form-check-radio { 3 | margin-bottom: 12px; 4 | position: relative; 5 | } 6 | 7 | .form-check { 8 | padding-left: 0; 9 | margin-bottom: .5rem; 10 | 11 | .form-check-label{ 12 | display: inline-block; 13 | position: relative; 14 | cursor: pointer; 15 | padding-left: 35px; 16 | line-height: 26px; 17 | margin-bottom: 0; 18 | } 19 | 20 | .form-check-sign::before, 21 | .form-check-sign::after { 22 | content: " "; 23 | display: inline-block; 24 | position: absolute; 25 | width: 24px; 26 | height: 24px; 27 | left: 0; 28 | cursor: pointer; 29 | border-radius: 6px; 30 | top: 0; 31 | background-color: #AAA7A4; 32 | -webkit-transition: opacity 0.3s linear; 33 | -moz-transition: opacity 0.3s linear; 34 | -o-transition: opacity 0.3s linear; 35 | -ms-transition: opacity 0.3s linear; 36 | transition: opacity 0.3s linear; 37 | } 38 | .form-check-sign::after { 39 | font-family: 'FontAwesome'; 40 | content: "\f00c"; 41 | top: -1px; 42 | text-align: center; 43 | font-size: 15px; 44 | opacity: 0; 45 | color: #FFF; 46 | border: 0; 47 | background-color: inherit; 48 | } 49 | &.disabled{ 50 | .form-check-label{ 51 | color: $dark-gray; 52 | opacity: .5; 53 | cursor: not-allowed; 54 | } 55 | } 56 | 57 | } 58 | 59 | .form-check.disabled .form-check-label, 60 | .form-check.disabled .form-check-label { 61 | 62 | } 63 | 64 | .form-check input[type="checkbox"], 65 | .form-check-radio input[type="radio"]{ 66 | opacity: 0; 67 | position: absolute; 68 | visibility: hidden; 69 | } 70 | .form-check input[type="checkbox"]:checked + .form-check-sign::after{ 71 | opacity: 1; 72 | } 73 | 74 | .form-control input[type="checkbox"]:disabled + .form-check-sign::before, 75 | .checkbox input[type="checkbox"]:disabled + .form-check-sign::after{ 76 | cursor: not-allowed; 77 | } 78 | 79 | .form-check .form-check-label input[type="checkbox"]:disabled + .form-check-sign, 80 | .form-check-radio input[type="radio"]:disabled + .form-check-sign{ 81 | pointer-events: none !important; 82 | } 83 | 84 | .form-check-radio{ 85 | margin-left: -3px; 86 | 87 | .form-check-label{ 88 | padding-left: 2rem; 89 | } 90 | &.disabled{ 91 | .form-check-label{ 92 | color: $dark-gray; 93 | opacity: .5; 94 | cursor: not-allowed; 95 | } 96 | } 97 | } 98 | 99 | .form-check-radio .form-check-sign::before{ 100 | font-family: 'FontAwesome'; 101 | content: "\f10c"; 102 | font-size: 22px; 103 | -webkit-font-smoothing: antialiased; 104 | -moz-osx-font-smoothing: grayscale; 105 | display: inline-block; 106 | position: absolute; 107 | opacity: .50; 108 | left: 5px; 109 | top: -5px; 110 | } 111 | 112 | .form-check-label input[type="checkbox"]:checked + .form-check-sign:before{ 113 | background-color: #66615B; 114 | } 115 | 116 | .form-check-radio input[type="radio"] + .form-check-sign:after, 117 | .form-check-radio input[type="radio"] { 118 | opacity: 0; 119 | @include transition-opacity(0.3s, linear); 120 | content:" "; 121 | display: block; 122 | } 123 | 124 | .form-check-radio input[type="radio"]:checked + .form-check-sign::after { 125 | font-family: 'FontAwesome'; 126 | content: "\f192"; 127 | top: -5px; 128 | position: absolute; 129 | left: 5px; 130 | opacity: 1; 131 | font-size: 22px; 132 | } 133 | 134 | .form-check-radio input[type="radio"]:checked + .form-check-sign::after{ 135 | opacity: 1; 136 | } 137 | 138 | 139 | .form-check-radio input[type="radio"]:disabled + .form-check-sign::before, 140 | .form-check-radio input[type="radio"]:disabled + .form-check-sign::after { 141 | color: $dark-gray; 142 | } 143 | -------------------------------------------------------------------------------- /client/pages/panel/promising-prospects/promising-prospects.js: -------------------------------------------------------------------------------- 1 | import { ReactiveVar } from "meteor/reactive-var"; 2 | import { checkTable } from "/imports/checkboxes.js"; 3 | import { 4 | showConfirmDialog, 5 | showWarningAlert, 6 | showSuccessNotification, 7 | } from "/client/helpers/helpers"; 8 | import { callServerMethod } from "/client/helpers/server-method"; 9 | 10 | Template.pagePanelPromisingProspects.helpers({ 11 | selector: function () { 12 | return { 13 | $or: [{ opens: { $gt: 1 } }, { clicks: { $gt: 0 } }], 14 | }; 15 | }, 16 | }); 17 | 18 | Template.pagePanelPromisingProspects.events({ 19 | "change .table th input[type=checkbox]": function (event, template) { 20 | checkTable(event); 21 | $("table.table tbody tr td input[type=checkbox]").each(function () { 22 | const checked = $(this).is(":checked"); 23 | const member = $(this).attr("data-id"); 24 | memberArray(checked, member, template); 25 | }); 26 | }, 27 | "change .table tr td input[type=checkbox]": function (event, template) { 28 | const checked = $(event.target).is(":checked"); 29 | const member = $(event.target).attr("data-id"); 30 | memberArray(checked, member, template); 31 | }, 32 | "click #markUsed": function (event, template) { 33 | if (!template.selectedMembers.get().length) 34 | showWarningAlert( 35 | TAPi18n.__("alerts.warning"), 36 | TAPi18n.__("alerts.noSelected") 37 | ); 38 | else { 39 | showConfirmDialog({ 40 | title: TAPi18n.__("confirm.remove"), 41 | confirmCallback: function () { 42 | callServerMethod({ 43 | methodName: "markAsUsed", 44 | params: { 45 | messages: template.selectedMembers.get(), 46 | }, 47 | resultCallback: function () { 48 | template.selectedMembers.set([]); 49 | showSuccessNotification(TAPi18n.__("alerts.done")); 50 | }, 51 | }); 52 | }, 53 | cancelCallback: function () {}, 54 | }); 55 | } 56 | }, 57 | }); 58 | 59 | Template.pagePanelPromisingProspects.onRendered(function () { 60 | const template = this; 61 | $(".table thead").addClass("text-primary"); 62 | $(".table thead").each(function () { 63 | $(this).find("th").first().append(''); 64 | }); 65 | $(".dataTables_wrapper") 66 | .find("table") 67 | .on("length.dt", function () { 68 | selectCheckboxes(template); 69 | }) 70 | .on("order.dt", function () { 71 | selectCheckboxes(template); 72 | }) 73 | .on("search.dt", function () { 74 | selectCheckboxes(template); 75 | }) 76 | .on("page.dt", function () { 77 | selectCheckboxes(template); 78 | }); 79 | }); 80 | Template.pagePanelPromisingProspects.onCreated(function () { 81 | this.selectedMembers = new ReactiveVar([]); 82 | }); 83 | 84 | Template.pagePanelPromisingProspects.onDestroyed(function () { 85 | this.selectedMembers.set([]); 86 | }); 87 | 88 | function selectCheckboxes(template) { 89 | const membersArray = template.selectedMembers.get(); 90 | template.$("table tr td input[type=checkbox]").each(function () { 91 | const member = $(this).attr("data-id"); 92 | const index = membersArray.indexOf(member); 93 | if (index > -1) $(this).prop("checked", true); 94 | }); 95 | $(".text-truncate span").tooltip({ 96 | placement: "left", 97 | }); 98 | $(".mail span").tooltip({ 99 | placement: "top", 100 | }); 101 | $(".dataTables_wrapper").find("table th:last-child").addClass("text-right"); 102 | $(".dataTables_wrapper") 103 | .find("table tr td button.preview") 104 | .addClass("pull-right"); 105 | } 106 | 107 | function memberArray(checked, member, template) { 108 | let membersArray = template.selectedMembers.get(); 109 | const index = membersArray.indexOf(member); 110 | if (member && checked) { 111 | //If do not exist 112 | if (index < 0) { 113 | membersArray.push(member); 114 | template.selectedMembers.set(membersArray); 115 | } 116 | } else if (member && !checked) { 117 | //If exist 118 | if (index > -1) { 119 | membersArray.splice(index, 1); 120 | } 121 | template.selectedMembers.set(membersArray); 122 | } else { 123 | return true; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /client/pages/panel/campaigns/newCampain.js: -------------------------------------------------------------------------------- 1 | import { 2 | showConfirmDialog, 3 | showWarningAlert, 4 | showSuccessNotification, 5 | } from "/client/helpers/helpers"; 6 | import { callServerMethod } from "/client/helpers/server-method"; 7 | import { SessionSelectedMembers } from "/client/helpers/sessions"; 8 | Template.newCampain.helpers({ 9 | howMany: function () { 10 | return SessionSelectedMembers.get().length 11 | ? SessionSelectedMembers.get().length 12 | : 0; 13 | }, 14 | }); 15 | 16 | Template.newCampain.events({ 17 | "submit form": function (event, template) { 18 | event.preventDefault(); 19 | const subject = $("#title").val(); 20 | const campaignTitle = $("#campaignTitle").val(); 21 | const senderName = $("#senderName").val(); 22 | const html = $("#text").summernote("code"); 23 | if (subject == "") { 24 | showWarningAlert(TAPi18n.__("alerts.title"), TAPi18n.__("alerts.title2")); 25 | return false; 26 | } 27 | if (html == "


" || html == "") { 28 | showWarningAlert(TAPi18n.__("alerts.html"), TAPi18n.__("alerts.html2")); 29 | return false; 30 | } 31 | if (campaignTitle == "") { 32 | showWarningAlert( 33 | TAPi18n.__("alerts.title"), 34 | TAPi18n.__("alerts.titleCampain") 35 | ); 36 | return false; 37 | } 38 | showConfirmDialog({ 39 | title: TAPi18n.__("confirm.send2"), 40 | confirmCallback: function () { 41 | callServerMethod({ 42 | methodName: "sendEmails", 43 | params: { 44 | members: SessionSelectedMembers.get(), 45 | campaign: campaignTitle, 46 | subject: subject, 47 | name: senderName, 48 | html: html, 49 | }, 50 | resultCallback: function () { 51 | Router.go("panelCampaigns"); 52 | showSuccessNotification(TAPi18n.__("alerts.done")); 53 | SessionSelectedMembers.nullify(); 54 | }, 55 | }); 56 | }, 57 | cancelCallback: function () {}, 58 | }); 59 | }, 60 | }); 61 | 62 | Template.newCampain.onRendered(function () { 63 | const fNameButton = function (context) { 64 | const ui = $.summernote.ui; 65 | 66 | // create button 67 | const button = ui.button({ 68 | contents: TAPi18n.__("campaign.addName"), 69 | click: function () { 70 | context.invoke("editor.insertText", "{first_name}"); 71 | }, 72 | }); 73 | 74 | return button.render(); // return button as jquery object 75 | }; 76 | const lNameButton = function (context) { 77 | const ui = $.summernote.ui; 78 | 79 | // create button 80 | const button = ui.button({ 81 | contents: TAPi18n.__("campaign.addLName"), 82 | click: function () { 83 | context.invoke("editor.insertText", "{last_name}"); 84 | }, 85 | }); 86 | 87 | return button.render(); // return button as jquery object 88 | }; 89 | const companyButton = function (context) { 90 | const ui = $.summernote.ui; 91 | 92 | // create button 93 | const button = ui.button({ 94 | contents: TAPi18n.__("campaign.addCompany"), 95 | click: function () { 96 | context.invoke("editor.insertText", "{company}"); 97 | }, 98 | }); 99 | 100 | return button.render(); // return button as jquery object 101 | }; 102 | $("#text").summernote({ 103 | toolbar: [ 104 | ["style", ["style"]], 105 | [ 106 | "font", 107 | [ 108 | "bold", 109 | "italic", 110 | "underline", 111 | "strikethrough", 112 | "superscript", 113 | "subscript", 114 | "clear", 115 | ], 116 | ], 117 | ["fontname", ["fontname"]], 118 | ["fontsize", ["fontsize"]], 119 | ["color", ["color"]], 120 | ["para", ["ol", "ul", "paragraph", "height"]], 121 | ["table", ["table"]], 122 | ["insert", ["link", "picture", "hr"]], 123 | ["view", ["undo", "redo"]], 124 | ["mybutton1", ["fNameButton"]], 125 | ["mybutton2", ["lNameButton"]], 126 | ["mybutton3", ["companyButton"]], 127 | ], 128 | buttons: { 129 | fNameButton: fNameButton, 130 | lNameButton: lNameButton, 131 | companyButton: companyButton, 132 | }, 133 | }); 134 | }); 135 | 136 | Template.newCampain.onDestroyed(function () {}); 137 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 17 | do not have permission to do that, you may request the second reviewer to merge it for you. 18 | 19 | ## Code of Conduct 20 | 21 | ### Our Pledge 22 | 23 | In the interest of fostering an open and welcoming environment, we as 24 | contributors and maintainers pledge to making participation in our project and 25 | our community a harassment-free experience for everyone, regardless of age, body 26 | size, disability, ethnicity, gender identity and expression, level of experience, 27 | nationality, personal appearance, race, religion, or sexual identity and 28 | orientation. 29 | 30 | ### Our Standards 31 | 32 | Examples of behavior that contributes to creating a positive environment 33 | include: 34 | 35 | * Using welcoming and inclusive language 36 | * Being respectful of differing viewpoints and experiences 37 | * Gracefully accepting constructive criticism 38 | * Focusing on what is best for the community 39 | * Showing empathy towards other community members 40 | 41 | Examples of unacceptable behavior by participants include: 42 | 43 | * The use of sexualized language or imagery and unwelcome sexual attention or 44 | advances 45 | * Trolling, insulting/derogatory comments, and personal or political attacks 46 | * Public or private harassment 47 | * Publishing others' private information, such as a physical or electronic 48 | address, without explicit permission 49 | * Other conduct which could reasonably be considered inappropriate in a 50 | professional setting 51 | 52 | ### Our Responsibilities 53 | 54 | Project maintainers are responsible for clarifying the standards of acceptable 55 | behavior and are expected to take appropriate and fair corrective action in 56 | response to any instances of unacceptable behavior. 57 | 58 | Project maintainers have the right and responsibility to remove, edit, or 59 | reject comments, commits, code, wiki edits, issues, and other contributions 60 | that are not aligned to this Code of Conduct, or to ban temporarily or 61 | permanently any contributor for other behaviors that they deem inappropriate, 62 | threatening, offensive, or harmful. 63 | 64 | ### Scope 65 | 66 | This Code of Conduct applies both within project spaces and in public spaces 67 | when an individual is representing the project or its community. Examples of 68 | representing a project or community include using an official project e-mail 69 | address, posting via an official social media account, or acting as an appointed 70 | representative at an online or offline event. Representation of a project may be 71 | further defined and clarified by project maintainers. 72 | 73 | ### Enforcement 74 | 75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 76 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 77 | complaints will be reviewed and investigated and will result in a response that 78 | is deemed necessary and appropriate to the circumstances. The project team is 79 | obligated to maintain confidentiality with regard to the reporter of an incident. 80 | Further details of specific enforcement policies may be posted separately. 81 | 82 | Project maintainers who do not follow or enforce the Code of Conduct in good 83 | faith may face temporary or permanent repercussions as determined by other 84 | members of the project's leadership. 85 | 86 | ### Attribution 87 | 88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 89 | available at [http://contributor-covenant.org/version/1/4][version] 90 | 91 | [homepage]: http://contributor-covenant.org 92 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /client/styles/paper-dashboard/plugins/_plugin-animate-bootstrap-notify.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | // This file was modified by Creative Tim to keep only the animation that we need for Bootstrap Notify 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | @charset "UTF-8"; 34 | 35 | /*! 36 | Animate.css - http://daneden.me/animate 37 | Licensed under the MIT license - http://opensource.org/licenses/MIT 38 | 39 | Copyright (c) 2015 Daniel Eden 40 | */ 41 | 42 | .animated { 43 | -webkit-animation-duration: 1s; 44 | animation-duration: 1s; 45 | -webkit-animation-fill-mode: both; 46 | animation-fill-mode: both; 47 | } 48 | 49 | .animated.infinite { 50 | -webkit-animation-iteration-count: infinite; 51 | animation-iteration-count: infinite; 52 | } 53 | 54 | .animated.hinge { 55 | -webkit-animation-duration: 2s; 56 | animation-duration: 2s; 57 | } 58 | 59 | .animated.bounceIn, 60 | .animated.bounceOut { 61 | -webkit-animation-duration: .75s; 62 | animation-duration: .75s; 63 | } 64 | 65 | .animated.flipOutX, 66 | .animated.flipOutY { 67 | -webkit-animation-duration: .75s; 68 | animation-duration: .75s; 69 | } 70 | 71 | @-webkit-keyframes shake { 72 | from, to { 73 | -webkit-transform: translate3d(0, 0, 0); 74 | transform: translate3d(0, 0, 0); 75 | } 76 | 77 | 10%, 30%, 50%, 70%, 90% { 78 | -webkit-transform: translate3d(-10px, 0, 0); 79 | transform: translate3d(-10px, 0, 0); 80 | } 81 | 82 | 20%, 40%, 60%, 80% { 83 | -webkit-transform: translate3d(10px, 0, 0); 84 | transform: translate3d(10px, 0, 0); 85 | } 86 | } 87 | 88 | @keyframes shake { 89 | from, to { 90 | -webkit-transform: translate3d(0, 0, 0); 91 | transform: translate3d(0, 0, 0); 92 | } 93 | 94 | 10%, 30%, 50%, 70%, 90% { 95 | -webkit-transform: translate3d(-10px, 0, 0); 96 | transform: translate3d(-10px, 0, 0); 97 | } 98 | 99 | 20%, 40%, 60%, 80% { 100 | -webkit-transform: translate3d(10px, 0, 0); 101 | transform: translate3d(10px, 0, 0); 102 | } 103 | } 104 | 105 | .shake { 106 | -webkit-animation-name: shake; 107 | animation-name: shake; 108 | } 109 | 110 | 111 | 112 | @-webkit-keyframes fadeInDown { 113 | from { 114 | opacity: 0; 115 | -webkit-transform: translate3d(0, -100%, 0); 116 | transform: translate3d(0, -100%, 0); 117 | } 118 | 119 | to { 120 | opacity: 1; 121 | -webkit-transform: none; 122 | transform: none; 123 | } 124 | } 125 | 126 | @keyframes fadeInDown { 127 | from { 128 | opacity: 0; 129 | -webkit-transform: translate3d(0, -100%, 0); 130 | transform: translate3d(0, -100%, 0); 131 | } 132 | 133 | to { 134 | opacity: 1; 135 | -webkit-transform: none; 136 | transform: none; 137 | } 138 | } 139 | 140 | .fadeInDown { 141 | -webkit-animation-name: fadeInDown; 142 | animation-name: fadeInDown; 143 | } 144 | 145 | 146 | @-webkit-keyframes fadeOut { 147 | from { 148 | opacity: 1; 149 | } 150 | 151 | to { 152 | opacity: 0; 153 | } 154 | } 155 | 156 | @keyframes fadeOut { 157 | from { 158 | opacity: 1; 159 | } 160 | 161 | to { 162 | opacity: 0; 163 | } 164 | } 165 | 166 | .fadeOut { 167 | -webkit-animation-name: fadeOut; 168 | animation-name: fadeOut; 169 | } 170 | 171 | @-webkit-keyframes fadeOutDown { 172 | from { 173 | opacity: 1; 174 | } 175 | 176 | to { 177 | opacity: 0; 178 | -webkit-transform: translate3d(0, 100%, 0); 179 | transform: translate3d(0, 100%, 0); 180 | } 181 | } 182 | 183 | @keyframes fadeOutDown { 184 | from { 185 | opacity: 1; 186 | } 187 | 188 | to { 189 | opacity: 0; 190 | -webkit-transform: translate3d(0, 100%, 0); 191 | transform: translate3d(0, 100%, 0); 192 | } 193 | } 194 | 195 | .fadeOutDown { 196 | -webkit-animation-name: fadeOutDown; 197 | animation-name: fadeOutDown; 198 | } 199 | 200 | @-webkit-keyframes fadeOutUp { 201 | from { 202 | opacity: 1; 203 | } 204 | 205 | to { 206 | opacity: 0; 207 | -webkit-transform: translate3d(0, -100%, 0); 208 | transform: translate3d(0, -100%, 0); 209 | } 210 | } 211 | 212 | @keyframes fadeOutUp { 213 | from { 214 | opacity: 1; 215 | } 216 | 217 | to { 218 | opacity: 0; 219 | -webkit-transform: translate3d(0, -100%, 0); 220 | transform: translate3d(0, -100%, 0); 221 | } 222 | } 223 | 224 | .fadeOutUp { 225 | -webkit-animation-name: fadeOutUp; 226 | animation-name: fadeOutUp; 227 | } 228 | -------------------------------------------------------------------------------- /client/pages/panel/gdpr/gdpr.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/styles/paper-dashboard/mixins/_buttons.scss: -------------------------------------------------------------------------------- 1 | // Mixin for generating new styles 2 | @mixin btn-styles($btn-color, $btn-states-color) { 3 | background-color: $btn-color; 4 | 5 | &:hover, 6 | &:focus, 7 | &:active, 8 | &.active, 9 | &:active:focus, 10 | &:active:hover, 11 | &.active:focus, 12 | &.active:hover, 13 | .show > &.dropdown-toggle, 14 | .show > &.dropdown-toggle:focus, 15 | .show > &.dropdown-toggle:hover { 16 | background-color: $btn-states-color !important; 17 | color: $white-color !important; 18 | box-shadow: none !important; 19 | } 20 | 21 | &:not([data-action]):hover{ 22 | box-shadow: none; 23 | } 24 | 25 | &.disabled, 26 | &:disabled, 27 | &[disabled], 28 | fieldset[disabled] & { 29 | &, 30 | &:hover, 31 | &:focus, 32 | &.focus, 33 | &:active, 34 | &.active { 35 | background-color: $btn-color; 36 | border-color: $btn-color; 37 | } 38 | } 39 | 40 | // btn-neutral style 41 | @if $btn-color == $white-color{ 42 | color: $primary-color; 43 | 44 | &.btn-danger{ 45 | color: $danger-color; 46 | 47 | &:hover, 48 | &:focus, 49 | &:active, 50 | &:active:focus{ 51 | color: $danger-states-color !important; 52 | } 53 | } 54 | 55 | &.btn-info{ 56 | color: $info-color !important; 57 | 58 | &:hover, 59 | &:focus, 60 | &:active, 61 | &:active:focus{ 62 | color: $info-states-color !important; 63 | } 64 | } 65 | 66 | &.btn-warning{ 67 | color: $warning-color !important; 68 | 69 | &:hover, 70 | &:focus, 71 | &:active, 72 | &:active:focus{ 73 | color: $warning-states-color !important; 74 | } 75 | } 76 | 77 | &.btn-success{ 78 | color: $success-color !important; 79 | 80 | &:hover, 81 | &:focus, 82 | &:active, 83 | &:active:focus{ 84 | color: $success-states-color !important; 85 | } 86 | } 87 | 88 | &.btn-default{ 89 | color: $default-color !important; 90 | 91 | &:hover, 92 | &:focus, 93 | &:active, 94 | &:active:focus{ 95 | color: $default-states-color !important; 96 | } 97 | } 98 | 99 | &.active, 100 | &:active, 101 | &:active:focus, 102 | &:active:hover, 103 | &.active:focus, 104 | &.active:hover, 105 | .show > &.dropdown-toggle, 106 | .show > &.dropdown-toggle:focus, 107 | .show > &.dropdown-toggle:hover { 108 | background-color: $white-color !important; 109 | color: $primary-states-color !important; 110 | box-shadow: none !important; 111 | } 112 | 113 | &:hover, 114 | &:focus{ 115 | color: $primary-states-color !important; 116 | 117 | &:not(.nav-link){ 118 | box-shadow: none; 119 | } 120 | 121 | } 122 | 123 | } @else { 124 | color: $white-color; 125 | } 126 | 127 | &.btn-simple{ 128 | color: $btn-color; 129 | border-color: $btn-color; 130 | 131 | &:hover, 132 | &:focus, 133 | &:active{ 134 | background-color: $transparent-bg; 135 | color: $btn-states-color; 136 | border-color: $btn-states-color; 137 | box-shadow: none; 138 | } 139 | } 140 | 141 | &.btn-link{ 142 | color: $btn-color; 143 | 144 | &:hover, 145 | &:focus, 146 | &:active, 147 | &:active:focus { 148 | background-color: $transparent-bg; 149 | color: $btn-states-color; 150 | text-decoration: none; 151 | box-shadow: none; 152 | } 153 | } 154 | } 155 | 156 | @mixin btn-outline-styles($btn-color, $btn-states-color){ 157 | background: $transparent-bg; 158 | border: 2px solid $btn-color !important; 159 | color: $btn-color; 160 | @include opacity(1); 161 | 162 | &:hover, 163 | &:focus, 164 | &:active, 165 | &:focus:active, 166 | &.active, 167 | .open > &.dropdown-toggle { 168 | background-color: $btn-color !important; 169 | color: $fill-font-color !important; 170 | border-color: $btn-color !important; 171 | .caret{ 172 | border-top-color: $fill-font-color !important; 173 | } 174 | } 175 | 176 | .caret{ 177 | border-top-color: $white-color !important; 178 | } 179 | 180 | &.disabled, 181 | &:disabled, 182 | &[disabled], 183 | fieldset[disabled] & { 184 | &, 185 | &:hover, 186 | &:focus, 187 | &.focus, 188 | &:active, 189 | &.active { 190 | background-color: $transparent-bg !important; 191 | border-color: $btn-color !important; 192 | } 193 | } 194 | } 195 | 196 | @mixin btn-size($padding-vertical, $padding-horizontal, $font-size, $border){ 197 | font-size: $font-size; 198 | border-radius: $border; 199 | padding: $padding-vertical $padding-horizontal; 200 | 201 | &.btn-simple{ 202 | padding: $padding-vertical - 1 $padding-horizontal - 1; 203 | } 204 | 205 | } 206 | 207 | @mixin rotate-180(){ 208 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); 209 | -webkit-transform: rotate(180deg); 210 | -ms-transform: rotate(180deg); 211 | transform: rotate(180deg); 212 | } 213 | -------------------------------------------------------------------------------- /client/pages/panel/dashboard/dashboard.html: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------