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"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mdi/js": "^7.4.47",
|
||||||
|
"@mdi/react": "^1.6.1",
|
||||||
|
"@tanstack/react-query": "^5.37.1",
|
||||||
"@tanstack/react-router": "^1.28.7",
|
"@tanstack/react-router": "^1.28.7",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,15 @@ settings:
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
dependencies:
|
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':
|
'@tanstack/react-router':
|
||||||
specifier: ^1.28.7
|
specifier: ^1.28.7
|
||||||
version: 1.28.7(react-dom@18.2.0)(react@18.2.0)
|
version: 1.28.7(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
|
@ -729,6 +738,16 @@ packages:
|
||||||
'@jridgewell/sourcemap-codec': 1.4.15
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
dev: true
|
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:
|
/@noble/curves@1.3.0:
|
||||||
resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==}
|
resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -920,6 +939,19 @@ packages:
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dev: false
|
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):
|
/@tanstack/react-router@1.28.7(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-eSOg/ffG8KJ9E3oAJnhiiQtz5dNM/7M5PL2BItZeAZF+0CSIGi0eI97orGDknY42iMdE+1KRkCKdfuutln2dxw==}
|
resolution: {integrity: sha512-eSOg/ffG8KJ9E3oAJnhiiQtz5dNM/7M5PL2BItZeAZF+0CSIGi0eI97orGDknY42iMdE+1KRkCKdfuutln2dxw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
@ -2275,7 +2307,6 @@ packages:
|
||||||
/object-assign@4.1.1:
|
/object-assign@4.1.1:
|
||||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/object-hash@3.0.0:
|
/object-hash@3.0.0:
|
||||||
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||||
|
|
@ -2458,6 +2489,14 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: 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:
|
/punycode@2.3.1:
|
||||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -2477,6 +2516,10 @@ packages:
|
||||||
scheduler: 0.23.0
|
scheduler: 0.23.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/react-is@16.13.1:
|
||||||
|
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-refresh@0.14.0:
|
/react-refresh@0.14.0:
|
||||||
resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==}
|
resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
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 { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
|
|
@ -5,6 +6,8 @@ import { ContextsProvider } from "./components/ContextsProvider";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { routeTree } from "./routeTree.gen";
|
import { routeTree } from "./routeTree.gen";
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
const router = createRouter({ routeTree });
|
const router = createRouter({ routeTree });
|
||||||
declare module "@tanstack/react-router" {
|
declare module "@tanstack/react-router" {
|
||||||
interface Register {
|
interface Register {
|
||||||
|
|
@ -15,7 +18,9 @@ declare module "@tanstack/react-router" {
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<ContextsProvider>
|
<ContextsProvider>
|
||||||
<RouterProvider router={router} />
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</QueryClientProvider>
|
||||||
</ContextsProvider>
|
</ContextsProvider>
|
||||||
</React.StrictMode>
|
</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 { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
|
import { CampaignsGrid } from "../components/CampaignsGrid";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/")({
|
export const Route = createLazyFileRoute("/")({
|
||||||
component: IndexPage,
|
component: IndexPage,
|
||||||
|
|
@ -14,12 +15,14 @@ function IndexPage() {
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p className="mb-8">
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci
|
||||||
nesciunt ipsa possimus sed? Maxime eos iusto facere natus commodi
|
nesciunt ipsa possimus sed? Maxime eos iusto facere natus commodi
|
||||||
consectetur ad pariatur minima quisquam. Soluta esse minus porro nemo
|
consectetur ad pariatur minima quisquam. Soluta esse minus porro nemo
|
||||||
at.
|
at.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<CampaignsGrid />
|
||||||
</div>
|
</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