import {
DislikeOutlined,
LikeOutlined,
RightOutlined,
} from "@ant-design/icons";
import "./App.css";
import { Badge, Flex, Skeleton } from "antd";
import { useEffect, useRef, useState } from "react";
import { motion } from "framer-motion";
function SkeletonPlaceholder() {
return (
<>
>
);
}
function UseVoteLocalStorage() {
const getVotes = () => {
const votes = localStorage.getItem("shx-product-pipeline-votes");
return votes === null ? [] : JSON.parse(votes);
};
const [storageVotes, setStorageVotes] = useState(getVotes());
const saveVotes = (votes) => {
setStorageVotes(votes);
localStorage.setItem("shx-product-pipeline-votes", JSON.stringify(votes));
};
const vote = (name, up) => {
const newVotes = [...storageVotes];
const existingVoteIndex = newVotes.findIndex((vote) => vote.n === name);
if (existingVoteIndex !== -1) {
newVotes[existingVoteIndex].t = up === true ? 1 : 0;
} else {
newVotes.push({
n: name,
t: up === true ? 1 : 0,
});
}
saveVotes(newVotes);
};
return {
votes: storageVotes,
vote: vote,
setVotes: saveVotes,
};
}
function Card({
rankingPosition,
name,
productVariant,
productCharacteristics,
rightComponent,
}) {
const MiddleComponent = () => {
return (
{name}
{productVariant !== undefined &&
productCharacteristics !== undefined && (
{productVariant}: {productCharacteristics}
)}
);
};
return (
{rankingPosition === undefined ? (
) : (
<>
#{rankingPosition}
>
)}
{rightComponent}
);
}
function VoteRequest(name, up) {
fetch(
`https://devdash.ex.umbach.dev/api/v1/productpipeline/vote?t=${
up === true ? "u" : "d"
}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: name,
}),
}
).catch(() => {});
}
function App() {
const { votes, vote, setVotes } = UseVoteLocalStorage();
const votesRef = useRef(votes);
useEffect(() => {
votesRef.current = votes;
}, [votes]);
const [products, setProducts] = useState({
NewProducts: [],
InWorkProducts: [],
FutureProducts: [],
});
useEffect(() => {
const fetchProducts = () =>
fetch("https://devdash.ex.umbach.dev/api/v1/productpipeline")
.then((res) => res.json())
.then((data) => {
setProducts(data);
// remove votes for products which no longer in future products
setVotes(
votesRef.current.filter((v) =>
data.FutureProducts.some((item) => item.Name === v.n)
)
);
})
.catch(() => {});
fetchProducts();
setInterval(() => fetchProducts(), 3000);
}, []);
return (
Immer auf dem aktuellen Stand sein
Neue Produkte
{products.NewProducts.length > 0 ? (
<>
{products.NewProducts.map((product, index) => (
window.open(product.Url)}
/>
}
/>
))}
>
) : (
)}
Was als nächstes kommt
Aktuell in Arbeit
{products.InWorkProducts.length > 0 ? (
<>
{products.InWorkProducts.map((product, index) => (
}
/>
))}
>
) : (
)}
Jede Stimme zählt
Zukünftige Produkte
{products.FutureProducts.length > 0 ? (
<>
{products.FutureProducts
/*.sort((a, b) =>
a.Name.localeCompare(b.Name)
)*/
.sort((a, b) => b.Votes - a.Votes)
.map((product, index) => (
vote.n === product.Name && vote.t === 0
) > -1
? "red"
: "#000",
}}
onClick={() => {
if (
votes.findIndex(
(vote) =>
vote.n === product.Name && vote.t === 0
) === -1
) {
VoteRequest(product.Name, false);
vote(product.Name, false);
// simulate vote before request is made
setProducts((products) => {
const newArr = products.FutureProducts.map(
(p) =>
p.Name === product.Name
? { ...p, Votes: p.Votes - 1 }
: p
);
return {
...products,
FutureProducts: newArr,
};
});
}
}}
/>
{product.Votes}
vote.n === product.Name && vote.t === 1
) > -1
? "green"
: "#000",
}}
onClick={() => {
if (
votes.findIndex(
(vote) =>
vote.n === product.Name && vote.t === 1
) === -1
) {
VoteRequest(product.Name, true);
vote(product.Name, true);
// simulate vote before request is made
setProducts((products) => {
const newArr = products.FutureProducts.map(
(p) =>
p.Name === product.Name
? { ...p, Votes: p.Votes + 1 }
: p
);
return {
...products,
FutureProducts: newArr,
};
});
}
}}
/>
}
/>
))}
>
) : (
)}
);
}
export default App;