Skip to content

Commit f022e4e

Browse files
committed
reduce retyping types
1 parent 5183501 commit f022e4e

11 files changed

Lines changed: 67 additions & 274 deletions

File tree

examples/blog/app/admin/[[...admin]]/page.tsx

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,12 @@
11
import { AdminUI } from "@opensaas/ui";
2-
import { getAdminContext } from "@opensaas/ui/server";
32
import type { ServerActionInput } from "@opensaas/ui/server";
4-
import { getContext } from "@opensaas/core";
5-
import { PrismaClient } from "@prisma/client";
63
import config from "../../../opensaas.config";
7-
8-
// Create Prisma client singleton
9-
const globalForPrisma = globalThis as unknown as {
10-
prisma: PrismaClient | undefined;
11-
};
12-
13-
const prisma = globalForPrisma.prisma ?? new PrismaClient();
14-
15-
if (process.env.NODE_ENV !== "production") {
16-
globalForPrisma.prisma = prisma;
17-
}
4+
import { getContext } from "@/lib/context";
185

196
// User-defined wrapper function for server actions
207
async function serverAction(props: ServerActionInput) {
218
"use server";
22-
const session = config.session ? await config.session.getSession() : null;
23-
const context = await getContext(config, prisma, session);
9+
const context = await getContext();
2410
return await context.serverAction(props);
2511
}
2612

