diff --git a/.changeset/sixty-colts-bathe.md b/.changeset/sixty-colts-bathe.md
new file mode 100644
index 0000000000..a9682143b2
--- /dev/null
+++ b/.changeset/sixty-colts-bathe.md
@@ -0,0 +1,4 @@
+---
+---
+
+Add company type and revenue tier collection during onboarding flow.
diff --git a/server/public/admin-members.html b/server/public/admin-members.html
index e5a7a046eb..d615dee74f 100644
--- a/server/public/admin-members.html
+++ b/server/public/admin-members.html
@@ -368,16 +368,17 @@
${member.company_name || 'Unknown'}
+ ${companyTypeText}
+ ${revenueTierText}
${member.owner_email || 'No email'}
${statusText}
${amountText}
${periodEndText}
- ${agreementText}
${stripeLink ? `💳 ` : ''}
📊
@@ -577,22 +600,47 @@ This action cannot be undone
}
function exportToCSV() {
- const headers = ['Company Name', 'Owner Email', 'Subscription Status', 'Current Period End', 'Agreement Signed', 'Created'];
+ const headers = ['Company Name', 'Company Type', 'Revenue Tier', 'Owner Email', 'Subscription Status', 'Amount', 'Current Period End', 'Created'];
const rows = filteredMembers.map(member => {
const periodEnd = member.subscription_current_period_end
? new Date(member.subscription_current_period_end * 1000).toLocaleDateString()
: '';
- const agreementSigned = member.agreement_signed_at
- ? new Date(member.agreement_signed_at).toLocaleDateString()
- : '';
const created = new Date(member.created_at).toLocaleDateString();
+ // Format company type
+ const companyTypeLabels = {
+ brand: 'Brand',
+ publisher: 'Publisher',
+ agency: 'Agency',
+ adtech: 'Ad Tech',
+ other: 'Other'
+ };
+ const companyType = member.is_personal ? 'Individual' : (companyTypeLabels[member.company_type] || '');
+
+ // Format revenue tier
+ const revenueTierLabels = {
+ under_1m: '< $1M',
+ '1m_5m': '$1-5M',
+ '5m_50m': '$5-50M',
+ '50m_250m': '$50-250M',
+ '250m_1b': '$250M-1B',
+ '1b_plus': '$1B+'
+ };
+ const revenueTier = member.is_personal ? '' : (revenueTierLabels[member.revenue_tier] || '');
+
+ // Format amount
+ const amount = member.subscription_amount
+ ? `$${(member.subscription_amount / 100).toFixed(2)}/${member.subscription_interval || ''}`
+ : '';
+
return [
member.company_name,
+ companyType,
+ revenueTier,
member.owner_email,
member.subscription_status || 'none',
+ amount,
periodEnd,
- agreementSigned,
created
];
});
diff --git a/server/public/dashboard.html b/server/public/dashboard.html
index 7d2ee999ab..d5e719f12c 100644
--- a/server/public/dashboard.html
+++ b/server/public/dashboard.html
@@ -525,6 +525,16 @@
+
+
+
+ ⚙️ Organization Settings
+
+
+
+
@@ -819,6 +829,15 @@ Create Your Pro
const billingData = await billingResponse.json();
return {
...org,
- billing: billingData.subscription,
+ billing: {
+ ...billingData.subscription,
+ company_type: billingData.company_type,
+ revenue_tier: billingData.revenue_tier,
+ is_personal: billingData.is_personal,
+ },
stripeCustomerId: billingData.stripe_customer_id,
customerSessionSecret: billingData.customer_session_secret,
billingError: null
@@ -2050,6 +2074,110 @@ Create Your Pro
}
}
+ // Organization settings helper labels
+ const companyTypeLabels = {
+ 'brand': 'Brand / Marketer',
+ 'publisher': 'Publisher',
+ 'agency': 'Agency',
+ 'adtech': 'Ad Tech Provider',
+ 'other': 'Other'
+ };
+
+ const revenueTierLabels = {
+ 'under_1m': '< $1M',
+ '1m_5m': '$1-5M',
+ '5m_50m': '$5-50M',
+ '50m_250m': '$50-250M',
+ '250m_1b': '$250M-1B',
+ '1b_plus': '$1B+'
+ };
+
+ // Render organization settings form
+ function renderOrgSettings(org) {
+ const container = document.getElementById('orgSettingsDetails');
+ if (!container) return;
+
+ // Get current values from billing data (which includes company_type, revenue_tier)
+ const companyType = org.billing?.company_type || null;
+ const revenueTier = org.billing?.revenue_tier || null;
+
+ container.innerHTML = `
+
+
+ Company Type
+
+ Not specified
+ ${Object.entries(companyTypeLabels).map(([value, label]) =>
+ `${label} `
+ ).join('')}
+
+
+
+
+ Annual Revenue
+
+ Not specified
+ ${Object.entries(revenueTierLabels).map(([value, label]) =>
+ `${label} `
+ ).join('')}
+
+
+
+
+
+
+ Save Settings
+
+
+ `;
+ }
+
+ // Save organization settings
+ async function saveOrgSettings(orgId) {
+ const companyType = document.getElementById('orgSettingsCompanyType').value || null;
+ const revenueTier = document.getElementById('orgSettingsRevenueTier').value || null;
+ const messageEl = document.getElementById('orgSettingsMessage');
+
+ try {
+ const response = await fetch(`/api/organizations/${orgId}/settings`, {
+ method: 'PATCH',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ company_type: companyType,
+ revenue_tier: revenueTier
+ }),
+ credentials: 'same-origin'
+ });
+
+ if (!response.ok) {
+ const data = await response.json();
+ throw new Error(data.message || 'Failed to save settings');
+ }
+
+ // Update the local org data
+ if (currentOrg && currentOrg.billing) {
+ currentOrg.billing.company_type = companyType;
+ currentOrg.billing.revenue_tier = revenueTier;
+ }
+
+ // Show success message
+ messageEl.style.display = 'block';
+ messageEl.style.background = 'var(--color-success-50)';
+ messageEl.style.color = 'var(--color-success-700)';
+ messageEl.textContent = 'Settings saved successfully';
+
+ setTimeout(() => {
+ messageEl.style.display = 'none';
+ }, 3000);
+ } catch (error) {
+ console.error('Error saving org settings:', error);
+ messageEl.style.display = 'block';
+ messageEl.style.background = 'var(--color-error-50)';
+ messageEl.style.color = 'var(--color-error-700)';
+ messageEl.textContent = error.message;
+ }
+ }
+
loadUserData();
// Handle browser back/forward cache (bfcache) restoration
diff --git a/server/public/join-cta.js b/server/public/join-cta.js
index 3963d61450..fe84865855 100644
--- a/server/public/join-cta.js
+++ b/server/public/join-cta.js
@@ -243,7 +243,7 @@ function renderJoinCta(options = {}) {
@@ -268,7 +268,7 @@ function renderJoinCta(options = {}) {
diff --git a/server/public/nav.js b/server/public/nav.js
index 4e72f29a5a..f38fddfcae 100644
--- a/server/public/nav.js
+++ b/server/public/nav.js
@@ -99,7 +99,7 @@
// Not logged in - show login/signup (links to AAO)
authSection = `
Log in
- Sign up
+ Sign up
`;
}
}
diff --git a/server/public/onboarding.html b/server/public/onboarding.html
index 39b86a2fe3..6d03916bb0 100644
--- a/server/public/onboarding.html
+++ b/server/public/onboarding.html
@@ -422,6 +422,16 @@
border-radius: var(--radius-md);
text-align: center;
}
+
+ /* Radio option hover styles */
+ .radio-option:hover {
+ border-color: var(--color-brand);
+ background: var(--color-primary-50);
+ }
+ .radio-option:has(input:checked) {
+ border-color: var(--color-brand);
+ background: var(--color-primary-100);
+ }
@@ -440,75 +450,207 @@ You have pending invitations
-
-
-
+
+
+
Are you joining as an individual or on behalf of a company?
+
+
- Join your company's existing membership. You may need an admin to approve your access.
+ Perfect for consultants, freelancers, academics, or those exploring the space.
-
+
- Start a new company membership. You'll be able to invite team members after setup.
+ For brands, publishers, agencies, and ad tech providers.
+
-
-