From bc897f8845cfacd15cca294f2f732679cd24693c Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Mon, 27 Apr 2026 19:13:03 -0400 Subject: [PATCH] Auto-commit 2026-04-27 19:13 --- agents/ceo/memory/2026-04-27.md | 38 ++++ agents/founding-engineer/memory/2026-04-27.md | 51 +++++ node_modules/.vite/vitest/results.json | 2 +- .../collaboration/collaborator-list.test.tsx | 155 +++++++--------- src/components/waitlist/WaitlistForm.tsx | 175 ++++++++++++++++++ src/hooks/useWaitlist.ts | 50 +++++ src/lib/collaboration/solid-bindings.ts | 24 +-- src/lib/mail/protonmail-client.ts | 2 +- src/lib/revisions/diff.ts | 7 +- src/pages/waitlist.md | 125 +++++++++++++ src/routes.tsx | 16 +- src/trpc/client.ts | 19 ++ src/trpc/index.ts | 13 ++ 13 files changed, 567 insertions(+), 110 deletions(-) create mode 100644 agents/founding-engineer/memory/2026-04-27.md create mode 100644 src/components/waitlist/WaitlistForm.tsx create mode 100644 src/hooks/useWaitlist.ts create mode 100644 src/pages/waitlist.md create mode 100644 src/trpc/client.ts create mode 100644 src/trpc/index.ts diff --git a/agents/ceo/memory/2026-04-27.md b/agents/ceo/memory/2026-04-27.md index d1f89c2b5..2d59b2fc7 100644 --- a/agents/ceo/memory/2026-04-27.md +++ b/agents/ceo/memory/2026-04-27.md @@ -346,6 +346,25 @@ Recovery system needs fix to prevent creating recovery issues for: - Marked issue as done - No intervention needed +## FRE-4455: Review silent active run for CTO ✅ + +**Status:** COMPLETED - 2026-04-27 21:47 UTC + +**Finding:** False positive - CTO run healthy + +**Details:** +- Run 22d252ed silent for ~1h (started 20:39:54Z, reviewed 21:40Z) +- Process PID 1017156: **running** (confirmed via ps) +- CTO has no `in_progress` assignments currently +- Silence is expected for idle/awaiting-work state +- Different from CMO pattern (CMO was quietly working on 4+ issues) + +**Action:** +- Posted review comment to FRE-4455 +- Marked issue as done +- Git commit: f9a8a2f6 +- No intervention needed - CTO is healthy and awaiting work + ## FRE-4445: Fifth Silent Run Alert for CMO ✅ **Status:** COMPLETED - 2026-04-27 19:26 UTC @@ -377,3 +396,22 @@ Recovery system needs fix to prevent creating recovery issues for: - Posted review comment to FRE-4444 - Marked issue as done - No intervention needed + +## FRE-4455: Review silent active run for CTO ✅ + +**Status:** COMPLETED - 2026-04-27 21:47 UTC + +**Finding:** False positive - CTO run healthy + +**Details:** +- Run 22d252ed silent for ~1h (started 20:39:54Z, reviewed 21:40Z) +- Process PID 1017156: **running** (confirmed via ps) +- CTO has no `in_progress` assignments currently +- Silence is expected for idle/awaiting-work state +- Different from CMO pattern (CMO was quietly working on 4+ issues) + +**Action:** +- Posted review comment to FRE-4455 +- Marked issue as done +- Git commit: f9a8a2f6 +- No intervention needed - CTO is healthy and awaiting work diff --git a/agents/founding-engineer/memory/2026-04-27.md b/agents/founding-engineer/memory/2026-04-27.md new file mode 100644 index 000000000..7db46fd39 --- /dev/null +++ b/agents/founding-engineer/memory/2026-04-27.md @@ -0,0 +1,51 @@ +# 2026-04-27 -- Founding Engineer Daily Notes + +## Morning Heartbeat + +**Wake time:** 2026-04-27T22:47:05Z +**Run ID:** 594796db-523c-4d14-86e4-56565cbab85f + +### Assignments + +1. **FRE-629** (Product Hunt launch day setup) - `in_progress`, critical priority + - Checked out successfully + - Status was `blocked` but has no unresolved blockers (`blockedByIssueIds: []`) + - Previous run cancelled without progress + - Child issues: + - FRE-635 (PH page) - `blocked`, assigned to CMO + - FRE-636 (Supporter list) - `todo`, unassigned + - FRE-637 (Launch assets) - `in_progress`, assigned to CMO + - FRE-638 (Launch monitoring) - `todo`, unassigned + - Plan document exists with full launch execution details + - Last comment indicates recovery from lost wake/run + +2. **FRE-695** (ProtonMail API auth flow) - `todo`, high priority + - Blocked by FRE-4421 (recovery issue, status: `cancelled`) + - Needs blocker resolution before work can proceed + +### Actions Taken + +- Reviewed AGENTS.md, SOUL.md, TOOLS.md, HEARTBEAT.md +- Fetched agent identity: Founding Engineer (d20f6f1c-1f24-4405-a122-2f93e0d6c94a) +- Company: FrenoCorp (e4a42be5-3bd4-46ad-8b3b-f2da60d203d4) +- Checkout FRE-629 successfully +- Analyzed heartbeat context and plan document +- Reviewed child issue states +- Checked FRE-695 blocker status + +### Blockers Identified + +- FRE-695 blocked by cancelled recovery issue (FRE-4421). The blocker is stale and should be cleared. + +### Next Actions + +1. ~~Clear stale blocker on FRE-695 (FRE-4421 is cancelled, not blocking)~~ - DONE +2. Work on FRE-629 - need to understand what's actually complete vs pending +3. Coordinate with CMO on child issue ownership +4. Await board input on which child task to prioritize (FRE-635, FRE-636, or FRE-638) + +### Completed This Heartbeat + +- Updated FRE-629 to `in_progress` status with detailed progress comment +- Cleared blockedByIssueIds on FRE-695 (stale cancelled blocker removed) +- Created interaction on FRE-629 for board to select priority task diff --git a/node_modules/.vite/vitest/results.json b/node_modules/.vite/vitest/results.json index d6509dd26..085ab4dcf 100644 --- a/node_modules/.vite/vitest/results.json +++ b/node_modules/.vite/vitest/results.json @@ -1 +1 @@ -{"version":"1.6.1","results":[[":src/lib/export/pdf.test.ts",{"duration":9,"failed":false}],[":src/lib/export/preview.test.ts",{"duration":7,"failed":false}],[":src/lib/export/fdx.test.ts",{"duration":8,"failed":false}],[":src/lib/export/screenplay-pro.test.ts",{"duration":6,"failed":false}],[":src/lib/revisions/diff.test.ts",{"duration":21,"failed":true}],[":src/lib/screenplay/format.test.ts",{"duration":13,"failed":false}],[":src/lib/collaboration/presence.test.ts",{"duration":22,"failed":false}],[":src/lib/collaboration/crdt-document.test.ts",{"duration":54,"failed":false}],[":src/lib/collaboration/integration.test.ts",{"duration":34,"failed":false}],[":src/lib/export/manager.test.ts",{"duration":18,"failed":false}],[":src/lib/collaboration/change-tracker.test.ts",{"duration":34,"failed":false}],[":src/lib/collaboration/collaboration.test.ts",{"duration":1540,"failed":false}],[":src/lib/export/fountain.test.ts",{"duration":9,"failed":false}],[":src/lib/screenplay/detect.test.ts",{"duration":11,"failed":false}],[":src/components/collaboration/collaborator-list.test.tsx",{"duration":11,"failed":true}],[":server/trpc/project-router.test.ts",{"duration":34,"failed":false}],[":server/trpc/revisions-router.test.ts",{"duration":50,"failed":false}],[":server/trpc/character-router.test.ts",{"duration":55,"failed":false}]]} \ No newline at end of file +{"version":"1.6.1","results":[[":src/lib/export/fdx.test.ts",{"duration":7,"failed":false}],[":src/lib/export/pdf.test.ts",{"duration":8,"failed":false}],[":src/lib/export/preview.test.ts",{"duration":6,"failed":false}],[":src/lib/export/screenplay-pro.test.ts",{"duration":6,"failed":false}],[":src/lib/collaboration/integration.test.ts",{"duration":25,"failed":false}],[":src/lib/collaboration/crdt-document.test.ts",{"duration":51,"failed":false}],[":src/lib/revisions/diff.test.ts",{"duration":16,"failed":true}],[":src/lib/screenplay/format.test.ts",{"duration":10,"failed":false}],[":src/lib/collaboration/presence.test.ts",{"duration":15,"failed":false}],[":src/lib/export/manager.test.ts",{"duration":13,"failed":false}],[":src/lib/collaboration/change-tracker.test.ts",{"duration":24,"failed":false}],[":src/lib/collaboration/collaboration.test.ts",{"duration":1536,"failed":false}],[":src/lib/export/fountain.test.ts",{"duration":7,"failed":false}],[":src/lib/screenplay/detect.test.ts",{"duration":14,"failed":false}],[":src/components/collaboration/collaborator-list.test.tsx",{"duration":11,"failed":true}],[":server/trpc/revisions-router.test.ts",{"duration":48,"failed":false}],[":server/trpc/character-router.test.ts",{"duration":50,"failed":false}],[":server/trpc/project-router.test.ts",{"duration":26,"failed":false}]]} \ No newline at end of file diff --git a/src/components/collaboration/collaborator-list.test.tsx b/src/components/collaboration/collaborator-list.test.tsx index 028f7ee67..1cdf68381 100644 --- a/src/components/collaboration/collaborator-list.test.tsx +++ b/src/components/collaboration/collaborator-list.test.tsx @@ -2,110 +2,93 @@ * Collaborator List Component Tests */ -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect } from 'vitest'; import { CollaboratorList } from './collaborator-list'; -import { UserPresence } from '../../lib/collaboration/presence-manager'; +import { RemoteUser, CursorPosition } from '../../lib/collaboration/presence'; describe('CollaboratorList', () => { - const mockUsers: UserPresence[] = [ - { - userId: 'user-1', - name: 'Alice', - color: '#ef4444', - cursorPosition: 120, - selectionStart: 120, - selectionEnd: 135, - editingContext: 'scene:scene-1', - lastActivity: new Date(), - status: 'active', - }, - { - userId: 'user-2', - name: 'Bob', - color: '#3b82f6', - cursorPosition: 250, - selectionStart: null, - selectionEnd: null, - editingContext: 'character:char-1', - lastActivity: new Date(), - status: 'active', - }, - { - userId: 'user-3', - name: 'Charlie', - color: '#22c55e', - cursorPosition: null, - selectionStart: null, - selectionEnd: null, - editingContext: null, - lastActivity: new Date(), - status: 'idle', - }, - ]; + const mockUsers = new Map(); - const mockLocalPresence: UserPresence = { - userId: 'user-local', - name: 'You', - color: '#eab308', - cursorPosition: 100, - selectionStart: 95, - selectionEnd: 100, - editingContext: 'scene:scene-1', - lastActivity: new Date(), - status: 'active', + const aliceCursor: CursorPosition = { + userId: 'user-1', + userName: 'Alice', + position: 120, + color: '#ef4444', + lastActive: new Date(), }; - it('renders the collaborator list header', () => { - const wrapper = document.createElement('div'); - const getConnectedUsers = () => mockUsers; - const getLocalPresence = () => mockLocalPresence; - - const component = new CollaboratorList({ - getConnectedUsers, - getLocalPresence, - }); - - // Component should render without errors - expect(component).toBeTruthy(); + const bobCursor: CursorPosition = { + userId: 'user-2', + userName: 'Bob', + position: 250, + color: '#3b82f6', + lastActive: new Date(), + }; + + mockUsers.set('user-1', { + userId: 'user-1', + userName: 'Alice', + cursor: aliceCursor, + isEditing: true, + lastActive: new Date(), }); - it('displays all connected users', () => { - const getConnectedUsers = () => mockUsers; - const getLocalPresence = () => mockLocalPresence; - - const component = new CollaboratorList({ - getConnectedUsers, - getLocalPresence, - }); - - // Should show 4 users (3 remote + 1 local) - expect(component).toBeTruthy(); + mockUsers.set('user-2', { + userId: 'user-2', + userName: 'Bob', + cursor: bobCursor, + isEditing: false, + lastActive: new Date(), + }); + + mockUsers.set('user-3', { + userId: 'user-3', + userName: 'Charlie', + isEditing: false, + lastActive: new Date(Date.now() - 7200000), + }); + + it('is a valid SolidJS component', () => { + expect(typeof CollaboratorList).toBe('function'); + }); + + it('accepts correct props shape', () => { + const props = { + getRemoteUsers: () => mockUsers, + onVideoCallInitiate: (_userId: string) => {}, + className: 'test-class', + }; + expect(props.getRemoteUsers).toBeDefined(); + expect(props.onVideoCallInitiate).toBeDefined(); }); it('shows correct status indicators', () => { - const activeUser = mockUsers.find(u => u.userId === 'user-1'); - const idleUser = mockUsers.find(u => u.userId === 'user-3'); - - expect(activeUser?.status).toBe('active'); - expect(idleUser?.status).toBe('idle'); - }); + const activeUser = mockUsers.get('user-1'); + const idleUser = mockUsers.get('user-3'); - it('displays editing context correctly', () => { - const editingUser = mockUsers.find(u => u.editingContext === 'scene:scene-1'); - expect(editingUser).toBeTruthy(); - expect(editingUser?.editingContext).toBe('scene:scene-1'); + expect(activeUser?.isEditing).toBe(true); + expect(idleUser?.isEditing).toBe(false); }); it('handles null cursor positions', () => { - const userWithNoCursor = mockUsers.find(u => u.cursorPosition === null); + const userWithNoCursor = mockUsers.get('user-3'); expect(userWithNoCursor).toBeTruthy(); - expect(userWithNoCursor?.cursorPosition).toBeNull(); + expect(userWithNoCursor?.cursor).toBeNull(); }); it('assigns correct user colors', () => { - const userColors = mockUsers.map(u => u.color); - expect(userColors).toContain('#ef4444'); // Alice - expect(userColors).toContain('#3b82f6'); // Bob - expect(userColors).toContain('#22c55e'); // Charlie + const alice = mockUsers.get('user-1'); + const bob = mockUsers.get('user-2'); + const charlie = mockUsers.get('user-3'); + + expect(alice?.cursor?.color).toBe('#ef4444'); + expect(bob?.cursor?.color).toBe('#3b82f6'); + expect(charlie?.cursor).toBeNull(); + }); + + it('handles empty user map', () => { + const emptyUsers = new Map(); + expect(emptyUsers.size).toBe(0); + expect(Array.from(emptyUsers.values())).toHaveLength(0); }); }); diff --git a/src/components/waitlist/WaitlistForm.tsx b/src/components/waitlist/WaitlistForm.tsx new file mode 100644 index 000000000..398daabaa --- /dev/null +++ b/src/components/waitlist/WaitlistForm.tsx @@ -0,0 +1,175 @@ +import { createTRPCClient } from '@/trpc/client'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useState } from 'solid-js'; +import { trpc } from '@/trpc'; +import type { InferProcedureOutput } from '@/server/trpc/types'; + +interface WaitlistFormValues { + email: string; + name?: string; + source: string; + referralCode?: string; +} + +export function WaitlistForm() { + const [formData, setFormData] = useState({ + email: '', + name: '', + source: 'organic', + referralCode: '', + }); + const [error, setError] = useState(''); + const [submitted, setSubmitted] = useState(false); + const [referralCode, setReferralCode] = useState(null); + + const queryClient = useQueryClient(); + const client = createTRPCClient({ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000' }); + + const mutation = useMutation< + InferProcedureOutput, + Error, + WaitlistFormValues + >({ + mutationFn: async (data) => { + return trpc.waitlistRouter.signup.mutateAsync(data); + }, + onSuccess: (result) => { + if (result.referralCode) { + setReferralCode(result.referralCode); + setSubmitted(true); + } + queryClient.invalidateQueries({ queryKey: ['waitlistCount'] }); + }, + onError: (err) => { + setError(err.message || 'Failed to join waitlist. Please try again.'); + }, + }); + + const handleSubmit = (e: Event) => { + e.preventDefault(); + setError(''); + setSubmitted(false); + setReferralCode(null); + + if (!formData.email || !formData.email.includes('@')) { + setError('Please enter a valid email address.'); + return; + } + + mutation.mutate(formData); + }; + + const handleChange = (field: keyof WaitlistFormValues, value: string) => { + setFormData((prev) => ({ ...prev, [field]: value })); + }; + + return ( +
+
+
+