@@ -36,11 +22,11 @@ interface AdminPageProps {
3622
export default async function AdminPage({ params, searchParams }: AdminPageProps) {
3723
const resolvedParams = await params;
3824
const resolvedSearchParams = await searchParams;
39-
const adminContext = await getAdminContext(config, prisma);
4025

4126
return (
4227
<AdminUI
43-
context={adminContext}
28+
context={await getContext()}
29+
config={config}
4430
params={resolvedParams.admin}
4531
searchParams={resolvedSearchParams}
4632
basePath="/admin"

examples/custom-field/app/admin/[[...admin]]/page.tsx

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,12 @@
11
import { AdminUI } from "@opensaas/ui";
2-
import { getAdminContext } from "@opensaas/ui/server";
32
import type { ServerActionInput } from "@opensaas/ui/server";
4-
import { getContext } from "@opensaas/core";
5-
import { PrismaClient } from "@prisma/client";
63
import config from "../../../opensaas.config";
7-
8-
// Create Prisma client singleton
9-
const globalForPrisma = globalThis as unknown as {
10-
prisma: PrismaClient | undefined;
11-
};
12-
13-
const prisma = globalForPrisma.prisma ?? new PrismaClient();
14-
15-
if (process.env.NODE_ENV !== "production") {
16-
globalForPrisma.prisma = prisma;
17-
}
4+
import { getContext } from "@/lib/context";
185

196
// User-defined wrapper function for server actions
207
async function serverAction(props: ServerActionInput) {
218
"use server";
22-
const session = config.session ? await config.session.getSession() : null;
23-
const context = await getContext(config, prisma, session);
9+
const context = await getContext();
2410
return await context.serverAction(props);
2511
}
2612

@@ -36,11 +22,12 @@ interface AdminPageProps {
3622
export default async function AdminPage({ params, searchParams }: AdminPageProps) {
3723
const resolvedParams = await params;
3824
const resolvedSearchParams = await searchParams;
39-
const adminContext = await getAdminContext(config, prisma);
25+
const adminContext = await getContext();
4026

4127
return (
4228
<AdminUI
4329
context={adminContext}
30+
config={config}
4431
params={resolvedParams.admin}
4532
searchParams={resolvedSearchParams}
4633
basePath="/admin"

packages/ui/src/components/AdminUI.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { Navigation } from "./Navigation.js";
33
import { Dashboard } from "./Dashboard.js";
44
import { ListView } from "./ListView.js";
55
import { ItemForm } from "./ItemForm.js";
6-
import type { AdminContext, ServerActionInput } from "../server/types.js";
7-
import { getListKeyFromUrl } from "@opensaas/core";
6+
import type { ServerActionInput } from "../server/types.js";
7+
import { AccessContext, getListKeyFromUrl, OpenSaaSConfig } from "@opensaas/core";
88

9-
export interface AdminUIProps {
10-
context: AdminContext;
9+
export interface AdminUIProps<TPrisma> {
10+
context: AccessContext<TPrisma>;
11+
config: OpenSaaSConfig;
1112
params?: string[];
1213
searchParams?: { [key: string]: string | string[] | undefined };
1314
basePath?: string;
@@ -25,13 +26,14 @@ export interface AdminUIProps {
2526
* - [list, 'create'] → ItemForm (create)
2627
* - [list, id] → ItemForm (edit)
2728
*/
28-
export function AdminUI({
29+
export function AdminUI<TPrisma>({
2930
context,
31+
config,
3032
params = [],
3133
searchParams = {},
3234
basePath = "/admin",
3335
serverAction,
34-
}: AdminUIProps) {
36+
}: AdminUIProps<TPrisma>) {
3537
// Parse route from params
3638
const [urlSegment, action] = params;
3739

@@ -46,12 +48,13 @@ export function AdminUI({
4648

4749
if (!listKey) {
4850
// Dashboard
49-
content = <Dashboard context={context} basePath={basePath} />;
51+
content = <Dashboard context={context} config={config} basePath={basePath} />;
5052
} else if (action === "create") {
5153
// Create form
5254
content = (
5355
<ItemForm
5456
context={context}
57+
config={config}
5558
listKey={listKey}
5659
mode="create"
5760
basePath={basePath}
@@ -63,6 +66,7 @@ export function AdminUI({
6366
content = (
6467
<ItemForm
6568
context={context}
69+
config={config}
6670
listKey={listKey}
6771
mode="edit"
6872
itemId={action}
@@ -78,6 +82,7 @@ export function AdminUI({
7882
content = (
7983
<ListView
8084
context={context}
85+
config={config}
8186
listKey={listKey}
8287
basePath={basePath}
8388
search={search}
@@ -88,7 +93,7 @@ export function AdminUI({
8893

8994
return (
9095
<div className="flex min-h-screen bg-background">
91-
<Navigation context={context} basePath={basePath} currentPath={currentPath} />
96+
<Navigation context={context} config={config} basePath={basePath} currentPath={currentPath} />
9297
<main className="flex-1 overflow-y-auto">{content}</main>
9398
</div>
9499
);

packages/ui/src/components/Dashboard.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
import Link from "next/link";
22
import { formatListName } from "../lib/utils.js";
3-
import type { AdminContext } from "../server/types.js";
4-
import { getDbKey, getUrlKey } from "@opensaas/core";
3+
import { AccessContext, getDbKey, getUrlKey, OpenSaaSConfig } from "@opensaas/core";
54
import { Card, CardContent, CardHeader, CardTitle } from "../primitives/card.js";
65

7-
export interface DashboardProps {
8-
context: AdminContext;
6+
export interface DashboardProps<TPrisma> {
7+
context: AccessContext<TPrisma>;
8+
config: OpenSaaSConfig;
99
basePath?: string;
1010
}
1111

1212
/**
1313
* Dashboard landing page showing all available lists
1414
* Server Component
1515
*/
16-
export async function Dashboard({
16+
export async function Dashboard<TPrisma>({
1717
context,
18+
config,
1819
basePath = "/admin",
19-
}: DashboardProps) {
20-
const lists = Object.keys(context.config.lists || {});
20+
}: DashboardProps<TPrisma>) {
21+
const lists = Object.keys(config.lists || {});
2122

2223
// Get counts for each list
2324
const listCounts = await Promise.all(
2425
lists.map(async (listKey) => {
2526
try {
26-
const dbContext = context.context as any;
27-
const count = await dbContext.db[getDbKey(listKey)]?.count();
27+
const count = await context.db[getDbKey(listKey)]?.count();
2828
return { listKey, count: count || 0 };
2929
} catch (error) {
3030
console.error(`Failed to get count for ${listKey}:`, error);
@@ -43,8 +43,7 @@ export async function Dashboard({
4343
{lists.length === 0 ? (
4444
<Card className="p-12 text-center">
4545
<p className="text-muted-foreground">
46-
No lists configured. Add lists to your opensaas.config.ts to get
47-
started.
46+
No lists configured. Add lists to your opensaas.config.ts to get started.
4847
</p>
4948
</Card>
5049
) : (

packages/ui/src/components/ItemForm.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import * as React from "react";
22
import Link from "next/link";
33
import { ItemFormClient } from "./ItemFormClient.js";
44
import { formatListName } from "../lib/utils.js";
5-
import type { AdminContext, ServerActionInput } from "../server/types.js";
6-
import { getDbKey, getUrlKey } from "@opensaas/core";
5+
import type { ServerActionInput } from "../server/types.js";
6+
import { AccessContext, getDbKey, getUrlKey, OpenSaaSConfig } from "@opensaas/core";
77

8-
export interface ItemFormProps {
9-
context: AdminContext;
8+
export interface ItemFormProps<TPrisma> {
9+
context: AccessContext<TPrisma>;
10+
config: OpenSaaSConfig;
1011
listKey: string;
1112
mode: "create" | "edit";
1213
itemId?: string;
@@ -19,15 +20,16 @@ export interface ItemFormProps {
1920
* Item form component - create or edit an item
2021
* Server Component that fetches data and sets up actions
2122
*/
22-
export async function ItemForm({
23+
export async function ItemForm<TPrisma>({
2324
context,
25+
config,
2426
listKey,
2527
mode,
2628
itemId,
2729
basePath = "/admin",
2830
serverAction,
29-
}: ItemFormProps) {
30-
const listConfig = context.config.lists[listKey];
31+
}: ItemFormProps<TPrisma>) {
32+
const listConfig = config.lists[listKey];
3133
const urlKey = getUrlKey(listKey);
3234

3335
if (!listConfig) {
@@ -90,12 +92,12 @@ export async function ItemForm({
9092
if (ref) {
9193
// Parse ref format: "ListName.fieldName"
9294
const relatedListName = ref.split(".")[0];
93-
const relatedListConfig = context.config.lists[relatedListName];
95+
const relatedListConfig = config.lists[relatedListName];
9496

9597
if (relatedListConfig) {
9698
try {
97-
const dbContext = context.context as any;
98-
const relatedItems = await dbContext.db[getDbKey(relatedListName)].findMany({});
99+
const dbContext = context.db;
100+
const relatedItems = await dbContext[getDbKey(relatedListName)].findMany({});
99101

100102
// Use 'name' field as label if it exists, otherwise use 'id'
101103
relationshipData[fieldName] = relatedItems.map((item: any) => ({

packages/ui/src/components/ListView.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import Link from "next/link";
22
import { ListViewClient } from "./ListViewClient.js";
33
import { formatListName } from "../lib/utils.js";
4-
import type { AdminContext } from "../server/types.js";
5-
import { getDbKey, getUrlKey, type PrismaClientLike } from "@opensaas/core";
4+
import {
5+
AccessContext,
6+
getDbKey,
7+
getUrlKey,
8+
OpenSaaSConfig,
9+
type PrismaClientLike,
10+
} from "@opensaas/core";
611

712
export interface ListViewProps<TPrisma extends PrismaClientLike = PrismaClientLike> {
8-
context: AdminContext<TPrisma>;
13+
context: AccessContext<TPrisma>;
14+
config: OpenSaaSConfig;
915
listKey: string;
1016
basePath?: string;
1117
columns?: string[];
@@ -20,6 +26,7 @@ export interface ListViewProps<TPrisma extends PrismaClientLike = PrismaClientLi
2026
*/
2127
export async function ListView<TPrisma extends PrismaClientLike = PrismaClientLike>({
2228
context,
29+
config,
2330
listKey,
2431
basePath = "/admin",
2532
columns,
@@ -29,7 +36,7 @@ export async function ListView<TPrisma extends PrismaClientLike = PrismaClientLi
2936
}: ListViewProps<TPrisma>) {
3037
const key = getDbKey(listKey);
3138
const urlKey = getUrlKey(listKey);
32-
const listConfig = context.config.lists[listKey];
39+
const listConfig = config.lists[listKey];
3340

3441
if (!listConfig) {
3542
return (
@@ -48,7 +55,7 @@ export async function ListView<TPrisma extends PrismaClientLike = PrismaClientLi
4855
let total = 0;
4956

5057
try {
51-
const dbContext = context.context.db;
58+
const dbContext = context.db;
5259
if (!dbContext || !dbContext[key]) {
5360
throw new Error(`Context for ${listKey} not found`);
5461
}

packages/ui/src/components/Navigation.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import Link from "next/link";
22
import { formatListName } from "../lib/utils.js";
3-
import type { AdminContext } from "../server/types.js";
4-
import { getUrlKey } from "@opensaas/core";
3+
import { AccessContext, getUrlKey, OpenSaaSConfig } from "@opensaas/core";
54

6-
export interface NavigationProps {
7-
context: AdminContext;
5+
export interface NavigationProps<TPrisma> {
6+
context: AccessContext<TPrisma>;
7+
config: OpenSaaSConfig;
88
basePath?: string;
99
currentPath?: string;
1010
}
@@ -13,12 +13,13 @@ export interface NavigationProps {
1313
* Navigation sidebar showing all lists
1414
* Server Component
1515
*/
16-
export function Navigation({
16+
export function Navigation<TPrisma>({
1717
context,
18+
config,
1819
basePath = "/admin",
1920
currentPath = "",
20-
}: NavigationProps) {
21-
const lists = Object.keys(context.config.lists || {});
21+
}: NavigationProps<TPrisma>) {
22+
const lists = Object.keys(config.lists || {});
2223

2324
return (
2425
<nav className="w-64 border-r border-border bg-card h-screen sticky top-0 flex flex-col">
@@ -78,7 +79,9 @@ export function Navigation({
7879
<div className="flex items-center space-x-3">
7980
<div className="h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center">
8081
<span className="text-sm font-medium">
81-
{String((context.session.data as Record<string, unknown>)?.name)?.[0]?.toUpperCase() || "?"}
82+
{String(
83+
(context.session.data as Record<string, unknown>)?.name,
84+
)?.[0]?.toUpperCase() || "?"}
8285
</span>
8386
</div>
8487
<div className="flex-1 min-w-0">

packages/ui/src/server/getAdminContext.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)