diff --git a/app/(defaults)/pricing/page.tsx b/app/(defaults)/pricing/page.tsx new file mode 100644 index 0000000..7e45ab6 --- /dev/null +++ b/app/(defaults)/pricing/page.tsx @@ -0,0 +1,39 @@ +import ComponentsPricingTableToggle from '@/components/pricing-table/components-pricing-table-toggle'; +import IconArrowLeft from '@/components/icon/icon-arrow-left'; +import PanelCodeHighlight from '@/components/panel-code-highlight'; +import { Metadata } from 'next'; +import Link from 'next/link'; +import React from 'react'; + +export const metadata: Metadata = { + title: 'Pricing Table', +}; + +const PricingTable = () => { + return ( +
+ +
+ {/* Basic */} + + + {/* Toggle */} + + + {/* Animated */} + +
+
+ ); +}; + +export default PricingTable; diff --git a/app/(defaults)/testing/page.tsx b/app/(defaults)/testing/page.tsx new file mode 100644 index 0000000..9d0b944 --- /dev/null +++ b/app/(defaults)/testing/page.tsx @@ -0,0 +1,268 @@ +'use client'; +import { useState } from 'react'; +import axios from 'axios'; +import { + Treemap, + Tooltip as ReTooltip, + ResponsiveContainer +} from 'recharts'; + +export default function Home() { + const [url, setUrl] = useState(''); + const [loading, setLoading] = useState(false); + const [reports, setReports] = useState(null); + const [error, setError] = useState(''); + const [activeTab, setActiveTab] = useState<'mobile' | 'desktop'>('mobile'); + + // --- Helper to parse estimated savings like "1.2 s" or "1200 ms" --- + function parseSavings(value: string): number { + if (!value) return 0; + const val = parseFloat(value); + if (value.toLowerCase().includes('ms')) return val; + if (value.toLowerCase().includes('s')) return val * 1000; + return val; + } + + const handleAudit = async () => { + if (!url) return setError('Enter a URL'); + + setLoading(true); + setError(''); + setReports(null); + + try { + const { data } = await axios.post('https://api.crawlerx.co/api/lighthouse/audit', { url }); + + // normalize the results + const normalizedResults: any = {}; + ['mobile', 'desktop'].forEach((tab) => { + const tabData = data.results?.[tab] || {}; + normalizedResults[tab] = { + report: { + scores: tabData.scores || {}, + metrics: tabData.metrics || {}, + opportunities: Array.isArray(tabData.opportunities) ? tabData.opportunities : [], + diagnostics: tabData.diagnostics || {}, + screenshot: tabData.screenshot || '', + }, + }; + }); + + setReports(normalizedResults); + setActiveTab('mobile'); + } catch (err: any) { + setError(err.response?.data?.message || err.message || 'Error'); + } finally { + setLoading(false); + } + }; + + const renderScoreCircle = (score: number) => { + const color = + score >= 90 ? 'text-green-500 border-green-500' : + score >= 50 ? 'text-yellow-500 border-yellow-500' : + 'text-red-500 border-red-500'; + + return ( +
+ {score} +
+ ); + }; + + const renderMetrics = (metrics: any = {}) => ( +
+ {Object.entries(metrics).map(([key, value]) => ( +
+

+ {key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())} +

+

{value ?? '-'}

+
+ ))} +
+ ); + + const renderOpportunities = (opportunities: any[] = []) => ( +
+

Opportunities

+ {opportunities.length === 0 &&

No suggestions available.

} + {opportunities.map((op, idx) => ( +
+

{op.title ?? '-'}

+

{op.description ?? '-'}

+ {op.estimatedSavings && ( +

Estimated Savings: {op.estimatedSavings}

+ )} +
+ ))} +
+ ); + + const renderTreeMap = (opportunities: any[] = []) => { + if (opportunities.length === 0) return null; + + const data = opportunities.map(op => ({ + name: op.title || 'Untitled', + size: parseSavings(op.estimatedSavings) + })); + + return ( +
+

Opportunities Tree Map

+
+ + + { + if (!payload || payload.length === 0) return null; + const item = payload[0].payload; + return ( +
+

{item.name}

+

{item.size} ms estimated savings

+
+ ); + }} + /> +
+
+
+
+ ); + }; + + const renderDiagnostics = (diagnostics: any = {}) => { + const keys = Object.keys(diagnostics); + return ( +
+

Diagnostics

+ {keys.length === 0 &&

No diagnostics available.

} +
+ {keys.map((key) => ( +
+
+

+ {key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())} +

+ {key === 'http2' && ( +

+ All resources should be served via HTTP/2 for better performance. +

+ )} +
+ + {diagnostics[key] ? 'Pass' : 'Fail'} + +
+ ))} +
+
+ ); + }; + + const currentReport = reports?.[activeTab]?.report || { + scores: {}, + metrics: {}, + opportunities: [], + diagnostics: {}, + screenshot: '', + }; + + return ( +
+

+ Lighthouse PageSpeed Audit +

+ + {/* Input */} +
+ setUrl(e.target.value)} + placeholder="Enter URL" + className="flex-1 px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500" + /> + +
+ + {error &&

{error}

} + + {reports && ( +
+ {/* Tabs */} +
+ + +
+ + {/* Top Scores */} +
+ {['performance', 'accessibility', 'bestPractices', 'seo', 'pwa'].map((key) => { + const score = currentReport.scores?.[key]; + if (score === undefined || score === null) return null; + return ( +
+

{key.charAt(0).toUpperCase() + key.slice(1)}

+ {renderScoreCircle(score)} +
+ ); + })} +
+ + {/* Metrics */} + {renderMetrics(currentReport.metrics)} + + {/* Opportunities */} + {renderOpportunities(currentReport.opportunities)} + + {/* Tree Map */} + {renderTreeMap(currentReport.opportunities)} + + {/* Diagnostics */} + {renderDiagnostics(currentReport.diagnostics)} + + {/* Screenshot */} + {currentReport.screenshot && ( +
+ {`${activeTab} +
+ )} +
+ )} +
+ ); +} diff --git a/components/auth/components-auth-forgot-form.tsx b/components/auth/components-auth-forgot-form.tsx index 0899e00..0aa186c 100644 --- a/components/auth/components-auth-forgot-form.tsx +++ b/components/auth/components-auth-forgot-form.tsx @@ -13,7 +13,7 @@ export default function ForgotPasswordForm() { setLoading(true); setMessage(""); try { - const res = await axios.post("http://localhost:3020/api/auth/forgot-password", { email }); + const res = await axios.post("https://api.crawlerx.co/api/auth/forgot-password", { email }); setMessage("✅ We’ve emailed you a reset code / link. Enter it below."); } catch (err: any) { console.error(err); diff --git a/components/auth/components-auth-reset-form.tsx b/components/auth/components-auth-reset-form.tsx index 8651927..a4f649f 100644 --- a/components/auth/components-auth-reset-form.tsx +++ b/components/auth/components-auth-reset-form.tsx @@ -20,7 +20,7 @@ export default function ResetPasswordForm() { setLoading(true); setMessage(""); try { - await axios.post("http://localhost:3020/api/auth/reset-password", { + await axios.post("https://api.crawlerx.co/api/auth/reset-password", { email, token, // ✅ use token from URL newPassword, diff --git a/components/highlight.tsx b/components/highlight.tsx new file mode 100644 index 0000000..2761a17 --- /dev/null +++ b/components/highlight.tsx @@ -0,0 +1,21 @@ +import 'highlight.js/styles/monokai-sublime.css'; +import hightlight from 'highlight.js'; +import { PropsWithChildren, useEffect, useRef } from 'react'; + +const CodeHighlight = ({ children }: PropsWithChildren) => { + const highlightElement = useRef(null); + + useEffect(() => { + if (highlightElement?.current) { + hightlight.highlightElement(highlightElement.current.querySelector('pre')); + } + }, []); + + return ( +
+ {children} +
+ ); +}; + +export default CodeHighlight; diff --git a/components/panel-code-highlight.tsx b/components/panel-code-highlight.tsx new file mode 100644 index 0000000..b6eb619 --- /dev/null +++ b/components/panel-code-highlight.tsx @@ -0,0 +1,37 @@ +'use client'; +import CodeHighlight from './highlight'; +import IconCode from '@/components/icon/icon-code'; +import React, { useState, ReactNode } from 'react'; + +interface PanelCodeHighlightProps { + children: ReactNode; + title?: string; + codeHighlight?: string; + id?: string; + className?: string; +} + +const PanelCodeHighlight = ({ children, title, codeHighlight, id, className = '' }: PanelCodeHighlightProps) => { + const [toggleCode, setToggleCode] = useState(false); + return ( +
+
+
{title}
+ +
+ {children} + {toggleCode && ( + +
{codeHighlight}
+
+ )} +
+ ); +}; + +export default PanelCodeHighlight; diff --git a/components/pricing-table/components-pricing-table-toggle.tsx b/components/pricing-table/components-pricing-table-toggle.tsx new file mode 100644 index 0000000..014ec25 --- /dev/null +++ b/components/pricing-table/components-pricing-table-toggle.tsx @@ -0,0 +1,168 @@ +'use client'; +import React, { useState } from 'react'; +import { loadStripe, Stripe } from '@stripe/stripe-js'; +import axios from 'axios'; + +interface Plan { + name: string; + desc: string; + monthly: number; + yearly: number; + features: string[]; + btnStyle: string; + popular?: boolean; + planId: string; +} + +// ⚡ Lazy load Stripe to avoid undefined.match errors +let stripePromise: Promise | null = null; +const getStripe = () => { + if (!stripePromise) { + const key = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY; + if (!key) throw new Error('Stripe publishable key is not defined!'); + stripePromise = loadStripe(key); + } + return stripePromise; +}; + +const ComponentsPricingTableToggle: React.FC = () => { + const [yearlyPrice, setYearlyPrice] = useState(false); + + const plans: Plan[] = [ + { + name: 'Starter SEO Crawl', + desc: 'Perfect for small websites. Crawl up to 5,000 pages per month.', + monthly: 19, + yearly: 19 * 12 * 0.8, + features: ['5,000 Pages/Month', 'Basic Crawl Reports', 'Email Support'], + btnStyle: 'btn-dark', + planId: 'starter-seo-crawl', + }, + { + name: 'Pro SEO Crawl', + desc: 'Best for medium websites. Crawl up to 50,000 pages per month.', + monthly: 49, + yearly: 49 * 12 * 0.8, + features: ['50,000 Pages/Month', 'Advanced Crawl Reports', 'Priority Support'], + btnStyle: 'btn-primary', + popular: true, + planId: 'pro-seo-crawl', + }, + { + name: 'Enterprise SEO Crawl', + desc: 'For large-scale websites. Unlimited crawling and dedicated support.', + monthly: 199, + yearly: 199 * 12 * 0.8, + features: ['Unlimited Pages', 'Custom Integrations', 'Dedicated Account Manager'], + btnStyle: 'btn-dark', + planId: 'enterprise-seo-crawl', + }, + ]; + + // 🔹 Stripe Checkout handler + const handleBuyNow = async (plan: Plan) => { + try { + const stripe = await getStripe(); + if (!stripe) return; + + // 🔹 Call backend API to create Checkout Session + const { data } = await axios.post( + 'https://api.crawlerx.co/api/payment/create-intent', + { + planId: plan.planId, + price: yearlyPrice ? plan.yearly : plan.monthly, + billing: yearlyPrice ? 'yearly' : 'monthly', + } + ); + + if (!data.sessionId) { + console.error('No sessionId returned from backend!'); + return; + } + + // 🔹 Redirect to Stripe Checkout + const { error } = await stripe.redirectToCheckout({ sessionId: data.sessionId }); + if (error) console.error('Stripe redirect error:', error.message); + } catch (err: any) { + console.error('Error creating checkout session:', err.response?.data || err.message); + } + }; + + return ( +
+
+ {/* Toggle */} +
+ Monthly + + + Yearly + + 20% Off + + +
+ + {/* Plans */} +
+ {plans.map((plan, idx) => ( +
+ {plan.popular && ( +
+ Most Popular +
+ )} +

{plan.name}

+

{plan.desc}

+
+ + ${yearlyPrice ? plan.yearly.toFixed(0) : plan.monthly} + {' '} + / {yearlyPrice ? 'yearly' : 'monthly'} +
+
+ + {plan.name} Features + +
    + {plan.features.map((f, i) => ( +
  • {f}
  • + ))} +
+
+ +
+ ))} +
+
+
+ ); +}; + +export default ComponentsPricingTableToggle; diff --git a/package-lock.json b/package-lock.json index 2215157..14e4001 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@emotion/react": "^11.10.6", "@headlessui/react": "^2.1.2", "@reduxjs/toolkit": "^2.2.7", + "@stripe/stripe-js": "^7.9.0", "@tippyjs/react": "^4.2.6", "@types/node": "^22.4.0", "@types/react": "18.3.10", @@ -19,6 +20,7 @@ "eslint": "8.57.0", "eslint-config-next": "14.2.13", "fast-xml-parser": "^5.2.5", + "highlight.js": "^11.11.1", "i18next": "^23.13.0", "next": "14.2.13", "ni18n": "^1.0.5", @@ -29,6 +31,7 @@ "react-perfect-scrollbar": "^1.5.8", "react-popper": "^2.3.0", "react-redux": "^9.1.2", + "recharts": "^3.2.1", "typescript": "^5.3.3", "universal-cookie": "^7.2.0", "yup": "^1.4.0" @@ -929,6 +932,15 @@ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==" }, + "node_modules/@stripe/stripe-js": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.9.0.tgz", + "integrity": "sha512-ggs5k+/0FUJcIgNY08aZTqpBTtbExkJMYMLSMwyucrhtWexVOEY1KJmhBsxf+E/Q15f5rbwBpj+t0t2AW2oCsQ==", + "license": "MIT", + "engines": { + "node": ">=12.16" + } + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -1011,6 +1023,69 @@ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -2153,6 +2228,127 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -2222,6 +2418,12 @@ } } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", @@ -2542,6 +2744,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-toolkit": { + "version": "1.39.10", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.10.tgz", + "integrity": "sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3124,6 +3336,12 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3613,6 +3831,15 @@ "node": ">= 0.4" } }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -3732,6 +3959,15 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -5372,6 +5608,33 @@ "node": ">=8.10.0" } }, + "node_modules/recharts": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.2.1.tgz", + "integrity": "sha512-0JKwHRiFZdmLq/6nmilxEZl3pqb4T+aKkOkOi/ZISRZwfBhVMgInxzlYU9D4KnCH3KINScLy68m/OvMXoYGZUw==", + "license": "MIT", + "dependencies": { + "@reduxjs/toolkit": "1.x.x || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", @@ -6059,6 +6322,12 @@ "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, "node_modules/tippy.js": { "version": "6.3.7", "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", @@ -6312,6 +6581,28 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", diff --git a/package.json b/package.json index d821555..a30fb5a 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@emotion/react": "^11.10.6", "@headlessui/react": "^2.1.2", "@reduxjs/toolkit": "^2.2.7", + "@stripe/stripe-js": "^7.9.0", "@tippyjs/react": "^4.2.6", "@types/node": "^22.4.0", "@types/react": "18.3.10", @@ -20,6 +21,7 @@ "eslint": "8.57.0", "eslint-config-next": "14.2.13", "fast-xml-parser": "^5.2.5", + "highlight.js": "^11.11.1", "i18next": "^23.13.0", "next": "14.2.13", "ni18n": "^1.0.5", @@ -30,6 +32,7 @@ "react-perfect-scrollbar": "^1.5.8", "react-popper": "^2.3.0", "react-redux": "^9.1.2", + "recharts": "^3.2.1", "typescript": "^5.3.3", "universal-cookie": "^7.2.0", "yup": "^1.4.0"