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.
@@ -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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antelope-cli",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "CLI-Tool for automating processes for Antelope-Systems ",
5
5
  "main": "index.js",
6
6
  "bin": {