Initial commit
This commit is contained in:
46
skills/tanstack-patterns/templates/auth-layout.tsx
Normal file
46
skills/tanstack-patterns/templates/auth-layout.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
// Grey Haven Studio - Protected Route Layout Template
|
||||
// Copy this template for _authenticated/_layout.tsx
|
||||
|
||||
import { Outlet, createFileRoute, redirect } from "@tanstack/react-router";
|
||||
import { Header } from "~/lib/components/layout/Header";
|
||||
import { Sidebar } from "~/lib/components/layout/Sidebar";
|
||||
import { getSession } from "~/lib/server/functions/auth";
|
||||
|
||||
// TODO: Update route path to match your file location
|
||||
export const Route = createFileRoute("/_authenticated/_layout")({
|
||||
// Auth check runs before loading
|
||||
beforeLoad: async ({ context }) => {
|
||||
const session = await getSession();
|
||||
|
||||
// Redirect to login if not authenticated
|
||||
if (!session) {
|
||||
throw redirect({
|
||||
to: "/auth/login",
|
||||
search: {
|
||||
redirect: context.location.href, // Save redirect URL
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Make session available to child routes
|
||||
return { session };
|
||||
},
|
||||
component: AuthenticatedLayout,
|
||||
});
|
||||
|
||||
function AuthenticatedLayout() {
|
||||
const { session } = Route.useRouteContext();
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen">
|
||||
{/* TODO: Update layout structure */}
|
||||
<Sidebar user={session.user} />
|
||||
<div className="flex-1">
|
||||
<Header user={session.user} />
|
||||
<main className="p-6">
|
||||
<Outlet /> {/* Child routes render here */}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
39
skills/tanstack-patterns/templates/custom-hook.ts
Normal file
39
skills/tanstack-patterns/templates/custom-hook.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
// Grey Haven Studio - Custom Query Hook Template
|
||||
// Copy this template for reusable query logic
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
// TODO: Import your server functions
|
||||
// import { getResource, updateResource } from "~/lib/server/functions/resources";
|
||||
|
||||
// TODO: Update hook name and parameter types
|
||||
export function useResource(resourceId: string, tenantId: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
// Query for fetching data
|
||||
const query = useQuery({
|
||||
queryKey: ["resource", resourceId], // Include resourceId in key
|
||||
queryFn: () => getResource(resourceId, tenantId),
|
||||
staleTime: 60000, // Grey Haven default: 1 minute
|
||||
});
|
||||
|
||||
// Mutation for updating data
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: (data: ResourceUpdate) => updateResource(resourceId, data, tenantId),
|
||||
onSuccess: (updatedResource) => {
|
||||
// Update cache with new data
|
||||
queryClient.setQueryData(["resource", resourceId], updatedResource);
|
||||
},
|
||||
});
|
||||
|
||||
// Return simplified interface
|
||||
return {
|
||||
resource: query.data,
|
||||
isLoading: query.isLoading,
|
||||
error: query.error,
|
||||
update: updateMutation.mutate,
|
||||
isUpdating: updateMutation.isPending,
|
||||
};
|
||||
}
|
||||
|
||||
// Usage in component:
|
||||
// const { resource, isLoading, update, isUpdating } = useResource(id, tenantId);
|
||||
30
skills/tanstack-patterns/templates/page-route.tsx
Normal file
30
skills/tanstack-patterns/templates/page-route.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
// Grey Haven Studio - Page Route Template
|
||||
// Copy this template for any page route
|
||||
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
// TODO: Import your server functions
|
||||
// import { getPageData } from "~/lib/server/functions/page";
|
||||
|
||||
// TODO: Update route path to match your file location
|
||||
export const Route = createFileRoute("/_authenticated/page")({
|
||||
// Loader fetches data on server before rendering
|
||||
loader: async ({ context }) => {
|
||||
const tenantId = context.session.tenantId;
|
||||
// TODO: Replace with your data fetching
|
||||
// return await getPageData(tenantId);
|
||||
return { data: "Replace me" };
|
||||
},
|
||||
component: PageComponent,
|
||||
});
|
||||
|
||||
function PageComponent() {
|
||||
const data = Route.useLoaderData(); // Type-safe loader data
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* TODO: Build your page UI */}
|
||||
<h1 className="text-2xl font-bold">Page Title</h1>
|
||||
<div>{JSON.stringify(data)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
35
skills/tanstack-patterns/templates/root-route.tsx
Normal file
35
skills/tanstack-patterns/templates/root-route.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
// Grey Haven Studio - TanStack Router Root Route Template
|
||||
// Copy this template for __root.tsx
|
||||
|
||||
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/router-devtools";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
|
||||
// Create QueryClient with Grey Haven defaults
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 60000, // 1 minute default
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: RootComponent,
|
||||
});
|
||||
|
||||
function RootComponent() {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<div className="min-h-screen bg-background">
|
||||
<Outlet /> {/* Child routes render here */}
|
||||
</div>
|
||||
{/* Dev tools (only in development) */}
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
<TanStackRouterDevtools position="bottom-right" />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
61
skills/tanstack-patterns/templates/server-function.ts
Normal file
61
skills/tanstack-patterns/templates/server-function.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
// Grey Haven Studio - Server Function Template
|
||||
// Copy this template for creating server functions
|
||||
|
||||
import { createServerFn } from "@tanstack/start";
|
||||
import { db } from "~/lib/server/db";
|
||||
// TODO: Import your database schema
|
||||
// import { resources } from "~/lib/server/schema/resources";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
|
||||
// GET server function (for fetching data)
|
||||
// TODO: Update function name and return type
|
||||
export const getResource = createServerFn("GET", async (
|
||||
resourceId: string,
|
||||
tenantId: string // ALWAYS include tenant_id!
|
||||
) => {
|
||||
// TODO: Replace with your query
|
||||
const resource = await db.query.resources.findFirst({
|
||||
where: and(
|
||||
eq(resources.id, resourceId),
|
||||
eq(resources.tenant_id, tenantId) // Multi-tenant isolation!
|
||||
),
|
||||
});
|
||||
|
||||
if (!resource) {
|
||||
throw new Error("Resource not found");
|
||||
}
|
||||
|
||||
return resource;
|
||||
});
|
||||
|
||||
// POST server function (for creating data)
|
||||
// TODO: Update function name and parameters
|
||||
export const createResource = createServerFn("POST", async (
|
||||
data: { name: string; description?: string },
|
||||
tenantId: string // ALWAYS include tenant_id!
|
||||
) => {
|
||||
// TODO: Replace with your insert
|
||||
const resource = await db.insert(resources).values({
|
||||
...data,
|
||||
tenant_id: tenantId, // Include tenant_id in insert!
|
||||
}).returning();
|
||||
|
||||
return resource[0];
|
||||
});
|
||||
|
||||
// DELETE server function (for deleting data)
|
||||
// TODO: Update function name
|
||||
export const deleteResource = createServerFn("DELETE", async (
|
||||
resourceId: string,
|
||||
tenantId: string // ALWAYS include tenant_id!
|
||||
) => {
|
||||
// TODO: Replace with your delete
|
||||
await db.delete(resources).where(
|
||||
and(
|
||||
eq(resources.id, resourceId),
|
||||
eq(resources.tenant_id, tenantId) // Ensure tenant isolation!
|
||||
)
|
||||
);
|
||||
|
||||
return { success: true };
|
||||
});
|
||||
Reference in New Issue
Block a user