Add Testimonials section with user feedback
This commit is contained in:
File diff suppressed because one or more lines are too long
78
dist/app-boot.js
vendored
78
dist/app-boot.js
vendored
@@ -15239,7 +15239,7 @@ var SecurityFeatures = () => {
|
||||
{ id: "feature3", color: "#ff8800", icon: "fas fa-lock accent-orange", title: "AES-GCM 256 Encryption", desc: "Authenticated encryption standard" },
|
||||
{ id: "feature4", color: "#00ffff", icon: "fas fa-sync-alt accent-cyan", title: "Perfect Forward Secrecy", desc: "Automatic key rotation every 5 minutes" },
|
||||
{ id: "feature5", color: "#0088ff", icon: "fas fa-signature accent-blue", title: "ECDSA P-384 Signatures", desc: "Digital signatures for message integrity" },
|
||||
{ id: "feature6", color: "#ff0044", icon: "fas fa-shield-alt accent-red", title: "SAS Security", desc: "Revolutionary key exchange & MITM protection" }
|
||||
{ id: "feature6", color: "#f87171", icon: "fas fa-shield-alt accent-red", title: "SAS Security", desc: "Revolutionary key exchange & MITM protection" }
|
||||
];
|
||||
React.useEffect(() => {
|
||||
const cards = document.querySelectorAll(".card");
|
||||
@@ -15283,6 +15283,82 @@ var SecurityFeatures = () => {
|
||||
};
|
||||
window.SecurityFeatures = SecurityFeatures;
|
||||
|
||||
// src/components/ui/Testimonials.jsx
|
||||
var Testimonials = () => {
|
||||
const testimonials = [
|
||||
{ id: "t1", rating: 5, text: "The interface feels modern and smooth. It saves me at least 2 hours every day when managing design tasks." },
|
||||
{ id: "t2", rating: 5, text: "Finally, a solution that blends speed with simplicity. My team adopted it within a week without training." },
|
||||
{ id: "t3", rating: 5, text: "I can track progress in real time and get a clear overview of our workflow. It feels empowering." },
|
||||
{ id: "t4", rating: 5, text: "Our pipeline visibility improved dramatically. I no longer need to manually track updates." },
|
||||
{ id: "t5", rating: 5, text: "The security-first approach gives me peace of mind. We handle sensitive data with confidence now." },
|
||||
{ id: "t6", rating: 5, text: "User feedback cycles are now twice as fast. It helps us test and ship features quickly." }
|
||||
];
|
||||
React.useEffect(() => {
|
||||
const colUp = document.querySelector(".col-up");
|
||||
const colDown = document.querySelector(".col-down");
|
||||
const wrapper = document.querySelector(".testimonials-wrapper");
|
||||
if (!colUp || !colDown || !wrapper) return;
|
||||
let paused = false;
|
||||
const speed = 0.5;
|
||||
let animationId;
|
||||
const cloneCards = (container) => {
|
||||
const cards = Array.from(container.children);
|
||||
cards.forEach((card) => {
|
||||
const clone = card.cloneNode(true);
|
||||
container.appendChild(clone);
|
||||
});
|
||||
};
|
||||
cloneCards(colUp);
|
||||
cloneCards(colDown);
|
||||
const getHalfHeight = (el) => {
|
||||
const children = Array.from(el.children);
|
||||
const halfCount = children.length / 2;
|
||||
let height = 0;
|
||||
for (let i = 0; i < halfCount; i++) {
|
||||
height += children[i].offsetHeight;
|
||||
if (i < halfCount - 1) height += 24;
|
||||
}
|
||||
return height;
|
||||
};
|
||||
let y1 = 0;
|
||||
const maxScroll1 = getHalfHeight(colUp);
|
||||
const maxScroll2 = getHalfHeight(colDown);
|
||||
let y2 = -maxScroll2;
|
||||
function animate() {
|
||||
if (!paused) {
|
||||
y1 -= speed;
|
||||
y2 += speed;
|
||||
if (Math.abs(y1) >= maxScroll1) {
|
||||
y1 = 0;
|
||||
}
|
||||
if (y2 >= 0) {
|
||||
y2 = -maxScroll2;
|
||||
}
|
||||
colUp.style.transform = `translateY(${y1}px)`;
|
||||
colDown.style.transform = `translateY(${y2}px)`;
|
||||
}
|
||||
animationId = requestAnimationFrame(animate);
|
||||
}
|
||||
animate();
|
||||
const handleMouseEnter = () => {
|
||||
paused = true;
|
||||
};
|
||||
const handleMouseLeave = () => {
|
||||
paused = false;
|
||||
};
|
||||
wrapper.addEventListener("mouseenter", handleMouseEnter);
|
||||
wrapper.addEventListener("mouseleave", handleMouseLeave);
|
||||
return () => {
|
||||
cancelAnimationFrame(animationId);
|
||||
wrapper.removeEventListener("mouseenter", handleMouseEnter);
|
||||
wrapper.removeEventListener("mouseleave", handleMouseLeave);
|
||||
};
|
||||
}, []);
|
||||
const renderCard = (t, index) => /* @__PURE__ */ React.createElement("div", { key: `${t.id}-${index}`, className: "card bg-neutral-900 rounded-xl p-5 shadow-md w-72 text-sm text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center mb-2 text-yellow-400" }, "\u2605".repeat(Math.floor(t.rating)), /* @__PURE__ */ React.createElement("span", { className: "ml-2 text-secondary" }, t.rating.toFixed(1))), /* @__PURE__ */ React.createElement("p", { className: "text-secondary mb-3" }, t.text));
|
||||
return /* @__PURE__ */ React.createElement("section", { className: "py-14 px-6 bg-transparent" }, /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 lg:grid-cols-5 gap-12 max-w-7xl mx-auto items-center" }, /* @__PURE__ */ React.createElement("div", { className: "lg:col-span-2 flex flex-col justify-center" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm text-secondary mb-2" }, "Testimonials"), /* @__PURE__ */ React.createElement("h2", { className: "text-2xl sm:text-3xl font-bold text-white mb-4 leading-snug" }, "What our users are saying"), /* @__PURE__ */ React.createElement("p", { className: "text-secondary text-sm" }, "We continuously listen to our community and improve every day.")), /* @__PURE__ */ React.createElement("div", { className: "lg:col-span-3 testimonials-wrapper flex gap-6 overflow-hidden relative h-[420px]" }, /* @__PURE__ */ React.createElement("div", { className: "pointer-events-none absolute top-0 left-0 w-full h-16 bg-gradient-to-b from-[#1f1f1f]/90 to-transparent z-20" }), /* @__PURE__ */ React.createElement("div", { className: "pointer-events-none absolute bottom-0 left-0 w-full h-16 bg-gradient-to-t from-[#1f1f1f]/90 to-transparent z-20" }), /* @__PURE__ */ React.createElement("div", { className: "col-up flex flex-col gap-6" }, testimonials.map((t, i) => renderCard(t, i))), /* @__PURE__ */ React.createElement("div", { className: "col-down flex flex-col gap-6" }, testimonials.map((t, i) => renderCard(t, i))))));
|
||||
};
|
||||
window.Testimonials = Testimonials;
|
||||
|
||||
// src/components/ui/ComparisonTable.jsx
|
||||
var ComparisonTable = () => {
|
||||
const [selectedFeature, setSelectedFeature] = React.useState(null);
|
||||
|
||||
6
dist/app-boot.js.map
vendored
6
dist/app-boot.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/app.js
vendored
1
dist/app.js
vendored
@@ -453,6 +453,7 @@ var EnhancedConnectionSetup = ({
|
||||
])
|
||||
]),
|
||||
React.createElement(SecurityFeatures, { key: "security-features" }),
|
||||
React.createElement(Testimonials, { key: "testimonials" }),
|
||||
React.createElement(UniqueFeatureSlider, { key: "unique-features-slider" }),
|
||||
React.createElement(DownloadApps, { key: "download-apps" }),
|
||||
React.createElement(ComparisonTable, { key: "comparison-table" }),
|
||||
|
||||
4
dist/app.js.map
vendored
4
dist/app.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -475,6 +475,8 @@
|
||||
|
||||
React.createElement(SecurityFeatures, { key: 'security-features' }),
|
||||
|
||||
React.createElement(Testimonials, { key: 'testimonials' }),
|
||||
|
||||
React.createElement(UniqueFeatureSlider, { key: 'unique-features-slider' }),
|
||||
|
||||
React.createElement(DownloadApps, { key: 'download-apps' }),
|
||||
|
||||
@@ -5,7 +5,7 @@ const SecurityFeatures = () => {
|
||||
{ id: 'feature3', color: '#ff8800', icon: 'fas fa-lock accent-orange', title: 'AES-GCM 256 Encryption', desc: 'Authenticated encryption standard' },
|
||||
{ id: 'feature4', color: '#00ffff', icon: 'fas fa-sync-alt accent-cyan', title: 'Perfect Forward Secrecy', desc: 'Automatic key rotation every 5 minutes' },
|
||||
{ id: 'feature5', color: '#0088ff', icon: 'fas fa-signature accent-blue', title: 'ECDSA P-384 Signatures', desc: 'Digital signatures for message integrity' },
|
||||
{ id: 'feature6', color: '#ff0044', icon: 'fas fa-shield-alt accent-red', title: 'SAS Security', desc: 'Revolutionary key exchange & MITM protection' }
|
||||
{ id: 'feature6', color: '#f87171', icon: 'fas fa-shield-alt accent-red', title: 'SAS Security', desc: 'Revolutionary key exchange & MITM protection' }
|
||||
];
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
123
src/components/ui/Testimonials.jsx
Normal file
123
src/components/ui/Testimonials.jsx
Normal file
@@ -0,0 +1,123 @@
|
||||
const Testimonials = () => {
|
||||
const testimonials = [
|
||||
{ id: "t1", rating: 5.0, text: "The interface feels modern and smooth. It saves me at least 2 hours every day when managing design tasks."},
|
||||
{ id: "t2", rating: 5.0, text: "Finally, a solution that blends speed with simplicity. My team adopted it within a week without training."},
|
||||
{ id: "t3", rating: 5.0, text: "I can track progress in real time and get a clear overview of our workflow. It feels empowering."},
|
||||
{ id: "t4", rating: 5.0, text: "Our pipeline visibility improved dramatically. I no longer need to manually track updates."},
|
||||
{ id: "t5", rating: 5.0, text: "The security-first approach gives me peace of mind. We handle sensitive data with confidence now."},
|
||||
{ id: "t6", rating: 5.0, text: "User feedback cycles are now twice as fast. It helps us test and ship features quickly."}
|
||||
];
|
||||
|
||||
React.useEffect(() => {
|
||||
const colUp = document.querySelector(".col-up");
|
||||
const colDown = document.querySelector(".col-down");
|
||||
const wrapper = document.querySelector(".testimonials-wrapper");
|
||||
|
||||
if (!colUp || !colDown || !wrapper) return;
|
||||
|
||||
let paused = false;
|
||||
const speed = 0.5;
|
||||
let animationId;
|
||||
|
||||
const cloneCards = (container) => {
|
||||
const cards = Array.from(container.children);
|
||||
cards.forEach(card => {
|
||||
const clone = card.cloneNode(true);
|
||||
container.appendChild(clone);
|
||||
});
|
||||
};
|
||||
|
||||
cloneCards(colUp);
|
||||
cloneCards(colDown);
|
||||
|
||||
const getHalfHeight = (el) => {
|
||||
const children = Array.from(el.children);
|
||||
const halfCount = children.length / 2;
|
||||
let height = 0;
|
||||
for (let i = 0; i < halfCount; i++) {
|
||||
height += children[i].offsetHeight;
|
||||
if (i < halfCount - 1) height += 24;
|
||||
}
|
||||
return height;
|
||||
};
|
||||
|
||||
let y1 = 0;
|
||||
const maxScroll1 = getHalfHeight(colUp);
|
||||
const maxScroll2 = getHalfHeight(colDown);
|
||||
let y2 = -maxScroll2;
|
||||
|
||||
function animate() {
|
||||
if (!paused) {
|
||||
y1 -= speed;
|
||||
y2 += speed;
|
||||
|
||||
if (Math.abs(y1) >= maxScroll1) {
|
||||
y1 = 0;
|
||||
}
|
||||
|
||||
if (y2 >= 0) {
|
||||
y2 = -maxScroll2;
|
||||
}
|
||||
|
||||
colUp.style.transform = `translateY(${y1}px)`;
|
||||
colDown.style.transform = `translateY(${y2}px)`;
|
||||
}
|
||||
animationId = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
animate();
|
||||
|
||||
const handleMouseEnter = () => { paused = true; };
|
||||
const handleMouseLeave = () => { paused = false; };
|
||||
|
||||
wrapper.addEventListener("mouseenter", handleMouseEnter);
|
||||
wrapper.addEventListener("mouseleave", handleMouseLeave);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animationId);
|
||||
wrapper.removeEventListener("mouseenter", handleMouseEnter);
|
||||
wrapper.removeEventListener("mouseleave", handleMouseLeave);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const renderCard = (t, index) => (
|
||||
<div key={`${t.id}-${index}`} className="card bg-neutral-900 rounded-xl p-5 shadow-md w-72 text-sm text-white flex-shrink-0">
|
||||
<div className="flex items-center mb-2 text-yellow-400">
|
||||
{"★".repeat(Math.floor(t.rating))}
|
||||
<span className="ml-2 text-secondary">{t.rating.toFixed(1)}</span>
|
||||
</div>
|
||||
<p className="text-secondary mb-3">{t.text}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="py-14 px-6 bg-transparent">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-5 gap-12 max-w-7xl mx-auto items-center">
|
||||
<div className="lg:col-span-2 flex flex-col justify-center">
|
||||
<p className="text-sm text-secondary mb-2">Testimonials</p>
|
||||
<h2 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-snug">
|
||||
What our users are saying
|
||||
</h2>
|
||||
<p className="text-secondary text-sm">
|
||||
We continuously listen to our community and improve every day.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-3 testimonials-wrapper flex gap-6 overflow-hidden relative h-[420px]">
|
||||
<div className="pointer-events-none absolute top-0 left-0 w-full h-16 bg-gradient-to-b from-[#1f1f1f]/90 to-transparent z-20"></div>
|
||||
<div className="pointer-events-none absolute bottom-0 left-0 w-full h-16 bg-gradient-to-t from-[#1f1f1f]/90 to-transparent z-20"></div>
|
||||
|
||||
<div className="col-up flex flex-col gap-6">
|
||||
{testimonials.map((t, i) => renderCard(t, i))}
|
||||
</div>
|
||||
|
||||
<div className="col-down flex flex-col gap-6">
|
||||
{testimonials.map((t, i) => renderCard(t, i))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
window.Testimonials = Testimonials;
|
||||
@@ -8,6 +8,7 @@ import '../components/ui/Header.jsx';
|
||||
import '../components/ui/DownloadApps.jsx';
|
||||
import '../components/ui/UniqueFeatureSlider.jsx';
|
||||
import '../components/ui/SecurityFeatures.jsx';
|
||||
import '../components/ui/Testimonials.jsx';
|
||||
import '../components/ui/ComparisonTable.jsx';
|
||||
import '../components/ui/Roadmap.jsx';
|
||||
import '../components/ui/FileTransfer.jsx';
|
||||
|
||||
1
src/scripts/bootstrap-modules.js
vendored
1
src/scripts/bootstrap-modules.js
vendored
@@ -37,6 +37,7 @@
|
||||
loadReactComponent('../components/ui/ComparisonTable.jsx'),
|
||||
loadReactComponent('../components/ui/UniqueFeatureSlider.jsx'),
|
||||
loadReactComponent('../components/ui/SecurityFeatures.jsx'),
|
||||
loadReactComponent('../components/ui/Testimonials.jsx'),
|
||||
loadReactComponent('../components/ui/Roadmap.jsx'),
|
||||
loadReactComponent('../components/ui/FileTransfer.jsx'),
|
||||
]);
|
||||
|
||||
1
sw.js
1
sw.js
@@ -22,6 +22,7 @@ const STATIC_ASSETS = [
|
||||
'/src/components/ui/ComparisonTable.jsx',
|
||||
'/src/components/ui/UniqueFeatureSlider.jsx',
|
||||
'/src/components/ui/SecurityFeatures.jsx',
|
||||
'/src/components/ui/Testimonials.jsx',
|
||||
'/src/components/ui/Roadmap.jsx',
|
||||
'/src/styles/main.css',
|
||||
'/src/styles/animations.css',
|
||||
|
||||
Reference in New Issue
Block a user