add TanStack Query & add icons & list campaigns from backend
This commit is contained in:
parent
de04100c19
commit
f7c9a64214
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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'}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export const CONFIG = {
|
||||
API_BASE_URL: "http://localhost:7231",
|
||||
};
|
||||
|
|
@ -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>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={router} />
|
||||
</QueryClientProvider>
|
||||
</ContextsProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
export const cn = (
|
||||
...args: Array<undefined | null | boolean | string>
|
||||
): string => {
|
||||
return args.filter((arg) => typeof arg === "string").join(" ");
|
||||
};
|
||||
Loading…
Reference in New Issue