add TanStack Query & add icons & list campaigns from backend

This commit is contained in:
osmannyildiz 2024-05-18 21:05:57 +03:00
parent de04100c19
commit f7c9a64214
8 changed files with 139 additions and 3 deletions

View File

@ -10,6 +10,9 @@
"preview": "vite preview"
},
"dependencies": {
"@mdi/js": "^7.4.47",
"@mdi/react": "^1.6.1",
"@tanstack/react-query": "^5.37.1",
"@tanstack/react-router": "^1.28.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -5,6 +5,15 @@ settings:
excludeLinksFromLockfile: false
dependencies:
'@mdi/js':
specifier: ^7.4.47
version: 7.4.47
'@mdi/react':
specifier: ^1.6.1
version: 1.6.1
'@tanstack/react-query':
specifier: ^5.37.1
version: 5.37.1(react@18.2.0)
'@tanstack/react-router':
specifier: ^1.28.7
version: 1.28.7(react-dom@18.2.0)(react@18.2.0)
@ -729,6 +738,16 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
/@mdi/js@7.4.47:
resolution: {integrity: sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==}
dev: false
/@mdi/react@1.6.1:
resolution: {integrity: sha512-4qZeDcluDFGFTWkHs86VOlHkm6gnKaMql13/gpIcUQ8kzxHgpj31NuCkD8abECVfbULJ3shc7Yt4HJ6Wu6SN4w==}
dependencies:
prop-types: 15.8.1
dev: false
/@noble/curves@1.3.0:
resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==}
dependencies:
@ -920,6 +939,19 @@ packages:
engines: {node: '>=12'}
dev: false
/@tanstack/query-core@5.36.1:
resolution: {integrity: sha512-BteWYEPUcucEu3NBcDAgKuI4U25R9aPrHSP6YSf2NvaD2pSlIQTdqOfLRsxH9WdRYg7k0Uom35Uacb6nvbIMJg==}
dev: false
/@tanstack/react-query@5.37.1(react@18.2.0):
resolution: {integrity: sha512-EhtBNA8GL3XFeSx6VYUjXQ96n44xe3JGKZCzBINrCYlxbZP6UwBafv7ti4eSRWc2Fy+fybQre0w17gR6lMzULA==}
peerDependencies:
react: ^18.0.0
dependencies:
'@tanstack/query-core': 5.36.1
react: 18.2.0
dev: false
/@tanstack/react-router@1.28.7(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-eSOg/ffG8KJ9E3oAJnhiiQtz5dNM/7M5PL2BItZeAZF+0CSIGi0eI97orGDknY42iMdE+1KRkCKdfuutln2dxw==}
engines: {node: '>=12'}
@ -2275,7 +2307,6 @@ packages:
/object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
dev: true
/object-hash@3.0.0:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
@ -2458,6 +2489,14 @@ packages:
hasBin: true
dev: true
/prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react-is: 16.13.1
dev: false
/punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@ -2477,6 +2516,10 @@ packages:
scheduler: 0.23.0
dev: false
/react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: false
/react-refresh@0.14.0:
resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==}
engines: {node: '>=0.10.0'}

View File

@ -0,0 +1,59 @@
import { mdiAlertCircleOutline } from "@mdi/js";
import Icon from "@mdi/react";
import { useQuery } from "@tanstack/react-query";
import { fetchCampaigns } from "../query/fetchers/campaigns";
import { cn } from "../utils/style";
interface Props {
className?: string;
}
export function CampaignsGrid({ className }: Props) {
const campaignsQuery = useQuery({
queryKey: ["campaigns"],
queryFn: fetchCampaigns,
});
if (campaignsQuery.isPending) {
return (
<div className={className}>
<span className="loading loading-spinner loading-lg"></span>
</div>
);
}
if (campaignsQuery.isError) {
return (
<div className={cn("flex justify-center", className)}>
<div role="alert" className="alert alert-error w-auto">
<Icon path={mdiAlertCircleOutline} size={1} />
<span>Couldn't load campaigns.</span>
</div>
</div>
);
}
return (
<div
className={cn(
"grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4",
className
)}
>
{campaignsQuery.data.map((campaign) => (
<div key={campaign._id} className="card bg-base-200">
<figure>
<img
src="https://img.daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.jpg"
alt="Shoes"
/>
</figure>
<div className="card-body items-start">
<h2 className="text-xl font-bold">{campaign.name}</h2>
<span>Lorem ipsum</span>
</div>
</div>
))}
</div>
);
}

3
frontend/src/config.ts Normal file
View File

@ -0,0 +1,3 @@
export const CONFIG = {
API_BASE_URL: "http://localhost:7231",
};

View File

@ -1,3 +1,4 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import React from "react";
import ReactDOM from "react-dom/client";
@ -5,6 +6,8 @@ import { ContextsProvider } from "./components/ContextsProvider";
import "./index.css";
import { routeTree } from "./routeTree.gen";
const queryClient = new QueryClient();
const router = createRouter({ routeTree });
declare module "@tanstack/react-router" {
interface Register {
@ -15,7 +18,9 @@ declare module "@tanstack/react-router" {
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ContextsProvider>
<RouterProvider router={router} />
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
</ContextsProvider>
</React.StrictMode>
);

View File

@ -0,0 +1,15 @@
import { CONFIG } from "../../config";
export const fetchCampaigns = async () => {
const resp = await fetch(`${CONFIG.API_BASE_URL}/campaigns`);
if (!resp.ok) {
throw new Error("Network error.");
}
const respBody = await resp.json();
if (!respBody.ok) {
throw new Error(respBody.message || "Something went wrong.");
}
return respBody.data;
};

View File

@ -1,4 +1,5 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import { CampaignsGrid } from "../components/CampaignsGrid";
export const Route = createLazyFileRoute("/")({
component: IndexPage,
@ -14,12 +15,14 @@ function IndexPage() {
</h2>
</div>
<p>
<p className="mb-8">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci
nesciunt ipsa possimus sed? Maxime eos iusto facere natus commodi
consectetur ad pariatur minima quisquam. Soluta esse minus porro nemo
at.
</p>
<CampaignsGrid />
</div>
);
}

View File

@ -0,0 +1,5 @@
export const cn = (
...args: Array<undefined | null | boolean | string>
): string => {
return args.filter((arg) => typeof arg === "string").join(" ");
};