filebrowser cleanup | privacy

This commit is contained in:
lovebird 2026-03-11 11:27:48 +01:00
parent 7787c948eb
commit c2ae4435e4
15 changed files with 1844 additions and 1697 deletions

View File

@ -30,5 +30,18 @@ export interface EcommerceBundleDependencies {
getAuthToken?: () => Promise<string | null>;
/** Locale for Stripe Elements (e.g. "en", "de"). */
locale?: string;
/** Translation function — receives an English key/HTML fragment, returns translated string. */
t?: (key: string) => string;
/** Impressum / Legal disclosure data (required in DACH). */
impressum?: {
company?: string;
representative?: string;
address?: string;
email?: string;
phone?: string;
vatId?: string;
register?: string;
responsibleContent?: string;
};
}
export declare const EcommerceBundle: React.FC<EcommerceBundleDependencies>;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,24 @@
export interface ImpressumPageProps {
/** Company / legal entity name. */
company?: string;
/** Legal representative. */
representative?: string;
/** Postal address. */
address?: string;
/** Contact email. */
email?: string;
/** Contact phone. */
phone?: string;
/** VAT / Tax ID (USt-IdNr.). */
vatId?: string;
/** Commercial register entry. */
register?: string;
/** Responsible for content (§ 18 Abs. 2 MStV). */
responsibleContent?: string;
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
/** Impressum / Legal Disclosure page — required by German TMG §5 / DDG. */
export declare function ImpressumPage({ company, representative, address, email, phone, vatId, register, responsibleContent, t: _t, className, }: ImpressumPageProps): import("react/jsx-runtime").JSX.Element;

View File

@ -3,8 +3,10 @@ export interface PrivacyPolicyPageProps {
siteName?: string;
/** Contact email for privacy inquiries. */
contactEmail?: string;
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
/** Public page with privacy / cookie information. */
export declare function PrivacyPolicyPage({ siteName, contactEmail, className, }: PrivacyPolicyPageProps): import("react/jsx-runtime").JSX.Element;
export declare function PrivacyPolicyPage({ siteName, contactEmail, t: _t, className, }: PrivacyPolicyPageProps): import("react/jsx-runtime").JSX.Element;

View File

@ -1,8 +1,10 @@
export interface ReturnsPageProps {
/** Number of days for return window. */
returnWindowDays?: number;
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
/** Public page with refund / return policy. */
export declare function ReturnsPage({ returnWindowDays, className }: ReturnsPageProps): import("react/jsx-runtime").JSX.Element;
export declare function ReturnsPage({ returnWindowDays, t: _t, className }: ReturnsPageProps): import("react/jsx-runtime").JSX.Element;

View File

@ -7,8 +7,10 @@ export interface ShippingRate {
export interface ShippingPageProps {
/** Custom shipping rates to display. Falls back to placeholder content. */
rates?: ShippingRate[];
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
/** Public page with shipping info and rates. */
export declare function ShippingPage({ rates, className }: ShippingPageProps): import("react/jsx-runtime").JSX.Element;
export declare function ShippingPage({ rates, t: _t, className }: ShippingPageProps): import("react/jsx-runtime").JSX.Element;

View File

@ -3,8 +3,10 @@ export interface TermsPageProps {
siteName?: string;
/** Contact email for legal inquiries. */
contactEmail?: string;
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
/** Optional terms of service page. */
export declare function TermsPage({ siteName, contactEmail, className, }: TermsPageProps): import("react/jsx-runtime").JSX.Element;
export declare function TermsPage({ siteName, contactEmail, t: _t, className, }: TermsPageProps): import("react/jsx-runtime").JSX.Element;

View File

@ -7,6 +7,7 @@ import { ShippingPage } from "./policies/ShippingPage";
import { ReturnsPage } from "./policies/ReturnsPage";
import { PrivacyPolicyPage } from "./policies/PrivacyPolicyPage";
import { TermsPage } from "./policies/TermsPage";
import { ImpressumPage } from "./policies/ImpressumPage";
export interface EcommerceBundleDependencies {
user: {
@ -40,10 +41,24 @@ export interface EcommerceBundleDependencies {
getAuthToken?: () => Promise<string | null>;
/** Locale for Stripe Elements (e.g. "en", "de"). */
locale?: string;
/** Translation function — receives an English key/HTML fragment, returns translated string. */
t?: (key: string) => string;
/** Impressum / Legal disclosure data (required in DACH). */
impressum?: {
company?: string;
representative?: string;
address?: string;
email?: string;
phone?: string;
vatId?: string;
register?: string;
responsibleContent?: string;
};
}
export const EcommerceBundle: React.FC<EcommerceBundleDependencies> = (props) => {
const { pathname } = useLocation();
const t = props.t || ((key: string) => key);
if (matchPath("/cart/*", pathname)) {
return <CartPage onCheckout={() => props.onNavigate('/checkout')} />;
@ -81,10 +96,11 @@ export const EcommerceBundle: React.FC<EcommerceBundleDependencies> = (props) =>
);
}
if (matchPath("/shipping/*", pathname)) return <ShippingPage />;
if (matchPath("/returns/*", pathname)) return <ReturnsPage />;
if (matchPath("/privacy/*", pathname)) return <PrivacyPolicyPage siteName={props.siteName || "PolyMech"} contactEmail={props.contactEmail || "privacy@polymech.org"} />;
if (matchPath("/terms/*", pathname)) return <TermsPage siteName={props.siteName || "PolyMech"} contactEmail={props.contactEmail || "legal@polymech.org"} />;
if (matchPath("/shipping/*", pathname)) return <ShippingPage t={t} />;
if (matchPath("/returns/*", pathname)) return <ReturnsPage t={t} />;
if (matchPath("/privacy/*", pathname)) return <PrivacyPolicyPage siteName={props.siteName || "PolyMech"} contactEmail={props.contactEmail || "privacy@polymech.org"} t={t} />;
if (matchPath("/terms/*", pathname)) return <TermsPage siteName={props.siteName || "PolyMech"} contactEmail={props.contactEmail || "legal@polymech.org"} t={t} />;
if (matchPath("/impressum/*", pathname)) return <ImpressumPage {...(props.impressum || {})} t={t} />;
return null; // Return null if no route matches (let App.tsx handle 404 if needed, though this component is usually mounted on specific routes)
};

View File

@ -37,6 +37,9 @@ export type { PrivacyPolicyPageProps } from "./policies/PrivacyPolicyPage";
export { TermsPage } from "./policies/TermsPage";
export type { TermsPageProps } from "./policies/TermsPage";
export { ImpressumPage } from "./policies/ImpressumPage";
export type { ImpressumPageProps } from "./policies/ImpressumPage";
export { PolicyLinks } from "./policies/PolicyLinks";
export type { PolicyLinksProps, PolicyLink } from "./policies/PolicyLinks";

View File

@ -0,0 +1,96 @@
import React from "react";
import { PolicyPage } from "./PolicyPage";
export interface ImpressumPageProps {
/** Company / legal entity name. */
company?: string;
/** Legal representative. */
representative?: string;
/** Postal address. */
address?: string;
/** Contact email. */
email?: string;
/** Contact phone. */
phone?: string;
/** VAT / Tax ID (USt-IdNr.). */
vatId?: string;
/** Commercial register entry. */
register?: string;
/** Responsible for content (§ 18 Abs. 2 MStV). */
responsibleContent?: string;
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
/** Impressum / Legal Disclosure page — required by German TMG §5 / DDG. */
export function ImpressumPage({
company = "Company Name",
representative = "Representative Name",
address = "Street, City, Country",
email = "info@example.com",
phone = "",
vatId = "",
register = "",
responsibleContent = "",
t: _t,
className,
}: ImpressumPageProps) {
const t = _t || ((k: string) => k);
return (
<PolicyPage title={t("Impressum — Legal Disclosure")} className={className}>
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Information pursuant to § 5 DDG</h3>` +
`<p><strong>${company}</strong><br/>` +
`${address}</p>` +
`<p><strong>Represented by:</strong><br/>${representative}</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Contact</h3>` +
`<p>Email: <a href="mailto:${email}" class="text-primary underline">${email}</a>` +
(phone ? `<br/>Phone: ${phone}` : ``) +
`</p>`
) }} />
{vatId && (
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>VAT ID</h3>` +
`<p>VAT identification number pursuant to § 27a UStG:<br/><strong>${vatId}</strong></p>`
) }} />
)}
{register && (
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Commercial Register</h3>` +
`<p>${register}</p>`
) }} />
)}
{responsibleContent && (
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Responsible for Content</h3>` +
`<p>${responsibleContent}</p>`
) }} />
)}
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>EU Dispute Resolution</h3>` +
`<p>The European Commission provides a platform for online dispute resolution (ODR): ` +
`<a href="https://ec.europa.eu/consumers/odr/" target="_blank" rel="noopener noreferrer" class="text-primary underline">https://ec.europa.eu/consumers/odr/</a>. ` +
`We are not willing or obliged to participate in dispute resolution proceedings before a consumer arbitration board.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Liability for Content</h3>` +
`<p>As a service provider, we are responsible for our own content on these pages in accordance with general legislation pursuant to § 7(1) DDG. According to §§ 8 to 10 DDG, however, we are not obliged as a service provider to monitor transmitted or stored third-party information or to investigate circumstances that indicate illegal activity.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Liability for Links</h3>` +
`<p>Our website contains links to external third-party websites over whose content we have no influence. We therefore cannot accept any liability for this third-party content. The respective provider or operator of the linked pages is always responsible for the content of the linked pages.</p>`
) }} />
</PolicyPage>
);
}

