chore: update dependencies and devDependencies in package.json

fix: refactor drizzle integration to use optional chaining for DATABASE_URL

fix: update Supabase integration to use getCookies instead of parseCookies

feat: implement user profile creation with validation schema

refactor: remove unused user-related functions and adjust signup logic

fix: update user validation schema to include first and last name

chore: enhance route tree with SSR support for router

refactor: rename createRouter to getRouter for clarity

fix: update RootDocument styles for background gradient

refactor: clean up _authed routes by removing unused queries

fix: adjust register form layout for better readability

chore: simplify Vite configuration by removing unnecessary options
This commit is contained in:
Jrodenas 2025-11-12 20:09:59 +01:00
parent 1a049d1193
commit 3371def3b9
16 changed files with 2228 additions and 8905 deletions

View File

@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.1.3/schema.json", "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"vcs": { "vcs": {
"enabled": false, "enabled": false,
"clientKind": "git", "clientKind": "git",
@ -27,6 +27,11 @@
"recommended": true "recommended": true
} }
}, },
"css": {
"parser": {
"tailwindDirectives": true
}
},
"javascript": { "javascript": {
"formatter": { "formatter": {
"quoteStyle": "double", "quoteStyle": "double",

10888
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,39 +12,39 @@
"check": "biome check" "check": "biome check"
}, },
"dependencies": { "dependencies": {
"@heroui/react": "^2.8.2", "@heroui/react": "^2.8.5",
"@supabase/ssr": "^0.6.1", "@supabase/ssr": "^0.7.0",
"@supabase/supabase-js": "^2.53.1", "@supabase/supabase-js": "^2.81.1",
"@tailwindcss/vite": "^4.1.11", "@tailwindcss/vite": "^4.1.17",
"@tanstack/react-query": "^5.84.1", "@tanstack/react-query": "^5.90.8",
"@tanstack/react-query-devtools": "^5.84.1", "@tanstack/react-query-devtools": "^5.90.2",
"@tanstack/react-router": "^1.130.12", "@tanstack/react-router": "^1.135.2",
"@tanstack/react-router-devtools": "^1.130.13", "@tanstack/react-router-devtools": "^1.135.2",
"@tanstack/react-router-with-query": "^1.130.12", "@tanstack/react-router-with-query": "^1.130.17",
"@tanstack/react-start": "^1.130.15", "@tanstack/react-start": "^1.135.2",
"@tanstack/router-plugin": "^1.130.15", "@tanstack/router-plugin": "^1.135.2",
"@vis.gl/react-google-maps": "^1.5.5", "@vis.gl/react-google-maps": "^1.7.1",
"drizzle-orm": "^0.44.4", "drizzle-orm": "^0.44.7",
"framer-motion": "^12.23.12", "framer-motion": "^12.23.12",
"postgres": "^3.4.7", "postgres": "^3.4.7",
"react": "^19.1.1", "react": "^19.2.0",
"react-dom": "^19.1.1", "react-dom": "^19.2.0",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwindcss": "^4.1.11", "zod": "^4.1.12"
"vite-tsconfig-paths": "^5.1.4",
"zod": "^4.0.17"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.1.3", "@biomejs/biome": "^2.3.5",
"@tanstack/react-router-ssr-query": "^1.131.5", "vite-tsconfig-paths": "^5.1.4",
"@tanstack/react-router-ssr-query": "^1.135.2",
"tailwindcss": "^4.1.17",
"@types/google.maps": "^3.58.1", "@types/google.maps": "^3.58.1",
"@types/react": "^19.1.9", "@types/react": "^19.2.4",
"@types/react-dom": "^19.1.7", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^4.7.0", "@vitejs/plugin-react": "^5.1.1",
"drizzle-kit": "^0.31.4", "drizzle-kit": "^0.31.6",
"jsdom": "^26.1.0", "jsdom": "^27.2.0",
"typescript": "^5.7.2", "typescript": "^5.9.3",
"vite": "^6.3.5", "vite": "^7.2.2",
"web-vitals": "^5.1.0" "web-vitals": "^5.1.0"
} }
} }

