Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/components/landing/ProPreview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ const TABLE_ROWS = [
{ key: 'fullscreen', free: true, pro: true },
{ key: 'insight', free: true, pro: true },
{ key: 'pseudocode', free: true, pro: true },
{ key: 'notes', free: true, pro: true },
{ key: 'notes', free: 'notesFree', pro: 'notesPro' },
{ key: 'videoExport', free: 'videoExportFree', pro: 'videoExportPro' },
{ key: 'customInput', free: false, pro: true },
{ key: 'comparison', free: false, pro: true },
{ key: 'presentation', free: false, pro: true },
{ key: 'quizExam', free: false, pro: true },
{ key: 'embedWidget', free: false, pro: true },
];

function ProPreview() {
Expand Down
44 changes: 28 additions & 16 deletions src/components/landing/ProPreview.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,14 @@ describe('ProPreview', () => {
expect(
screen.getByText('Presentation Mode (Dedicated For Teachers)')
).toBeInTheDocument();
expect(screen.getByText('Step-by-step Pseudocode')).toBeInTheDocument();
expect(
screen.getByText('Save Your Notes on Every Algorithm')
screen.getByText('Custom Quiz & Exam Generator')
).toBeInTheDocument();
expect(
screen.getByText('Embeddable Visualizer Widget')
).toBeInTheDocument();
expect(screen.getByText('Step-by-step Pseudocode')).toBeInTheDocument();
expect(screen.getByText('Study Notes')).toBeInTheDocument();
});

it('should render check icon for features on both plans', () => {
Expand All @@ -150,7 +154,7 @@ describe('ProPreview', () => {
it('should render x icon for features missing on the free plan', () => {
const { container } = renderComponent();
const crosses = excludedIcons(container);
expect(crosses.length).toBe(3);
expect(crosses.length).toBe(5);
crosses.forEach(cross => {
expect(cross).toHaveClass('text-red-500');
});
Expand All @@ -171,20 +175,18 @@ describe('ProPreview', () => {
).toBe('Included');
});

it('should mark pseudocode and notes as included on both plans', () => {
it('should mark pseudocode as included on both plans', () => {
const { container } = renderComponent();
const rows = [...container.querySelectorAll('tbody tr')];
['Step-by-step Pseudocode', 'Save Your Notes on Every Algorithm'].forEach(
label => {
const row = rows.find(tr => tr.textContent.includes(label));
const cells = row.querySelectorAll('td');
expect(cells[1].querySelector('svg').getAttribute('aria-label')).toBe(
'Included'
);
expect(cells[2].querySelector('svg').getAttribute('aria-label')).toBe(
'Included'
);
}
const pseudocodeRow = rows.find(tr =>
tr.textContent.includes('Step-by-step Pseudocode')
);
const cells = pseudocodeRow.querySelectorAll('td');
expect(cells[1].querySelector('svg').getAttribute('aria-label')).toBe(
'Included'
);
expect(cells[2].querySelector('svg').getAttribute('aria-label')).toBe(
'Included'
);
});

Expand All @@ -196,6 +198,16 @@ describe('ProPreview', () => {
expect(screen.getByText('Watermark-Free Exports')).toBeInTheDocument();
});

it('should render study notes cells as text instead of icons', () => {
renderComponent();
expect(
screen.getByText('Basic Text Notes per Algorithm')
).toBeInTheDocument();
expect(
screen.getByText('Study Guide Export, Code Snippets & Search')
).toBeInTheDocument();
});

it('should not render any price', () => {
renderComponent();
expect(screen.queryByText('Free forever')).not.toBeInTheDocument();
Expand All @@ -222,7 +234,7 @@ describe('ProPreview', () => {
const proColumnCells = [...container.querySelectorAll('tbody tr')].map(
tr => tr.querySelectorAll('td')[2]
);
expect(proColumnCells.length).toBe(12);
expect(proColumnCells.length).toBe(14);
});
});

Expand Down
22 changes: 19 additions & 3 deletions src/i18n/locales/ar/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
},
"eyebrow": "الخطة الاحترافية",
"headline": "تقدّم أكثر مع الخطة الاحترافية",
"subheadline": "النسخة المدفوعة القادمة ستفتح ميزات أقوى مثل المدخلات المخصصة والمقارنات جنبًا إلى جنب والتصدير بدون علامة مائية والمزيد.",
"subheadline": "النسخة المدفوعة القادمة ستفتح ميزات أقوى مثل المدخلات المخصصة والمقارنات جنبًا إلى جنب والتصدير بدون علامة مائية والاختبارات المخصصة والأدوات القابلة للتضمين.",
"featuresHeading": "ما تفتحه الخطة الاحترافية",
"features": {
"customInput": {
Expand All @@ -95,6 +95,18 @@
"presentation": {
"title": "وضع العرض",
"body": "استخدم عرضًا تعليميًا أكثر تركيزًا مع واجهة أبسط وحالات مرئية أكبر."
},
"quizExam": {
"title": "مولّد الاختبارات والامتحانات المخصصة",
"body": "حوّل أي مجموعة خوارزميات إلى اختبار قابل للتصحيح أو امتحان محدّد بوقت، قابل للتصدير كملف PDF."
},
"studyGuide": {
"title": "ملاحظات دراسية متطورة",
"body": "صدّر ملاحظاتك كدليل دراسة، وأرفق مقتطفات أكواد قابلة للتشغيل، وابحث في كل خوارزمية درستها."
},
"embedWidget": {
"title": "أداة قابلة للتضمين",
"body": "ضع عارضًا تفاعليًا مباشرًا في موقع كورسك أو شرائحك أو مدونتك."
}
},
"form": {
Expand Down Expand Up @@ -1452,7 +1464,7 @@
"badge": "قريباً",
"badgeJoined": "أنت على القائمة",
"heading": "انطلق أبعد مع الخطة الاحترافية",
"subheading": "مصممة للتحضير للمقابلات والتدريس في الصف: مدخلات مخصصة، مقارنات جنباً إلى جنب، تصدير بدون علامة مائية، والمزيد قريباً.",
"subheading": "مصممة للتحضير للمقابلات والتدريس في الصف: مدخلات مخصصة، مقارنات جنباً إلى جنب، تصدير بدون علامة مائية، اختبارات مخصصة، عارضات قابلة للتضمين، والمزيد قريباً.",
"cta": "انضم إلى قائمة الانتظار",
"ctaJoined": "الخطة الاحترافية",
"table": {
Expand All @@ -1473,8 +1485,12 @@
"customInput": "مدخلات مخصصة",
"comparison": "وضع مقارنة الخوارزميات",
"presentation": "وضع العرض (مخصص للمعلمين)",
"quizExam": "مولّد الاختبارات والامتحانات المخصصة",
"embedWidget": "أداة عارض قابلة للتضمين",
"pseudocode": "شبه الشيفرة خطوة بخطوة",
"notes": "ملاحظات على كل خوارزمية"
"notes": "ملاحظات دراسية",
"notesFree": "ملاحظات نصية أساسية على كل خوارزمية",
"notesPro": "تصدير دليل دراسة، مقتطفات أكواد، وبحث"
}
},
"faq": {
Expand Down
22 changes: 19 additions & 3 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
},
"eyebrow": "The Pro Plan",
"headline": "Go further with the Pro Plan",
"subheadline": "The upcoming paid plan will unlock more powerful features like custom inputs, side-by-side comparisons, watermark-free exports, and more.",
"subheadline": "The upcoming paid plan will unlock more powerful features like custom inputs, side-by-side comparisons, watermark-free exports, custom quizzes, and embeddable widgets.",
"featuresHeading": "What the Pro Plan unlocks",
"features": {
"customInput": {
Expand All @@ -95,6 +95,18 @@
"presentation": {
"title": "Presentation mode",
"body": "Use a focused teaching view with cleaner chrome and larger visual states."
},
"quizExam": {
"title": "Custom quiz & exam generator",
"body": "Turn any algorithm set into a gradeable quiz or timed exam, exportable as PDF."
},
"studyGuide": {
"title": "Supercharged study notes",
"body": "Export your notes as a study guide, attach runnable code snippets, and search across every algorithm you've studied."
},
"embedWidget": {
"title": "Embeddable widget",
"body": "Drop a live, interactive visualizer into your own course site, slides, or blog."
}
},
"form": {
Expand Down Expand Up @@ -1452,7 +1464,7 @@
"badge": "Coming Soon",
"badgeJoined": "You're on the list",
"heading": "Go Further with Pro",
"subheading": "Built for interview prep and classroom teaching: custom inputs, side-by-side comparisons, watermark-free exports, and more...",
"subheading": "Built for interview prep and classroom teaching: custom inputs, side-by-side comparisons, watermark-free exports, custom quizzes, embeddable visualizers, and more...",
"cta": "Join The Waitlist",
"ctaJoined": "View Your Spot",
"table": {
Expand All @@ -1473,8 +1485,12 @@
"customInput": "Custom Inputs",
"comparison": "Algorithms Comparison Mode",
"presentation": "Presentation Mode (Dedicated For Teachers)",
"quizExam": "Custom Quiz & Exam Generator",
"embedWidget": "Embeddable Visualizer Widget",
"pseudocode": "Step-by-step Pseudocode",
"notes": "Save Your Notes on Every Algorithm"
"notes": "Study Notes",
"notesFree": "Basic Text Notes per Algorithm",
"notesPro": "Study Guide Export, Code Snippets & Search"
}
},
"faq": {
Expand Down
22 changes: 19 additions & 3 deletions src/i18n/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
},
"eyebrow": "Le Plan Pro",
"headline": "Allez plus loin avec le Plan Pro",
"subheadline": "La version payante à venir débloquera des fonctionnalités plus puissantes comme des entrées personnalisées, des comparaisons côte à côte, des exports sans filigrane et plus encore.",
"subheadline": "La version payante à venir débloquera des fonctionnalités plus puissantes comme des entrées personnalisées, des comparaisons côte à côte, des exports sans filigrane, des quiz personnalisés et des widgets intégrables.",
"featuresHeading": "Ce que le Plan Pro débloque",
"features": {
"customInput": {
Expand All @@ -95,6 +95,18 @@
"presentation": {
"title": "Mode présentation",
"body": "Utilisez une vue d'enseignement plus ciblée avec moins de chrome et des états visuels plus grands."
},
"quizExam": {
"title": "Générateur de quiz et d'examens personnalisés",
"body": "Transformez n'importe quel ensemble d'algorithmes en quiz noté ou examen chronométré, exportable en PDF."
},
"studyGuide": {
"title": "Notes d'étude améliorées",
"body": "Exportez vos notes en guide de révision, joignez des extraits de code exécutables et recherchez dans chaque algorithme que vous avez étudié."
},
"embedWidget": {
"title": "Widget intégrable",
"body": "Intégrez un visualiseur interactif en direct dans votre site de cours, vos diapositives ou votre blog."
}
},
"form": {
Expand Down Expand Up @@ -1452,7 +1464,7 @@
"badge": "Bientôt Disponible",
"badgeJoined": "Vous êtes sur la liste",
"heading": "Allez Plus Loin avec Pro",
"subheading": "Conçu pour la préparation aux entretiens et l'enseignement en classe : entrées personnalisées, comparaisons côte à côte, exports sans filigrane, et plus encore, bientôt disponible.",
"subheading": "Conçu pour la préparation aux entretiens et l'enseignement en classe : entrées personnalisées, comparaisons côte à côte, exports sans filigrane, quiz personnalisés, visualiseurs intégrables, et plus encore, bientôt disponible.",
"cta": "Rejoignez la liste d'attente",
"ctaJoined": "Plan Pro",
"table": {
Expand All @@ -1473,8 +1485,12 @@
"customInput": "Entrées personnalisées",
"comparison": "Mode comparaison d'algorithmes",
"presentation": "Mode présentation (dédié aux enseignants)",
"quizExam": "Générateur de quiz et d'examens personnalisés",
"embedWidget": "Widget visualiseur intégrable",
"pseudocode": "Pseudo-code pas à pas",
"notes": "Notes sur chaque algorithme"
"notes": "Notes d'étude",
"notesFree": "Notes texte de base sur chaque algorithme",
"notesPro": "Export en guide de révision, extraits de code et recherche"
}
},
"faq": {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/LandingPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { SHOW_LANDING_SOCIAL_PROOF } from '../components/landing/landingSocialPr

function LandingPage() {
return (
<div className="min-h-screen relative overflow-x-hidden">
<div className="min-h-screen relative overflow-x-clip">
<ProWaitlistBanner source="landing" />

{/* Animated Tech Pattern */}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/LandingPage.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('LandingPage', () => {
const mainContainer = container.firstChild;
expect(mainContainer).toHaveClass('min-h-screen');
expect(mainContainer).toHaveClass('relative');
expect(mainContainer).toHaveClass('overflow-x-hidden');
expect(mainContainer).toHaveClass('overflow-x-clip');
});

it('should have header below the banner', () => {
Expand Down
6 changes: 6 additions & 0 deletions src/pages/ProComingSoonPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
import { useEffect, useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import {
CodeBlock,
Columns,
Exam,
Export,
Notebook,
PlusCircle,
Presentation,
Sliders,
Expand All @@ -34,6 +37,9 @@
{ key: 'comparison', Icon: Columns },
{ key: 'export', Icon: Export },
{ key: 'presentation', Icon: Presentation },
{ key: 'quizExam', Icon: Exam },
{ key: 'studyGuide', Icon: Notebook },
{ key: 'embedWidget', Icon: CodeBlock },
];

const PRO_SORT_BAR_HEIGHTS = [40, 65, 30, 80, 50, 70];
Expand Down Expand Up @@ -77,7 +83,7 @@
? 'already_joined'
: 'idle';
});
const [position, setPosition] = useState(null);

Check warning on line 86 in src/pages/ProComingSoonPage.jsx

View workflow job for this annotation

GitHub Actions / Code Quality

'position' is assigned a value but never used. Allowed unused vars must match /^[A-Z_]|^motion$/u

Check warning on line 86 in src/pages/ProComingSoonPage.jsx

View workflow job for this annotation

GitHub Actions / Code Quality

'position' is assigned a value but never used. Allowed unused vars must match /^[A-Z_]|^motion$/u
const [errorKey, setErrorKey] = useState(null);
const [waitlistCount, setWaitlistCount] = useState(0);

Expand Down
Loading