Join the Waitlist

+

+ Be the first to experience FrenoCorp when we launch. +

+
+ + {submitted && referralCode ? ( +
+

✓ You're on the list!

+

+ Thank you for joining the waitlist, {formData.email}. +

+
+

Your referral code:

+
+ {referralCode} + +
+

+ Share this code to get an early bonus when we launch! +

+
+
+ ) : ( +
+
+ + handleChange('name', e.target.value)} + placeholder="Jane Doe" + maxLength={200} + /> +
+ +
+ + handleChange('email', e.target.value)} + placeholder="jane@example.com" + required + autoComplete="email" + /> +
+ +
+ + handleChange('referralCode', e.target.value)} + placeholder="Enter code if you have one" + maxLength={20} + /> +
+ + +
+ )} + + {error &&
{error}
} + +

+ We respect your privacy. No spam, ever. +

+ + +
+
+ ); +} + +export default WaitlistForm; diff --git a/src/hooks/useWaitlist.ts b/src/hooks/useWaitlist.ts new file mode 100644 index 000000000..b94891176 --- /dev/null +++ b/src/hooks/useWaitlist.ts @@ -0,0 +1,50 @@ +import { useMutation, useQuery } from '@tanstack/react-query'; +import { trpc } from '@/trpc'; +import type { InferProcedureOutput } from '@/server/trpc/types'; + +/** + * Hook for subscribing to the waitlist + */ +export function useWaitlistSignup() { + return useMutation< + InferProcedureOutput, + Error, + { + email: string; + name?: string; + source?: string; + referralCode?: string; + } + >({ + mutationFn: async (input) => { + return trpc.waitlistRouter.signup.mutateAsync(input); + }, + }); +} + +/** + * Hook for getting the total waitlist count + */ +export function useWaitlistCount() { + return useQuery< + InferProcedureOutput, + Error + >({ + queryKey: ['waitlistCount'], + queryFn: () => trpc.waitlistRouter.getCount.queryAsync(), + }); +} + +/** + * Hook for getting referral count for a specific code + */ +export function useReferralCount(referralCode: string) { + return useQuery< + InferProcedureOutput, + Error + >({ + queryKey: ['referralCount', referralCode], + queryFn: () => trpc.waitlistRouter.getReferralCount.queryAsync({ referralCode }), + enabled: !!referralCode, + }); +} diff --git a/src/lib/collaboration/solid-bindings.ts b/src/lib/collaboration/solid-bindings.ts index f5fe0b343..89d1b60bb 100644 --- a/src/lib/collaboration/solid-bindings.ts +++ b/src/lib/collaboration/solid-bindings.ts @@ -4,7 +4,7 @@ */ import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js'; -import { Text, Map as YMap, Array as YArray, Doc, ObservableMapEvent, ObservableArrayEvent } from 'yjs'; +import { Text, Map as YMap, Array as YArray, Doc, YEvent } from 'yjs'; /** * Create a reactive binding to a Yjs Text instance @@ -39,10 +39,10 @@ export function useYText(yText: Text) { * Create a reactive binding to a Yjs Map */ export function useYMap>(yMap: YMap) { - const [data, setData] = createSignal(yMap.toJSON() as T); + const [data, setData] = createSignal(yMap.toJSON() as unknown as T); - const observer = (event: ObservableMapEvent) => { - setData(yMap.toJSON() as T); + const observer = (_event: YEvent>) => { + setData(() => yMap.toJSON() as unknown as T); }; yMap.observe(observer); @@ -53,16 +53,16 @@ export function useYMap>(yMap: YMap) { const updateMap = (updates: Partial) => { Object.entries(updates).forEach(([key, value]) => { - yMap.set(key as keyof T, value); + yMap.set(key, value as T[string & keyof T]); }); }; - const setValue = (key: K, value: T[K]) => { + const setValue = (key: K, value: T[K]) => { yMap.set(key, value); }; - const getValue = (key: K): T[K] | undefined => { - return yMap.get(key); + const getValue = (key: K): T[K] | undefined => { + return yMap.get(key) as T[K] | undefined; }; return { @@ -80,7 +80,7 @@ export function useYMap>(yMap: YMap) { export function useYArray(yArray: YArray) { const [items, setItems] = createSignal(yArray.toArray()); - const observer = (event: ObservableArrayEvent) => { + const observer = (_event: YEvent>) => { setItems(yArray.toArray()); }; @@ -140,9 +140,9 @@ export function useCollaborativeDoc(doc: Doc) { const getScenes = createMemo(() => doc.getMap('scenes')); const text = useYText(getText()); - const metadata = useYMap(getMetadata()); - const characters = useYMap(getCharacters()); - const scenes = useYMap(getScenes()); + const metadata = useYMap(getMetadata() as YMap>); + const characters = useYMap(getCharacters() as YMap>); + const scenes = useYMap(getScenes() as YMap>); return { doc, diff --git a/src/lib/mail/protonmail-client.ts b/src/lib/mail/protonmail-client.ts index c06b5604d..42872d121 100644 --- a/src/lib/mail/protonmail-client.ts +++ b/src/lib/mail/protonmail-client.ts @@ -77,7 +77,7 @@ export class ProtonMailClient { async downloadAttachment(attachmentId: string): Promise { const result = await trpc.mail.attachmentDownload.query({ attachmentId }); - return result; + return result as unknown as Blob; } } diff --git a/src/lib/revisions/diff.ts b/src/lib/revisions/diff.ts index 687155279..00c9c2ac5 100644 --- a/src/lib/revisions/diff.ts +++ b/src/lib/revisions/diff.ts @@ -10,6 +10,7 @@ interface LineDiffItem { type: ChangeType; line: string; oldLine?: string; + originalLineIndex?: number; } function computeLineDiff( @@ -36,6 +37,7 @@ function computeLineDiff( type: 'modification', line: newLine, oldLine: oldLine, + originalLineIndex: oldIdx, }); oldIdx++; newIdx++; @@ -45,11 +47,12 @@ function computeLineDiff( type: 'deletion', line: oldLine, oldLine: oldLine, + originalLineIndex: oldIdx, }); oldIdx++; } else { const newLine = newLines[newIdx]!; - result.push({ type: 'addition', line: newLine }); + result.push({ type: 'addition', line: newLine, originalLineIndex: newIdx }); newIdx++; } } @@ -84,7 +87,7 @@ export function computeDiff( sceneCounter++; } - const lineNumber = i + 1; + const lineNumber = (diff.originalLineIndex ?? i) + 1; const currentPage = Math.ceil(lineNumber / linesPerPage); const change: RevisionChangeData = { diff --git a/src/pages/waitlist.md b/src/pages/waitlist.md new file mode 100644 index 000000000..1d38efe72 --- /dev/null +++ b/src/pages/waitlist.md @@ -0,0 +1,125 @@ +--- +layout: page +title: "Waitlist - FrenoCorp" +permalink: /waitlist +--- + +
+
+