View File

@ -1,10 +1,8 @@
import { drizzle } from "drizzle-orm/postgres-js"
import postgres from "postgres"
import { drizzle } from 'drizzle-orm/postgres-js' const connectionString = process.env.DATABASE_URL ?? ""
import postgres from 'postgres'
const connectionString = process.env.DATABASE_URL!
// Disable prefetch as it is not supported for "Transaction" pool mode // Disable prefetch as it is not supported for "Transaction" pool mode
const client = postgres(connectionString, { prepare: false }) const client = postgres(connectionString, { prepare: false })
export const db = drizzle(client); export const db = drizzle(client)

View File

@ -1,5 +1,5 @@
import { createServerClient } from "@supabase/ssr" import { createServerClient } from "@supabase/ssr"
import { parseCookies, setCookie } from "@tanstack/react-start/server" import { getCookies, setCookie } from "@tanstack/react-start/server"
export function getSupabaseServerClient() { export function getSupabaseServerClient() {
return createServerClient( return createServerClient(
@ -8,7 +8,7 @@ export function getSupabaseServerClient() {
{ {
cookies: { cookies: {
getAll() { getAll() {
return Object.entries(parseCookies()).map(([name, value]) => ({ return Object.entries(getCookies()).map(([name, value]) => ({
name, name,
value value
})) }))

View File

@ -1,7 +1,6 @@
import { useMutation } from "@tanstack/react-query" import { useMutation } from "@tanstack/react-query"
import { toast } from "sonner" import { toast } from "sonner"
import type z from "zod" import type z from "zod"
import { createProfile } from "@/lib/server/user"
import { profileFormSchema } from "@/lib/validation/user" import { profileFormSchema } from "@/lib/validation/user"
import { useValidation } from "../useValidation" import { useValidation } from "../useValidation"
@ -13,7 +12,7 @@ export const useProfile = () => {
}) })
const signup = useMutation({ const signup = useMutation({
mutationKey: ["create-profile"], mutationKey: ["create-profile"],
mutationFn: async (data: TProfileForm) => createProfile({ data }), mutationFn: async (data: TProfileForm) => {},
onSuccess: () => { onSuccess: () => {
toast.success("Your profile is created..", { toast.success("Your profile is created..", {
id: "create-profile" id: "create-profile"

View File

@ -1,14 +1,7 @@
import { redirect } from "@tanstack/react-router" import { redirect } from "@tanstack/react-router"
import { createServerFn } from "@tanstack/react-start" import { createServerFn } from "@tanstack/react-start"
import { eq } from "drizzle-orm"
import { db } from "@/integrations/drizzle"
import { profiles, users } from "@/integrations/drizzle/db/schema"
import { getSupabaseServerClient } from "@/integrations/supabase/supabase" import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
import { import { loginFormSchema, signupFormSchema } from "../validation/user"
loginFormSchema,
profileFormSchema,
signupFormSchema
} from "../validation/user"
export const getUser = createServerFn().handler(async () => { export const getUser = createServerFn().handler(async () => {
const supabase = getSupabaseServerClient() const supabase = getSupabaseServerClient()
@ -34,7 +27,7 @@ export const getUser = createServerFn().handler(async () => {
export const loginUser = createServerFn({ export const loginUser = createServerFn({
method: "POST" method: "POST"
}) })
.validator(loginFormSchema) .inputValidator(loginFormSchema)
.handler(async ({ data }) => { .handler(async ({ data }) => {
const supabase = getSupabaseServerClient() const supabase = getSupabaseServerClient()
const login = await supabase.auth.signInWithPassword({ const login = await supabase.auth.signInWithPassword({
@ -72,7 +65,7 @@ export const logoutUser = createServerFn().handler(async () => {
}) })
export const signupUser = createServerFn({ method: "POST" }) export const signupUser = createServerFn({ method: "POST" })
.validator(signupFormSchema) .inputValidator(signupFormSchema)
.handler(async ({ data }) => { .handler(async ({ data }) => {
const supabase = getSupabaseServerClient() const supabase = getSupabaseServerClient()
const { error } = await supabase.auth.signUp({ const { error } = await supabase.auth.signUp({
@ -96,27 +89,3 @@ export const signupUser = createServerFn({ method: "POST" })
href: data.redirectUrl || "/" href: data.redirectUrl || "/"
}) })
}) })
export const getAllUsers = createServerFn().handler(async () => {
const response = await db.select().from(users)
return response
})
export const createProfile = createServerFn({ method: "POST" })
.validator(profileFormSchema)
.handler(async ({ data }) => {
await db.insert(profiles).values(data).returning()
})
export const getProfile = createServerFn({ method: "POST" })
.validator((data: { id: string }) => data)
.handler(async ({ data }) => {
const { id } = data
const response = await db
.select()
.from(profiles)
.where(eq(profiles.id, id))
.limit(1)
return response[0] ?? null
})

View File

@ -13,6 +13,8 @@ export const signupFormSchema = z.object({
redirectUrl: z.string().optional() redirectUrl: z.string().optional()
}) })
export const profileFormSchema= z.object({ export const profileFormSchema = z.object({
id: z.uuid() id: z.uuid(),
firstName: z.string().min(1, "First name is required"),
lastName: z.string().min(1, "Last name is required")
}) })

View File

@ -179,3 +179,12 @@ const rootRouteChildren: RootRouteChildren = {
export const routeTree = rootRouteImport export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren) ._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>() ._addFileTypes<FileRouteTypes>()
import type { getRouter } from './router.tsx'
import type { createStart } from '@tanstack/react-start'
declare module '@tanstack/react-start' {
interface Register {
ssr: true
router: Awaited<ReturnType<typeof getRouter>>
}
}

View File

@ -1,13 +1,13 @@
import { createRouter as createTanstackRouter } from "@tanstack/react-router" import { createRouter } from "@tanstack/react-router"
import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query" import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query"
import { createQueryContext } from "./integrations/tanstack-query/context.tsx" import { createQueryContext } from "./integrations/tanstack-query/context.tsx"
import { QueryProvider } from "./integrations/tanstack-query/provider.tsx" import { QueryProvider } from "./integrations/tanstack-query/provider.tsx"
import { routeTree } from "./routeTree.gen.ts" import { routeTree } from "./routeTree.gen.ts"
export const createRouter = () => { export const getRouter = () => {
const { queryClient } = createQueryContext() const { queryClient } = createQueryContext()
const router = createTanstackRouter({ const router = createRouter({
routeTree, routeTree,
context: { queryClient, user: null }, context: { queryClient, user: null },
defaultPreload: "intent", defaultPreload: "intent",
@ -30,6 +30,6 @@ export const createRouter = () => {
declare module "@tanstack/react-router" { declare module "@tanstack/react-router" {
interface Register { interface Register {
router: ReturnType<typeof createRouter> router: ReturnType<typeof getRouter>
} }
} }

View File

@ -54,7 +54,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
</head> </head>
<body> <body>
<div <div
className="min-h-screen bg-gradient-to-br from-green-200 to-emerald-400" className="min-h-screen bg-linear-to-br from-green-200 to-emerald-400"
style={{ style={{
backgroundImage: backgroundImage:
"radial-gradient(50% 50% at 95% 5%, #34d399 0%, #6ee7b7 70%, #f5f5f5 100%)" "radial-gradient(50% 50% at 95% 5%, #34d399 0%, #6ee7b7 70%, #f5f5f5 100%)"

View File

@ -6,7 +6,6 @@ import {
DropdownTrigger DropdownTrigger
} from "@heroui/react" } from "@heroui/react"
import { createFileRoute, Outlet } from "@tanstack/react-router" import { createFileRoute, Outlet } from "@tanstack/react-router"
import { getProfile } from "@/lib/server/user"
export const Route = createFileRoute("/_authed")({ export const Route = createFileRoute("/_authed")({
beforeLoad: ({ context }) => { beforeLoad: ({ context }) => {
@ -15,10 +14,9 @@ export const Route = createFileRoute("/_authed")({
// TODO: Redirect to login page // TODO: Redirect to login page
} }
}, },
loader: ({ context: { queryClient, user } }) => { loader: ({ context }) => {
queryClient.ensureQueryData({ context.queryClient.ensureQueryData({
queryKey: ["profile"], queryKey: ["profile"]
queryFn: () => getProfile({ data: { id: user?.id as string } })
}) })
}, },
errorComponent: ({ error }) => { errorComponent: ({ error }) => {

View File

@ -4,12 +4,9 @@ import {
CardBody, CardBody,
CardFooter, CardFooter,
CardHeader, CardHeader,
Chip, Chip
Divider
} from "@heroui/react" } from "@heroui/react"
import { useQuery } from "@tanstack/react-query"
import { createFileRoute } from "@tanstack/react-router" import { createFileRoute } from "@tanstack/react-router"
import { getAllUsers, getProfile } from "@/lib/server/user"
export const Route = createFileRoute("/_authed/dashboard")({ export const Route = createFileRoute("/_authed/dashboard")({
component: RouteComponent component: RouteComponent
@ -18,26 +15,12 @@ export const Route = createFileRoute("/_authed/dashboard")({
function RouteComponent() { function RouteComponent() {
const { user } = Route.useRouteContext() const { user } = Route.useRouteContext()
const { data } = useQuery({
queryKey: ["users"],
queryFn: async () => {
return getAllUsers()
}
})
const { data: profile } = useQuery({
queryKey: ["profile"],
queryFn: async () => {
return getProfile({ data: { id: user?.id as string } })
}
})
return ( return (
<div className="flex justify-center items-center flex-col gap-2"> <div className="flex justify-center items-center flex-col gap-2">
<Card className="max-w-6xl border-none" fullWidth shadow="none"> <Card className="max-w-6xl border-none" fullWidth shadow="none">
<CardHeader className="mb-0 pb-0 justify-between flex-wrap gap-2"> <CardHeader className="mb-0 pb-0 justify-between flex-wrap gap-2">
<Chip variant="light" size="lg"> <Chip variant="light" size="lg">
Inicio {profile?.firstName ?? "demo"} Inicio {user?.name ?? "demo"}
</Chip> </Chip>
<div className="rounded-md bg-warning/30 border-2 border-warning/70 px-3 py-1"> <div className="rounded-md bg-warning/30 border-2 border-warning/70 px-3 py-1">
<p> 🏗 La web está en mantenimiento</p> <p> 🏗 La web está en mantenimiento</p>
@ -137,5 +120,3 @@ function RouteComponent() {
</div> </div>
) )
} }
export default RouteComponent

View File

@ -31,7 +31,7 @@ function RouteComponent() {
<Textarea name="description" label="Descripción" /> <Textarea name="description" label="Descripción" />
<Textarea name="differentiator" label="¿Qué te hace diferente?" /> <Textarea name="differentiator" label="¿Qué te hace diferente?" />
<Slider name="coverage_areas" label="Areas de cobertura" /> <Slider name="coverage_areas" label="Areas de cobertura" />
<Input name="services" label="Servicios" />{" "} <Input name="services" label="Servicios" />
{/* Crear varios checkboxes */} {/* Crear varios checkboxes */}
<Button isLoading={isPending} type="submit"> <Button isLoading={isPending} type="submit">
Crear usuario Crear usuario

View File

@ -10,9 +10,7 @@ const config = defineConfig({
projects: ['./tsconfig.json'], projects: ['./tsconfig.json'],
}), }),
tailwindcss(), tailwindcss(),
tanstackStart({ tanstackStart(),
customViteReactPlugin: true,
}),
viteReact(), viteReact(),
], ],
}) })