This commit is contained in:
Tomas Dvorak
2026-03-13 14:34:19 +01:00
parent 84a8acf944
commit 30d70a6aeb
126 changed files with 27297 additions and 29069 deletions
@@ -3,6 +3,7 @@ import { useParams } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { getProduct, addToCart, EshopProductVariant } from '../services/eshopApi';
import { Box, Heading, Text, Image, Badge, HStack, VStack, Button, Select, useToast } from '@chakra-ui/react';
import { sanitizeRichHtml } from '../utils/sanitizeHtml';
const formatPrice = (cents: number, currency: string) => {
const value = cents / 100;
@@ -20,6 +21,7 @@ const ProductDetailPage: React.FC = () => {
enabled: !!slug,
});
const [variantId, setVariantId] = useState<number | undefined>(undefined);
const descriptionHtml = React.useMemo(() => sanitizeRichHtml(data?.description_html), [data?.description_html]);
if (isLoading) return <Text>Načítání produktu</Text>;
if (isError || !data) return <Text>Produkt nebyl nalezen.</Text>;
@@ -76,9 +78,9 @@ const ProductDetailPage: React.FC = () => {
<Button colorScheme="blue" onClick={handleAddToCart} maxW="260px">
Přidat do košíku
</Button>
{data.description_html && (
{descriptionHtml && (
<Box mt={4} fontSize="sm" color="gray.700">
<div dangerouslySetInnerHTML={{ __html: data.description_html }} />
<div dangerouslySetInnerHTML={{ __html: descriptionHtml }} />
</Box>
)}
</VStack>
+1 -1
View File
@@ -1 +1 @@
/// <reference types="react-scripts" />
/// <reference types="vite/client" />
+28
View File
@@ -0,0 +1,28 @@
import DOMPurify from 'dompurify';
const ADDITIONAL_TAGS = ['iframe'];
const ADDITIONAL_ATTRS = ['allow', 'allowfullscreen', 'class', 'rel', 'style', 'target'];
export function sanitizeRichHtml(html: string | null | undefined): string {
const sanitized = DOMPurify.sanitize(html ?? '', {
USE_PROFILES: { html: true },
ADD_TAGS: ADDITIONAL_TAGS,
ADD_ATTR: ADDITIONAL_ATTRS,
});
if (typeof window === 'undefined' || !sanitized) {
return sanitized;
}
const template = window.document.createElement('template');
template.innerHTML = sanitized;
template.content.querySelectorAll<HTMLAnchorElement>('a[target="_blank"]').forEach((anchor) => {
const rel = new Set((anchor.getAttribute('rel') ?? '').split(/\s+/).filter(Boolean));
rel.add('noopener');
rel.add('noreferrer');
anchor.setAttribute('rel', Array.from(rel).join(' '));
});
return template.innerHTML;
}