-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from Ropold/frontend1
Frontend1
- Loading branch information
Showing
14 changed files
with
404 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,194 @@ | ||
import { UserDetails } from "./model/UserDetailsModel.ts"; | ||
import { useState, useEffect } from "react"; | ||
import { useNavigate } from "react-router-dom"; | ||
import axios from "axios"; | ||
|
||
export default function AddMemoryCard() { | ||
type MemoryCardProps = { | ||
userDetails: UserDetails | null; | ||
} | ||
|
||
export default function AddMemoryCard(props: Readonly<MemoryCardProps>) { | ||
|
||
const [name, setName] = useState<string>(""); | ||
const [category, setCategory] = useState<string>("CLOUDINARY_IMAGE"); | ||
const [description, setDescription] = useState<string>(""); | ||
const [image, setImage] = useState<File | null>(null); | ||
const [imageUrl, setImageUrl] = useState<string>(""); // Setzt den Image-URL State | ||
const [matchId, setMatchId] = useState<number>(1); // Defaultwert als Zahl (1) | ||
const [errorMessages, setErrorMessages] = useState<string[]>([]); | ||
const [showPopup, setShowPopup] = useState(false); | ||
|
||
const navigate = useNavigate(); | ||
|
||
// useEffect, um imageUrl zu setzen, wenn die Kategorie sich ändert und GitHub Avatar ausgewählt wird | ||
useEffect(() => { | ||
if (category === "GITHUB_AVATAR" && props.userDetails?.html_url) { | ||
setImageUrl(props.userDetails.html_url); // Setze imageUrl auf die GitHub URL | ||
} else { | ||
setImageUrl(""); // Leere das imageUrl bei anderen Kategorien | ||
} | ||
}, [category, props.userDetails?.html_url]); // Abhängig von der Kategorie und der GitHub-URL des Benutzers | ||
|
||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { | ||
e.preventDefault(); | ||
|
||
const data = new FormData(); | ||
|
||
if (image) { | ||
data.append("image", image); | ||
} | ||
|
||
const memoryData = { | ||
name, | ||
matchId: matchId, // matchId als number | ||
category, | ||
description, | ||
isActive: true, | ||
appUserGithubId: props.userDetails?.id, | ||
appUserUsername: props.userDetails?.login, | ||
appUserAvatarUrl: props.userDetails?.avatar_url, | ||
appUserGithubUrl: props.userDetails?.html_url, | ||
imageUrl: imageUrl, | ||
}; | ||
|
||
data.append("memoryModelDto", new Blob([JSON.stringify(memoryData)], {type: "application/json"})); | ||
|
||
console.log("memoryData:", memoryData); | ||
|
||
axios | ||
.post("/api/memory-hub", data, { | ||
headers: { | ||
"Content-Type": "multipart/form-data", | ||
} | ||
}) | ||
.then((response) => { | ||
console.log("Antwort vom Server:", response.data); | ||
navigate(`/memory/${response.data.id}`); | ||
}) | ||
.catch((error) => { | ||
if (error.response && error.response.status === 400 && error.response.data) { | ||
const errorMessages = error.response.data; | ||
const errors: string[] = []; | ||
Object.keys(errorMessages).forEach((field) => { | ||
errors.push(`${field}: ${errorMessages[field]}`); | ||
}); | ||
|
||
setErrorMessages(errors); | ||
setShowPopup(true); | ||
} else { | ||
alert("An unexpected error occurred. Please try again."); | ||
} | ||
}); | ||
} | ||
|
||
const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
if (e.target.files) { | ||
setImage(e.target.files[0]); | ||
} | ||
} | ||
|
||
const handleClosePopup = () => { | ||
setShowPopup(false); | ||
setErrorMessages([]); | ||
}; | ||
|
||
// onChange für den matchId-Input | ||
const onMatchIdChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
const value = parseInt(e.target.value); // Umwandlung von String in Zahl | ||
if (!isNaN(value)) { | ||
setMatchId(Math.min(20, Math.max(1, value))); // Stellen sicher, dass der Wert im Bereich 1 bis 20 liegt | ||
} | ||
}; | ||
|
||
return ( | ||
<div> | ||
<div className="edit-form"> | ||
<h2>Add Memory Card</h2> | ||
<form onSubmit={handleSubmit}> | ||
<label> | ||
Name: | ||
<input | ||
className="input-small" | ||
type="text" | ||
value={name} | ||
onChange={(e) => setName(e.target.value)} | ||
/> | ||
</label> | ||
|
||
<label> | ||
Category: | ||
<select | ||
className="input-small" | ||
value={category} | ||
onChange={(e) => setCategory(e.target.value)} | ||
required | ||
> | ||
<option value="CLOUDINARY_IMAGE">Cloudinary Image</option> | ||
<option value="GITHUB_AVATAR">GitHub Avatar</option> | ||
</select> | ||
</label> | ||
|
||
<label> | ||
Description: | ||
<textarea | ||
className="textarea-large" | ||
value={description} | ||
onChange={(e) => setDescription(e.target.value)} | ||
/> | ||
</label> | ||
|
||
<label> | ||
Match ID (default 1, max 20): | ||
<input | ||
className="input-small" | ||
type="number" | ||
value={matchId} | ||
onChange={onMatchIdChange} // Verwendung der angepassten Methode | ||
min="1" | ||
max="20" | ||
/> | ||
</label> | ||
|
||
<label> | ||
Image: | ||
<input | ||
type="file" | ||
onChange={onFileChange} | ||
disabled={category === "GITHUB_AVATAR"} // Deaktiviert das Dateiauswahlfeld, wenn GitHub Avatar ausgewählt ist | ||
/> | ||
</label> | ||
|
||
{category === "GITHUB_AVATAR" && props.userDetails?.avatar_url && ( | ||
<img | ||
src={props.userDetails.avatar_url} | ||
className="memory-card-image" | ||
alt="GitHub Avatar" | ||
/> | ||
)} | ||
|
||
{image && category === "CLOUDINARY_IMAGE" && ( | ||
<img src={URL.createObjectURL(image)} className="memory-card-image" alt="Preview" /> | ||
)} | ||
|
||
<div className="button-group"> | ||
<button type="submit">Add Memory Card</button> | ||
</div> | ||
</form> | ||
|
||
{showPopup && ( | ||
<div className="popup-overlay"> | ||
<div className="popup-content"> | ||
<h3>Validation Errors</h3> | ||
<ul> | ||
{errorMessages.map((msg, index) => ( | ||
<li key={index}>{msg}</li> | ||
))} | ||
</ul> | ||
<div className="popup-actions"> | ||
<button className="popup-cancel" onClick={handleClosePopup}>Close</button> | ||
</div> | ||
</div> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { MemoryModel } from "./model/MemoryModel.ts"; | ||
import { useParams } from "react-router-dom"; | ||
import {DefaultMemory} from "./model/DefaultMemory.ts"; | ||
|
||
type DetailsProps = { | ||
allMemories: MemoryModel[]; | ||
}; | ||
|
||
|
||
export default function Details(props: Readonly<DetailsProps>) { | ||
const { id } = useParams<{ id: string }>(); | ||
|
||
// Suche das Memory-Objekt mit der passenden ID | ||
const memory = props.allMemories.find(mem => mem.id === id) || DefaultMemory; | ||
|
||
return ( | ||
<div> | ||
<h2>Memory Details</h2> | ||
{memory ? ( | ||
<div> | ||
<h3>{memory.name}</h3> | ||
<p><strong>Category:</strong> {memory.category}</p> | ||
<p><strong>Description:</strong> {memory.description}</p> | ||
<p><strong>Active:</strong> {memory.isActive ? "Yes" : "No"}</p> | ||
<p> | ||
<strong>Created by:</strong> | ||
<a href={memory.appUserGithubUrl} target="_blank" rel="noopener noreferrer"> | ||
{memory.appUserUsername} | ||
</a> | ||
</p> | ||
<img src={memory.imageUrl} alt={memory.name} width={300} /> | ||
</div> | ||
) : ( | ||
<p>Memory not found.</p> | ||
)} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,19 @@ | ||
import {MemoryModel} from "./model/MemoryModel.ts"; | ||
import MemoryCard from "./MemoryCard"; | ||
|
||
export default function Home(){ | ||
return( | ||
|
||
type HomeProps = { | ||
activeMemories: MemoryModel[]; | ||
} | ||
|
||
export default function Home(props: Readonly<HomeProps>) { | ||
return ( | ||
<div> | ||
<h2>MemoryHub - Home</h2> | ||
<div> | ||
{props.activeMemories.map(memory => ( | ||
<MemoryCard key={memory.id} memory={memory} /> | ||
))} | ||
</div> | ||
</div> | ||
) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import {useNavigate} from "react-router-dom"; | ||
import {MemoryModel} from "./model/MemoryModel.ts"; | ||
|
||
type MemoryCardProps = { | ||
memory: MemoryModel | ||
} | ||
|
||
export default function MemoryCard(props: Readonly<MemoryCardProps>) { | ||
|
||
const navigate = useNavigate(); | ||
|
||
const handleCardClick = () => { | ||
navigate(`/memory/${props.memory.id}`); | ||
} | ||
|
||
return ( | ||
<div className="memory-card" onClick={handleCardClick}> | ||
<h3>{props.memory.name}</h3> | ||
<img src={props.memory.imageUrl} alt={props.memory.name} className="memory-card-image" /> | ||
</div> | ||
); | ||
} |
2 changes: 1 addition & 1 deletion
2
frontend/src/components/MyMemoriesCards.tsx → frontend/src/components/MyMemories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.