mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
Merge branch 'master' into ft-modify-script-monitor
This commit is contained in:
@@ -241,7 +241,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWIqpBpStiqQp',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Growth',
|
||||
@@ -255,21 +255,21 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWKiTdQ6NiQFw',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9Iox3l2YqLTDR',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IlBKhsFz4hV2',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
},
|
||||
];
|
||||
} else {
|
||||
@@ -286,7 +286,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoVgJu5PKMLRJU',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Growth',
|
||||
@@ -300,21 +300,21 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoViZshjqzZ0vv',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9Ii6Qj3HLdtty',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IjvX2Flsvlcg',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWIqpBpStiqQp',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Growth',
|
||||
@@ -325,21 +325,21 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWKiTdQ6NiQFw',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9Iox3l2YqLTDR',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IlBKhsFz4hV2',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
},
|
||||
];
|
||||
} else {
|
||||
@@ -356,7 +356,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoVgJu5PKMLRJU',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Growth',
|
||||
@@ -370,21 +370,21 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoViZshjqzZ0vv',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9Ii6Qj3HLdtty',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IjvX2Flsvlcg',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -29,9 +29,17 @@ router.put(
|
||||
verificationToken
|
||||
);
|
||||
|
||||
if (!doesTxtRecordExist) {
|
||||
const { result, txtRecords } = doesTxtRecordExist;
|
||||
|
||||
if (!result) {
|
||||
const records =
|
||||
txtRecords.length > 1
|
||||
? txtRecords.join(', ')
|
||||
: txtRecords[0];
|
||||
return sendErrorResponse(req, res, {
|
||||
message: 'TXT record not found',
|
||||
message: `Please specify ${verificationToken} in your DNS. Looks like your current ${
|
||||
txtRecords.length > 1 ? 'records are' : 'record is'
|
||||
} ${records}`,
|
||||
code: 400,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ router.post('/', async function(req, res) {
|
||||
data.country = body.country;
|
||||
data.message = body.message || null;
|
||||
data.whitepaperName = body.whitepaper_name || null;
|
||||
data.source = JSON.parse(body.source) || null;
|
||||
const lead = await LeadService.create(data);
|
||||
return sendItemResponse(req, res, lead);
|
||||
} catch (error) {
|
||||
|
||||
@@ -29,7 +29,7 @@ module.exports = {
|
||||
planId: 'plan_GoWIqpBpStiqQp',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
monitorLimit: 5,
|
||||
userLimit: 1,
|
||||
extraUserFee: 264,
|
||||
@@ -51,7 +51,7 @@ module.exports = {
|
||||
planId: 'plan_GoWKiTdQ6NiQFw',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
monitorLimit: 10,
|
||||
userLimit: 1,
|
||||
extraUserFee: 588,
|
||||
@@ -62,7 +62,7 @@ module.exports = {
|
||||
planId: 'plan_H9Iox3l2YqLTDR',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
monitorLimit: 9999,
|
||||
userLimit: 1,
|
||||
extraUserFee: 99,
|
||||
@@ -73,7 +73,7 @@ module.exports = {
|
||||
planId: 'plan_H9IlBKhsFz4hV2',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
monitorLimit: 10,
|
||||
userLimit: 1,
|
||||
extraUserFee: 588,
|
||||
@@ -98,7 +98,7 @@ module.exports = {
|
||||
planId: 'plan_GoVgJu5PKMLRJU',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
monitorLimit: 5,
|
||||
userLimit: 1,
|
||||
extraUserFee: 264,
|
||||
@@ -120,7 +120,7 @@ module.exports = {
|
||||
planId: 'plan_GoViZshjqzZ0vv',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
monitorLimit: 10,
|
||||
userLimit: 1,
|
||||
extraUserFee: 588,
|
||||
@@ -131,7 +131,7 @@ module.exports = {
|
||||
planId: 'plan_H9Ii6Qj3HLdtty',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
monitorLimit: 9999,
|
||||
userLimit: 1,
|
||||
extraUserFee: 99,
|
||||
@@ -142,7 +142,7 @@ module.exports = {
|
||||
planId: 'plan_H9IjvX2Flsvlcg',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
monitorLimit: 10,
|
||||
userLimit: 1,
|
||||
extraUserFee: 588,
|
||||
|
||||
@@ -19,7 +19,7 @@ const leadSchema = new Schema({
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
},
|
||||
|
||||
source: Object,
|
||||
deletedById: { type: String, ref: 'User', index: true },
|
||||
});
|
||||
module.exports = mongoose.model('Lead', leadSchema);
|
||||
|
||||
@@ -67,7 +67,7 @@ const statusSchema = new Schema({
|
||||
// show or hide the probe bar
|
||||
hideProbeBar: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
default: true,
|
||||
},
|
||||
// show or hide uptime (%) on the status page
|
||||
hideUptime: {
|
||||
|
||||
@@ -1,98 +1,97 @@
|
||||
const mongoose = require('../config/db');
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
const userSchema = new Schema({
|
||||
name: { type: String, index: true },
|
||||
email: String,
|
||||
tempEmail: String,
|
||||
password: String,
|
||||
isVerified: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
sso: { type: String, ref: 'Sso', index: true },
|
||||
companyName: String,
|
||||
companyRole: String,
|
||||
companySize: String,
|
||||
referral: String,
|
||||
companyPhoneNumber: String,
|
||||
|
||||
airtableId: String,
|
||||
|
||||
onCallAlert: Array,
|
||||
profilePic: String,
|
||||
|
||||
twoFactorAuthEnabled: { type: Boolean, default: false },
|
||||
twoFactorSecretCode: String,
|
||||
otpauth_url: String,
|
||||
backupCodes: Array,
|
||||
|
||||
jwtRefreshToken: String,
|
||||
stripeCustomerId: String,
|
||||
resetPasswordToken: String,
|
||||
resetPasswordExpires: String,
|
||||
createdAt: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
timezone: String,
|
||||
lastActive: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
coupon: String,
|
||||
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
paymentFailedDate: {
|
||||
type: Date,
|
||||
default: null,
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: ['master-admin', 'user'],
|
||||
},
|
||||
isBlocked: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
adminNotes: [
|
||||
{
|
||||
note: { type: String },
|
||||
createdAt: { type: Date },
|
||||
const userSchema = new Schema(
|
||||
{
|
||||
name: { type: String, index: true },
|
||||
email: String,
|
||||
tempEmail: String,
|
||||
password: String,
|
||||
isVerified: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
sso: { type: String, ref: 'Sso', index: true },
|
||||
companyName: String,
|
||||
companyRole: String,
|
||||
companySize: String,
|
||||
referral: String,
|
||||
companyPhoneNumber: String,
|
||||
|
||||
deleted: { type: Boolean, default: false },
|
||||
airtableId: String,
|
||||
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
},
|
||||
onCallAlert: Array,
|
||||
profilePic: String,
|
||||
|
||||
deletedById: { type: String, ref: 'User', index: true },
|
||||
alertPhoneNumber: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
alertPhoneVerificationCode: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
alertPhoneVerificationCodeRequestTime: {
|
||||
type: Date,
|
||||
},
|
||||
tempAlertPhoneNumber: String,
|
||||
tutorial: Object,
|
||||
createdBy: { type: String, ref: 'User' },
|
||||
identification: [
|
||||
{
|
||||
subscription: Object,
|
||||
userAgent: String,
|
||||
twoFactorAuthEnabled: { type: Boolean, default: false },
|
||||
twoFactorSecretCode: String,
|
||||
otpauth_url: String,
|
||||
backupCodes: Array,
|
||||
|
||||
jwtRefreshToken: String,
|
||||
stripeCustomerId: String,
|
||||
resetPasswordToken: String,
|
||||
resetPasswordExpires: String,
|
||||
timezone: String,
|
||||
lastActive: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
],
|
||||
source: Object,
|
||||
});
|
||||
coupon: String,
|
||||
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
paymentFailedDate: {
|
||||
type: Date,
|
||||
default: null,
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: ['master-admin', 'user'],
|
||||
},
|
||||
isBlocked: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
adminNotes: [
|
||||
{
|
||||
note: { type: String },
|
||||
createdAt: { type: Date },
|
||||
},
|
||||
],
|
||||
|
||||
deleted: { type: Boolean, default: false },
|
||||
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
},
|
||||
|
||||
deletedById: { type: String, ref: 'User', index: true },
|
||||
alertPhoneNumber: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
alertPhoneVerificationCode: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
alertPhoneVerificationCodeRequestTime: {
|
||||
type: Date,
|
||||
},
|
||||
tempAlertPhoneNumber: String,
|
||||
tutorial: Object,
|
||||
createdBy: { type: String, ref: 'User' },
|
||||
identification: [
|
||||
{
|
||||
subscription: Object,
|
||||
userAgent: String,
|
||||
},
|
||||
],
|
||||
source: Object,
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
module.exports = mongoose.model('User', userSchema);
|
||||
|
||||
@@ -50,6 +50,34 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
logLeads: function({
|
||||
name,
|
||||
country,
|
||||
email,
|
||||
fullname,
|
||||
message,
|
||||
phone,
|
||||
source,
|
||||
type,
|
||||
volume,
|
||||
website,
|
||||
}) {
|
||||
if (!base) return;
|
||||
|
||||
return base('Leads').create({
|
||||
Name: name,
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
Country: country,
|
||||
'Full Name': fullname,
|
||||
Message: message,
|
||||
Type: type,
|
||||
Volume: volume,
|
||||
Website: website,
|
||||
Source: JSON.stringify(source),
|
||||
});
|
||||
},
|
||||
|
||||
deleteUser: function(airtableId) {
|
||||
if (!base) return;
|
||||
|
||||
|
||||
@@ -306,14 +306,9 @@ module.exports = {
|
||||
componentId: component._id,
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
monitors.map(async monitor => {
|
||||
await MonitorService.deleteBy(
|
||||
{ _id: monitor._id },
|
||||
userId
|
||||
);
|
||||
})
|
||||
);
|
||||
for (const monitor of monitors) {
|
||||
await MonitorService.deleteBy({ _id: monitor._id }, userId);
|
||||
}
|
||||
await NotificationService.create(
|
||||
component.projectId,
|
||||
`A Component ${component.name} was deleted from the project by ${component.deletedById.name}`,
|
||||
|
||||
@@ -156,9 +156,10 @@ module.exports = {
|
||||
// records is an array of arrays
|
||||
// flatten the array to a single array
|
||||
const txtRecords = flatten(records);
|
||||
return txtRecords.some(
|
||||
const result = txtRecords.some(
|
||||
txtRecord => verificationToken === txtRecord
|
||||
);
|
||||
return { result, txtRecords };
|
||||
} catch (error) {
|
||||
if (error.code === 'ENODATA') {
|
||||
throw {
|
||||
|
||||
@@ -23,6 +23,7 @@ module.exports = {
|
||||
lead.country = data.country;
|
||||
lead.message = data.message;
|
||||
lead.whitepaperName = data.whitepaperName;
|
||||
lead.source = data.source;
|
||||
|
||||
lead.templateName = 'Request Demo';
|
||||
if (data.whitepaperName) {
|
||||
@@ -43,6 +44,17 @@ module.exports = {
|
||||
); //whitepaper name should be stored in moreInfo.
|
||||
}
|
||||
}
|
||||
AirtableService.logLeads({
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
phone: data.phone,
|
||||
country: data.country,
|
||||
message: data.message,
|
||||
website: data.website,
|
||||
source: data.source,
|
||||
volume: data.companySize,
|
||||
type: data.type,
|
||||
});
|
||||
return lead;
|
||||
} catch (error) {
|
||||
ErrorService.log('leadService.create', error);
|
||||
@@ -64,3 +76,4 @@ module.exports = {
|
||||
const LeadsModel = require('../models/lead');
|
||||
const MailService = require('./mailService');
|
||||
const ErrorService = require('./errorService');
|
||||
const AirtableService = require('./airtableService');
|
||||
|
||||
@@ -451,24 +451,21 @@ module.exports = {
|
||||
const statusPages = await this.findBy({
|
||||
'monitors.monitor': monitorId,
|
||||
});
|
||||
for (const statusPage of statusPages) {
|
||||
const monitors = statusPage.monitors.filter(
|
||||
monitorData =>
|
||||
String(
|
||||
monitorData.monitor._id || monitorData.monitor
|
||||
) !== String(monitorId)
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
statusPages.map(async statusPage => {
|
||||
const monitors = statusPage.monitors.filter(
|
||||
monitorData =>
|
||||
String(monitorData.monitor) !== String(monitorId)
|
||||
if (monitors.length !== statusPage.monitors.length) {
|
||||
await this.updateOneBy(
|
||||
{ _id: statusPage._id },
|
||||
{ monitors }
|
||||
);
|
||||
|
||||
if (monitors.length !== statusPage.monitors.length) {
|
||||
statusPage = await this.updateOneBy(
|
||||
{ _id: statusPage._id },
|
||||
{ monitors }
|
||||
);
|
||||
}
|
||||
|
||||
return statusPage;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
ErrorService.log('statusPageService.removeMonitor', error);
|
||||
throw error;
|
||||
|
||||
@@ -297,7 +297,6 @@ module.exports = {
|
||||
} else {
|
||||
const newUser = await UserService.create({
|
||||
email,
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
|
||||
invitedTeamMembers.push(newUser);
|
||||
|
||||
@@ -5,7 +5,7 @@ module.exports = function getSlug(name) {
|
||||
name = String(name);
|
||||
if (!name || !name.trim()) return;
|
||||
|
||||
let slug = slugify(name, { remove: /[*+~.()'"!:@]+/g });
|
||||
let slug = slugify(name, { remove: /[&*+~.,\\/()|'"!:@]+/g });
|
||||
slug = `${slug}-${generate('1234567890', 8)}`;
|
||||
slug = slug.toLowerCase();
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This script rollbacks every project if any of the deployment fails
|
||||
chmod +x ./ci/scripts/job-status.sh
|
||||
|
||||
function rollback {
|
||||
export status=`./ci/scripts/job-status.sh deploy_production_$1`
|
||||
export status=`./ci/scripts/job-status.sh production_$1`
|
||||
if [[ $status == \"success\" ]]
|
||||
then
|
||||
echo "Rolling back $1"
|
||||
@@ -17,7 +17,7 @@ function rollback {
|
||||
}
|
||||
|
||||
function check {
|
||||
export status=`./ci/scripts/job-status.sh deploy_production_$1`
|
||||
export status=`./ci/scripts/job-status.sh production_$1`
|
||||
if [[ $status == \"failed\" ]]
|
||||
then
|
||||
echo "Deployment unsuccessful for $1, rolling back all new deployments"
|
||||
|
||||
@@ -6,7 +6,7 @@ This script rollbacks every project if any of the deployment fails
|
||||
chmod +x ./ci/scripts/job-status.sh
|
||||
|
||||
function rollback {
|
||||
export status=`./ci/scripts/job-status.sh deploy_staging_$1`
|
||||
export status=`./ci/scripts/job-status.sh staging_$1`
|
||||
if [[ $status == \"success\" ]]
|
||||
then
|
||||
echo "Rolling back $1"
|
||||
@@ -23,7 +23,7 @@ function rollback {
|
||||
}
|
||||
|
||||
function check {
|
||||
export status=`./ci/scripts/job-status.sh deploy_staging_$1`
|
||||
export status=`./ci/scripts/job-status.sh staging_$1`
|
||||
if [[ $status == \"failed\" ]]
|
||||
then
|
||||
echo "Deployment unsuccessful for $1, rolling back all new deployments"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
deploy_production_accounts:
|
||||
production_accounts:
|
||||
stage: Deploy
|
||||
retry: 2
|
||||
allow_failure: true
|
||||
@@ -28,7 +28,7 @@ deploy_production_accounts:
|
||||
name: production
|
||||
|
||||
# DEPLOYMENT STAGE - Accounts
|
||||
deploy_staging_accounts:
|
||||
staging_accounts:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
deploy_production_admin-dashboard:
|
||||
production_admin-dashboard:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
script:
|
||||
@@ -27,7 +27,7 @@ deploy_production_admin-dashboard:
|
||||
name: production
|
||||
|
||||
# DEPLOYMENT STAGE - Admin Dashboard
|
||||
deploy_staging_admin-dashboard:
|
||||
staging_admin-dashboard:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
script:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
deploy_production_api-docs:
|
||||
production_api-docs:
|
||||
stage: Deploy
|
||||
retry: 2
|
||||
allow_failure: true
|
||||
@@ -28,7 +28,7 @@ deploy_production_api-docs:
|
||||
name: production
|
||||
|
||||
# DEPLOYMENT STAGE - Api docs
|
||||
deploy_staging_api-docs:
|
||||
staging_api-docs:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# DEPLOYMENT STAGE - BACKEND
|
||||
deploy_staging_backend:
|
||||
staging_backend:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -29,7 +29,7 @@ deploy_staging_backend:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_backend:
|
||||
production_backend:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# DEPLOYMENT STAGE - Dashboard
|
||||
deploy_staging_dashboard:
|
||||
staging_dashboard:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -29,7 +29,7 @@ deploy_staging_dashboard:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_dashboard:
|
||||
production_dashboard:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## DEPLOYMENT STAGE FOR STAGING - fyipe-acme-http-01-staging
|
||||
deploy_staging_fyipe-acme-http-01:
|
||||
staging_fyipe-acme-http-01:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -27,7 +27,7 @@ deploy_staging_fyipe-acme-http-01:
|
||||
name: staging
|
||||
|
||||
## DEPLOYMENT STAGE FOR PRODUCTION - fyipe-acme-http-01
|
||||
deploy_production_fyipe-acme-http-01:
|
||||
production_fyipe-acme-http-01:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## DEPLOYMENT STAGE FOR STAGING - fyipe-gl-manager-staging
|
||||
deploy_staging_fyipe-gl-manager:
|
||||
staging_fyipe-gl-manager:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -27,7 +27,7 @@ deploy_staging_fyipe-gl-manager:
|
||||
name: staging
|
||||
|
||||
## DEPLOYMENT STAGE FOR PRODUCTION - fyipe-gl-manager
|
||||
deploy_production_fyipe-gl-manager:
|
||||
production_fyipe-gl-manager:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## DEPLOYMENT STAGE FOR STAGING - fyipe-le-store-staging
|
||||
deploy_staging_fyipe-le-store:
|
||||
staging_fyipe-le-store:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -27,7 +27,7 @@ deploy_staging_fyipe-le-store:
|
||||
name: staging
|
||||
|
||||
## DEPLOYMENT STAGE FOR PRODUCTION - fyipe-le-store
|
||||
deploy_production_fyipe-le-store:
|
||||
production_fyipe-le-store:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# DEPLOYMENT STAGE - Haraka
|
||||
deploy_staging_haraka:
|
||||
staging_haraka:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -29,7 +29,7 @@ deploy_staging_haraka:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_haraka:
|
||||
production_haraka:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# DEPLOYMENT STAGE - HELM CHART
|
||||
deploy_staging_helm-chart:
|
||||
staging_helm-chart:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
script:
|
||||
@@ -36,7 +36,7 @@ deploy_staging_helm-chart:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_helm-chart:
|
||||
production_helm-chart:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
script:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
deploy_production_home:
|
||||
production_home:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -28,7 +28,7 @@ deploy_production_home:
|
||||
name: production
|
||||
|
||||
# DEPLOYMENT STAGE - Home
|
||||
deploy_staging_home:
|
||||
staging_home:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# DEPLOYMENT STAGE - HTTP-TEST-SERVER
|
||||
deploy_staging_http_test_server:
|
||||
staging_http_test_server:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
script:
|
||||
@@ -28,7 +28,7 @@ deploy_staging_http_test_server:
|
||||
name: staging
|
||||
|
||||
# DEPLOYMENT STAGE - HTTP-TEST-SERVER
|
||||
deploy_production_http_test_server:
|
||||
production_http_test_server:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
script:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# DEPLOYMENT STAGE - Init Script
|
||||
deploy_staging_init-script:
|
||||
staging_init-script:
|
||||
stage: Deploy
|
||||
retry: 2
|
||||
allow_failure: true
|
||||
@@ -30,7 +30,7 @@ deploy_staging_init-script:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_init-script:
|
||||
production_init-script:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## DEPLOYMENT STAGE - JAVA SDK
|
||||
deploy_staging_java-sdk:
|
||||
staging_java-sdk:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -41,7 +41,7 @@ deploy_staging_java-sdk:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_java-sdk:
|
||||
production_java-sdk:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## DEPLOYMENT STAGE - JS SDK
|
||||
deploy_staging_js-sdk:
|
||||
staging_js-sdk:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -36,7 +36,7 @@ deploy_staging_js-sdk:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_js-sdk:
|
||||
production_js-sdk:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# DEPLOYMENT STAGE - LICENSING
|
||||
deploy_staging_licensing:
|
||||
staging_licensing:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -29,7 +29,7 @@ deploy_staging_licensing:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_licensing:
|
||||
production_licensing:
|
||||
stage: Deploy
|
||||
retry: 2
|
||||
allow_failure: true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## DEPLOYMENT STAGE - PHP SDK
|
||||
deploy_staging_php-sdk:
|
||||
staging_php-sdk:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -36,7 +36,7 @@ deploy_staging_php-sdk:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_php-sdk:
|
||||
production_php-sdk:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
deploy_production_probe:
|
||||
production_probe:
|
||||
stage: Deploy
|
||||
retry: 2
|
||||
allow_failure: true
|
||||
@@ -29,7 +29,7 @@ deploy_production_probe:
|
||||
name: production
|
||||
|
||||
# DEPLOYMENT STAGE - Probe
|
||||
deploy_staging_probe:
|
||||
staging_probe:
|
||||
stage: Deploy
|
||||
retry: 2
|
||||
allow_failure: true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## DEPLOYMENT STAGE - PYTHON SDK
|
||||
deploy_staging_python-sdk:
|
||||
staging_python-sdk:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
@@ -34,7 +34,7 @@ deploy_staging_python-sdk:
|
||||
name: staging
|
||||
|
||||
## DEPLOYMENT STAGE - PYTHON SDK
|
||||
deploy_production_python-sdk:
|
||||
production_python-sdk:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
retry: 2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# DEPLOYMENT STAGE - status-page
|
||||
deploy_staging_status-page:
|
||||
staging_status-page:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
script:
|
||||
@@ -28,7 +28,7 @@ deploy_staging_status-page:
|
||||
environment:
|
||||
name: staging
|
||||
|
||||
deploy_production_status-page:
|
||||
production_status-page:
|
||||
stage: Deploy
|
||||
allow_failure: true
|
||||
script:
|
||||
|
||||
@@ -22,6 +22,9 @@ RUN npm ci --only=production
|
||||
# Copy app source
|
||||
COPY . /usr/src/app
|
||||
|
||||
# Install purgecss to mame CSS files smaller.
|
||||
RUN npm install purgecss -g
|
||||
|
||||
# Bundle app source
|
||||
RUN npm run build
|
||||
|
||||
|
||||
29477
dashboard/package-lock.json
generated
29477
dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -89,6 +89,7 @@
|
||||
"lint": "eslint .",
|
||||
"dev": "PORT=3000 react-scripts start",
|
||||
"build": "react-scripts build && npm run build-sw",
|
||||
"postbuild": "purgecss --css build/assets/css/*.css --content build/index.html build/static/js/*.js --output build/assets/css",
|
||||
"build-sw": "node ./src/sw-build.js",
|
||||
"test": "jest --forceExit --testSequencer ./src/test/puppeteer/CustomSequencer.js --runInBand ./src/test/puppeteer/*.test.js",
|
||||
"enterprise-test": "jest --forceExit --runInBand ./src/test/puppeteer/*.test.enterprise.js",
|
||||
@@ -105,6 +106,7 @@
|
||||
"fyipe": "^3.0.10051",
|
||||
"jest-localstorage-mock": "^2.2.0",
|
||||
"npm-force-resolutions": "0.0.3",
|
||||
"purgecss": "^4.0.3",
|
||||
"redux-mock-store": "^1.5.3",
|
||||
"should": "^13.2.3"
|
||||
},
|
||||
|
||||
@@ -121,7 +121,8 @@ div.block-chart div.bar:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
******* Tipsy Tooltips ******/ .tipsy {
|
||||
/******* Tipsy Tooltips ******/
|
||||
.tipsy {
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
padding: 5px;
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { putApi, deleteApi } from '../api';
|
||||
import * as types from '../constants/domain';
|
||||
|
||||
export function resetDomain() {
|
||||
return {
|
||||
type: types.RESET_VERIFY_DOMAIN,
|
||||
};
|
||||
}
|
||||
|
||||
export function verifyDomainRequest() {
|
||||
return {
|
||||
type: types.VERIFY_DOMAIN_REQUEST,
|
||||
|
||||
@@ -670,7 +670,7 @@ export function closeIncidentSuccess(incident) {
|
||||
}
|
||||
|
||||
export function closeIncident(projectId, incidentId) {
|
||||
//This fucntion will switch to incidentId of the params beig passed.
|
||||
//This function will switch to incidentId of the params beig passed.
|
||||
return function(dispatch) {
|
||||
const promise = postApi(
|
||||
`incident/${projectId}/close/${incidentId}`,
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import React, { useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { CrumbItem, Breadcrumbs } from 'react-breadcrumbs-dynamic';
|
||||
import { PropTypes } from 'prop-types';
|
||||
import { Spinner } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
|
||||
function BreadCrumbs({ styles, showDeleteBtn, close, name }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
function BreadCrumbs({
|
||||
styles,
|
||||
showDeleteBtn,
|
||||
close,
|
||||
name,
|
||||
closeIncidentRequest,
|
||||
}) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const closeAllIncidents = async () => {
|
||||
setLoading(true);
|
||||
await close();
|
||||
setLoading(false);
|
||||
};
|
||||
@@ -50,7 +57,8 @@ function BreadCrumbs({ styles, showDeleteBtn, close, name }) {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{showDeleteBtn && name === 'Home' && (
|
||||
|
||||
{loading && showDeleteBtn && name === 'Home' && (
|
||||
<div
|
||||
id="incidents-close-all-btn"
|
||||
style={{ height: 'fit-content' }}
|
||||
@@ -63,29 +71,34 @@ function BreadCrumbs({ styles, showDeleteBtn, close, name }) {
|
||||
style={{ marginTop: '0' }}
|
||||
></div>
|
||||
Close all Resolved Incidents
|
||||
<span style={{ marginLeft: '5px' }}>
|
||||
{loading && (
|
||||
<Spinner
|
||||
style={{
|
||||
stroke: '#000000',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
<span style={{ marginLeft: '5px' }}></span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ShouldRender if={closeIncidentRequest.requesting !== false}>
|
||||
<Spinner
|
||||
style={{
|
||||
stroke: '#000000',
|
||||
}}
|
||||
/>
|
||||
</ShouldRender>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
BreadCrumbs.displayName = 'BreadCrumbs';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
closeIncidentRequest: state.incident.closeincident,
|
||||
};
|
||||
};
|
||||
BreadCrumbs.propTypes = {
|
||||
styles: PropTypes.string.isRequired,
|
||||
showDeleteBtn: PropTypes.bool,
|
||||
close: PropTypes.func,
|
||||
name: PropTypes.string,
|
||||
closeIncidentRequest: PropTypes.object,
|
||||
};
|
||||
|
||||
export default BreadCrumbs;
|
||||
export default connect(mapStateToProps, null)(BreadCrumbs);
|
||||
|
||||
@@ -873,7 +873,7 @@ export class IncidentStatus extends Component {
|
||||
<div className="bs-margin-right">
|
||||
<span className="bs-content-create bs-text-bold">
|
||||
Created
|
||||
by
|
||||
by{' '}
|
||||
</span>
|
||||
<span className=" bs-text-bold">
|
||||
{this
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { bindActionCreators, compose } from 'redux';
|
||||
import { FormLoader } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { openModal, closeModal } from '../../actions/modal';
|
||||
@@ -141,7 +142,7 @@ MonitorViewChangeComponentBox.propTypes = {
|
||||
component: PropTypes.object,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
export default compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps, mapDispatchToProps)
|
||||
)(MonitorViewChangeComponentBox);
|
||||
|
||||
@@ -392,6 +392,38 @@ class Search extends Component {
|
||||
return 'db-SideNav-icon--appLog';
|
||||
case 'Performance Tracker':
|
||||
return 'db-SideNav-icon--performanceTracker';
|
||||
case 'Home':
|
||||
return 'db-SideNav-icon--home';
|
||||
case 'Report':
|
||||
return 'db-SideNav-icon--report';
|
||||
case 'Back':
|
||||
return 'db-SideNav-icon--back';
|
||||
case 'Security':
|
||||
return 'db-SideNav-icon--security';
|
||||
case 'businessSettings':
|
||||
return 'db-SideNav-icon--businessSettings';
|
||||
case 'consulting':
|
||||
return 'db-SideNav-icon--consulting';
|
||||
case 'email':
|
||||
return 'db-SideNav-icon--email';
|
||||
case 'sms':
|
||||
return 'db-SideNav-icon--sms';
|
||||
case 'callrouting':
|
||||
return 'db-SideNav-icon--callrouting';
|
||||
case 'integration':
|
||||
return 'db-SideNav-icon--integration';
|
||||
case 'probes':
|
||||
return 'db-SideNav-icon--probes';
|
||||
case 'git':
|
||||
return 'db-SideNav-icon--git';
|
||||
case 'docker':
|
||||
return 'db-SideNav-icon--docker';
|
||||
case 'apis':
|
||||
return 'db-SideNav-icon--apis';
|
||||
case 'user':
|
||||
return 'db-SideNav-icon--user';
|
||||
case 'password':
|
||||
return 'db-SideNav-icon--password';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -93,11 +93,10 @@ export class CustomerBalance extends Component {
|
||||
.then(response => {
|
||||
const { status, amount_received } = response.data;
|
||||
const { paymentIntent } = this.props;
|
||||
|
||||
|
||||
if (status === 'succeeded') {
|
||||
const creditedBalance = amount_received / 100;
|
||||
getProjects();
|
||||
|
||||
getProjects().then( () =>
|
||||
openModal({
|
||||
id: MessageBoxId,
|
||||
content: MessageBox,
|
||||
@@ -105,7 +104,7 @@ export class CustomerBalance extends Component {
|
||||
message: `Transaction successful, your balance is now ${(
|
||||
balance + creditedBalance
|
||||
).toFixed(2)}$`,
|
||||
});
|
||||
}));
|
||||
} else {
|
||||
this.handlePaymentIntent(paymentIntent.client_secret);
|
||||
}
|
||||
|
||||
@@ -116,12 +116,14 @@ let RenderMonitor = ({
|
||||
id="monitor-name"
|
||||
component={RenderSelect}
|
||||
options={[
|
||||
...allMonitors.map(m => ({
|
||||
value: m._id,
|
||||
label: `${
|
||||
getParentComponent(m).name
|
||||
} / ${m.name}`,
|
||||
})),
|
||||
...allMonitors
|
||||
.filter(m => getParentComponent(m))
|
||||
.map(m => ({
|
||||
value: m._id,
|
||||
label: `${
|
||||
getParentComponent(m).name
|
||||
} / ${m.name}`,
|
||||
})),
|
||||
]}
|
||||
onChange={() => resetSelectedCharts()}
|
||||
/>
|
||||
|
||||
@@ -4,9 +4,12 @@ import PropTypes from 'prop-types';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { ListLoader } from '../basic/Loader';
|
||||
import { resetDomain } from '../../actions/domain';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
class VerifyDomainModal extends Component {
|
||||
componentDidMount() {
|
||||
this.props.resetDomain();
|
||||
window.addEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
@@ -298,6 +301,10 @@ VerifyDomainModal.propTypes = {
|
||||
domainField: PropTypes.object,
|
||||
requesting: PropTypes.bool,
|
||||
propArr: PropTypes.array,
|
||||
resetDomain: PropTypes.func,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(VerifyDomainModal);
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ resetDomain }, dispatch);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(VerifyDomainModal);
|
||||
|
||||
@@ -321,7 +321,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWIqpBpStiqQp',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
title: 'Startup Plan',
|
||||
},
|
||||
{
|
||||
@@ -336,7 +336,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWKiTdQ6NiQFw',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
title: 'Growth Plan',
|
||||
},
|
||||
{
|
||||
@@ -344,14 +344,14 @@ export const PricingPlan = {
|
||||
planId: 'plan_H9Iox3l2YqLTDR',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IlBKhsFz4hV2',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188 / Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
title: 'Scale Plan',
|
||||
},
|
||||
];
|
||||
@@ -369,7 +369,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoVgJu5PKMLRJU',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
title: 'Startup Plan',
|
||||
},
|
||||
{
|
||||
@@ -384,7 +384,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoViZshjqzZ0vv',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
title: 'Growth Plan',
|
||||
},
|
||||
{
|
||||
@@ -392,14 +392,14 @@ export const PricingPlan = {
|
||||
planId: 'plan_H9Ii6Qj3HLdtty',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IjvX2Flsvlcg',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188 / Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
title: 'Scale Plan',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -5,6 +5,7 @@ export const CREATE_DOMAIN_FAILURE = 'CREATE_DOMAIN_FAILURE';
|
||||
export const VERIFY_DOMAIN_REQUEST = 'VERIFY_DOMAIN_REQUEST';
|
||||
export const VERIFY_DOMAIN_SUCCESS = 'VERIFY_DOMAIN_SUCCESS';
|
||||
export const VERIFY_DOMAIN_FAILURE = 'VERIFY_DOMAIN_FAILURE';
|
||||
export const RESET_VERIFY_DOMAIN = 'RESET_VERIFY_DOMAIN';
|
||||
|
||||
export const DELETE_DOMAIN_REQUEST = 'DELETE_DOMAIN_REQUEST';
|
||||
export const DELETE_DOMAIN_SUCCESS = 'DELETE_DOMAIN_SUCCESS';
|
||||
|
||||
@@ -82,7 +82,7 @@ class Groups extends Component {
|
||||
route={getParentRoute(pathname)}
|
||||
name="Project Settings"
|
||||
/>
|
||||
<BreadCrumbItem route={pathname} name="Groups" />
|
||||
<BreadCrumbItem route={pathname} name="Team Groups" />
|
||||
<div className="Margin-vertical--12">
|
||||
<div>
|
||||
<div id="settingsPage">
|
||||
|
||||
@@ -121,6 +121,7 @@ import {
|
||||
VERIFY_DOMAIN_FAILURE,
|
||||
VERIFY_DOMAIN_REQUEST,
|
||||
VERIFY_DOMAIN_SUCCESS,
|
||||
RESET_VERIFY_DOMAIN,
|
||||
CREATE_DOMAIN_REQUEST,
|
||||
CREATE_DOMAIN_SUCCESS,
|
||||
CREATE_DOMAIN_FAILURE,
|
||||
@@ -773,6 +774,17 @@ export default function statusPage(state = INITIAL_STATE, action) {
|
||||
addMoreDomain: false,
|
||||
};
|
||||
|
||||
case RESET_VERIFY_DOMAIN:
|
||||
return {
|
||||
...state,
|
||||
verifyDomain: {
|
||||
...state.verifyDomain,
|
||||
requesting: false,
|
||||
success: false,
|
||||
error: null,
|
||||
},
|
||||
};
|
||||
|
||||
case VERIFY_DOMAIN_REQUEST:
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
|
||||
=======================================================
|
||||
|
||||
# CHECKLIST
|
||||
|
||||
=======================================================
|
||||
|
||||
- Backup your database.
|
||||
|
||||
- Run this script:
|
||||
|
||||
```
|
||||
# Deploy to production
|
||||
git checkout hotfix-release
|
||||
|
||||
5
home/public/js/getCookies.js
Normal file
5
home/public/js/getCookies.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// This is basicaly meant to get a cookie by name
|
||||
var getCookiebyName = function (name) {
|
||||
var pair = document.cookie.match(new RegExp(name + '=([^;]+)'));
|
||||
return !!pair ? pair[1] : null;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
let accountsUrl = window.location.origin+'/accounts';
|
||||
let backendUrl = window.location.origin+'/api'
|
||||
let backendUrl = window.location.hostname==='localhost'? 'http://localhost:3002': window.location.origin+'/api'
|
||||
|
||||
|
||||
//eslint-disable-next-line
|
||||
function loginUrl(extra) {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<div class="content">
|
||||
|
||||
<header>
|
||||
<h1 style="line-height: unset; font-size: 43px !important;" class="Header-title common-PageTitle header-font header-size black">Monitor any API. Get alerted instantly when things don't look right.</h1>
|
||||
<h1 style="line-height: unset; font-size: 43px !important;" class="Header-title common-PageTitle header-font header-size black">Monitor your API. Get alerted instantly when things don't look right.</h1>
|
||||
<div style="margin-top: -100px">
|
||||
<%- include("stars", {
|
||||
description1: '"...monitors and parses *ANY* API response"',
|
||||
@@ -280,7 +280,7 @@
|
||||
by: "Sanjay Malik, Mindspoke"
|
||||
},
|
||||
{
|
||||
quote: "Fyipe lets us shave critical minutes during downtime by alerting the right team member and escalating effec",
|
||||
quote: "Fyipe lets us shave critical minutes during downtime by alerting the right team member and escalating effectively",
|
||||
by: "Sarah Yusuf, Genesys"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-Box">
|
||||
<button style="margin-right: 5px; font-size: 10px;" class="common-Button"
|
||||
<button id="accept-cookies" style="margin-right: 5px; font-size: 10px;" class="common-Button"
|
||||
onclick="setCookie('acceptCookies', true, 365);" data-analytics-action="contact_sales"
|
||||
data-analytics-source="header_cta">
|
||||
Accept Cookies
|
||||
|
||||
2145
home/views/demo.ejs
2145
home/views/demo.ejs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -161,17 +161,17 @@
|
||||
for (const param of params) {
|
||||
sourceObj = { ...sourceObj, [`${param[0]}`]: param[1] }
|
||||
}
|
||||
// Set a Cookie
|
||||
function setCookie(cName, cValue, expDays) {
|
||||
let date = new Date();
|
||||
date.setTime(date.getTime() + (expDays * 24 * 60 * 60 * 1000));
|
||||
const expires = "expires=" + date.toUTCString();
|
||||
// Remember to set back the domain since you used localhost for development purposes
|
||||
document.cookie = cName + "=" + cValue + "; " + expires + ";" + "; " + "domain=" + window.location.hostname;
|
||||
}
|
||||
// Apply setCookie
|
||||
setCookie('source', JSON.stringify(sourceObj), 1);
|
||||
}
|
||||
// Set a Cookie
|
||||
function setCookie(cName, cValue, expDays) {
|
||||
let date = new Date();
|
||||
date.setTime(date.getTime() + (expDays * 24 * 60 * 60 * 1000));
|
||||
const expires = "expires=" + date.toUTCString();
|
||||
|
||||
document.cookie = cName + "=" + cValue + "; " + expires + "; path=/" + "; " + "domain="+window.location.host;
|
||||
}
|
||||
// Apply setCookie
|
||||
setCookie('source', JSON.stringify(sourceObj), 1);
|
||||
</script>
|
||||
<img height="1" width="1" style="display:none" alt="Facebook"
|
||||
src="https://www.facebook.com/tr?id=244894116420899&ev=PageView&noscript=1" />
|
||||
|
||||
@@ -245,6 +245,9 @@
|
||||
<img src="/img/v3/pricing/header/plan-star.svg" width="26" height="26" alt="fees icon"> Basic
|
||||
On-Call Scheduling.
|
||||
</li>
|
||||
<li class="Plan-listItem">
|
||||
<img src="/img/v3/pricing/header/plan-star.svg" width="26" height="26" alt="fees icon">Unlimited Subscribers and Status Page Viewers
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="Plan-cardPricing">
|
||||
@@ -308,6 +311,9 @@
|
||||
<img src="/img/v3/pricing/header/plan-star.svg" width="26" height="26" alt="fees icon"> Advanced
|
||||
On-Call Scheduling.
|
||||
</li>
|
||||
<li class="Plan-listItem">
|
||||
<img src="/img/v3/pricing/header/plan-star.svg" width="26" height="26" alt="fees icon">Unlimited Subscribers and Status Page Viewers
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="Plan-cardPricing">
|
||||
@@ -370,6 +376,9 @@
|
||||
<img src="/img/v3/pricing/header/plan-star.svg" width="26" height="26" alt="fees icon"> Advanced
|
||||
On-Call Scheduling.
|
||||
</li>
|
||||
<li class="Plan-listItem">
|
||||
<img src="/img/v3/pricing/header/plan-star.svg" width="26" height="26" alt="fees icon">Unlimited Subscribers and Status Page Viewers
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="Plan-cardPricing">
|
||||
@@ -659,6 +668,22 @@
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
</tr>
|
||||
<tr class="pricing-compare-table">
|
||||
<td class="pricing-compare-table">
|
||||
<span class="pricing-compare-table font14">
|
||||
Unlimited Private Status Page viewers <br />
|
||||
<i>Add unlimited internal employees to view your private status page.</i>
|
||||
</span>
|
||||
</td>
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
<td class="default pricing-compare-table"><span class="tick pricing-compare-table"><img height="20"
|
||||
width="20" src="/img/tick.svg" alt="yes" /></span></td>
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
</tr>
|
||||
<tr class="pricing-compare-table">
|
||||
<td class="pricing-compare-table">
|
||||
<span class="pricing-compare-table font14">
|
||||
@@ -1115,7 +1140,40 @@
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="pricing-compare-table">
|
||||
<td class="pricing-compare-table">
|
||||
<span class="pricing-compare-table font14">
|
||||
Cancel Anytime <br />
|
||||
<i>Cancel anytime, no questions asked.</i>
|
||||
</span>
|
||||
</td>
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
<td class="default pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
</tr>
|
||||
<tr class="pricing-compare-table">
|
||||
<td class="pricing-compare-table">
|
||||
<span class="pricing-compare-table font14">
|
||||
Discounted Annual Plan <br />
|
||||
<i>Save 18% with an annual plan.</i>
|
||||
</span>
|
||||
</td>
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
<td class="default pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
|
||||
<td class="pricing-compare-table"><span class="tick pricing-compare-table"><img height="20" width="20"
|
||||
src="/img/tick.svg" alt="yes" /></span></td>
|
||||
</tr>
|
||||
<tr class="pricing-compare-table">
|
||||
<td class="pricing-compare-table">
|
||||
<span class="pricing-compare-table font14">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en" id="contact">
|
||||
|
||||
<head>
|
||||
<title>Fyipe | Monitor your Website's every second</title>
|
||||
<title>Fyipe | Monitor your website every second</title>
|
||||
|
||||
<meta name="description"
|
||||
content="Fyipe monitors websites, API's, and servers and alerts your team if something goes wrong. It also keeps your customers updated about any downtime.">
|
||||
@@ -17,26 +17,28 @@
|
||||
amplitude.getInstance().logEvent('PAGE VIEW: HOME > PRODUCT > WEBSITE MONITORING');
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
function onClickFunc() {
|
||||
let playButton = document.getElementById("playButton");
|
||||
let gif = document.getElementById("uptime-monitoring-gif");
|
||||
let videoDiv= document.getElementById("videoDiv");
|
||||
let video= document.getElementById("video");
|
||||
<script>
|
||||
function onClickFunc(index) {
|
||||
let playButton = document.getElementById(`playButton-${index}`);
|
||||
let gif = document.getElementById(`gif-${index}`);
|
||||
let videoDiv= document.getElementById(`videoDiv-${index}`);
|
||||
let video= document.getElementById(`video-${index}`);
|
||||
let grid = document.getElementById(`grid-${index}`);
|
||||
playButton.style.display = "none";
|
||||
gif.style.display="none";
|
||||
videoDiv.style.display="block";
|
||||
// grid.style.width="1000px";
|
||||
video.src += "?autoplay=1";
|
||||
}
|
||||
function mouseOverFunc()
|
||||
function mouseOverFunc(index)
|
||||
{
|
||||
let playButton = document.getElementById("playButton");
|
||||
let playButton = document.getElementById(`playButton-${index}`);
|
||||
playButton.src="/img/play-button-black.svg";
|
||||
playButton.style.cursor="pointer";
|
||||
}
|
||||
function mouseOutFunc()
|
||||
function mouseOutFunc(index)
|
||||
{
|
||||
let playButton = document.getElementById("playButton");
|
||||
let playButton = document.getElementById(`playButton-${index}`);
|
||||
playButton.src="/img/play-button-grey.svg";
|
||||
}
|
||||
</script>
|
||||
@@ -49,29 +51,86 @@
|
||||
<div class="globalContent">
|
||||
<div id="main-content">
|
||||
|
||||
|
||||
<section class="marquee nav-animation-element">
|
||||
|
||||
<div id="bubble-overlay">
|
||||
<div class="content">
|
||||
|
||||
<header>
|
||||
<h1 class="Header-title common-PageTitle header-font header-size black">Website Monitoring</h1>
|
||||
<p style="margin-top:50px"><i>Downtime happens.</i> <span class="highlight"> Fyipe's monitoring lets you know exactly what is wrong with your service, at the right time, </span> saving you critical minutes during downtime. </p>
|
||||
<h1 style="line-height: unset; font-size: 43px !important;" class="Header-title common-PageTitle header-font header-size black">Monitor your website. Get alerted instantly when things don't look right.</h1>
|
||||
<div style="margin-top: -100px">
|
||||
<%- include("stars", {
|
||||
description1: '"...monitors a lot of website metrics"',
|
||||
name1: "- AlternativeTo",
|
||||
link1: "https://alternativeto.net/software/fyipe/about",
|
||||
description2: '"...does so much more than just monitoring."',
|
||||
name2: "- Capterra",
|
||||
link2: "https://www.capterra.com/p/176513/Fyipe/",
|
||||
description3: '"...alerts the second our website goes down."',
|
||||
name3: "- G2 Crowd",
|
||||
link3: "https://www.g2.com/products/fyipe/reviews"
|
||||
}) %>
|
||||
</div>
|
||||
<p style="margin-top:50px; font-size: 20px"><span class="highlight">Analyze uptime & performance of your website.</span> Improve poorly performing pages to improve your app. Get alerted by SMS, Email or Call when things go wrong.</p>
|
||||
</header>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="
|
||||
margin: 0 auto;
|
||||
;">
|
||||
<div class="checkbox-items" style="max-width: 1000px; margin: auto;">
|
||||
<div class="checkbox-feature top-margin-10">
|
||||
<div><img height="22" src="/img/check-mark-button.svg" /> </div> <div
|
||||
class="checkbox-text">Unlimited Website Monitoring</div>
|
||||
</div>
|
||||
<div class="checkbox-feature top-margin-10">
|
||||
<div><img height="22" src="/img/check-mark-button.svg" /></div> <div
|
||||
class="checkbox-text">Get Email, SMS & Call alerts</div>
|
||||
</div>
|
||||
|
||||
<div class="checkbox-feature top-margin-10">
|
||||
<div><img height="22" src="/img/check-mark-button.svg" /></div> <div
|
||||
class="checkbox-text">Monitor on any criteria </div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="checkbox-items" style="max-width: 1000px; margin: auto;">
|
||||
<div class="checkbox-feature top-margin-10">
|
||||
<div><img height="22" src="/img/check-mark-button.svg" /> </div> <div
|
||||
class="checkbox-text">Performance Reports</div>
|
||||
</div>
|
||||
<div class="checkbox-feature top-margin-10">
|
||||
<div><img height="22" src="/img/check-mark-button.svg" /></div> <div
|
||||
class="checkbox-text">SSL Monitoring</div>
|
||||
</div>
|
||||
|
||||
<div class="checkbox-feature top-margin-10">
|
||||
<div><img height="22" src="/img/check-mark-button.svg" /></div> <div
|
||||
class="checkbox-text">SEO Monitoring</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<div class="gif-container">
|
||||
<img src="/img/animated-gif/uptime-monitoring.gif" class="imageshadow" alt="Uptime Monitoring" id="uptime-monitoring-gif" style="border-radius: 5px;width: 100%;" />
|
||||
<img class="play" id="playButton" onmouseout="mouseOutFunc()"onmouseover="mouseOverFunc()"onclick="onClickFunc()" src="/img/play-button-grey.svg"/>
|
||||
<div id="videoDiv" class="videoClass">
|
||||
<iframe id="video" width="850" height="500" src="https://www.youtube.com/embed/C1WcqtlG0AM?modestbranding=1&rel=0&enablejsapi=1&controls=1&frameborder=0&allowfullscreen=1" rel="0" enablejsapi="1" modestbranding="1" controls="1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div>
|
||||
<div class="vid_section">
|
||||
<div class="grid-1"></div>
|
||||
<div class="grid-2 text-center" id="grid-1">
|
||||
<img src="/img/animated-gif/uptime-monitoring.gif" class="imageshadow" alt="Uptime Monitoring" id="gif-1" style="border-radius: 5px;width: 100%;" />
|
||||
|
||||
<img class="play" style="z-index: 3" id="playButton-1" onmouseout="mouseOutFunc(1)"onmouseover="mouseOverFunc(1)"onclick="onClickFunc(1)" src="/img/play-button-grey.svg"/>
|
||||
<div id="videoDiv-1" class="videoClass">
|
||||
<iframe id="video" width="850" height="500" src="https://www.youtube.com/embed/C1WcqtlG0AM?modestbranding=1&rel=0&enablejsapi=1&controls=1&frameborder=0&allowfullscreen=1" rel="0" enablejsapi="1" modestbranding="1" controls="1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="grid-3">
|
||||
<img src="/img/3mindemobg.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% include ./cta-buttons %>
|
||||
|
||||
@@ -115,6 +174,10 @@
|
||||
<% include ./cta-buttons %>
|
||||
|
||||
<% include ./customer-section %>
|
||||
<%- include('feature-table', {
|
||||
title:"We don't just do Website Monitoring, <br/><span class='highlight'> We do <i>everything</i> in terms of SRE and DevOps. </span>",
|
||||
description: "Start with website monitoring and then expand into whole lot more without increasing your bottom line. <br/> One interface. One conversation. One permission model. Thousands of features. "
|
||||
}) %>
|
||||
<div class="sectionBorderTop">
|
||||
<% include ./comparision-chart %>
|
||||
</div>
|
||||
@@ -209,6 +272,33 @@
|
||||
</div>
|
||||
|
||||
<% include ./cta-buttons %>
|
||||
|
||||
<%- include("testimonials", {
|
||||
title: "Software teams around the world love Fyipe!",
|
||||
description: "Software development teams around the world have switched to Fyipe to fundamentally improve their SRE workflow to build better and more reliable software.",
|
||||
quotes: [
|
||||
{
|
||||
quote: "Having website and app reliability alerts lets our team be more accountable and responsible to software they are building. They now own the code.",
|
||||
by: "Sanjay Malik, Mindspoke"
|
||||
},
|
||||
{
|
||||
quote: "Fyipe lets us shave critical minutes during downtime by alerting the right team member and escalating effectively",
|
||||
by: "Sarah Yusuf, Genesys"
|
||||
},
|
||||
{
|
||||
quote: "No more writing and maintaining internal tools to monitor our software. Fyipe let's us focus on our business.",
|
||||
by: "Jennifer Jones, Improbable"
|
||||
},
|
||||
{
|
||||
quote: "Incident management is now a breeze. Everyone in the company knows what is wrong and what needs to be fixed.",
|
||||
by: "Shubham Aggarwal, CloudBoost"
|
||||
},
|
||||
{
|
||||
quote: "We can see exactly what the state of the software and our service is. Less confusion and we're much more efficient.",
|
||||
by: "Trevos Crosse, PNI Digital"
|
||||
}
|
||||
]
|
||||
}) %>
|
||||
|
||||
<header style="padding-top:50px; border-top: 1px solid rgba(0,0,0,.07); border-bottom:unset">
|
||||
<h2 class="header">How monitoring helps your business?</h2>
|
||||
|
||||
@@ -41,7 +41,7 @@ async function run() {
|
||||
await removeField(
|
||||
statusPageCollection,
|
||||
{ _id: statusPage._id },
|
||||
{ domain: '' }
|
||||
'domain'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ async function run() {
|
||||
await removeField(
|
||||
statusPageCollection,
|
||||
{ _id: statusPage._id },
|
||||
{ monitorIds: '' }
|
||||
'monitorIds'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ async function run() {
|
||||
await removeField(
|
||||
scheduledEventCollection,
|
||||
{ _id: event._id },
|
||||
{ monitorId: '' }
|
||||
'monitorId'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ async function run() {
|
||||
await removeField(
|
||||
scheduledEventNoteCollection,
|
||||
{ _id: eventNote._id },
|
||||
{ incident_state: '' }
|
||||
'incident_state'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ async function run() {
|
||||
await removeField(
|
||||
monitorCollection,
|
||||
{ _id: monitor._id },
|
||||
{ monitorCategoryId: '' }
|
||||
'monitorCategoryId'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,11 +15,7 @@ async function run() {
|
||||
|
||||
await update(monitorCollection, { _id: monitor._id }, data);
|
||||
|
||||
await removeField(
|
||||
monitorCollection,
|
||||
{ _id: monitor._id },
|
||||
{ variables: '' }
|
||||
);
|
||||
await removeField(monitorCollection, { _id: monitor._id }, 'variables');
|
||||
}
|
||||
|
||||
return `Script ran for ${monitors.length} monitors`;
|
||||
|
||||
@@ -23,7 +23,19 @@ async function run() {
|
||||
await removeField(
|
||||
incomingRequestCollection,
|
||||
{ _id: request._id },
|
||||
{ filterCriteria: '', filterCondition: '', filterText: '' }
|
||||
'filterCriteria'
|
||||
);
|
||||
|
||||
await removeField(
|
||||
incomingRequestCollection,
|
||||
{ _id: request._id },
|
||||
'filterCondition'
|
||||
);
|
||||
|
||||
await removeField(
|
||||
incomingRequestCollection,
|
||||
{ _id: request._id },
|
||||
'filterText'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ async function run() {
|
||||
const monitors = await find(monitorCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
|
||||
@@ -6,7 +6,7 @@ async function run() {
|
||||
const applicationSecurities = await find(applicationSecurityCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < applicationSecurities.length; i++) {
|
||||
|
||||
@@ -6,7 +6,7 @@ async function run() {
|
||||
const containerSecurities = await find(containerSecurityCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < containerSecurities.length; i++) {
|
||||
|
||||
@@ -6,7 +6,7 @@ async function run() {
|
||||
const logContainers = await find(logContainerCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < logContainers.length; i++) {
|
||||
|
||||
@@ -6,7 +6,7 @@ async function run() {
|
||||
const errorTrackers = await find(errortrackerCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < errorTrackers.length; i++) {
|
||||
|
||||
@@ -6,7 +6,7 @@ async function run() {
|
||||
const components = await find(componentCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
|
||||
@@ -6,7 +6,7 @@ async function run() {
|
||||
const projects = await find(projectCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (const project of projects) {
|
||||
|
||||
@@ -14,7 +14,7 @@ async function run() {
|
||||
{ remoteLoginUrl: sso.samlSsoUrl }
|
||||
);
|
||||
|
||||
await removeField(ssoCollection, { _id: sso._id }, { samlSsoUrl: '' });
|
||||
await removeField(ssoCollection, { _id: sso._id }, 'samlSsoUrl');
|
||||
}
|
||||
|
||||
return `Script ran for ${ssos.length} ssos`;
|
||||
|
||||
@@ -6,7 +6,7 @@ async function run() {
|
||||
const statusPages = await find(statusPageCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < statusPages.length; i++) {
|
||||
|
||||
@@ -6,7 +6,7 @@ async function run() {
|
||||
const schedules = await find(schedulesCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < schedules.length; i++) {
|
||||
|
||||
@@ -7,7 +7,7 @@ async function run() {
|
||||
const items = await find(scheduledCollection, {
|
||||
$or: [
|
||||
{ slug: { $exists: false } },
|
||||
{ slug: { $regex: /[*+~.()'"!:@]+/g } },
|
||||
{ slug: { $regex: /[&*+~.,\\/()|'"!:@]+/g } },
|
||||
],
|
||||
});
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
|
||||
@@ -33,12 +33,12 @@ async function removeMany(collection, query) {
|
||||
async function removeField(collection, query, field) {
|
||||
return global.db
|
||||
.collection(collection)
|
||||
.updateOne(query, { $unset: field }, { multi: true });
|
||||
.updateOne(query, { $unset: { [field]: '' } }, { multi: true });
|
||||
}
|
||||
async function removeFieldsFromMany(collection, query, field) {
|
||||
return global.db
|
||||
.collection(collection)
|
||||
.updateMany(query, { $unset: field }, { multi: true });
|
||||
.updateMany(query, { $unset: { [field]: '' } }, { multi: true });
|
||||
}
|
||||
|
||||
async function rename(oldCollectionName, newCollectionName) {
|
||||
|
||||
@@ -5,7 +5,7 @@ module.exports = function getSlug(name) {
|
||||
name = String(name);
|
||||
if (!name || !name.trim()) return;
|
||||
|
||||
let slug = slugify(name, { remove: /[*+~.()'"!:@]+/g });
|
||||
let slug = slugify(name, { remove: /[&*+~.,\\/()|'"!:@]+/g });
|
||||
slug = `${slug}-${generate('1234567890', 8)}`;
|
||||
slug = slug.toLowerCase();
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,42 +1,13 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="451.847px" height="451.847px" viewBox="0 0 451.847 451.847" style="enable-background:new 0 0 451.847 451.847;"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<path d="M225.923,354.706c-8.098,0-16.195-3.092-22.369-9.263L9.27,151.157c-12.359-12.359-12.359-32.397,0-44.751
|
||||
c12.354-12.354,32.388-12.354,44.748,0l171.905,171.915l171.906-171.909c12.359-12.354,32.391-12.354,44.744,0
|
||||
c12.365,12.354,12.365,32.392,0,44.751L248.292,345.449C242.115,351.621,234.018,354.706,225.923,354.706z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
|
||||
<g>
|
||||
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-linejoin="bevel" stroke-miterlimit="10" points="15,24 32,41
|
||||
49,24 "/>
|
||||
</g>
|
||||
<g>
|
||||
<circle fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" cx="32" cy="32" r="30.999"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 988 B After Width: | Height: | Size: 734 B |
13
status-page/public/images/up-arrow.svg
Normal file
13
status-page/public/images/up-arrow.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
|
||||
<g>
|
||||
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-linejoin="bevel" stroke-miterlimit="10" points="15,40 32,23
|
||||
49,40 "/>
|
||||
</g>
|
||||
<g>
|
||||
<circle fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" cx="32" cy="32" r="30.999"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 734 B |
@@ -1135,9 +1135,16 @@ margin-left:10px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.sp__icon--forward {
|
||||
background-image: url('../images/right-arrow.svg');
|
||||
.sp__icon--down {
|
||||
background-image: url('../images/arrow-down.svg');
|
||||
background-repeat: no-repeat;
|
||||
margin-top:0
|
||||
}
|
||||
|
||||
.sp__icon--up {
|
||||
background-image: url('../images/up-arrow.svg');
|
||||
background-repeat: no-repeat;
|
||||
margin-top:0
|
||||
}
|
||||
|
||||
.sp__icon--forward:hover {
|
||||
|
||||
376
status-page/src/components/Collapsible/Collapsible.js
Normal file
376
status-page/src/components/Collapsible/Collapsible.js
Normal file
@@ -0,0 +1,376 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import setInTransition from './setInTransition';
|
||||
|
||||
class Collapsible extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.timeout = undefined;
|
||||
|
||||
// Defaults the dropdown to be closed
|
||||
if (props.open) {
|
||||
this.state = {
|
||||
isClosed: false,
|
||||
shouldSwitchAutoOnNextCycle: false,
|
||||
height: 'auto',
|
||||
transition: 'none',
|
||||
hasBeenOpened: true,
|
||||
overflow: props.overflowWhenOpen,
|
||||
inTransition: false,
|
||||
};
|
||||
} else {
|
||||
this.state = {
|
||||
isClosed: true,
|
||||
shouldSwitchAutoOnNextCycle: false,
|
||||
height: 0,
|
||||
transition: `height ${props.transitionTime}ms ${props.easing}`,
|
||||
hasBeenOpened: false,
|
||||
overflow: 'hidden',
|
||||
inTransition: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.state.shouldOpenOnNextCycle) {
|
||||
this.continueOpenCollapsible();
|
||||
}
|
||||
|
||||
if (
|
||||
(prevState.height === 'auto' || prevState.height === 0) &&
|
||||
this.state.shouldSwitchAutoOnNextCycle === true
|
||||
) {
|
||||
window.clearTimeout(this.timeout);
|
||||
this.timeout = window.setTimeout(() => {
|
||||
// Set small timeout to ensure a true re-render
|
||||
this.setState({
|
||||
height: 0,
|
||||
overflow: 'hidden',
|
||||
isClosed: true,
|
||||
shouldSwitchAutoOnNextCycle: false,
|
||||
});
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// If there has been a change in the open prop (controlled by accordion)
|
||||
if (prevProps.open !== this.props.open) {
|
||||
if (this.props.open === true) {
|
||||
this.openCollapsible();
|
||||
this.props.onOpening();
|
||||
} else {
|
||||
this.closeCollapsible();
|
||||
this.props.onClosing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
closeCollapsible() {
|
||||
const { innerRef } = this;
|
||||
|
||||
this.setState({
|
||||
shouldSwitchAutoOnNextCycle: true,
|
||||
height: innerRef.scrollHeight,
|
||||
transition: `height ${
|
||||
this.props.transitionCloseTime
|
||||
? this.props.transitionCloseTime
|
||||
: this.props.transitionTime
|
||||
}ms ${this.props.easing}`,
|
||||
inTransition: setInTransition(innerRef.scrollHeight),
|
||||
});
|
||||
}
|
||||
|
||||
openCollapsible() {
|
||||
this.setState({
|
||||
inTransition: setInTransition(this.innerRef.scrollHeight),
|
||||
shouldOpenOnNextCycle: true,
|
||||
});
|
||||
}
|
||||
|
||||
continueOpenCollapsible = () => {
|
||||
const { innerRef } = this;
|
||||
|
||||
this.setState({
|
||||
height: innerRef.scrollHeight,
|
||||
transition: `height ${this.props.transitionTime}ms ${this.props.easing}`,
|
||||
isClosed: false,
|
||||
hasBeenOpened: true,
|
||||
inTransition: setInTransition(innerRef.scrollHeight),
|
||||
shouldOpenOnNextCycle: false,
|
||||
});
|
||||
};
|
||||
|
||||
handleTriggerClick = event => {
|
||||
if (this.props.triggerDisabled || this.state.inTransition) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if (this.props.handleTriggerClick) {
|
||||
this.props.handleTriggerClick(this.props.accordionPosition);
|
||||
} else {
|
||||
if (this.state.isClosed === true) {
|
||||
this.openCollapsible();
|
||||
this.props.onOpening();
|
||||
this.props.onTriggerOpening();
|
||||
} else {
|
||||
this.closeCollapsible();
|
||||
this.props.onClosing();
|
||||
this.props.onTriggerClosing();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderNonClickableTriggerElement() {
|
||||
if (
|
||||
this.props.triggerSibling &&
|
||||
typeof this.props.triggerSibling === 'string'
|
||||
) {
|
||||
return (
|
||||
<span
|
||||
className={`${this.props.classParentString}__trigger-sibling`}
|
||||
>
|
||||
{this.props.triggerSibling}
|
||||
</span>
|
||||
);
|
||||
} else if (
|
||||
this.props.triggerSibling &&
|
||||
typeof this.props.triggerSibling === 'function'
|
||||
) {
|
||||
return this.props.triggerSibling();
|
||||
} else if (this.props.triggerSibling) {
|
||||
return <this.props.triggerSibling />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
handleTransitionEnd = e => {
|
||||
// only handle transitions that origin from the container of this component
|
||||
if (e.target !== this.innerRef) {
|
||||
return;
|
||||
}
|
||||
// Switch to height auto to make the container responsive
|
||||
if (!this.state.isClosed) {
|
||||
this.setState({
|
||||
height: 'auto',
|
||||
overflow: this.props.overflowWhenOpen,
|
||||
inTransition: false,
|
||||
});
|
||||
this.props.onOpen();
|
||||
} else {
|
||||
this.setState({ inTransition: false });
|
||||
this.props.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
setInnerRef = ref => (this.innerRef = ref);
|
||||
|
||||
render() {
|
||||
const dropdownStyle = {
|
||||
height: this.state.height,
|
||||
WebkitTransition: this.state.transition,
|
||||
msTransition: this.state.transition,
|
||||
transition: this.state.transition,
|
||||
overflow: this.state.overflow,
|
||||
margin: '0 35px',
|
||||
};
|
||||
|
||||
const openClass = this.state.isClosed ? 'is-closed' : 'is-open';
|
||||
const disabledClass = this.props.triggerDisabled ? 'is-disabled' : '';
|
||||
|
||||
//If user wants different text when tray is open
|
||||
const trigger =
|
||||
this.state.isClosed === false &&
|
||||
this.props.triggerWhenOpen !== undefined
|
||||
? this.props.triggerWhenOpen
|
||||
: this.props.trigger;
|
||||
|
||||
const ContentContainerElement = this.props.contentContainerTagName;
|
||||
|
||||
// If user wants a trigger wrapping element different than 'span'
|
||||
const TriggerElement = this.props.triggerTagName;
|
||||
|
||||
// Don't render children until the first opening of the Collapsible if lazy rendering is enabled
|
||||
const children =
|
||||
this.props.lazyRender &&
|
||||
!this.state.hasBeenOpened &&
|
||||
this.state.isClosed &&
|
||||
!this.state.inTransition
|
||||
? null
|
||||
: this.props.children;
|
||||
|
||||
// Construct CSS classes strings
|
||||
const triggerClassString = `${
|
||||
this.props.classParentString
|
||||
}__trigger ${openClass} ${disabledClass} ${
|
||||
this.state.isClosed
|
||||
? this.props.triggerClassName
|
||||
: this.props.triggerOpenedClassName
|
||||
}`;
|
||||
const parentClassString = `${this.props.classParentString} ${
|
||||
this.state.isClosed
|
||||
? this.props.className
|
||||
: this.props.openedClassName
|
||||
}`;
|
||||
const outerClassString = `${this.props.classParentString}__contentOuter ${this.props.contentOuterClassName}`;
|
||||
const innerClassString = `${this.props.classParentString}__contentInner ${this.props.contentInnerClassName}`;
|
||||
|
||||
return (
|
||||
<ContentContainerElement
|
||||
className={parentClassString.trim()}
|
||||
{...this.props.containerElementProps}
|
||||
>
|
||||
<TriggerElement
|
||||
className={triggerClassString.trim()}
|
||||
onClick={this.handleTriggerClick}
|
||||
style={
|
||||
!this.state.isClosed
|
||||
? { ...this.props.triggerStyle, marginBottom: 25 }
|
||||
: this.props.triggerStyle
|
||||
}
|
||||
onKeyPress={event => {
|
||||
const { key } = event;
|
||||
if (
|
||||
(key === ' ' &&
|
||||
this.props.triggerTagName.toLowerCase() !==
|
||||
'button') ||
|
||||
key === 'Enter'
|
||||
) {
|
||||
this.handleTriggerClick(event);
|
||||
}
|
||||
}}
|
||||
tabIndex={this.props.tabIndex && this.props.tabIndex}
|
||||
{...this.props.triggerElementProps}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
this.state.isClosed
|
||||
? this.props.closedIconClass
|
||||
: this.props.openIconClass
|
||||
}
|
||||
style={{ marginRight: '5px ' }}
|
||||
/>
|
||||
{trigger}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style={this.props.statusColorStyle}></div>
|
||||
</div>
|
||||
</TriggerElement>
|
||||
|
||||
{this.renderNonClickableTriggerElement()}
|
||||
|
||||
<div
|
||||
className={outerClassString.trim()}
|
||||
style={dropdownStyle}
|
||||
onTransitionEnd={this.handleTransitionEnd}
|
||||
ref={this.setInnerRef}
|
||||
hidden={
|
||||
this.props.contentHiddenWhenClosed &&
|
||||
this.state.isClosed &&
|
||||
!this.state.inTransition
|
||||
}
|
||||
>
|
||||
<div className={innerClassString.trim()}>{children}</div>
|
||||
</div>
|
||||
</ContentContainerElement>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Collapsible.propTypes = {
|
||||
transitionTime: PropTypes.number,
|
||||
transitionCloseTime: PropTypes.number,
|
||||
triggerTagName: PropTypes.string,
|
||||
easing: PropTypes.string,
|
||||
open: PropTypes.bool,
|
||||
containerElementProps: PropTypes.object,
|
||||
triggerElementProps: PropTypes.object,
|
||||
classParentString: PropTypes.string,
|
||||
openedClassName: PropTypes.string,
|
||||
triggerStyle: PropTypes.object,
|
||||
statusColorStyle: PropTypes.object,
|
||||
triggerClassName: PropTypes.string,
|
||||
triggerOpenedClassName: PropTypes.string,
|
||||
contentOuterClassName: PropTypes.string,
|
||||
contentInnerClassName: PropTypes.string,
|
||||
accordionPosition: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
handleTriggerClick: PropTypes.func,
|
||||
onOpen: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
onOpening: PropTypes.func,
|
||||
onClosing: PropTypes.func,
|
||||
onTriggerOpening: PropTypes.func,
|
||||
onTriggerClosing: PropTypes.func,
|
||||
trigger: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
||||
triggerWhenOpen: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
||||
triggerDisabled: PropTypes.bool,
|
||||
lazyRender: PropTypes.bool,
|
||||
overflowWhenOpen: PropTypes.oneOf([
|
||||
'hidden',
|
||||
'visible',
|
||||
'auto',
|
||||
'scroll',
|
||||
'inherit',
|
||||
'initial',
|
||||
'unset',
|
||||
]),
|
||||
contentHiddenWhenClosed: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.object,
|
||||
triggerSibling: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
|
||||
tabIndex: PropTypes.number,
|
||||
contentContainerTagName: PropTypes.string,
|
||||
closedIconClass: PropTypes.string,
|
||||
openIconClass: PropTypes.string,
|
||||
};
|
||||
|
||||
Collapsible.defaultProps = {
|
||||
transitionTime: 400,
|
||||
transitionCloseTime: null,
|
||||
triggerTagName: 'span',
|
||||
easing: 'linear',
|
||||
open: false,
|
||||
classParentString: 'Collapsible',
|
||||
triggerDisabled: false,
|
||||
lazyRender: false,
|
||||
overflowWhenOpen: 'hidden',
|
||||
contentHiddenWhenClosed: false,
|
||||
openedClassName: '',
|
||||
triggerStyle: null,
|
||||
triggerClassName: '',
|
||||
triggerOpenedClassName: '',
|
||||
contentOuterClassName: '',
|
||||
contentInnerClassName: '',
|
||||
className: '',
|
||||
triggerSibling: null,
|
||||
onOpen: () => {},
|
||||
onClose: () => {},
|
||||
onOpening: () => {},
|
||||
onClosing: () => {},
|
||||
onTriggerOpening: () => {},
|
||||
onTriggerClosing: () => {},
|
||||
tabIndex: null,
|
||||
contentContainerTagName: 'div',
|
||||
closedIconClass: '',
|
||||
openIconClass: '',
|
||||
statusColorStyle: {},
|
||||
};
|
||||
|
||||
export default Collapsible;
|
||||
@@ -0,0 +1,3 @@
|
||||
const setInTransition = innerRefScrollHeight => innerRefScrollHeight !== 0;
|
||||
|
||||
export default setInTransition;
|
||||
@@ -7,7 +7,13 @@ import ShouldRender from './ShouldRender';
|
||||
import Footer from './Footer';
|
||||
import NotesMain from './NotesMain';
|
||||
import EventsMain from './EventsMain';
|
||||
import { API_URL, ACCOUNTS_URL, getServiceStatus } from '../config';
|
||||
import {
|
||||
API_URL,
|
||||
ACCOUNTS_URL,
|
||||
getServiceStatus,
|
||||
filterProbeData,
|
||||
getMonitorStatus,
|
||||
} from '../config';
|
||||
import moment from 'moment';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { bindActionCreators } from 'redux';
|
||||
@@ -27,6 +33,7 @@ import AnnouncementLogs from './AnnouncementLogs';
|
||||
import PastEvent from './PastEvent';
|
||||
import { fetchFutureEvents, fetchPastEvents } from '../actions/status';
|
||||
import OngoingSchedule from './OngoingSchedule';
|
||||
import Collapsible from './Collapsible/Collapsible';
|
||||
|
||||
const greenBackground = {
|
||||
display: 'inline-block',
|
||||
@@ -183,26 +190,17 @@ class Main extends Component {
|
||||
this.setLastAlive();
|
||||
}
|
||||
|
||||
groupBy(collection, property) {
|
||||
let i = 0,
|
||||
val,
|
||||
index;
|
||||
const values = [],
|
||||
result = [];
|
||||
|
||||
for (; i < collection.length; i++) {
|
||||
val = collection[i][property]
|
||||
? collection[i][property]['name']
|
||||
: 'no-category';
|
||||
index = values.indexOf(val);
|
||||
if (index > -1) {
|
||||
result[index].push(collection[i]);
|
||||
} else {
|
||||
values.push(val);
|
||||
result.push([collection[i]]);
|
||||
getCategories(collection, property) {
|
||||
const collectionArray = [];
|
||||
collection.forEach(monitor => {
|
||||
if (
|
||||
monitor[property] &&
|
||||
collectionArray.indexOf(monitor[property].name) === -1
|
||||
) {
|
||||
collectionArray.push(monitor[property].name);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
return collectionArray;
|
||||
}
|
||||
|
||||
groupedMonitors = () => {
|
||||
@@ -212,83 +210,168 @@ class Main extends Component {
|
||||
this.props.statusData.monitorsData.length > 0
|
||||
) {
|
||||
const monitorData = this.props.statusData.monitorsData;
|
||||
const groupedMonitorData = this.groupBy(
|
||||
const resourceCategories = this.getCategories(
|
||||
monitorData,
|
||||
'monitorCategoryId'
|
||||
'resourceCategory'
|
||||
);
|
||||
const uncategorized = monitorData.filter(
|
||||
mon =>
|
||||
mon.resourceCategory === undefined || !mon.resourceCategory
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="uptime-graph-header"
|
||||
style={{ flexDirection: 'column' }}
|
||||
>
|
||||
{resourceCategories.map(categoryName => {
|
||||
const filteredResource = monitorData.filter(
|
||||
resource =>
|
||||
resource.resourceCategory &&
|
||||
resource.resourceCategory.name === categoryName
|
||||
);
|
||||
|
||||
return this.CollapsableGroup(
|
||||
categoryName,
|
||||
filteredResource
|
||||
);
|
||||
})}
|
||||
{uncategorized &&
|
||||
uncategorized.length > 0 &&
|
||||
this.CollapsableGroup('Uncategorized', uncategorized)}
|
||||
</div>
|
||||
);
|
||||
return groupedMonitorData.map((groupedMonitors, i) => {
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className="uptime-graph-header"
|
||||
style={{ flexDirection: 'column' }}
|
||||
>
|
||||
{groupedMonitors.map((monitor, i) => {
|
||||
return (
|
||||
<>
|
||||
<MonitorInfo
|
||||
monitor={monitor}
|
||||
selectedCharts={
|
||||
this.props.monitors.filter(
|
||||
m =>
|
||||
monitor._id ===
|
||||
m.monitor._id
|
||||
)[0]
|
||||
}
|
||||
key={i}
|
||||
id={`monitor${i}`}
|
||||
resourceCategory={
|
||||
monitor.resourceCategory
|
||||
}
|
||||
isGroupedByMonitorCategory={
|
||||
this.props.statusData
|
||||
.isGroupedByMonitorCategory
|
||||
}
|
||||
theme={
|
||||
this.props.statusData.theme ===
|
||||
'Clean Theme'
|
||||
? true
|
||||
: false
|
||||
}
|
||||
/>
|
||||
{this.props.monitors.some(
|
||||
m => monitor._id === m.monitor._id
|
||||
) && (
|
||||
<LineChartsContainer
|
||||
monitor={monitor}
|
||||
selectedCharts={
|
||||
this.props.monitors.filter(
|
||||
m =>
|
||||
monitor._id ===
|
||||
m.monitor._id
|
||||
)[0]
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{i <
|
||||
this.props.statusData.monitorsData
|
||||
.length -
|
||||
1 && (
|
||||
<div
|
||||
style={{
|
||||
margin: '30px 0px',
|
||||
backgroundColor:
|
||||
'rgb(232, 232, 232)',
|
||||
height: '1px',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return <NoMonitor />;
|
||||
}
|
||||
};
|
||||
|
||||
CollapsableGroup = (categoryName, monitors) => {
|
||||
const { probes, activeProbe, statusData } = this.props;
|
||||
const theme = statusData.theme === 'Clean Theme' ? true : false;
|
||||
|
||||
const categoryStatuses = monitors.map(monitor => {
|
||||
const probe =
|
||||
probes && probes.length > 0
|
||||
? probes[probes.length < 2 ? 0 : activeProbe]
|
||||
: null;
|
||||
const statuses = filterProbeData(monitor, probe);
|
||||
const monitorStatus = getMonitorStatus(statuses);
|
||||
return monitorStatus;
|
||||
});
|
||||
|
||||
const categoryStatusBk = categoryStatuses.includes('offline')
|
||||
? 'rgba(250, 109, 70, 1)'
|
||||
: categoryStatuses.includes('degraded')
|
||||
? 'rgba(255, 222, 36, 1)'
|
||||
: 'rgba(108, 219, 86, 1)';
|
||||
|
||||
const collapsibleStyle = {
|
||||
backgroundColor: 'rgb(246 246 246)',
|
||||
width: '100%',
|
||||
padding: '7px 10px',
|
||||
fontSize: ' 12px',
|
||||
fontWeight: '400',
|
||||
color: 'black',
|
||||
marginBottom: '0',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
border: '1px solid rgb(236 236 236)',
|
||||
};
|
||||
|
||||
if (!theme) {
|
||||
//if its a classic theme then, change some styles.
|
||||
collapsibleStyle.backgroundColor = 'rgb(247 247 247)';
|
||||
collapsibleStyle.border = '1px solid rgb(228 228 228)';
|
||||
}
|
||||
|
||||
return (
|
||||
<Collapsible
|
||||
trigger={
|
||||
categoryName.charAt(0).toUpperCase() + categoryName.slice(1)
|
||||
}
|
||||
triggerStyle={collapsibleStyle}
|
||||
open={true}
|
||||
contentContainerTagName="div"
|
||||
triggerTagName="div"
|
||||
transitionTime="200"
|
||||
lazyRender={true}
|
||||
closedIconClass="sp__icon sp__icon--down"
|
||||
openIconClass="sp__icon sp__icon--up"
|
||||
statusColorStyle={{
|
||||
borderRadius: ' 100px',
|
||||
height: '8px',
|
||||
width: '8px',
|
||||
backgroundColor: categoryStatusBk,
|
||||
}}
|
||||
>
|
||||
{monitors.map((monitor, i) => {
|
||||
return (
|
||||
<>
|
||||
<MonitorInfo
|
||||
monitor={monitor}
|
||||
selectedCharts={
|
||||
this.props.monitors.filter(
|
||||
m => monitor._id === m.monitor._id
|
||||
)[0]
|
||||
}
|
||||
key={i}
|
||||
id={`monitor${i}`}
|
||||
resourceCategory={monitor.resourceCategory}
|
||||
isGroupedByMonitorCategory={false}
|
||||
theme={
|
||||
this.props.statusData.theme ===
|
||||
'Clean Theme'
|
||||
? true
|
||||
: false
|
||||
}
|
||||
/>
|
||||
{this.props.monitors.some(
|
||||
m => monitor._id === m.monitor._id
|
||||
) && (
|
||||
<LineChartsContainer
|
||||
monitor={monitor}
|
||||
selectedCharts={
|
||||
this.props.monitors.filter(
|
||||
m => monitor._id === m.monitor._id
|
||||
)[0]
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{i < monitors.length - 1 ||
|
||||
(i === monitors.length - 1 &&
|
||||
categoryName.toLowerCase() ===
|
||||
'uncategorized' &&
|
||||
!theme) ? (
|
||||
<div
|
||||
style={{
|
||||
margin: '30px 0px',
|
||||
backgroundColor: 'rgb(232, 232, 232)',
|
||||
height: '1px',
|
||||
}}
|
||||
/>
|
||||
) : (i === monitors.length - 1 &&
|
||||
categoryName.toLowerCase() !==
|
||||
'uncategorized') ||
|
||||
(i === monitors.length - 1 &&
|
||||
categoryName.toLowerCase() ===
|
||||
'uncategorized' &&
|
||||
theme) ? (
|
||||
<div
|
||||
style={{
|
||||
marginBottom: '30px',
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</Collapsible>
|
||||
);
|
||||
};
|
||||
|
||||
selectbutton = index => {
|
||||
this.props.selectedProbe(index);
|
||||
};
|
||||
@@ -478,7 +561,9 @@ class Main extends Component {
|
||||
{ name: 'Future Scheduled Events', key: 'maintenance' },
|
||||
{ name: 'Footer', key: 'footer' },
|
||||
],
|
||||
invisible: [{ name: 'Scheduled Events Completed', key: 'pastEvents' }],
|
||||
invisible: [
|
||||
{ name: 'Scheduled Events Completed', key: 'pastEvents' },
|
||||
],
|
||||
};
|
||||
|
||||
let visibleLayout =
|
||||
@@ -487,16 +572,7 @@ class Main extends Component {
|
||||
if (!visibleLayout) {
|
||||
visibleLayout = defaultLayout;
|
||||
}
|
||||
let resourcesServiceOverlap = false;
|
||||
visibleLayout.visible.forEach((item, i) => {
|
||||
if (
|
||||
item.key === 'services' &&
|
||||
visibleLayout.visible[i - 1] &&
|
||||
visibleLayout.visible[i - 1].key === 'resources'
|
||||
) {
|
||||
resourcesServiceOverlap = true;
|
||||
}
|
||||
});
|
||||
|
||||
const layoutObj = {
|
||||
header: (
|
||||
<>
|
||||
@@ -654,6 +730,7 @@ class Main extends Component {
|
||||
style={{
|
||||
borderTopWidth: '1px',
|
||||
...contentBackground,
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
{this.groupedMonitors()}
|
||||
@@ -886,11 +963,7 @@ class Main extends Component {
|
||||
className="white box"
|
||||
style={{
|
||||
...contentBackground,
|
||||
borderBottomLeftRadius:
|
||||
resourcesServiceOverlap && 0,
|
||||
borderBottomRightRadius:
|
||||
resourcesServiceOverlap && 0,
|
||||
marginTop: resourcesServiceOverlap ? 0 : '50px',
|
||||
marginTop: '50px',
|
||||
}}
|
||||
>
|
||||
<div className="largestatus">
|
||||
@@ -929,17 +1002,13 @@ class Main extends Component {
|
||||
className="content"
|
||||
style={{
|
||||
position: 'relative',
|
||||
marginTop: resourcesServiceOverlap ? 0 : '50px',
|
||||
marginTop: '50px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="white box"
|
||||
style={{
|
||||
...contentBackground,
|
||||
borderTopLeftRadius:
|
||||
resourcesServiceOverlap && 0,
|
||||
borderTopRightRadius:
|
||||
resourcesServiceOverlap && 0,
|
||||
}}
|
||||
>
|
||||
<ShouldRender
|
||||
@@ -976,7 +1045,7 @@ class Main extends Component {
|
||||
className="uptime-graphs box-inner"
|
||||
style={
|
||||
isGroupedByMonitorCategory
|
||||
? { paddingBottom: 0 }
|
||||
? { padding: 0 }
|
||||
: { paddingBottom: 35 }
|
||||
}
|
||||
>
|
||||
|
||||
@@ -273,6 +273,7 @@ describe('Member Restriction', () => {
|
||||
});
|
||||
// adding a subProject is only allowed on growth plan and above
|
||||
await init.addSubProject(subProjectName, page);
|
||||
await init.saasLogout(page);
|
||||
|
||||
done();
|
||||
});
|
||||
@@ -284,10 +285,7 @@ describe('Member Restriction', () => {
|
||||
|
||||
test(
|
||||
'should show unauthorised modal when a team member who is not an admin or owner of the project tries to update alert option',
|
||||
async done => {
|
||||
browser = await puppeteer.launch(utils.puppeteerLaunchConfig);
|
||||
page = await browser.newPage();
|
||||
await page.setUserAgent(utils.agent);
|
||||
async done => {
|
||||
|
||||
await init.registerAndLoggingTeamMember(
|
||||
{ email: teamEmail, password },
|
||||
|
||||
@@ -8,6 +8,7 @@ let browser, page;
|
||||
const email = utils.generateRandomBusinessEmail();
|
||||
const password = '1234567890';
|
||||
let slaName = 'fxPro';
|
||||
let newSlaName = 'newFxPro';
|
||||
const duration = '15';
|
||||
const alertTime = '10';
|
||||
const component = utils.generateRandomString();
|
||||
@@ -265,14 +266,7 @@ describe('Incident Communication SLA', () => {
|
||||
await init.pageClick(page, '#alertTime');
|
||||
await init.pageType(page, '#alertTime', alertTime);
|
||||
await init.page$Eval(page, '#isDefault', elem => elem.click());
|
||||
await init.pageClick(page, '#createSlaBtn');
|
||||
await init.pageWaitForSelector(page, '.ball-beat', {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await init.pageWaitForSelector(page, '.ball-beat', {
|
||||
hidden: true,
|
||||
});
|
||||
await init.pageClick(page, '#createSlaBtn');
|
||||
|
||||
const sla = await init.pageWaitForSelector(
|
||||
page,
|
||||
@@ -332,7 +326,7 @@ describe('Incident Communication SLA', () => {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await init.pageClick(page, '#name');
|
||||
await init.pageClick(page, '#name', {clickCount: 3});
|
||||
await init.pageType(page, '#name', ' ');
|
||||
await init.pageClick(page, '#editSlaBtn');
|
||||
|
||||
@@ -393,22 +387,14 @@ describe('Incident Communication SLA', () => {
|
||||
await init.pageWaitForSelector(page, '#communicationSlaForm', {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
slaName = 'newFxPro';
|
||||
await init.pageClick(page, '#name');
|
||||
await init.pageType(page, '#name', slaName);
|
||||
await init.pageClick(page, '#editSlaBtn');
|
||||
await init.pageWaitForSelector(page, '.ball-beat', {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await init.pageWaitForSelector(page, '.ball-beat', {
|
||||
hidden: true,
|
||||
});
|
||||
});
|
||||
await init.pageClick(page, '#name', {clickCount: 3});
|
||||
await init.pageType(page, '#name', newSlaName);
|
||||
await init.pageClick(page, '#editSlaBtn');
|
||||
|
||||
const sla = await init.pageWaitForSelector(
|
||||
page,
|
||||
`#incidentSla_${slaName}`,
|
||||
`#incidentSla_${newSlaName}`,
|
||||
{
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
@@ -535,30 +521,23 @@ describe('Incident Communication SLA', () => {
|
||||
|
||||
await init.pageWaitForSelector(
|
||||
page,
|
||||
`#deleteIncidentSlaBtn_${slaName}`,
|
||||
`#deleteIncidentSlaBtn_${newSlaName}`,
|
||||
{
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
}
|
||||
);
|
||||
await init.pageClick(page, `#deleteIncidentSlaBtn_${slaName}`);
|
||||
await init.pageClick(page, `#deleteIncidentSlaBtn_${newSlaName}`);
|
||||
|
||||
await init.pageWaitForSelector(page, '#deleteIncidentSlaBtn', {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await init.pageClick(page, '#deleteIncidentSlaBtn');
|
||||
await init.pageWaitForSelector(page, '.ball-beat', {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await init.pageWaitForSelector(page, '.ball-beat', {
|
||||
hidden: true,
|
||||
});
|
||||
await init.pageClick(page, '#deleteIncidentSlaBtn');
|
||||
|
||||
const sla = await init.pageWaitForSelector(
|
||||
page,
|
||||
`#incidentSla_${slaName}`,
|
||||
`#incidentSla_${newSlaName}`,
|
||||
{
|
||||
hidden: true,
|
||||
}
|
||||
|
||||
@@ -78,8 +78,7 @@ describe('Incident Reports API', () => {
|
||||
timeout: init.timeout,
|
||||
});
|
||||
|
||||
// Navigate to Component details
|
||||
await init.navigateToComponentDetails(componentName, page);
|
||||
await page.goto(utils.DASHBOARD_URL, { waitUntil : 'networkidle2'}); // Incident Status is present on Dashboard and Incident Detail Page
|
||||
await init.pageWaitForSelector(page, '#closeIncident_0', {
|
||||
visible: true,
|
||||
timeout: 100000,
|
||||
@@ -124,7 +123,7 @@ describe('Incident Reports API', () => {
|
||||
});
|
||||
|
||||
// Navigate to Component details
|
||||
await init.navigateToComponentDetails(componentName, page);
|
||||
await page.goto(utils.DASHBOARD_URL, { waitUntil : 'networkidle2'});
|
||||
await init.pageWaitForSelector(page, '#closeIncident_1', {
|
||||
visible: true,
|
||||
timeout: 100000,
|
||||
|
||||
@@ -48,7 +48,7 @@ describe('Incident Timeline API', () => {
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await init.page$Eval(page, '#url', e => e.click());
|
||||
await init.pageType(page, '#url', utils.HTTP_TEST_SERVER_URL);
|
||||
await init.pageType(page, '#url', 'https://google.com'); //'HTTP_TEST_SERVER' auto generates incidents and this breaks the test. Also, the tests are not dependent on HTTP_TEST_SERVER
|
||||
await init.page$Eval(page, 'button[type=submit]', e => e.click());
|
||||
await init.pageWaitForSelector(
|
||||
page,
|
||||
@@ -390,9 +390,10 @@ describe('Incident Timeline API', () => {
|
||||
hidden: true,
|
||||
});
|
||||
|
||||
const incidentMessage = await init.page$(
|
||||
const incidentMessage = await init.pageWaitForSelector(
|
||||
page,
|
||||
`#content_${type}_incident_message_0`
|
||||
`#content_${type}_incident_message_0`,
|
||||
{hidden : true}
|
||||
);
|
||||
expect(incidentMessage).toEqual(null);
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ describe('Incoming HTTP Request', () => {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await init.pageClick(page, '#name');
|
||||
await init.pageClick(page, '#name', {clickCount : 3});
|
||||
// change the name of the incoming http request
|
||||
await init.pageType(page, '#name', 'newName');
|
||||
await init.pageClick(page, '#editIncomingRequest');
|
||||
|
||||
@@ -151,7 +151,7 @@ describe('Incident Priority API', () => {
|
||||
await init.pageWaitForSelector(page, editButtonLastRowIndentifier);
|
||||
await init.pageClick(page, editButtonLastRowIndentifier);
|
||||
await init.pageWaitForSelector(page, '#EditIncidentPriority');
|
||||
await init.pageClick(page, 'input[name=name]');
|
||||
await init.pageClick(page, 'input[name=name]', {clickCount:3});
|
||||
await page.keyboard.press('Backspace');
|
||||
await init.pageType(page, 'input[name=name]', newPriorityName);
|
||||
await init.pageClick(page, '#EditIncidentPriority');
|
||||
|
||||
95
tests/saas-tests/home/downloadWhitepaper.test.js
Normal file
95
tests/saas-tests/home/downloadWhitepaper.test.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const utils = require('../../test-utils');
|
||||
const init = require('../../test-init');
|
||||
const axios = require('axios');
|
||||
|
||||
let page, browser;
|
||||
|
||||
// user credentials
|
||||
const email = utils.generateRandomBusinessEmail();
|
||||
const queryString = '?utm_source=runningtest&good=thankyou&kill=love&ion=pure';
|
||||
let queryObj = {};
|
||||
|
||||
describe('Download Whitepaper form', () => {
|
||||
beforeAll(async done => {
|
||||
jest.setTimeout(init.timeout);
|
||||
|
||||
browser = await puppeteer.launch(utils.puppeteerLaunchConfig);
|
||||
page = await browser.newPage();
|
||||
await page.setUserAgent(utils.agent);
|
||||
await page.goto(`${utils.HOME_URL}${queryString}`, {
|
||||
waitUntil: 'networkidle2',
|
||||
});
|
||||
await init.pageClick(page, '#accept-cookies');
|
||||
await page.goto(`${utils.HOME_URL}/enterprise/resources`, {
|
||||
waitUntil: 'networkidle2',
|
||||
});
|
||||
await page.goto(
|
||||
`${utils.HOME_URL}/enterprise/download-resource/website-monitoring`,
|
||||
{
|
||||
waitUntil: 'networkidle2',
|
||||
}
|
||||
);
|
||||
await page.goto(`${utils.HOME_URL}/enterprise/resources`);
|
||||
await init.pageWaitForSelector(page, '#website-monitoring', {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
init.pageClick(page, '#website-monitoring'),
|
||||
]);
|
||||
await init.pageWaitForSelector(page, '#form-section', {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await init.pageType(page, '#fullname', utils.user.name);
|
||||
await init.pageType(page, '#email', email);
|
||||
await init.pageType(page, '#phone', utils.user.phone);
|
||||
await init.pageType(page, '#website', utils.user.website);
|
||||
await init.pageClick(page, '#country');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.down('Enter');
|
||||
await init.pageClick(page, '#volume');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.down('Enter');
|
||||
await init.pageClick(page, '#request-resource-btn');
|
||||
|
||||
const params = new URLSearchParams(queryString);
|
||||
// formating query string to an object
|
||||
for (const param of params) {
|
||||
queryObj = { ...queryObj, [`${param[0]}`]: param[1] };
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(async done => {
|
||||
await browser.close();
|
||||
done();
|
||||
});
|
||||
|
||||
test(
|
||||
'redirected query string should be save as source in the leads schema',
|
||||
async done => {
|
||||
const data = {
|
||||
collection: 'leads',
|
||||
query: { email: email },
|
||||
};
|
||||
const config = {
|
||||
method: 'post',
|
||||
url: utils.INIT_SCRIPT_URL + '/find',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: data,
|
||||
};
|
||||
const res = await axios(config);
|
||||
const sourceObj = res.data[0].source;
|
||||
for (const key in sourceObj) {
|
||||
expect(sourceObj[key]).toEqual(queryObj[key]);
|
||||
}
|
||||
done();
|
||||
},
|
||||
init.timeout
|
||||
);
|
||||
});
|
||||
83
tests/saas-tests/home/requestDemo.test.js
Normal file
83
tests/saas-tests/home/requestDemo.test.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const utils = require('../../test-utils');
|
||||
const init = require('../../test-init');
|
||||
const axios = require('axios');
|
||||
|
||||
let page, browser;
|
||||
|
||||
// user credentials
|
||||
const email = utils.generateRandomBusinessEmail();
|
||||
const queryString = '?utm_source=runningtest&good=thankyou&kill=love&ion=pure';
|
||||
let queryObj = {};
|
||||
|
||||
describe('Demo form', () => {
|
||||
beforeAll(async done => {
|
||||
jest.setTimeout(init.timeout);
|
||||
|
||||
browser = await puppeteer.launch(utils.puppeteerLaunchConfig);
|
||||
page = await browser.newPage();
|
||||
await page.setUserAgent(utils.agent);
|
||||
await page.goto(`${utils.HOME_URL}${queryString}`, {
|
||||
waitUntil: 'networkidle2',
|
||||
});
|
||||
await init.pageClick(page, '#accept-cookies');
|
||||
await page.goto(`${utils.HOME_URL}/enterprise/demo`, {
|
||||
waitUntil: 'networkidle2',
|
||||
});
|
||||
await init.pageWaitForSelector(page, '#form-section');
|
||||
await init.pageType(page, '#fullname', utils.user.name);
|
||||
await init.pageType(page, '#email', email);
|
||||
await init.pageType(page, '#Phone', utils.user.phone);
|
||||
await init.pageType(page, '#website', utils.user.website);
|
||||
await init.pageClick(page, '#country');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.down('Enter');
|
||||
await init.pageClick(page, '#volume');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.down('Enter');
|
||||
await init.pageType(page, '#message', utils.user.message);
|
||||
await init.pageClick(page, '#request-demo-btn');
|
||||
await init.pageWaitForSelector(page, '#success');
|
||||
// Check if user's email is submitted successfully
|
||||
await init.pageWaitForSelector(page, '.submitted-email', {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
|
||||
const params = new URLSearchParams(queryString);
|
||||
// formating query string to an object
|
||||
for (const param of params) {
|
||||
queryObj = { ...queryObj, [`${param[0]}`]: param[1] };
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(async done => {
|
||||
await browser.close();
|
||||
done();
|
||||
});
|
||||
|
||||
test(
|
||||
'redirected query string should be save as source in the leads schema',
|
||||
async () => {
|
||||
const data = {
|
||||
collection: 'leads',
|
||||
query: { email: email },
|
||||
};
|
||||
const config = {
|
||||
method: 'post',
|
||||
url: utils.INIT_SCRIPT_URL + '/find',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: data,
|
||||
};
|
||||
const res = await axios(config);
|
||||
const sourceObj = res.data[0].source;
|
||||
for (const key in sourceObj) {
|
||||
expect(sourceObj[key]).toEqual(queryObj[key]);
|
||||
}
|
||||
},
|
||||
init.timeout
|
||||
);
|
||||
});
|
||||
83
tests/saas-tests/status-page/status-probeBar.test.js
Normal file
83
tests/saas-tests/status-page/status-probeBar.test.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const utils = require('../../test-utils');
|
||||
const puppeteer = require('puppeteer');
|
||||
const init = require('../../test-init');
|
||||
|
||||
let page, browser;
|
||||
|
||||
const email = utils.generateRandomBusinessEmail();
|
||||
const password = '1234567890';
|
||||
const user = {
|
||||
email,
|
||||
password,
|
||||
};
|
||||
|
||||
const projectName = utils.generateRandomString();
|
||||
const statusPageName = utils.generateRandomString();
|
||||
|
||||
describe('Probe bar test', () => {
|
||||
beforeAll(async done => {
|
||||
jest.setTimeout(init.timeout);
|
||||
|
||||
browser = await puppeteer.launch(utils.puppeteerLaunchConfig);
|
||||
page = await browser.newPage();
|
||||
await page.setUserAgent(utils.agent);
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(async done => {
|
||||
await browser.close();
|
||||
done();
|
||||
});
|
||||
|
||||
test(
|
||||
'Probe bar should not show by default',
|
||||
async done => {
|
||||
await init.registerUser(user, page);
|
||||
await init.renameProject(projectName, page);
|
||||
await init.growthPlanUpgrade(page); // Only Monthly growth plan can enable subscribers in status-page
|
||||
|
||||
// Create a Status-Page and Scheduled Maintenance to display in the Status-Page Url
|
||||
await page.goto(utils.DASHBOARD_URL, {
|
||||
waitUntil: 'networkidle2',
|
||||
});
|
||||
|
||||
await init.pageWaitForSelector(page, '#statusPages');
|
||||
await init.pageClick(page, '#statusPages');
|
||||
await init.pageWaitForSelector(
|
||||
page,
|
||||
`#btnCreateStatusPage_${projectName}`
|
||||
);
|
||||
await init.pageClick(page, `#btnCreateStatusPage_${projectName}`);
|
||||
await init.pageWaitForSelector(page, '#name');
|
||||
await init.pageWaitForSelector(page, 'input[id=name]', {
|
||||
visible: true,
|
||||
timeout: init.timeout,
|
||||
});
|
||||
await init.pageClick(page, 'input[id=name]');
|
||||
await page.focus('input[id=name]');
|
||||
await init.pageType(page, 'input[id=name]', statusPageName);
|
||||
await init.pageClick(page, '#btnCreateStatusPage');
|
||||
await init.pageWaitForSelector(page, '#statusPagesListContainer');
|
||||
await init.pageWaitForSelector(page, '#viewStatusPage');
|
||||
await init.pageClick(page, '#viewStatusPage');
|
||||
await init.pageWaitForSelector(page, `#header-${statusPageName}`);
|
||||
await init.pageWaitForSelector(page, '#publicStatusPageUrl');
|
||||
let link = await init.page$(
|
||||
page,
|
||||
'#publicStatusPageUrl > span > a'
|
||||
);
|
||||
link = await link.getProperty('href');
|
||||
link = await link.jsonValue();
|
||||
await page.goto(link);
|
||||
|
||||
// To confirm if the probe shows after creating a status.
|
||||
const probeBar = await page.evaluate(() => {
|
||||
const el = document.querySelector('.bs-probes');
|
||||
return el ? el.innerText : '';
|
||||
});
|
||||
expect(probeBar).toMatch('');
|
||||
done();
|
||||
},
|
||||
init.timeout
|
||||
);
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user