antelope-cli 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ansible/onboarding-ca/README.md +72 -72
- package/ansible/onboarding-ca/onboard-ca.yml +41 -41
- package/ansible/onboarding-ca/roles/onboard_ca/defaults/main.yml +24 -24
- package/ansible/onboarding-ca/roles/onboard_ca/meta/main.yml +10 -10
- package/ansible/onboarding-ca/roles/onboard_ca/tasks/main.yml +76 -76
- package/ansible/onboarding-ca/roles/onboard_ca/tasks/per_branch.yml +106 -106
- package/ansible/onboarding-ca/roles/onboard_ca/vars/main.yml +6 -6
- package/index.js +2 -0
- package/onboarding-ca/brand.js +13 -13
- package/onboarding-ca/index.js +94 -94
- package/onboarding-ca/riskDisclaimer.js +27 -27
- package/onboarding-ca/scss.js +24 -24
- package/onboarding-ca/templates/risk-disclaimer.html +12 -12
- package/onboarding-crm/brand.js +21 -0
- package/onboarding-crm/createConfig.js +38 -0
- package/onboarding-crm/index.js +157 -0
- package/onboarding-crm/themes.js +92 -0
- package/onboarding-crm/translations.js +37 -0
- package/onboarding-crm/variablesColors.js +54 -0
- package/package.json +1 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const brandModule = require('./brand.js');
|
|
4
|
+
const variablesColors = require('./variablesColors.js');
|
|
5
|
+
const themes = require('./themes.js');
|
|
6
|
+
const translations = require('./translations.js');
|
|
7
|
+
const createConfig = require('./createConfig.js');
|
|
8
|
+
|
|
9
|
+
const ALLOWED_TYPES = ['CORP_FL', 'IB_FL', 'CORP_AFF_FL', 'CORP_AFF_IB_PORTAL', 'CORP_AFF_MSQ'];
|
|
10
|
+
|
|
11
|
+
const HELP = `
|
|
12
|
+
Usage: ant-cli onboarding-crm --brand_name <name> [options]
|
|
13
|
+
|
|
14
|
+
Onboards a CRM brand in the current web-crm checkout — same edits as "onboarding", but
|
|
15
|
+
every value is taken from flags (interactive prompts are only a fallback in a TTY):
|
|
16
|
+
src/ng1/assets/css/sass/variables-colors.scss (+ $colors entry and color shades)
|
|
17
|
+
src/ng1/assets/css/sass/materialism/themes.scss (+ theme maps and .theme-template block)
|
|
18
|
+
src/assets/brands/<folder>/languages/<lang>.json (one empty file per language)
|
|
19
|
+
src/configs/configs.ts (rewritten for the brand)
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--brand_name <name> Display name → configs title, e.g. "Zenith Horizon Group" (required)
|
|
23
|
+
--brand_assets_folder <f> Brand assets folder / connectedBrandName. Default: derived from
|
|
24
|
+
--brand_name (lowercased, spaces removed)
|
|
25
|
+
--color <hex> Brand hex color, e.g. "#1a2b3c". Empty ⇒ default palette ($corp)
|
|
26
|
+
--logo_url <url> Logo image URL (black background) (required)
|
|
27
|
+
--favicon_url <url> Favicon image URL (required)
|
|
28
|
+
--languages <csv> 2-letter language codes, comma-separated, e.g. "en,ar,de" (required)
|
|
29
|
+
--fcm_sender_id <id> Firebase FCMSenderId (required)
|
|
30
|
+
--type <type> One of: ${ALLOWED_TYPES.join(', ')} (required)
|
|
31
|
+
-h, --help Show this help
|
|
32
|
+
|
|
33
|
+
Run from the root of a web-crm checkout — files are written relative to the current directory.
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const FLAGS = {
|
|
37
|
+
'--brand_name': 'brandName',
|
|
38
|
+
'--brand_assets_folder': 'brandAssetsFolder',
|
|
39
|
+
'--color': 'color',
|
|
40
|
+
'--logo_url': 'logo',
|
|
41
|
+
'--favicon_url': 'favicon',
|
|
42
|
+
'--languages': 'languages',
|
|
43
|
+
'--fcm_sender_id': 'fcmSenderId',
|
|
44
|
+
'--type': 'type'
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function parseArgs(argv) {
|
|
48
|
+
const opts = {};
|
|
49
|
+
for (let i = 0; i < argv.length; i++) {
|
|
50
|
+
const a = argv[i];
|
|
51
|
+
if (a === '--help' || a === '-h') opts.help = true;
|
|
52
|
+
else if (FLAGS[a]) opts[FLAGS[a]] = argv[++i];
|
|
53
|
+
}
|
|
54
|
+
return opts;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const ask = (rl, question) => new Promise((resolve) => rl.question(question, (a) => resolve(a)));
|
|
58
|
+
|
|
59
|
+
// Resolve a value: prefer the flag; otherwise prompt (interactive only). Returns undefined
|
|
60
|
+
// when absent and non-interactive, so the caller can report it as a missing required field.
|
|
61
|
+
async function resolve(opts, key, promptText, interactive, rl) {
|
|
62
|
+
if (opts[key] !== undefined) return opts[key];
|
|
63
|
+
if (interactive) return ask(rl, promptText);
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function run() {
|
|
68
|
+
const opts = parseArgs(process.argv.slice(3));
|
|
69
|
+
if (opts.help) {
|
|
70
|
+
console.log(HELP);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(chalk.underline(chalk.cyan('Antelope CRM on-boarding\n')));
|
|
75
|
+
|
|
76
|
+
// Interactive only in a real terminal and when not forced non-interactive (CI / Ansible).
|
|
77
|
+
const interactive = Boolean(process.stdin.isTTY) && !process.env.CI;
|
|
78
|
+
const rl = interactive ? readline.createInterface({ input: process.stdin, output: process.stdout }) : null;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const brandName = await resolve(opts, 'brandName', "What is the new brand's name?\n", interactive, rl);
|
|
82
|
+
const kebabCaseName = brandName ? brandName.toLowerCase().replace(/ /g, '-') : '';
|
|
83
|
+
const connectedBrandName = opts.brandAssetsFolder
|
|
84
|
+
? opts.brandAssetsFolder.trim().toLowerCase().replace(/ /g, '')
|
|
85
|
+
: (brandName ? brandName.toLowerCase().replace(/ /g, '') : '');
|
|
86
|
+
|
|
87
|
+
// Color is optional (empty ⇒ default palette).
|
|
88
|
+
const color = opts.color !== undefined
|
|
89
|
+
? opts.color
|
|
90
|
+
: (interactive ? await ask(rl, `Please set a color for "${kebabCaseName}", use hex value, leave empty for default.\n`) : '');
|
|
91
|
+
|
|
92
|
+
const logo = await resolve(opts, 'logo', 'Please enter the URL to the logo image suitable for a black background:\n', interactive, rl);
|
|
93
|
+
const favicon = await resolve(opts, 'favicon', 'Please provide the url to your favicon image:\n', interactive, rl);
|
|
94
|
+
const languagesInput = await resolve(opts, 'languages', 'Enter the languages (2-letter codes) separated by commas:\n', interactive, rl);
|
|
95
|
+
const fcmSenderId = await resolve(opts, 'fcmSenderId', 'Enter the FCMSenderId value (see https://xsites.atlassian.net/wiki/spaces/IKB/pages/777650347/Onboarding+Branding+Manual+for+devs#Firebase): ', interactive, rl);
|
|
96
|
+
const typeInput = await resolve(opts, 'type', `Enter the type value (allowed values: ${ALLOWED_TYPES.join(', ')}): `, interactive, rl);
|
|
97
|
+
|
|
98
|
+
// Validate required fields.
|
|
99
|
+
const missing = [];
|
|
100
|
+
if (!brandName) missing.push('--brand_name');
|
|
101
|
+
if (!logo) missing.push('--logo_url');
|
|
102
|
+
if (!favicon) missing.push('--favicon_url');
|
|
103
|
+
if (!languagesInput) missing.push('--languages');
|
|
104
|
+
if (!fcmSenderId) missing.push('--fcm_sender_id');
|
|
105
|
+
if (!typeInput) missing.push('--type');
|
|
106
|
+
if (missing.length) {
|
|
107
|
+
console.error(chalk.red(`Error: missing required value(s): ${missing.join(', ')}`));
|
|
108
|
+
console.log(HELP);
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const type = typeInput.toUpperCase();
|
|
114
|
+
if (!ALLOWED_TYPES.includes(type)) {
|
|
115
|
+
console.error(chalk.red(`Error: invalid --type "${typeInput}". Allowed: ${ALLOWED_TYPES.join(', ')}`));
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const languages = languagesInput.trim().split(',').map((l) => l.trim()).filter(Boolean);
|
|
121
|
+
if (!languages.length) {
|
|
122
|
+
console.error(chalk.red('Error: --languages must contain at least one 2-letter code.'));
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
brandModule.updateBrand({
|
|
128
|
+
brandName,
|
|
129
|
+
kebabCaseName,
|
|
130
|
+
connectedBrandName,
|
|
131
|
+
color: color || '',
|
|
132
|
+
logo,
|
|
133
|
+
favicon,
|
|
134
|
+
languages,
|
|
135
|
+
FCMSenderId: fcmSenderId,
|
|
136
|
+
type
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
console.log(chalk.bold(chalk.yellow(`\nOnboarding "${brandName}" (folder: ${connectedBrandName})`)));
|
|
140
|
+
console.log(chalk.bold(chalk.yellow('\nStep 1: Add to variables-colors.scss')));
|
|
141
|
+
await variablesColors.start();
|
|
142
|
+
console.log(chalk.bold(chalk.yellow('\nStep 2: Add to themes.scss')));
|
|
143
|
+
await themes.start();
|
|
144
|
+
console.log(chalk.bold(chalk.yellow('\nStep 3: Adding translations')));
|
|
145
|
+
await translations.start();
|
|
146
|
+
console.log(chalk.bold(chalk.yellow('\nStep 4: Editing the config file')));
|
|
147
|
+
await createConfig.start();
|
|
148
|
+
console.log(chalk.green('\nCRM onboarding completed!'));
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error(chalk.red(err) + '\n Please delete files and code added by this failed session.');
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
} finally {
|
|
153
|
+
if (rl) rl.close();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
run();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const brandModule = require('./brand.js');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const themesPath = 'src/ng1/assets/css/sass/materialism/themes.scss';
|
|
5
|
+
|
|
6
|
+
function addColorToMap(mapName, key, value, data) {
|
|
7
|
+
const mapRegex = new RegExp(`\\$${mapName}:\\s*\\(([\\s\\S]*?)\\)`);
|
|
8
|
+
const match = data.match(mapRegex);
|
|
9
|
+
if (match) {
|
|
10
|
+
const mapContent = match[1];
|
|
11
|
+
const newMapContent = `$${mapName}: (${mapContent},\n '${key}': ${value})`;
|
|
12
|
+
data = data.replace(mapRegex, newMapContent);
|
|
13
|
+
} else {
|
|
14
|
+
throw new Error(`Error: Could not find ${mapName} map in the file`);
|
|
15
|
+
}
|
|
16
|
+
return data;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Same edits as onboarding/themes.js, driven by resolved brand state (no prompts).
|
|
20
|
+
function start() {
|
|
21
|
+
const brand = brandModule.getBrand();
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const themeVar = `
|
|
24
|
+
.theme-template-${brand.connectedBrandName} {
|
|
25
|
+
#logo {
|
|
26
|
+
display: inline-block;
|
|
27
|
+
width: 240px;
|
|
28
|
+
height: 120px;
|
|
29
|
+
background: url('${brand.logo}') no-repeat center center;
|
|
30
|
+
background-size: contain;
|
|
31
|
+
}
|
|
32
|
+
.navbar-toggle-container {
|
|
33
|
+
background: #222C38;
|
|
34
|
+
|
|
35
|
+
.icon-bar {
|
|
36
|
+
background: #fff !important;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
.sidebar {
|
|
40
|
+
background: #222C38;
|
|
41
|
+
color: #ffffff;
|
|
42
|
+
|
|
43
|
+
ul a,
|
|
44
|
+
i {
|
|
45
|
+
color: #ffffff;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.brand-logo,
|
|
49
|
+
.brand-logo-text {
|
|
50
|
+
display: none;
|
|
51
|
+
}
|
|
52
|
+
.user-logged-in:after {
|
|
53
|
+
background: #26303C;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
\n`;
|
|
58
|
+
fs.readFile(themesPath, 'utf8', (err, data) => {
|
|
59
|
+
if (err) {
|
|
60
|
+
return reject(err);
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
data = addColorToMap(
|
|
64
|
+
'theme-colors',
|
|
65
|
+
brand.connectedBrandName,
|
|
66
|
+
brand.color ? `$${brand.connectedBrandName}` : '$corp',
|
|
67
|
+
data
|
|
68
|
+
);
|
|
69
|
+
data = addColorToMap(
|
|
70
|
+
'theme-secondary-colors',
|
|
71
|
+
brand.connectedBrandName,
|
|
72
|
+
brand.color ? `'${brand.connectedBrandName}'` : "'corp'",
|
|
73
|
+
data
|
|
74
|
+
);
|
|
75
|
+
} catch (mapError) {
|
|
76
|
+
return reject(mapError);
|
|
77
|
+
}
|
|
78
|
+
const insertionPoint = data.lastIndexOf('@each $color-name');
|
|
79
|
+
const newFileContent = data.slice(0, insertionPoint) + themeVar + data.slice(insertionPoint);
|
|
80
|
+
fs.writeFile(themesPath, newFileContent, 'utf8', (error) => {
|
|
81
|
+
if (error) {
|
|
82
|
+
reject(error);
|
|
83
|
+
} else {
|
|
84
|
+
console.log(chalk.cyan('themes.scss file updated successfully!'));
|
|
85
|
+
resolve(null);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { start };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const brandModule = require('./brand.js');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function createTranslationsFolder(translationsDirectory) {
|
|
7
|
+
const folderPath = path.join(translationsDirectory, 'languages');
|
|
8
|
+
if (!fs.existsSync(folderPath)) {
|
|
9
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
return folderPath;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function createLanguageFiles(languages, folderPath) {
|
|
15
|
+
languages.forEach((language) => {
|
|
16
|
+
const filePath = path.join(folderPath, `${language}.json`);
|
|
17
|
+
fs.writeFileSync(filePath, '{}', 'utf8');
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Same as onboarding/translations.js, but the languages list is resolved from flags/prompt upfront.
|
|
22
|
+
function start() {
|
|
23
|
+
const brand = brandModule.getBrand();
|
|
24
|
+
const translationsDir = `src/assets/brands/${brand.connectedBrandName}`;
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
try {
|
|
27
|
+
const folderPath = createTranslationsFolder(translationsDir);
|
|
28
|
+
createLanguageFiles(brand.languages, folderPath);
|
|
29
|
+
console.log(chalk.cyan('Translations added successfully!'));
|
|
30
|
+
resolve(null);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
reject(error + '\n Please add the translations manually.');
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { start };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const brandModule = require('./brand.js');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const variablesColorsPath = 'src/ng1/assets/css/sass/variables-colors.scss';
|
|
5
|
+
const defaultColor = '$corp';
|
|
6
|
+
|
|
7
|
+
// Same edits as onboarding/variablesColors.js, driven by resolved brand state (no prompts).
|
|
8
|
+
function start() {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
try {
|
|
11
|
+
const { connectedBrandName, color: hexColor } = brandModule.getBrand();
|
|
12
|
+
let data = fs.readFileSync(variablesColorsPath, 'utf8');
|
|
13
|
+
|
|
14
|
+
// Check if $colors map exists in the file
|
|
15
|
+
const colorsRegex = /\$colors:\s*\(([\s\S]*?)\)/;
|
|
16
|
+
const match = data.match(colorsRegex);
|
|
17
|
+
if (match) {
|
|
18
|
+
const colorsMap = match[1];
|
|
19
|
+
|
|
20
|
+
// Append the new brand to the colors map
|
|
21
|
+
const newColorsMap = `$colors: (${colorsMap},\n '${connectedBrandName}': ${
|
|
22
|
+
hexColor ? '$' + connectedBrandName : defaultColor
|
|
23
|
+
})`;
|
|
24
|
+
|
|
25
|
+
// Replace the $colors map in the file with the new one
|
|
26
|
+
data = data.replace(colorsRegex, newColorsMap);
|
|
27
|
+
} else {
|
|
28
|
+
return reject('Error: Could not find $colors map in the file');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const brandVar = `\$${connectedBrandName}: (
|
|
32
|
+
'lighten-5': lighten(${hexColor}, 25%),
|
|
33
|
+
'lighten-4': lighten(${hexColor}, 20%),
|
|
34
|
+
'lighten-3': lighten(${hexColor}, 15%),
|
|
35
|
+
'lighten-2': lighten(${hexColor}, 10%),
|
|
36
|
+
'lighten-1': lighten(${hexColor}, 5%),
|
|
37
|
+
'base': ${hexColor},
|
|
38
|
+
'darken-1': darken(${hexColor}, 5%),
|
|
39
|
+
'darken-2': darken(${hexColor}, 10%),
|
|
40
|
+
'darken-3': darken(${hexColor}, 15%),
|
|
41
|
+
'darken-4': darken(${hexColor}, 20%),
|
|
42
|
+
);\n`;
|
|
43
|
+
|
|
44
|
+
const newData = hexColor ? brandVar + data : data;
|
|
45
|
+
fs.writeFileSync(variablesColorsPath, newData, 'utf8');
|
|
46
|
+
console.log(chalk.cyan('\nvariables-colors.scss file updated successfully!'));
|
|
47
|
+
resolve(null);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
reject(error);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { start };
|