feat: implement Sonner for toast notifications
This commit is contained in:
parent
5d178709ef
commit
83b86ab0f0
35
.vscode/settings.json
vendored
Normal file
35
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"files.watcherExclude": {
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"files.readonlyInclude": {
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports.biome": "explicit"
|
||||
}
|
||||
}
|
||||
141
package-lock.json
generated
141
package-lock.json
generated
@ -7,6 +7,8 @@
|
||||
"name": "template",
|
||||
"dependencies": {
|
||||
"@heroui/react": "^2.8.2",
|
||||
"@supabase/ssr": "^0.6.1",
|
||||
"@supabase/supabase-js": "^2.53.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/react-query": "^5.84.1",
|
||||
"@tanstack/react-query-devtools": "^5.84.1",
|
||||
@ -18,6 +20,7 @@
|
||||
"framer-motion": "^12.23.12",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
@ -6020,6 +6023,114 @@
|
||||
"integrity": "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/@supabase/auth-js": {
|
||||
"version": "2.71.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.71.1.tgz",
|
||||
"integrity": "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/functions-js": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.5.tgz",
|
||||
"integrity": "sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/node-fetch": {
|
||||
"version": "2.6.15",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz",
|
||||
"integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/node-fetch/node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@supabase/node-fetch/node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/@supabase/node-fetch/node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/postgrest-js": {
|
||||
"version": "1.19.4",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz",
|
||||
"integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/realtime-js": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.15.0.tgz",
|
||||
"integrity": "sha512-SEIWApsxyoAe68WU2/5PCCuBwa11LL4Bb8K3r2FHCt3ROpaTthmDiWEhnLMGayP05N4QeYrMk0kyTZOwid/Hjw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.13",
|
||||
"@types/phoenix": "^1.6.6",
|
||||
"@types/ws": "^8.18.1",
|
||||
"ws": "^8.18.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/ssr": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.6.1.tgz",
|
||||
"integrity": "sha512-QtQgEMvaDzr77Mk3vZ3jWg2/y+D8tExYF7vcJT+wQ8ysuvOeGGjYbZlvj5bHYsj/SpC0bihcisnwPrM4Gp5G4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@supabase/supabase-js": "^2.43.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/storage-js": {
|
||||
"version": "2.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.10.4.tgz",
|
||||
"integrity": "sha512-cvL02GarJVFcNoWe36VBybQqTVRq6wQSOCvTS64C+eyuxOruFIm1utZAY0xi2qKtHJO3EjKaj8iWJKySusDmAQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/supabase-js": {
|
||||
"version": "2.54.0",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.54.0.tgz",
|
||||
"integrity": "sha512-DLw83YwBfAaFiL3oWV26+sHRdeCGtxmIKccjh/Pndze3BWM4fZghzYKhk3ElOQU8Bluq4AkkCJ5bM5Szl/sfRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@supabase/auth-js": "2.71.1",
|
||||
"@supabase/functions-js": "2.4.5",
|
||||
"@supabase/node-fetch": "2.6.15",
|
||||
"@supabase/postgrest-js": "1.19.4",
|
||||
"@supabase/realtime-js": "2.15.0",
|
||||
"@supabase/storage-js": "^2.10.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||
@ -7009,7 +7120,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz",
|
||||
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.10.0"
|
||||
}
|
||||
@ -7020,6 +7130,12 @@
|
||||
"integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/phoenix": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz",
|
||||
"integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz",
|
||||
@ -7052,6 +7168,15 @@
|
||||
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yauzl": {
|
||||
"version": "2.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
|
||||
@ -12739,6 +12864,16 @@
|
||||
"seroval-plugins": "~1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sonner": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz",
|
||||
"integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
|
||||
@ -13443,8 +13578,7 @@
|
||||
"version": "7.10.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
||||
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unenv": {
|
||||
"version": "1.10.0",
|
||||
@ -14411,7 +14545,6 @@
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
"framer-motion": "^12.23.12",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
|
||||
13
src/integrations/sonner/provider.tsx
Normal file
13
src/integrations/sonner/provider.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { Toaster } from "sonner"
|
||||
|
||||
export const SonnerProvider = () => {
|
||||
return (
|
||||
<Toaster
|
||||
position="top-right"
|
||||
duration={3000}
|
||||
closeButton
|
||||
richColors
|
||||
visibleToasts={3}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -1,24 +1,24 @@
|
||||
import { parseCookies, setCookie } from '@tanstack/react-start/server'
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import { createServerClient } from "@supabase/ssr"
|
||||
import { parseCookies, setCookie } from "@tanstack/react-start/server"
|
||||
|
||||
export function getSupabaseServerClient() {
|
||||
return createServerClient(
|
||||
process.env.SUPABASE_URL!,
|
||||
process.env.SUPABASE_ANON_KEY!,
|
||||
process.env.SUPABASE_URL as string,
|
||||
process.env.SUPABASE_ANON_KEY as string,
|
||||
{
|
||||
cookies: {
|
||||
getAll() {
|
||||
return Object.entries(parseCookies()).map(([name, value]) => ({
|
||||
name,
|
||||
value,
|
||||
value
|
||||
}))
|
||||
},
|
||||
setAll(cookies) {
|
||||
cookies.forEach((cookie) => {
|
||||
setCookie(cookie.name, cookie.value)
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export const createRouter = () => {
|
||||
return routerWithQueryClient(
|
||||
createTanstackRouter({
|
||||
routeTree,
|
||||
context: { ...rqContext },
|
||||
context: { ...rqContext, user: null },
|
||||
defaultPreload: "intent",
|
||||
Wrap: (props: { children: React.ReactNode }) => {
|
||||
return <QueryProvider {...rqContext}>{props.children}</QueryProvider>
|
||||
|
||||
@ -1,62 +1,63 @@
|
||||
import css from "@styles/globals.css?url";
|
||||
import type { QueryClient } from "@tanstack/react-query";
|
||||
import css from "@styles/globals.css?url"
|
||||
import type { QueryClient } from "@tanstack/react-query"
|
||||
import {
|
||||
createRootRouteWithContext,
|
||||
HeadContent,
|
||||
Scripts,
|
||||
} from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
import { HeroUIProvider } from "@/integrations/heroui/provider";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase";
|
||||
Scripts
|
||||
} from "@tanstack/react-router"
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"
|
||||
import { createServerFn } from "@tanstack/react-start"
|
||||
import { HeroUIProvider } from "@/integrations/heroui/provider"
|
||||
import { SonnerProvider } from "@/integrations/sonner/provider"
|
||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
||||
|
||||
interface MyRouterContext {
|
||||
queryClient: QueryClient;
|
||||
user: null;
|
||||
queryClient: QueryClient
|
||||
user: null
|
||||
}
|
||||
const fetchUser = createServerFn({ method: "GET" }).handler(async () => {
|
||||
const supabase = getSupabaseServerClient();
|
||||
const { data, error: _error } = await supabase.auth.getUser();
|
||||
const supabase = getSupabaseServerClient()
|
||||
const { data, error: _error } = await supabase.auth.getUser()
|
||||
|
||||
if (!data.user?.email) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
email: data.user.email,
|
||||
};
|
||||
});
|
||||
email: data.user.email
|
||||
}
|
||||
})
|
||||
|
||||
export const Route = createRootRouteWithContext<MyRouterContext>()({
|
||||
beforeLoad: async () => {
|
||||
const user = await fetchUser();
|
||||
const user = await fetchUser()
|
||||
return {
|
||||
user,
|
||||
};
|
||||
user
|
||||
}
|
||||
},
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
charSet: "utf-8"
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
content: "width=device-width, initial-scale=1"
|
||||
},
|
||||
{
|
||||
title: "TanStack Start Starter",
|
||||
},
|
||||
title: "TanStack Start Starter"
|
||||
}
|
||||
],
|
||||
links: [
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: css,
|
||||
},
|
||||
],
|
||||
href: css
|
||||
}
|
||||
]
|
||||
}),
|
||||
|
||||
shellComponent: RootDocument,
|
||||
});
|
||||
shellComponent: RootDocument
|
||||
})
|
||||
|
||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
@ -66,9 +67,10 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
</head>
|
||||
<body>
|
||||
<HeroUIProvider>{children}</HeroUIProvider>
|
||||
<SonnerProvider />
|
||||
<TanStackRouterDevtools />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,39 +1,39 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { createServerFn } from "@tanstack/react-start"
|
||||
// import { Login } from "../components/Login";
|
||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase";
|
||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
||||
|
||||
export const loginFn = createServerFn({ method: "POST" })
|
||||
.validator((d: { email: string; password: string }) => d)
|
||||
.handler(async ({ data }) => {
|
||||
const supabase = getSupabaseServerClient();
|
||||
const supabase = getSupabaseServerClient()
|
||||
const response = await supabase.auth.signInWithPassword({
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
});
|
||||
console.log(response);
|
||||
password: data.password
|
||||
})
|
||||
console.log(response)
|
||||
if (response.error) {
|
||||
return {
|
||||
error: true,
|
||||
message: response.error.message,
|
||||
};
|
||||
message: response.error.message
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
export const Route = createFileRoute("/_authed")({
|
||||
beforeLoad: ({ context }) => {
|
||||
console.log("contextw", context);
|
||||
console.log("contextw", context)
|
||||
if (!context?.user) {
|
||||
throw new Error("Not authenticated");
|
||||
throw new Error("Not authenticated")
|
||||
}
|
||||
},
|
||||
errorComponent: ({ error }) => {
|
||||
if (error.message === "Not authenticated") {
|
||||
<p>
|
||||
;<p>
|
||||
Not authenticated. Please <a href="/login">login</a>.
|
||||
</p>;
|
||||
</p>
|
||||
}
|
||||
|
||||
throw error;
|
||||
},
|
||||
});
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { Button } from "@heroui/react";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { Button } from "@heroui/react"
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
|
||||
export const Route = createFileRoute("/_authed/post")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
component: RouteComponent
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
const navigate = Route.useNavigate();
|
||||
const navigate = Route.useNavigate()
|
||||
return (
|
||||
<div>
|
||||
Hello "/_authed/post"!{" "}
|
||||
@ -15,7 +15,7 @@ function RouteComponent() {
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigate({
|
||||
to: "/logout",
|
||||
to: "/logout"
|
||||
})
|
||||
}
|
||||
>
|
||||
@ -23,5 +23,5 @@ function RouteComponent() {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import logo from "@assets/logo.svg";
|
||||
import { Button } from "@heroui/react";
|
||||
import { createFileRoute, getRouteApi } from "@tanstack/react-router";
|
||||
import logo from "@assets/logo.svg"
|
||||
import { Button } from "@heroui/react"
|
||||
import { createFileRoute, getRouteApi } from "@tanstack/react-router"
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
component: App,
|
||||
});
|
||||
const apiRouter = getRouteApi("/");
|
||||
component: App
|
||||
})
|
||||
const apiRouter = getRouteApi("/")
|
||||
function App() {
|
||||
const navigate = apiRouter.useNavigate();
|
||||
const navigate = apiRouter.useNavigate()
|
||||
return (
|
||||
<div className="text-center">
|
||||
<header className="min-h-screen flex flex-col items-center justify-center bg-[#282c34] text-white text-[calc(10px+2vmin)]">
|
||||
@ -23,7 +23,7 @@ function App() {
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigate({
|
||||
to: "/login",
|
||||
to: "/login"
|
||||
})
|
||||
}
|
||||
>
|
||||
@ -32,8 +32,8 @@ function App() {
|
||||
<Button
|
||||
onPress={() => {
|
||||
navigate({
|
||||
to: "/signup",
|
||||
});
|
||||
to: "/signup"
|
||||
})
|
||||
}}
|
||||
>
|
||||
Signup
|
||||
@ -41,5 +41,5 @@ function App() {
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,53 +1,53 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { createFileRoute, getRouteApi } from "@tanstack/react-router";
|
||||
import { useServerFn } from "@tanstack/react-start";
|
||||
import { loginFn } from "./_authed";
|
||||
import { Button, Form, Input } from "@heroui/react";
|
||||
import { Button, Form, Input } from "@heroui/react"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { createFileRoute, getRouteApi } from "@tanstack/react-router"
|
||||
import { useServerFn } from "@tanstack/react-start"
|
||||
import { loginFn } from "./_authed"
|
||||
|
||||
export const Route = createFileRoute("/login")({
|
||||
component: LoginComp,
|
||||
});
|
||||
const apiRouter = getRouteApi("/login");
|
||||
component: LoginComp
|
||||
})
|
||||
const apiRouter = getRouteApi("/login")
|
||||
function LoginComp() {
|
||||
const navigate = apiRouter.useNavigate();
|
||||
const loginFunction = useServerFn(loginFn);
|
||||
const navigate = apiRouter.useNavigate()
|
||||
const loginFunction = useServerFn(loginFn)
|
||||
const loginMutation = useMutation({
|
||||
mutationKey: ["login"],
|
||||
mutationFn: async (data: { email: string; password: string }) => {
|
||||
return loginFunction({
|
||||
data: {
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
},
|
||||
});
|
||||
password: data.password
|
||||
}
|
||||
})
|
||||
},
|
||||
onSuccess: (data, ctx) => {
|
||||
console.log("Login successful", data);
|
||||
console.log("ctx", ctx);
|
||||
console.log("Login successful", data)
|
||||
console.log("ctx", ctx)
|
||||
|
||||
if (data?.error) {
|
||||
alert(data.message);
|
||||
return;
|
||||
alert(data.message)
|
||||
return
|
||||
}
|
||||
navigate({
|
||||
to: "/post",
|
||||
});
|
||||
to: "/post"
|
||||
})
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log("No se ha podido procesar el login", error);
|
||||
},
|
||||
});
|
||||
console.log("No se ha podido procesar el login", error)
|
||||
}
|
||||
})
|
||||
return (
|
||||
<div className="flex justify-center items-center flex-col h-screen">
|
||||
<p className="font-semibold mb-3">Login</p>
|
||||
<Form
|
||||
className="grid gap-2 max-w-sm w-full"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const email = formData.get("email") as string;
|
||||
const password = formData.get("password") as string;
|
||||
loginMutation.mutate({ email, password });
|
||||
e.preventDefault()
|
||||
const formData = new FormData(e.currentTarget)
|
||||
const email = formData.get("email") as string
|
||||
const password = formData.get("password") as string
|
||||
loginMutation.mutate({ email, password })
|
||||
}}
|
||||
>
|
||||
<Input name="email" type="email" placeholder="Email" />
|
||||
@ -55,5 +55,5 @@ function LoginComp() {
|
||||
<Button type="submit">Enviar</Button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase";
|
||||
import { redirect, createFileRoute } from "@tanstack/react-router";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router"
|
||||
import { createServerFn } from "@tanstack/react-start"
|
||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
||||
|
||||
const logoutFn = createServerFn().handler(async () => {
|
||||
const supabase = getSupabaseServerClient();
|
||||
const { error } = await supabase.auth.signOut();
|
||||
const supabase = getSupabaseServerClient()
|
||||
const { error } = await supabase.auth.signOut()
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
error: true,
|
||||
message: error.message,
|
||||
};
|
||||
message: error.message
|
||||
}
|
||||
}
|
||||
|
||||
throw redirect({
|
||||
href: "/",
|
||||
});
|
||||
});
|
||||
href: "/"
|
||||
})
|
||||
})
|
||||
|
||||
export const Route = createFileRoute("/logout")({
|
||||
preload: false,
|
||||
loader: () => logoutFn(),
|
||||
});
|
||||
loader: () => logoutFn()
|
||||
})
|
||||
|
||||
@ -1,67 +1,67 @@
|
||||
import { redirect, createFileRoute } from "@tanstack/react-router";
|
||||
import { createServerFn, useServerFn } from "@tanstack/react-start";
|
||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase";
|
||||
import { Button, Form, Input } from "@heroui/react";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { Button, Form, Input } from "@heroui/react"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router"
|
||||
import { createServerFn, useServerFn } from "@tanstack/react-start"
|
||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
||||
|
||||
export const signupFn = createServerFn({ method: "POST" })
|
||||
.validator(
|
||||
(d: { email: string; password: string; redirectUrl?: string }) => d
|
||||
)
|
||||
.handler(async ({ data }) => {
|
||||
const supabase = getSupabaseServerClient();
|
||||
const supabase = getSupabaseServerClient()
|
||||
const { error } = await supabase.auth.signUp({
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
});
|
||||
password: data.password
|
||||
})
|
||||
if (error) {
|
||||
return {
|
||||
error: true,
|
||||
message: error.message,
|
||||
};
|
||||
message: error.message
|
||||
}
|
||||
}
|
||||
|
||||
throw redirect({
|
||||
href: data.redirectUrl || "/",
|
||||
});
|
||||
});
|
||||
href: data.redirectUrl || "/"
|
||||
})
|
||||
})
|
||||
|
||||
export const Route = createFileRoute("/signup")({
|
||||
component: SignupComp,
|
||||
});
|
||||
component: SignupComp
|
||||
})
|
||||
|
||||
function SignupComp() {
|
||||
const signup = useServerFn(signupFn);
|
||||
const signup = useServerFn(signupFn)
|
||||
const signupMutation = useMutation({
|
||||
mutationKey: ["signup"],
|
||||
mutationFn: async (data: {
|
||||
email: string;
|
||||
password: string;
|
||||
redirectUrl?: string;
|
||||
email: string
|
||||
password: string
|
||||
redirectUrl?: string
|
||||
}) => {
|
||||
return signup({
|
||||
data: {
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
redirectUrl: data.redirectUrl,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
redirectUrl: data.redirectUrl
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return (
|
||||
<div className="flex justify-center items-center flex-col h-screen">
|
||||
<p className="font-semibold mb-3">Signup</p>
|
||||
<Form
|
||||
className="grid gap-2 max-w-5xl w-full"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const email = formData.get("email") as string;
|
||||
const password = formData.get("password") as string;
|
||||
e.preventDefault()
|
||||
const formData = new FormData(e.currentTarget)
|
||||
const email = formData.get("email") as string
|
||||
const password = formData.get("password") as string
|
||||
signupMutation.mutate({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
password
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Input name="email" type="email" placeholder="Email" />
|
||||
@ -71,5 +71,5 @@ function SignupComp() {
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user