Skip to content

Commit 98e7293

Browse files
committed
feat: add hooks for ObjectStack client and organization management
- Introduced `useObjectStackClient` hook to initialize ObjectStackClient with the configured API base URL. - Added `useOrganizationMembers` and `useOrganizationInvitations` hooks for managing organization members and invitations. - Implemented `useSession` and related hooks for session and organization management, including user authentication and organization switching. - Created new routes for authentication and organization management, including device authorization. - Established a responsive layout with Tailwind CSS for the application. - Configured Vite for development and production environments, including HMR and API proxy settings. - Added TypeScript configuration for improved type safety and module resolution.
1 parent 1678089 commit 98e7293

63 files changed

Lines changed: 4230 additions & 37 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
99
---
1010

11+
## Important
12+
13+
not make big edits of more than ~20000 bytes or so. If it fails, break one edit down to multiple smaller ones.
14+
1115
## Quick Reference — Build & Test Commands
1216

1317
```bash

apps/account/index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<link rel="preconnect" href="https://fonts.googleapis.com">
7+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
8+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet">
9+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
10+
<title>ObjectStack Account</title>
11+
</head>
12+
<body>
13+
<div id="root"></div>
14+
<script type="module" src="/src/main.tsx"></script>
15+
</body>
16+
</html>

apps/account/package.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"name": "@objectstack/account",
3+
"version": "0.1.0",
4+
"description": "ObjectStack Account - End-user account & organization self-service portal",
5+
"license": "Apache-2.0",
6+
"private": true,
7+
"type": "module",
8+
"files": [
9+
"dist"
10+
],
11+
"scripts": {
12+
"dev": "vite",
13+
"build": "vite build",
14+
"typecheck": "tsc --noEmit",
15+
"test": "vitest run",
16+
"test:watch": "vitest",
17+
"preview": "vite preview"
18+
},
19+
"dependencies": {
20+
"@objectstack/client": "workspace:*",
21+
"@objectstack/client-react": "workspace:*",
22+
"@objectstack/spec": "workspace:*",
23+
"@radix-ui/react-avatar": "^1.1.11",
24+
"@radix-ui/react-checkbox": "^1.3.3",
25+
"@radix-ui/react-collapsible": "^1.1.12",
26+
"@radix-ui/react-dialog": "^1.1.15",
27+
"@radix-ui/react-dropdown-menu": "^2.1.16",
28+
"@radix-ui/react-label": "^2.1.8",
29+
"@radix-ui/react-scroll-area": "^1.2.10",
30+
"@radix-ui/react-select": "^2.2.6",
31+
"@radix-ui/react-separator": "^1.1.8",
32+
"@radix-ui/react-slot": "^1.2.4",
33+
"@radix-ui/react-switch": "^1.2.6",
34+
"@radix-ui/react-tabs": "^1.1.13",
35+
"@radix-ui/react-toast": "^1.2.15",
36+
"@radix-ui/react-tooltip": "^1.2.8",
37+
"@tanstack/react-router": "^1.168.23",
38+
"class-variance-authority": "^0.7.1",
39+
"clsx": "^2.1.1",
40+
"lucide-react": "^1.8.0",
41+
"react": "^19.2.5",
42+
"react-dom": "^19.2.5",
43+
"tailwind-merge": "^3.5.0"
44+
},
45+
"devDependencies": {
46+
"@tailwindcss/postcss": "^4.2.2",
47+
"@tanstack/router-plugin": "^1.91.5",
48+
"@testing-library/jest-dom": "^6.6.3",
49+
"@testing-library/react": "^16.1.0",
50+
"@testing-library/user-event": "^14.5.2",
51+
"@types/react": "^19.2.14",
52+
"@types/react-dom": "^19.2.3",
53+
"@vitejs/plugin-react": "^6.0.1",
54+
"@vitest/coverage-v8": "^4.1.4",
55+
"autoprefixer": "^10.4.27",
56+
"happy-dom": "^20.8.9",
57+
"postcss": "^8.5.10",
58+
"tailwindcss": "^4.2.2",
59+
"typescript": "^6.0.3",
60+
"vite": "^8.0.9",
61+
"vitest": "^4.1.4"
62+
}
63+
}

apps/account/postcss.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
'@tailwindcss/postcss': {},
4+
autoprefixer: {},
5+
},
6+
}

apps/account/src/App.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* App Component
5+
*
6+
* Main application wrapper that provides the TanStack Router instance.
7+
*/
8+
9+
import { RouterProvider } from '@tanstack/react-router';
10+
import { router } from './router';
11+
12+
export default function App() {
13+
return <RouterProvider router={router} />;
14+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import { useEffect, useState } from 'react';
4+
import { useClient } from '@objectstack/client-react';
5+
import { Button } from '@/components/ui/button';
6+
7+
interface SocialProvider {
8+
id: string;
9+
name: string;
10+
enabled: boolean;
11+
type?: 'social' | 'oidc';
12+
}
13+
14+
interface Props {
15+
mode: 'sign-in' | 'sign-up';
16+
}
17+
18+
export function SocialSignInButtons({ mode }: Props) {
19+
const client = useClient() as any;
20+
const [providers, setProviders] = useState<SocialProvider[]>([]);
21+
const [loading, setLoading] = useState(true);
22+
23+
useEffect(() => {
24+
if (!client?.auth?.getConfig) return;
25+
client.auth.getConfig()
26+
.then((res: any) => {
27+
const list: SocialProvider[] = res?.socialProviders ?? res?.data?.socialProviders ?? [];
28+
setProviders(list.filter((p) => p.enabled));
29+
})
30+
.catch((err: unknown) => {
31+
console.warn('[SocialSignInButtons] failed to load auth config', err);
32+
})
33+
.finally(() => setLoading(false));
34+
}, [client]);
35+
36+
if (loading || providers.length === 0) return null;
37+
38+
const label = mode === 'sign-in' ? 'Continue with' : 'Sign up with';
39+
40+
return (
41+
<div className="flex flex-col gap-2">
42+
{providers.map((p) => (
43+
<Button
44+
key={p.id}
45+
type="button"
46+
variant="outline"
47+
className="w-full"
48+
onClick={() =>
49+
client.auth.signInWithProvider(p.id, {
50+
callbackURL: window.location.origin + import.meta.env.BASE_URL + 'login',
51+
errorCallbackURL: window.location.origin + import.meta.env.BASE_URL + 'login',
52+
type: p.type ?? 'social',
53+
})
54+
}
55+
>
56+
{/* TODO: replace with provider icon from lucide-react or simple-icons */}
57+
<span className="mr-2 flex h-4 w-4 items-center justify-center rounded-sm bg-muted text-[10px] font-bold uppercase">
58+
{p.id[0]}
59+
</span>
60+
{label} {p.name}
61+
</Button>
62+
))}
63+
<div className="relative my-1">
64+
<div className="absolute inset-0 flex items-center">
65+
<span className="w-full border-t" />
66+
</div>
67+
<div className="relative flex justify-center text-xs uppercase">
68+
<span className="bg-card px-2 text-muted-foreground">or continue with email</span>
69+
</div>
70+
</div>
71+
</div>
72+
);
73+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
"use client"
4+
5+
import * as React from "react"
6+
import * as AvatarPrimitive from "@radix-ui/react-avatar"
7+
8+
import { cn } from "@/lib/utils"
9+
10+
const Avatar = React.forwardRef<
11+
React.ElementRef<typeof AvatarPrimitive.Root>,
12+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
13+
>(({ className, ...props }, ref) => (
14+
<AvatarPrimitive.Root
15+
ref={ref}
16+
className={cn(
17+
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
18+
className
19+
)}
20+
{...props}
21+
/>
22+
))
23+
Avatar.displayName = AvatarPrimitive.Root.displayName
24+
25+
const AvatarImage = React.forwardRef<
26+
React.ElementRef<typeof AvatarPrimitive.Image>,
27+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
28+
>(({ className, ...props }, ref) => (
29+
<AvatarPrimitive.Image
30+
ref={ref}
31+
className={cn("aspect-square h-full w-full", className)}
32+
{...props}
33+
/>
34+
))
35+
AvatarImage.displayName = AvatarPrimitive.Image.displayName
36+
37+
const AvatarFallback = React.forwardRef<
38+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
39+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
40+
>(({ className, ...props }, ref) => (
41+
<AvatarPrimitive.Fallback
42+
ref={ref}
43+
className={cn(
44+
"flex h-full w-full items-center justify-center rounded-full bg-muted",
45+
className
46+
)}
47+
{...props}
48+
/>
49+
))
50+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
51+
52+
export { Avatar, AvatarImage, AvatarFallback }
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import * as React from "react"
4+
import { cva, type VariantProps } from "class-variance-authority"
5+
6+
import { cn } from "@/lib/utils"
7+
8+
const badgeVariants = cva(
9+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
10+
{
11+
variants: {
12+
variant: {
13+
default:
14+
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
15+
secondary:
16+
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
17+
destructive:
18+
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
19+
outline: "text-foreground",
20+
},
21+
},
22+
defaultVariants: {
23+
variant: "default",
24+
},
25+
}
26+
)
27+
28+
export interface BadgeProps
29+
extends React.HTMLAttributes<HTMLDivElement>,
30+
VariantProps<typeof badgeVariants> {}
31+
32+
function Badge({ className, variant, ...props }: BadgeProps) {
33+
return (
34+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
35+
)
36+
}
37+
38+
export { Badge, badgeVariants }

0 commit comments

Comments
 (0)