This commit is contained in:
@@ -3,6 +3,9 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import ReactQuill from "react-quill";
|
||||
import "react-quill/dist/quill.snow.css";
|
||||
import DOMPurify from "dompurify";
|
||||
|
||||
const apiBase = import.meta.env.VITE_API_URL;
|
||||
|
||||
@@ -54,7 +57,7 @@ const NewsManager = () => {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
title: newTitle,
|
||||
description: newDescription,
|
||||
description: DOMPurify.sanitize(newDescription),
|
||||
image_url: newImageUrl,
|
||||
team: newTeam,
|
||||
}),
|
||||
@@ -166,10 +169,30 @@ const NewsManager = () => {
|
||||
value={newTitle}
|
||||
onChange={(e) => setNewTitle(e.target.value)}
|
||||
/>
|
||||
<Textarea
|
||||
placeholder="Beschreibung"
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={newDescription}
|
||||
onChange={(e) => setNewDescription(e.target.value)}
|
||||
onChange={setNewDescription}
|
||||
className="bg-white rounded-md"
|
||||
modules={{
|
||||
toolbar: [
|
||||
[{ header: [1, 2, false] }],
|
||||
["bold", "italic", "underline", "strike"],
|
||||
[{ list: "ordered" }, { list: "bullet" }],
|
||||
["link"],
|
||||
["clean"],
|
||||
],
|
||||
}}
|
||||
formats={[
|
||||
"header",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"strike",
|
||||
"list",
|
||||
"bullet",
|
||||
"link",
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* Upload */}
|
||||
@@ -235,14 +258,19 @@ const NewsManager = () => {
|
||||
<CardTitle>{item.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-gray-600 mb-2">{item.description}</p>
|
||||
{item.image_url && (
|
||||
<img
|
||||
src={`${apiBase}${item.image_url}`}
|
||||
alt={item.title}
|
||||
className="w-full rounded-md my-2 max-h-40 object-cover"
|
||||
className="w-full rounded-md mb-3 max-h-40 object-cover"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className="text-gray-600 mb-2 line-clamp-3 prose max-w-none"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(item.description),
|
||||
}}
|
||||
/>
|
||||
<p className="text-xs text-gray-400">
|
||||
Erstellt am{" "}
|
||||
{new Date(item.created_at).toLocaleDateString("de-DE")}
|
||||
|
||||
@@ -55,14 +55,14 @@ const ContactSection = () => {
|
||||
<MapPin className="h-5 w-5 text-frog-500 mt-1 mr-3" />
|
||||
<div>
|
||||
<p className="font-medium">Adresse</p>
|
||||
<p className="text-gray-600">Sporthalle Laudenbach<br />Schulstraße 15<br />69514 Laudenbach</p>
|
||||
<p className="text-gray-600">TG Laudenbach<br />Eleker Straße 3<br />69514 Laudenbach</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start">
|
||||
<Phone className="h-5 w-5 text-frog-500 mt-1 mr-3" />
|
||||
<div>
|
||||
<p className="font-medium">Telefon</p>
|
||||
<p className="text-gray-600">+49 6201 123456</p>
|
||||
<p className="text-gray-600">+49 6201 7835919</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start">
|
||||
@@ -72,17 +72,6 @@ const ContactSection = () => {
|
||||
<p className="text-gray-600">info@tg-laudenbach-volleyball.de</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start">
|
||||
<Clock className="h-5 w-5 text-frog-500 mt-1 mr-3" />
|
||||
<div>
|
||||
<p className="font-medium">Trainingszeiten</p>
|
||||
<div className="text-gray-600">
|
||||
<p>Mo: 18:00 - 22:00 Uhr</p>
|
||||
<p>Di & Do: 19:00 - 21:00 Uhr</p>
|
||||
<p>Fr: 17:00 - 19:00 Uhr (Jugend)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {useEffect, useState} from "react";
|
||||
import DOMPurify from "dompurify";
|
||||
|
||||
type NewsItem = {
|
||||
id: number,
|
||||
@@ -44,11 +45,15 @@ const NewsSection = () => {
|
||||
{news.map((item) => (
|
||||
<Card key={item.id} className="overflow-hidden hover:shadow-lg transition-shadow">
|
||||
<div className="h-48 overflow-hidden">
|
||||
<img
|
||||
src={item.image_url || "/images/default-news.jpg"}
|
||||
alt={item.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<img
|
||||
src={
|
||||
item.image_url
|
||||
? `${import.meta.env.VITE_API_URL}${item.image_url}`
|
||||
: "/images/default-news.jpg"
|
||||
}
|
||||
alt={item.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<CardHeader>
|
||||
<CardTitle>{item.title}</CardTitle>
|
||||
@@ -57,12 +62,19 @@ const NewsSection = () => {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>{item.description}</p>
|
||||
<div
|
||||
className="text-gray-700 line-clamp-3 prose max-w-none prose-p:my-1 prose-a:text-frog-600"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(item.description),
|
||||
}}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button variant="ghost" className="text-frog-600 hover:text-frog-700 p-0 hover:bg-transparent">
|
||||
Weiterlesen <ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
<Link to="/alle-neuigkeiten" className="ml-auto">
|
||||
<Button variant="ghost" className="text-frog-600 hover:text-frog-700 p-0 hover:bg-transparent">
|
||||
Weiterlesen <ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import DOMPurify from "dompurify";
|
||||
|
||||
const apiBase = import.meta.env.VITE_API_URL;
|
||||
|
||||
@@ -85,7 +85,11 @@ const AlleNeuigkeitenPage = () => {
|
||||
activeCardId === item.id ? "" : "line-clamp-3 overflow-hidden"
|
||||
}`}
|
||||
>
|
||||
<ReactMarkdown>{item.description}</ReactMarkdown>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(item.description),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => toggleCard(item.id)}
|
||||
|
||||
Reference in New Issue
Block a user