View File

@ -1,85 +1,73 @@
import React from "react";
import { PolicyPage } from "./PolicyPage";
export interface PrivacyPolicyPageProps {
/** Business / site name used in the policy text. */
siteName?: string;
/** Contact email for privacy inquiries. */
contactEmail?: string;
/** Optional extra class names. */
className?: string;
}
/** Public page with privacy / cookie information. */
export function PrivacyPolicyPage({
siteName = "Our Store",
contactEmail = "privacy@example.com",
className,
}: PrivacyPolicyPageProps) {
return (
<PolicyPage title="Privacy Policy" className={className}>
<p>
At <strong>{siteName}</strong>, we are committed to protecting your personal information
and your right to privacy. This policy explains what information we collect, how we use
it, and what rights you have in relation to it.
</p>
<h3>Information We Collect</h3>
<ul>
<li>
<strong>Personal information:</strong> name, email, shipping address, and payment
details provided during checkout.
</li>
<li>
<strong>Usage data:</strong> pages visited, time spent, browser type, and device
information collected automatically.
</li>
<li>
<strong>Cookies:</strong> small data files stored on your device to improve your
browsing experience and remember your preferences.
</li>
</ul>
<h3>How We Use Your Information</h3>
<ul>
<li>To process and fulfill your orders.</li>
<li>To communicate with you about orders, updates, and promotions.</li>
<li>To improve our website and services.</li>
<li>To comply with legal obligations.</li>
</ul>
<h3>Data Sharing</h3>
<p>
We do not sell your personal data. We share information only with service providers
necessary to fulfill your order (e.g., payment processors, shipping carriers) and as
required by law.
</p>
<h3>Cookies</h3>
<p>
We use essential cookies for site functionality and optional analytics cookies to
understand usage patterns. You can manage cookie preferences through your browser
settings.
</p>
<h3>Your Rights</h3>
<p>
You may request access to, correction of, or deletion of your personal data at any
time by contacting us at{" "}
<a href={`mailto:${contactEmail}`} className="text-primary underline">
{contactEmail}
</a>
.
</p>
<h3>Contact Us</h3>
<p>
If you have questions about this privacy policy, please contact us at{" "}
<a href={`mailto:${contactEmail}`} className="text-primary underline">
{contactEmail}
</a>
.
</p>
</PolicyPage>
);
}
import React from "react";
import { PolicyPage } from "./PolicyPage";
export interface PrivacyPolicyPageProps {
/** Business / site name used in the policy text. */
siteName?: string;
/** Contact email for privacy inquiries. */
contactEmail?: string;
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
/** Public page with privacy / cookie information. */
export function PrivacyPolicyPage({
siteName = "Our Store",
contactEmail = "privacy@example.com",
t: _t,
className,
}: PrivacyPolicyPageProps) {
const t = _t || ((k: string) => k);
return (
<PolicyPage title={t("Privacy Policy")} className={className}>
<p dangerouslySetInnerHTML={{ __html: t(
`At <strong>${siteName}</strong>, privacy is not a checkbox — it is a core value. We believe you should be able to browse, shop, and interact with our platform without being tracked, profiled, or surveilled. We go beyond legal compliance to ensure your data is handled with the respect it deserves.`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Privacy by Design</h3>` +
`<p>We have intentionally built our platform without invasive tracking technologies. There are no third-party analytics scripts, no advertising pixels, no behavioral profiling, and no cross-site tracking — not because the law requires it, but because we believe it is the right thing to do.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Information We Collect</h3><ul>` +
`<li><strong>Order information:</strong> name, email, shipping address, and payment details you provide during checkout — used solely to process and fulfill your orders.</li>` +
`<li><strong>Account information:</strong> email and profile data if you create an account — used to manage your orders and preferences.</li>` +
`<li><strong>Anonymized usage data:</strong> we collect basic, anonymized page-visit data (with IP addresses truncated) to understand how our site is used. This data cannot be used to identify you personally.</li>` +
`</ul>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>What We Will Never Do</h3><ul>` +
`<li>Sell, rent, or share your personal data with third parties for marketing or advertising.</li>` +
`<li>Use third-party tracking scripts, analytics services, or advertising networks.</li>` +
`<li>Use tracking pixels or monitor individual email opens and clicks.</li>` +
`<li>Build advertising profiles or track you across other websites.</li>` +
`<li>Monetize your data in any way.</li>` +
`</ul>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Cookies</h3>` +
`<p>We use only essential, first-party cookies required for the site to function (e.g., keeping you logged in, maintaining your shopping cart). We do not set any third-party or advertising cookies.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Data Sharing</h3>` +
`<p>We share information only with service providers strictly necessary to fulfill your order (e.g., payment processors, shipping carriers) and as required by law. These providers are contractually obligated to protect your data.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Your Rights</h3>` +
`<p>The only personal data we store is the shipping address you enter during checkout. You can request correction or deletion of this data at any time by contacting us at <a href="mailto:${contactEmail}" class="text-primary underline">${contactEmail}</a>. We will respond promptly.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Contact Us</h3>` +
`<p>If you have questions about this privacy policy, please contact us at <a href="mailto:${contactEmail}" class="text-primary underline">${contactEmail}</a>.</p>`
) }} />
</PolicyPage>
);
}

View File

@ -1,62 +1,61 @@
import React from "react";
import { RotateCcw, CheckCircle, XCircle, Clock } from "lucide-react";
import { PolicyPage } from "./PolicyPage";
export interface ReturnsPageProps {
/** Number of days for return window. */
returnWindowDays?: number;
/** Optional extra class names. */
className?: string;
}
/** Public page with refund / return policy. */
export function ReturnsPage({ returnWindowDays = 30, className }: ReturnsPageProps) {
return (
<PolicyPage title="Returns & Refund Policy" className={className}>
{/* Quick summary */}
<div className="not-prose mb-8 grid gap-4 sm:grid-cols-2">
{[
{ icon: Clock, label: `${returnWindowDays}-day return window` },
{ icon: RotateCcw, label: "Free returns on defective items" },
{ icon: CheckCircle, label: "Full refund to original payment" },
{ icon: XCircle, label: "No restocking fees" },
].map(({ icon: Icon, label }) => (
<div
key={label}
className="flex items-center gap-3 rounded-lg border border-border/50 bg-accent/5 p-4"
>
<Icon className="h-5 w-5 shrink-0 text-primary" />
<span className="text-sm font-medium">{label}</span>
</div>
))}
</div>
<h3>Eligibility</h3>
<p>
Items must be returned within <strong>{returnWindowDays} days</strong> of delivery in their
original, unused condition with all tags and packaging intact.
</p>
<h3>How to Initiate a Return</h3>
<ol>
<li>Contact our support team with your order number.</li>
<li>Receive a prepaid return label (for defective items) or return instructions.</li>
<li>Ship the item back using the provided label or your preferred carrier.</li>
</ol>
<h3>Refund Processing</h3>
<p>
Once we receive and inspect the returned item, your refund will be processed within
510 business days to your original payment method. You will receive an email
confirmation when the refund has been issued.
</p>
<h3>Exceptions</h3>
<p>
The following items are not eligible for return: gift cards, downloadable products,
and items marked as final sale. Perishable goods cannot be returned unless they arrive
damaged or defective.
</p>
</PolicyPage>
);
}
import React from "react";
import { RotateCcw, CheckCircle, XCircle, Clock } from "lucide-react";
import { PolicyPage } from "./PolicyPage";
export interface ReturnsPageProps {
/** Number of days for return window. */
returnWindowDays?: number;
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
/** Public page with refund / return policy. */
export function ReturnsPage({ returnWindowDays = 30, t: _t, className }: ReturnsPageProps) {
const t = _t || ((k: string) => k);
return (
<PolicyPage title={t("Returns & Refund Policy")} className={className}>
{/* Quick summary */}
<div className="not-prose mb-8 grid gap-4 sm:grid-cols-2">
{[
{ icon: Clock, label: t(`${returnWindowDays}-day return window`) },
{ icon: RotateCcw, label: t("Free returns on defective items") },
{ icon: CheckCircle, label: t("Full refund to original payment") },
{ icon: XCircle, label: t("No restocking fees") },
].map(({ icon: Icon, label }) => (
<div
key={label}
className="flex items-center gap-3 rounded-lg border border-border/50 bg-accent/5 p-4"
>
<Icon className="h-5 w-5 shrink-0 text-primary" />
<span className="text-sm font-medium">{label}</span>
</div>
))}
</div>
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Eligibility</h3>` +
`<p>Items must be returned within <strong>${returnWindowDays} days</strong> of delivery in their original, unused condition with all tags and packaging intact.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>How to Initiate a Return</h3><ol>` +
`<li>Contact our support team with your order number.</li>` +
`<li>Receive a prepaid return label (for defective items) or return instructions.</li>` +
`<li>Ship the item back using the provided label or your preferred carrier.</li>` +
`</ol>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Refund Processing</h3>` +
`<p>Once we receive and inspect the returned item, your refund will be processed within 510 business days to your original payment method. You will receive an email confirmation when the refund has been issued.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Exceptions</h3>` +
`<p>The following items are not eligible for return: gift cards, downloadable products, and items marked as final sale. Perishable goods cannot be returned unless they arrive damaged or defective.</p>`
) }} />
</PolicyPage>
);
}

View File

@ -1,88 +1,88 @@
import React from "react";
import { Truck, Clock, Globe, DollarSign } from "lucide-react";
import { cn } from "@/lib/utils";
import { PolicyPage } from "./PolicyPage";
export interface ShippingRate {
region: string;
method: string;
estimate: string;
price: string;
}
export interface ShippingPageProps {
/** Custom shipping rates to display. Falls back to placeholder content. */
rates?: ShippingRate[];
/** Optional extra class names. */
className?: string;
}
const defaultRates: ShippingRate[] = [
{ region: "Domestic", method: "Standard", estimate: "57 business days", price: "$4.99" },
{ region: "Domestic", method: "Express", estimate: "23 business days", price: "$12.99" },
{ region: "International", method: "Standard", estimate: "1020 business days", price: "$14.99" },
{ region: "International", method: "Express", estimate: "58 business days", price: "$29.99" },
];
/** Public page with shipping info and rates. */
export function ShippingPage({ rates = defaultRates, className }: ShippingPageProps) {
return (
<PolicyPage title="Shipping Information" className={className}>
{/* Highlights */}
<div className="not-prose mb-8 grid gap-4 sm:grid-cols-2">
{[
{ icon: Truck, label: "Free shipping on orders over $75" },
{ icon: Clock, label: "Same-day dispatch on orders before 2 PM" },
{ icon: Globe, label: "We ship worldwide" },
{ icon: DollarSign, label: "No hidden fees at checkout" },
].map(({ icon: Icon, label }) => (
<div
key={label}
className="flex items-center gap-3 rounded-lg border border-border/50 bg-accent/5 p-4"
>
<Icon className="h-5 w-5 shrink-0 text-primary" />
<span className="text-sm font-medium">{label}</span>
</div>
))}
</div>
{/* Rates table */}
<h3>Shipping Rates</h3>
<div className="not-prose overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b text-left text-muted-foreground">
<th className="pb-2 pr-4 font-medium">Region</th>
<th className="pb-2 pr-4 font-medium">Method</th>
<th className="pb-2 pr-4 font-medium">Estimate</th>
<th className="pb-2 font-medium text-right">Price</th>
</tr>
</thead>
<tbody>
{rates.map((r, i) => (
<tr key={i} className={cn("border-b border-border/30", i % 2 === 0 && "bg-accent/5")}>
<td className="py-2.5 pr-4">{r.region}</td>
<td className="py-2.5 pr-4">{r.method}</td>
<td className="py-2.5 pr-4 text-muted-foreground">{r.estimate}</td>
<td className="py-2.5 text-right font-medium">{r.price}</td>
</tr>
))}
</tbody>
</table>
</div>
<h3>Processing Time</h3>
<p>
Orders placed before 2:00 PM (local time) on business days are typically processed and
shipped the same day. Orders placed after this cut-off or on weekends/holidays will be
processed the next business day.
</p>
<h3>Tracking</h3>
<p>
Once your order ships, you will receive a confirmation email with a tracking number.
You can use this number to track your package on the carrier's website.
</p>
</PolicyPage>
);
}
import React from "react";
import { Truck, Clock, Globe, DollarSign } from "lucide-react";
import { cn } from "@/lib/utils";
import { PolicyPage } from "./PolicyPage";
export interface ShippingRate {
region: string;
method: string;
estimate: string;
price: string;
}
export interface ShippingPageProps {
/** Custom shipping rates to display. Falls back to placeholder content. */
rates?: ShippingRate[];
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
const defaultRates: ShippingRate[] = [
{ region: "Domestic", method: "Standard", estimate: "57 business days", price: "$4.99" },
{ region: "Domestic", method: "Express", estimate: "23 business days", price: "$12.99" },
{ region: "International", method: "Standard", estimate: "1020 business days", price: "$14.99" },
{ region: "International", method: "Express", estimate: "58 business days", price: "$29.99" },
];
/** Public page with shipping info and rates. */
export function ShippingPage({ rates = defaultRates, t: _t, className }: ShippingPageProps) {
const t = _t || ((k: string) => k);
return (
<PolicyPage title={t("Shipping Information")} className={className}>
{/* Highlights */}
<div className="not-prose mb-8 grid gap-4 sm:grid-cols-2">
{[
{ icon: Truck, label: t("Free shipping on orders over $75") },
{ icon: Clock, label: t("Same-day dispatch on orders before 2 PM") },
{ icon: Globe, label: t("We ship worldwide") },
{ icon: DollarSign, label: t("No hidden fees at checkout") },
].map(({ icon: Icon, label }) => (
<div
key={label}
className="flex items-center gap-3 rounded-lg border border-border/50 bg-accent/5 p-4"
>
<Icon className="h-5 w-5 shrink-0 text-primary" />
<span className="text-sm font-medium">{label}</span>
</div>
))}
</div>
{/* Rates table — headers translated, values passed through */}
<h3>{t("Shipping Rates")}</h3>
<div className="not-prose overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b text-left text-muted-foreground">
<th className="pb-2 pr-4 font-medium">{t("Region")}</th>
<th className="pb-2 pr-4 font-medium">{t("Method")}</th>
<th className="pb-2 pr-4 font-medium">{t("Estimate")}</th>
<th className="pb-2 font-medium text-right">{t("Price")}</th>
</tr>
</thead>
<tbody>
{rates.map((r, i) => (
<tr key={i} className={cn("border-b border-border/30", i % 2 === 0 && "bg-accent/5")}>
<td className="py-2.5 pr-4">{t(r.region)}</td>
<td className="py-2.5 pr-4">{t(r.method)}</td>
<td className="py-2.5 pr-4 text-muted-foreground">{r.estimate}</td>
<td className="py-2.5 text-right font-medium">{r.price}</td>
</tr>
))}
</tbody>
</table>
</div>
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Processing Time</h3>` +
`<p>Orders placed before 2:00 PM (local time) on business days are typically processed and shipped the same day. Orders placed after this cut-off or on weekends/holidays will be processed the next business day.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Tracking</h3>` +
`<p>Once your order ships, you will receive a confirmation email with a tracking number. You can use this number to track your package on the carrier's website.</p>`
) }} />
</PolicyPage>
);
}

