Skip to content

Commit 874908b

Browse files
committed
Initial commit
1 parent 86cf1c5 commit 874908b

36 files changed

+1312
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
27+
# local env files
28+
.env*.local
29+
30+
# vercel
31+
.vercel
32+
33+
# typescript
34+
*.tsbuildinfo
35+
next-env.d.ts
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public-hoist-pattern[]=*
2+
public-hoist-pattern[]=!tailwindcss
3+
shamefully-hoist=false
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# marketing-spend-demo
2+
3+
## Overview
4+
5+
This demo showcases **AgGrid** (Library) for **tables** in **react**.
6+
7+
## Path
8+
9+
```
10+
apps/react/tables/libraries/AgGrid/marketing-spend-demo/
11+
```
12+
13+
## Package Name
14+
15+
`@apps/react-tables-AgGrid-marketing-spend-demo`
16+
17+
## Directory Structure
18+
19+
```
20+
marketing-spend-demo/
21+
├── app/
22+
│ ├── layout.tsx # Root layout
23+
│ └── page.tsx # Main page
24+
├── components/
25+
│ ├── header/ # Header components (Velt notifications, etc.)
26+
│ │ └── header.tsx
27+
│ ├── sidebar/ # Sidebar components
28+
│ │ └── sidebar.tsx
29+
│ └── document/ # Main document/canvas logic
30+
│ └── document-canvas.tsx
31+
├── hooks/ # Custom React hooks
32+
├── lib/ # Utility functions
33+
│ └── utils.ts
34+
├── public/ # Static assets
35+
├── styles/ # Global styles
36+
│ └── globals.css
37+
├── .npmrc # pnpm config to prevent Tailwind v4 hoisting
38+
├── next.config.js
39+
├── tailwind.config.js
40+
├── tsconfig.json
41+
├── components.json # shadcn/ui configuration
42+
└── package.json
43+
```
44+
45+
## Getting Started
46+
47+
### Install Dependencies
48+
49+
From the monorepo root:
50+
51+
```bash
52+
pnpm -w install
53+
```
54+
55+
### Run Development Server
56+
57+
```bash
58+
cd apps/react/tables/libraries/AgGrid/marketing-spend-demo
59+
pnpm dev
60+
```
61+
62+
Or from the root:
63+
64+
```bash
65+
pnpm --filter @apps/react-tables-AgGrid-marketing-spend-demo dev
66+
```
67+
68+
### Build for Production
69+
70+
```bash
71+
pnpm --filter @apps/react-tables-AgGrid-marketing-spend-demo build
72+
```
73+
74+
## Structure
75+
76+
- **Framework**: react
77+
- **Document**: tables
78+
- **Implementation**: libraries
79+
- **Library/Solution**: AgGrid
80+
- **Demo**: marketing-spend-demo
81+
82+
## Component Organization
83+
84+
- **`components/header/`** - Contains Velt components like notifications, presence indicators, header buttons
85+
- **`components/sidebar/`** - Contains sidebar-related components
86+
- **`components/document/`** - Contains the main application logic and AgGrid integration
87+
- **`hooks/`** - Custom React hooks for state management and side effects
88+
- **`lib/`** - Utility functions and helpers
89+
90+
## Important Configuration
91+
92+
### .npmrc File
93+
This demo includes a `.npmrc` file that prevents pnpm from hoisting Tailwind CSS v4 from other workspace packages. This is necessary because:
94+
- This demo uses Tailwind CSS v3.4.x with traditional PostCSS configuration
95+
- Other apps in the monorepo may use Tailwind CSS v4
96+
- Without the `.npmrc`, pnpm would hoist v4 and cause PostCSS errors
97+
98+
**Do not delete the `.npmrc` file** - it ensures the correct Tailwind version is used.
99+
100+
## Next Steps
101+
102+
1. Add your AgGrid implementation in `components/document/`
103+
2. Add Velt collaboration features in `components/header/`
104+
3. Update this README with specific usage instructions
105+
4. Add the demo to `master-sample-app` if it should be showcased
106+
5. Update deployment configs (Vercel, GitHub Actions) if needed
107+
108+
## Learn More
109+
110+
- [Monorepo Structure Guide](../../../../README_MONOREPO.md)
111+
- [Structure Documentation](../../../../docs/structure.md)
112+
- [Velt Documentation](https://docs.velt.dev)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
3+
export async function POST(req: NextRequest) {
4+
try {
5+
const { userId, organizationId, email, isAdmin } = await req.json();
6+
if (!userId || !organizationId) {
7+
return NextResponse.json({ error: 'Missing userId or organizationId' }, { status: 400 });
8+
}
9+
if (!process.env.VELT_AUTH_TOKEN) {
10+
return NextResponse.json({ error: 'Server configuration error: missing VELT_AUTH_TOKEN' }, { status: 500 });
11+
}
12+
const body = {
13+
data: {
14+
userId,
15+
userProperties: {
16+
organizationId,
17+
...(typeof isAdmin === 'boolean' ? { isAdmin } : {}),
18+
...(email ? { email } : {}),
19+
},
20+
},
21+
};
22+
const res = await fetch('https://api.velt.dev/v2/auth/token/get', {
23+
method: 'POST',
24+
headers: { 'Content-Type': 'application/json', 'x-velt-api-key': process.env.NEXT_PUBLIC_VELT_API_KEY!, 'x-velt-auth-token': process.env.VELT_AUTH_TOKEN! },
25+
body: JSON.stringify(body),
26+
});
27+
const json = await res.json();
28+
const token = json?.result?.data?.token;
29+
if (!res.ok || !token) {
30+
return NextResponse.json({ error: json?.error?.message || 'Failed to generate token' }, { status: 500 });
31+
}
32+
return NextResponse.json({ token });
33+
} catch {
34+
return NextResponse.json({ error: 'Internal error' }, { status: 500 });
35+
}
36+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'use client';
2+
import { useMemo, useEffect, useState, useRef } from 'react';
3+
4+
// [Velt] Minimal hard-coded current document hook
5+
export type CurrentDocument = {
6+
documentId: string;
7+
documentName: string;
8+
};
9+
10+
export function useCurrentDocument(): CurrentDocument {
11+
const [documentId, setDocumentId] = useState<string>('');
12+
const isInitialized = useRef(false);
13+
14+
useEffect(() => {
15+
// Prevent double initialization (React Strict Mode, HMR, etc.)
16+
if (isInitialized.current) return;
17+
18+
// 1. Check URL for documentId parameter first
19+
const urlParams = new URLSearchParams(window.location.search);
20+
let docId = urlParams.get('documentId');
21+
22+
if (docId) {
23+
// Use document ID from URL (shareable link)
24+
setDocumentId(docId);
25+
localStorage.setItem('reactflow-document-id', docId);
26+
} else {
27+
// 2. Check localStorage for existing document
28+
const stored = localStorage.getItem('reactflow-document-id');
29+
if (stored) {
30+
docId = stored;
31+
} else {
32+
// 3. Generate new document ID
33+
docId = `doc-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
34+
localStorage.setItem('reactflow-document-id', docId);
35+
}
36+
37+
// Update URL with document ID for shareability
38+
const newUrl = `${window.location.pathname}?documentId=${docId}`;
39+
window.history.pushState({}, '', newUrl);
40+
41+
setDocumentId(docId);
42+
}
43+
44+
isInitialized.current = true;
45+
}, []);
46+
47+
return useMemo(
48+
() => ({
49+
documentId: documentId || 'loading',
50+
documentName: "React Flow Canvas",
51+
}),
52+
[documentId]
53+
);
54+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
'use client';
2+
3+
export { useCurrentDocument } from './DocumentContext';
4+
export type { CurrentDocument } from './DocumentContext';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { Metadata } from 'next'
2+
import '../styles/globals.css'
3+
4+
export const metadata: Metadata = {
5+
title: 'marketing-spend-demo',
6+
description: 'Library demo for AgGrid',
7+
}
8+
9+
export default function RootLayout({
10+
children,
11+
}: {
12+
children: React.ReactNode
13+
}) {
14+
return (
15+
<html lang="en">
16+
<body>{children}</body>
17+
</html>
18+
)
19+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import DocumentCanvas from '@/components/document/document-canvas'
2+
3+
export default function Home() {
4+
return (
5+
<main className="flex h-screen w-screen">
6+
<DocumentCanvas />
7+
</main>
8+
)
9+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"use client";
2+
3+
import React from "react";
4+
import { AppUserProvider } from "./AppUserContext";
5+
import { useCurrentDocument } from "@/app/document/DocumentContext";
6+
7+
/**
8+
* Client component that connects DocumentContext to AppUserProvider
9+
* This ensures users are scoped per document-id
10+
*/
11+
export function AppProviders({ children }: { children: React.ReactNode }) {
12+
const { documentId } = useCurrentDocument();
13+
14+
return (
15+
<AppUserProvider documentId={documentId}>
16+
{children}
17+
</AppUserProvider>
18+
);
19+
}

0 commit comments

Comments
 (0)