better review design, caddy now listens on localhost too

This commit is contained in:
2026-05-14 20:54:13 +02:00
parent 05541e60b5
commit c25e8b5c0a
10 changed files with 137 additions and 111 deletions
+12
View File
@@ -9,3 +9,15 @@ api.marvinkrausser.com {
portfolio.marvinkrausser.com {
reverse_proxy portfolio_website:8081
}
http://localhost:8080 {
reverse_proxy cnn_website:8080
}
http://localhost:8000 {
reverse_proxy cnn_api:8000
}
http://localhost:8081 {
reverse_proxy portfolio_website:8081
}
-28
View File
@@ -2,34 +2,6 @@
height: 40px;
}
.custom-button {
font-size: 1rem;
font-weight: bold;
height: 40px;
padding: 10px 20px;
background: var(--surface);
color: rgb(0, 0, 0);
cursor: pointer;
border-radius: 20px;
border: 2px solid var(--surface);
user-select: none;
}
.custom-button:hover {
border-color: white;
color: white;
}
.custom-button.inactive {
opacity: 0.3;
cursor: default;
color: black;
}
.custom-button.inactive:hover {
border-color: var(--surface);
}
.species-item {
list-style: none;
position: relative;
+1 -1
View File
@@ -3,7 +3,7 @@ import './Bird_CNN.css';
function Bird_CNN() {
const apiUrl = process.env.NODE_ENV === "development"
? "https://api.marvinkrausser.com"
? "http://localhost:8000"
: "https://api.marvinkrausser.com";
const [file, setFile] = useState(null);
+47 -8
View File
@@ -1,3 +1,10 @@
.review-description {
margin: 70px;
font-size: 1.1rem;
width: min(800px, 70vw);
height: fit-content;
}
.input-box {
display: flex;
gap: 80px;
@@ -44,6 +51,7 @@
height: fit-content;
gap: 0px;
user-select: none;
--radius: 16px;
}
.review-select-dropdown.rating {
@@ -59,12 +67,12 @@
outline: none;
color: black;
font-weight: bold;
border-radius: 16px;
border-radius: var(--radius);
}
.review-select-button.active {
border-top-right-radius: 16px;
border-top-left-radius: 16px;
border-top-right-radius: var(--radius);
border-top-left-radius: var(--radius);
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
@@ -75,11 +83,16 @@
border-color: white;
}
.review-select-menu.grid {
.review-select-menu {
width: 100%;
grid-auto-rows: 1fr;
}
.rating .review-select-menu {
width: 300%;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-rows: 1fr;
transform: translateX(-33.3333%);
}
.review-select-item {
@@ -95,9 +108,35 @@
border-radius: 0;
}
.review-select-item.last {
border-bottom-left-radius: 16px;
border-bottom-right-radius: 16px;
.rating .review-select-box-0 {
grid-column: 1;
grid-row: 1;
border-bottom-left-radius: var(--radius);
border-top-left-radius: var(--radius);
}
.rating .review-select-box-1 {
grid-column: 2;
grid-row: 1;
}
.rating .review-select-box-2 {
grid-column: 3;
grid-row: 1;
border-bottom-right-radius: var(--radius);
border-top-right-radius: var(--radius);
}
.rating .review-select-box-3 {
grid-column: 2;
grid-row: 2;
border-bottom-left-radius: var(--radius);
border-bottom-right-radius: var(--radius);
}
.website .review-select-box-1 {
border-bottom-left-radius: var(--radius);
border-bottom-right-radius: var(--radius);
}
.review-select-item:hover {
+25 -8
View File
@@ -3,6 +3,10 @@ import './Reviews.css';
import SelectDropdown from './components/SelectDropdown';
function Reviews() {
const apiUrl = process.env.NODE_ENV === "development"
? "http://localhost:8000"
: "https://api.marvinkrausser.com";
const [error, setError] = useState(null);
const [selectedWebsite, setSelectedWebsite] = useState("Not Specified");
const [selectedRating, setSelectedRating] = useState(1);
@@ -12,8 +16,11 @@ function Reviews() {
const fetchReviews = async () => {
const response = await fetch(`api.marvinkrausser/review`, {
method: "GET",
const response = await fetch(`${apiUrl}/review`, {
method: "POST",
headers: {
"Authorization": `Bearer 1234`,
},
});
if (!response.ok) {
@@ -24,12 +31,20 @@ function Reviews() {
setError(false);
}
console.log(response);
const result = await response.json();
}
const sendReview = async (review) => {
const sendReview = async () => {
const review = {
"website": selectedWebsite,
"rating": selectedRating,
"text": text,
"created_at": "today"
}
const response = await fetch(`api.marvinkrausser/review`, {
const response = await fetch(`${apiUrl}/review`, {
method: "POST",
body: JSON.stringify(review),
});
@@ -47,12 +62,10 @@ function Reviews() {
const changeSelectedWebsite = (website) => {
setSelectedWebsite(website);
console.log(selectedWebsite);
};
const changeSelectedRating = (rating) => {
setSelectedRating(rating);
console.log(selectedRating);
}
const websites = [
@@ -75,6 +88,7 @@ function Reviews() {
return (<>
<div className='site-box'>
<h1 className='site-headline'>Reviews</h1>
<p className='review-description'>I would greatly appreciate receiving a review. You are welcome to write it in either English or German. All fields are optional, and all reviews are submitted anonymously.</p>
@@ -88,7 +102,6 @@ function Reviews() {
defaultValue={websites.at(-1)}
classNames={"website"}
changeInputParent={changeSelectedWebsite}
flex={true}
/>
</div>
@@ -116,9 +129,13 @@ function Reviews() {
defaultValue={ratings.at(0)}
classNames={"rating"}
changeInputParent={changeSelectedRating}
flex={false}
/>
</div>
<button id='button-send' style={{ display: "none" }} onClick={sendReview} />
<label htmlFor="button-send" className="custom-button">
Send Review
</label>
</div>
</div>
</>)
-26
View File
@@ -1,26 +0,0 @@
import { useState, useEffect } from 'react';
function DropwdownFlex(props) {
const values = props.values;
const classNames = props.classNames;
const changeInputParent = props.changeInputParent;
const selectedInput = props.selectedInput;
return (
<div className={`review-select-menu flex ${classNames}`}>
{values.map((item, i) =>
String(selectedInput) !== String(item) && (
<div
key={item}
className={i >= (values.length - (selectedInput == values.at(-1) ? 2 : 1)) ? `review-select-item ${classNames} last` : `review-select-item ${classNames}`}
onClick={() => changeInputParent(item)}
>
{item}
</div>
)
)}
</div>
);
}
export default DropwdownFlex;
-26
View File
@@ -1,26 +0,0 @@
import { useState, useEffect } from 'react';
function DropwdownGrid(props) {
const values = props.values;
const classNames = props.classNames;
const changeInputParent = props.changeInputParent;
const selectedInput = props.selectedInput;
return (
<div className={`review-select-menu grid ${classNames}`}>
{values.map((item, i) =>
String(selectedInput) !== String(item) && (
<div
key={item}
className={i >= (values.length - (selectedInput == values.at(-1) ? 2 : 1)) ? `review-select-item grid-box-${i} ${classNames} last` : `review-select-item grid-box-${i} ${classNames}`}
onClick={() => changeInputParent(item)}
>
{item}
</div>
)
)}
</div>
);
}
export default DropwdownGrid;
@@ -0,0 +1,3 @@
.review-select-menu {
display: grid;
}
+19 -14
View File
@@ -1,6 +1,5 @@
import { useState, useRef, useEffect } from 'react';
import DropwdownFlex from './DropdownFlex';
import DropwdownGrid from './DropdownGrid';
import './SelectDropdown.css';
function SelectDropdown(props) {
const values = props.values;
@@ -8,17 +7,11 @@ function SelectDropdown(props) {
const classNames = props.classNames;
const changeInputParent = props.changeInputParent;
const parentClass = props.parentClass;
const flex = props.flex;
const [selectVisibility, setSelectVisibility] = useState(false);
const SelectButton = useRef();
const [selectedInput, setSelectedInput] = useState(defaultValue);
const SelectMenu =
flex === true
? DropwdownFlex
: DropwdownGrid;
useEffect(() => {
function handleClick(event) {
if (SelectButton.current && !SelectButton.current.contains(event.target)
@@ -39,6 +32,21 @@ function SelectDropdown(props) {
changeInputParent(input);
};
const CreateItems = () => {
const indices = Array.from({ length: values.length - 1 }, (_, i) => values.length - 2 - i);
return values.map((item, i) =>
String(selectedInput) !== String(item) && (
<div
key={item}
className={`review-select-item review-select-box-${indices.pop()} ${classNames}` + (i >= (values.length - (selectedInput == values.at(-1) ? 2 : 1)) ? ' last' : '')}
onClick={() => changeSelectedInput(item)}
>
{item}
</div>
)
)
}
return (
<div className={`review-select-dropdown ${classNames} ${parentClass}`} >
@@ -46,12 +54,9 @@ function SelectDropdown(props) {
{selectedInput}
</button>
{selectVisibility &&
<SelectMenu
classNames={classNames}
changeInputParent={changeSelectedInput}
values={values}
selectedInput={selectedInput}
/>}
<div className={`review-select-menu ${classNames}`}>
<CreateItems />
</div>}
</ div>
);
+30
View File
@@ -57,3 +57,33 @@ h4 {
width: 100%;
height: fit-content;
}
.custom-button {
font-size: 1rem;
font-weight: bold;
height: 40px;
padding: 10px 20px;
background: var(--surface);
color: rgb(0, 0, 0);
cursor: pointer;
border-radius: 20px;
border: 2px solid var(--surface);
user-select: none;
display: flex;
align-items: center;
}
.custom-button:hover {
border-color: white;
color: white;
}
.custom-button.inactive {
opacity: 0.3;
cursor: default;
color: black;
}
.custom-button.inactive:hover {
border-color: var(--surface);
}