View File

@ -1,77 +1,65 @@
import React from "react";
import { PolicyPage } from "./PolicyPage";
export interface TermsPageProps {
/** Business / site name. */
siteName?: string;
/** Contact email for legal inquiries. */
contactEmail?: string;
/** Optional extra class names. */
className?: string;
}
/** Optional terms of service page. */
export function TermsPage({
siteName = "Our Store",
contactEmail = "legal@example.com",
className,
}: TermsPageProps) {
return (
<PolicyPage title="Terms of Service" className={className}>
<p>
By accessing and using <strong>{siteName}</strong>, you agree to be bound by these
Terms of Service.
</p>
<h3>Use of the Site</h3>
<p>
You agree to use this site only for lawful purposes and in a manner that does not
infringe on the rights of others or restrict their use and enjoyment of the site.
</p>
<h3>Products & Pricing</h3>
<p>
All product descriptions and prices are subject to change without notice. We reserve
the right to modify or discontinue any product at any time. Prices are displayed in
the store's base currency and may exclude taxes and shipping costs, which are
calculated at checkout.
</p>
<h3>Orders & Payment</h3>
<p>
By placing an order, you make an offer to purchase the selected products. We reserve
the right to refuse or cancel any order for any reason, including pricing errors or
suspected fraud.
</p>
<h3>Intellectual Property</h3>
<p>
All content on this site including text, images, logos, and software is the
property of {siteName} or its licensors and is protected by applicable intellectual
property laws.
</p>
<h3>Limitation of Liability</h3>
<p>
To the fullest extent permitted by law, {siteName} shall not be liable for any
indirect, incidental, or consequential damages arising from your use of the site or
purchase of products.
</p>
<h3>Changes to These Terms</h3>
<p>
We may update these Terms of Service from time to time. Continued use of the site
after changes constitutes acceptance of the revised terms.
</p>
<h3>Contact</h3>
<p>
For questions about these terms, contact us at{" "}
<a href={`mailto:${contactEmail}`} className="text-primary underline">
{contactEmail}
</a>
.
</p>
</PolicyPage>
);
}
import React from "react";
import { PolicyPage } from "./PolicyPage";
export interface TermsPageProps {
/** Business / site name. */
siteName?: string;
/** Contact email for legal inquiries. */
contactEmail?: string;
/** Translation function. */
t?: (key: string) => string;
/** Optional extra class names. */
className?: string;
}
/** Optional terms of service page. */
export function TermsPage({
siteName = "Our Store",
contactEmail = "legal@example.com",
t: _t,
className,
}: TermsPageProps) {
const t = _t || ((k: string) => k);
return (
<PolicyPage title={t("Terms of Service")} className={className}>
<p dangerouslySetInnerHTML={{ __html: t(
`By accessing and using <strong>${siteName}</strong>, you agree to be bound by these Terms of Service.`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Use of the Site</h3>` +
`<p>You agree to use this site only for lawful purposes and in a manner that does not infringe on the rights of others or restrict their use and enjoyment of the site.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Products &amp; Pricing</h3>` +
`<p>All product descriptions and prices are subject to change without notice. We reserve the right to modify or discontinue any product at any time. Prices are displayed in the store's base currency and may exclude taxes and shipping costs, which are calculated at checkout.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Orders &amp; Payment</h3>` +
`<p>By placing an order, you make an offer to purchase the selected products. We reserve the right to refuse or cancel any order for any reason, including pricing errors or suspected fraud.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Intellectual Property</h3>` +
`<p>All content on this site — including text, images, logos, and software — is the property of ${siteName} or its licensors and is protected by applicable intellectual property laws.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Limitation of Liability</h3>` +
`<p>To the fullest extent permitted by law, ${siteName} shall not be liable for any indirect, incidental, or consequential damages arising from your use of the site or purchase of products.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Changes to These Terms</h3>` +
`<p>We may update these Terms of Service from time to time. Continued use of the site after changes constitutes acceptance of the revised terms.</p>`
) }} />
<div dangerouslySetInnerHTML={{ __html: t(
`<h3>Contact</h3>` +
`<p>For questions about these terms, contact us at <a href="mailto:${contactEmail}" class="text-primary underline">${contactEmail}</a>.</p>`
) }} />
</PolicyPage>
);
}