react-miami-stickers
Version:
Interactive CLI tool for gathering user information for Miami stickers
295 lines (294 loc) ⢠10.3 kB
JavaScript
import inquirer from 'inquirer';
import chalk from 'chalk';
import figlet from 'figlet';
import ora from 'ora';
import axios from 'axios';
import fs from 'fs/promises';
const API_BASE_URL = 'https://aegis.exon.dev/api/rm';
const sleep = (ms = 1000) => new Promise((resolve) => setTimeout(resolve, ms));
// Check if running in admin mode
const isAdminMode = process.argv.includes('--admin');
async function checkEmailExists(email) {
try {
const response = await axios.get(`${API_BASE_URL}?email=${encodeURIComponent(email)}`);
return response.data.exists === true;
}
catch (error) {
if (error.response?.status === 404) {
return false;
}
// For any other errors, let the registration continue but log the error
console.log(chalk.yellow('Warning: Could not verify email uniqueness. Proceeding anyway...'));
return false;
}
}
async function exportToCSV(data) {
const headers = ['ID', 'Email', 'First Name', 'Last Name', 'Address Line 1', 'Address Line 2', 'City', 'State', 'ZIP Code', 'Created At', 'Updated At'];
const rows = data.map(item => [
item.id,
item.email,
item.first_name,
item.last_name,
item.address_line1,
item.address_line2 || '',
item.city,
item.state,
item.zip_code,
item.created_at,
item.updated_at
]);
const csvContent = [
headers.join(','),
...rows.map(row => row.map(cell => `"${cell}"`).join(','))
].join('\n');
const fileName = `waitlist_export_${new Date().toISOString().split('T')[0]}.csv`;
await fs.writeFile(fileName, csvContent);
return fileName;
}
async function adminMode() {
const { apiKey } = await inquirer.prompt([
{
type: 'password',
name: 'apiKey',
message: chalk.yellow('Please enter the API key:'),
validate: (input) => {
if (!input)
return 'API key is required';
return true;
}
}
]);
const spinner = ora('Fetching waitlist data...').start();
try {
const response = await axios.get(`${API_BASE_URL}?API_KEY=${apiKey}`);
spinner.succeed('Data fetched successfully!');
const fileName = await exportToCSV(response.data.data);
console.log(chalk.green(`\nData exported to ${fileName} successfully! š`));
process.exit(0);
}
catch (error) {
spinner.fail('Failed to fetch data');
if (error.response?.status === 401) {
console.error(chalk.red('Invalid API key'));
}
else {
console.error(chalk.red('An error occurred while fetching data'));
}
process.exit(1);
}
}
async function welcome() {
console.clear();
const title = chalk.bold.cyan(figlet.textSync('reactMiami', {
font: 'Standard',
horizontalLayout: 'fitted',
}));
console.log(title);
console.log(chalk.bold.magenta('š“ Welcome to the Miami Stickers Registration! Let\'s get you set up! šļø'));
await sleep();
}
async function getUserInfo() {
const questions = [
{
type: 'input',
name: 'firstName',
message: chalk.green('What is your first name?'),
validate: (input) => {
if (input.length < 2) {
return 'Please enter a valid first name (at least 2 characters)';
}
return true;
},
},
{
type: 'input',
name: 'lastName',
message: chalk.green('What is your last name?'),
validate: (input) => {
if (input.length < 2) {
return 'Please enter a valid last name (at least 2 characters)';
}
return true;
},
},
{
type: 'input',
name: 'email',
message: chalk.blue('What is your email address?'),
validate: (input) => {
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(input)) {
return 'Please enter a valid email address (e.g., user@example.com)';
}
return true;
},
},
{
type: 'input',
name: 'phone',
message: chalk.yellow('What is your phone number?'),
validate: (input) => {
const cleanPhone = input.replace(/[\s()-]/g, '');
const phoneRegex = /^(\+?1)?[0-9]{10}$/;
if (!phoneRegex.test(cleanPhone)) {
return 'Please enter a valid 10-digit phone number (e.g., 123-456-7890)';
}
return true;
},
},
{
type: 'input',
name: 'addressLine1',
message: chalk.magenta('Address Line 1 (Street address)'),
validate: (input) => {
if (input.length < 5) {
return 'Please enter a valid street address';
}
return true;
},
},
{
type: 'input',
name: 'addressLine2',
message: chalk.magenta('Address Line 2 (Apt, Suite, etc.) - Optional'),
},
{
type: 'input',
name: 'city',
message: chalk.magenta('City'),
validate: (input) => {
if (input.length < 2) {
return 'Please enter a valid city name';
}
return true;
},
},
{
type: 'input',
name: 'state',
message: chalk.magenta('State (2-letter code)'),
validate: (input) => {
const stateRegex = /^[A-Z]{2}$/i;
if (!stateRegex.test(input)) {
return 'Please enter a valid 2-letter state code (e.g., FL)';
}
return true;
},
filter: (input) => input.toUpperCase(),
},
{
type: 'input',
name: 'zipCode',
message: chalk.magenta('ZIP Code'),
validate: (input) => {
const zipRegex = /^\d{5}(-\d{4})?$/;
if (!zipRegex.test(input)) {
return 'Please enter a valid ZIP code (e.g., 12345 or 12345-6789)';
}
return true;
},
}
];
return inquirer.prompt(questions);
}
async function confirmDetails(userInfo) {
console.clear();
console.log(chalk.cyan.bold('\nš Please review your information:\n'));
console.log(chalk.green('Name: ') + `${userInfo.firstName} ${userInfo.lastName}`);
console.log(chalk.blue('Email: ') + userInfo.email);
console.log(chalk.yellow('Phone: ') + userInfo.phone);
console.log(chalk.magenta('Address:'));
console.log(chalk.magenta(' Street: ') + userInfo.addressLine1);
if (userInfo.addressLine2) {
console.log(chalk.magenta(' Unit/Suite: ') + userInfo.addressLine2);
}
console.log(chalk.magenta(' City: ') + userInfo.city);
console.log(chalk.magenta(' State: ') + userInfo.state);
console.log(chalk.magenta(' ZIP: ') + userInfo.zipCode);
const { isConfirmed } = await inquirer.prompt([
{
type: 'confirm',
name: 'isConfirmed',
message: chalk.cyan('\nIs this information correct?'),
default: true,
},
]);
if (isConfirmed) {
const spinner = ora('Checking email availability...').start();
try {
const exists = await checkEmailExists(userInfo.email);
spinner.stop();
if (exists) {
console.log(chalk.red('\nThis email is already registered. Please use a different email address.'));
await sleep(2000);
return false;
}
return true;
}
catch (error) {
spinner.fail('Error checking email availability');
console.log(chalk.yellow('\nUnable to verify email. Please try again.'));
await sleep(2000);
return false;
}
}
return false;
}
async function submitToAPI(userInfo) {
const spinner = ora('Submitting your information...').start();
try {
const formattedUserInfo = {
email: userInfo.email,
firstName: userInfo.firstName,
lastName: userInfo.lastName,
phone: userInfo.phone,
address: {
line1: userInfo.addressLine1,
line2: userInfo.addressLine2 || '',
city: userInfo.city,
state: userInfo.state,
zipCode: userInfo.zipCode
}
};
const response = await axios.post(API_BASE_URL, formattedUserInfo);
await sleep();
spinner.succeed('Successfully submitted your information! š');
console.log(chalk.green.bold('\nThank you for registering! You\'ll receive your Miami stickers soon! š“'));
}
catch (error) {
spinner.fail('Something went wrong!');
if (error.response?.data?.error) {
console.error(chalk.red(`Error: ${error.response.data.error}`));
}
else {
console.error(chalk.red('An unexpected error occurred. Please try again later.'));
}
process.exit(1);
}
}
async function main() {
try {
if (isAdminMode) {
await adminMode();
return;
}
await welcome();
let isConfirmed = false;
let userInfo;
while (!isConfirmed) {
userInfo = await getUserInfo();
isConfirmed = await confirmDetails(userInfo);
if (!isConfirmed) {
console.log(chalk.yellow('\nLet\'s enter your information again...\n'));
await sleep(1000);
}
}
await submitToAPI(userInfo);
}
catch (error) {
console.error(chalk.red('An error occurred:'), error);
process.exit(1);
}
}
main();