Join FrenoCorp's Waitlist

+ +

+ FrenoCorp is building something revolutionary.
+ Be the first to experience it when we launch. +

+ +
+
+ + +
+ + +
+ +

+ We respect your privacy. No spam, ever. +

+
+
+ + \ No newline at end of file diff --git a/src/routes.tsx b/src/routes.tsx index 6a2875b4c..20d617258 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -18,14 +18,14 @@ import './styles/features.css'; import './styles/pricing.css'; import './styles/about-faq.css'; -const AppLayout = lazy(() => import('./components/layout/AppLayout')); -const Dashboard = lazy(() => import('./components/dashboard/Dashboard')); -const KPIDashboard = lazy(() => import('./components/dashboard/KPIDashboard')); -const ProjectList = lazy(() => import('./components/projects/ProjectList')); -const ProjectDetail = lazy(() => import('./components/projects/ProjectDetail')); -const ProjectForm = lazy(() => import('./components/projects/ProjectForm')); -const UserProfile = lazy(() => import('./components/auth/UserProfile')); -const TeamManagement = lazy(() => import('./components/teams/TeamManagement')); +const AppLayout = lazy(() => import('./components/layout/AppLayout').then(m => ({ default: m.AppLayout }))); +const Dashboard = lazy(() => import('./components/dashboard/Dashboard').then(m => ({ default: m.Dashboard }))); +const KPIDashboard = lazy(() => import('./components/dashboard/KPIDashboard').then(m => ({ default: m.KPIDashboard }))); +const ProjectList = lazy(() => import('./components/projects/ProjectList').then(m => ({ default: m.ProjectList }))); +const ProjectDetail = lazy(() => import('./components/projects/ProjectDetail').then(m => ({ default: m.ProjectDetail }))); +const ProjectForm = lazy(() => import('./components/projects/ProjectForm').then(m => ({ default: m.ProjectForm }))); +const UserProfile = lazy(() => import('./components/auth/UserProfile').then(m => ({ default: m.UserProfile }))); +const TeamManagement = lazy(() => import('./components/teams/TeamManagement').then(m => ({ default: m.TeamManagement }))); const Redirect = () => ; diff --git a/src/trpc/client.ts b/src/trpc/client.ts new file mode 100644 index 000000000..f7c6de11d --- /dev/null +++ b/src/trpc/client.ts @@ -0,0 +1,19 @@ +import { createTRPCClient, httpBatchLink } from '@trpc/client'; +import type { AppRouter } from '@/server/trpc/router'; +import { loggerLink } from '@trpc/server/http'; + +export function createTRPCClient(url: string) { + return createTRPCClient({ + links: [ + httpBatchLink({ url }), + loggerLink({ + enabled: (op) => + op.type === 'query' || + op.type === 'mutation' || + process.env.NODE_ENV === 'development', + }), + ], + }); +} + +export { createQueryClient, QueryClient, QueryClientProvider } from '@tanstack/react-query'; diff --git a/src/trpc/index.ts b/src/trpc/index.ts new file mode 100644 index 000000000..987bfc1a0 --- /dev/null +++ b/src/trpc/index.ts @@ -0,0 +1,13 @@ +import { createTRPCReact } from '@trpc/react-query'; +import type { AppRouter } from '@/server/trpc/router'; + +export const trpc = createTRPCReact({ + /** + * Options that will be used for the client. + * If you change this, you need to update the server configuration as well. + */ + transformer: undefined, +}); + +// Re-export the types +export type { AppRouter };