News gefixt
Some checks are pending
Deploy Volleyball Dev / deploy (push) Waiting to run

This commit is contained in:
Marc Wieland
2025-04-25 16:31:03 +02:00
parent ff6c9dc91a
commit 041ffcad96
6 changed files with 540 additions and 30 deletions

View File

@@ -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")}

View File

@@ -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>

View File

@@ -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>
))}

View File

@@ -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)}