create-varity-app 2.0.0-beta.16 → 2.0.0-beta.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-varity-app",
3
- "version": "2.0.0-beta.16",
3
+ "version": "2.0.0-beta.18",
4
4
  "description": "Create production-ready apps with auth, database, and payments built in",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -2,5 +2,14 @@
2
2
  > my-saas-app@0.1.0 build /home/macoding/varity-workspace/varity-sdk-private/templates/saas-starter
3
3
  > next build
4
4
 
5
+ ⚠ Warning: Next.js inferred your workspace root, but it may not be correct.
6
+ We detected multiple lockfiles and selected the directory of /home/macoding/varity-workspace/package-lock.json as the root directory.
7
+ To silence this warning, set `outputFileTracingRoot` in your Next.js config, or consider removing one of the lockfiles if it's not needed.
8
+ See https://nextjs.org/docs/app/api-reference/config/next-config-js/output#caveats for more information.
9
+ Detected additional lockfiles:
10
+ * /home/macoding/varity-workspace/varity-sdk-private/pnpm-lock.yaml
11
+
5
12
  ▲ Next.js 15.5.11
6
13
 
14
+ Creating an optimized production build ...
15
+ ⚠ Mismatching @next/swc version, detected: 15.5.7 while Next.js is on 15.5.11. Please ensure these match
@@ -17,7 +17,7 @@
17
17
  | Settings | Working | 4 tabs with backend persistence via DB Proxy |
18
18
  | Command Palette | Working | Cmd+K / Ctrl+K |
19
19
  | Color Themes | Working | 4 presets (Blue, Purple, Green, Orange) |
20
- | Static Export | Working | `output: 'export'` for IPFS deployment |
20
+ | Static Export | Working | `output: 'export'` for static hosting (global CDN) |
21
21
  | Mobile Nav | Working | Responsive sidebar with hamburger menu |
22
22
 
23
23
  ## Known Issues
@@ -5,15 +5,9 @@ const nextConfig = {
5
5
  trailingSlash: true,
6
6
  productionBrowserSourceMaps: false,
7
7
  webpack: (config, { isServer, dev }) => {
8
- // Suppress MetaMask SDK warning for @react-native-async-storage
9
- config.resolve.fallback = {
10
- ...config.resolve.fallback,
11
- '@react-native-async-storage/async-storage': false,
12
- };
13
- // Force production devtool to avoid 35MB eval-source-map chunks
14
- if (!dev && !isServer) {
15
- config.devtool = false;
16
- }
8
+ // Suppress unused optional peer dependencies from UI Kit internals
9
+ ['@react-native-async-storage/async-storage', 'viem', 'viem/chains', '@solana/kit', '@solana/sysvars', '@solana-program/token-2022', 'x402', '@coinbase/wallet-sdk', '@walletconnect/ethereum-provider'].forEach(pkg => { config.resolve.alias[pkg] = false; });
10
+ if (!dev && !isServer) config.devtool = false;
17
11
  return config;
18
12
  },
19
13
  };
@@ -13,7 +13,8 @@
13
13
  "test:e2e:ui": "playwright test --ui",
14
14
  "test:e2e:headed": "playwright test --headed",
15
15
  "test:e2e:debug": "playwright test --debug",
16
- "prepare": "husky install"
16
+ "prepare": "husky install",
17
+ "deploy": "varitykit app deploy"
17
18
  },
18
19
  "dependencies": {
19
20
  "@varity-labs/sdk": "workspace:^",
@@ -7,7 +7,11 @@ import { useProjects, useTasks, useTeam } from '@/lib/hooks';
7
7
  import { CommandPalette } from '@varity-labs/ui-kit';
8
8
  import { Menu, X } from 'lucide-react';
9
9
 
10
- // Conditionally import Privy/UI-Kit components
10
+ // Defensively import Privy/UI-Kit components at runtime (not statically) so the
11
+ // dashboard renders gracefully even if @varity-labs/ui-kit hasn't been installed
12
+ // yet (e.g. during local scaffolding before `npm install` completes).
13
+ // This is intentional — it is NOT a sign that something is broken.
14
+ // See KNOWN_ISSUES.md for details on this pattern.
11
15
  let DashboardLayout: any = null;
12
16
  let PrivyProtectedRoute: any = null;
13
17
  let PrivyStackComponent: any = null;
@@ -19,7 +23,9 @@ try {
19
23
  PrivyProtectedRoute = uiKit.PrivyProtectedRoute;
20
24
  PrivyStackComponent = uiKit.PrivyStack;
21
25
  usePrivyHook = uiKit.usePrivy;
22
- } catch {}
26
+ } catch {
27
+ // ui-kit not installed or not yet available — DashboardShell fallback renders below
28
+ }
23
29
 
24
30
  function RedirectToLogin() {
25
31
  const router = useRouter();
@@ -113,8 +119,8 @@ function DashboardShell({ children }: { children: React.ReactNode }) {
113
119
  // eslint-disable-next-line react-hooks/rules-of-hooks -- conditional on require() success, stable across renders
114
120
  const privy = usePrivyHook ? usePrivyHook() : { user: null, logout: async () => {} };
115
121
  const { user, logout } = privy;
116
- const router = useRouter();
117
122
  const pathname = usePathname();
123
+ const router = useRouter();
118
124
  const isMobile = useIsMobile();
119
125
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
120
126
  const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
@@ -154,7 +160,7 @@ function DashboardShell({ children }: { children: React.ReactNode }) {
154
160
 
155
161
  const handleLogout = async () => {
156
162
  await logout();
157
- window.location.href = '/';
163
+ router.push('/');
158
164
  };
159
165
 
160
166
  // Fallback layout when DashboardLayout from ui-kit isn't available
@@ -255,9 +261,8 @@ function DashboardShell({ children }: { children: React.ReactNode }) {
255
261
  showSidebar={!isMobile}
256
262
  user={{
257
263
  name: userName,
258
- address: userEmail,
264
+ email: userEmail,
259
265
  }}
260
- onNavigate={(path: string) => router.push(path)}
261
266
  onLogout={handleLogout}
262
267
  onNavigateToProfile={() => router.push('/dashboard/settings')}
263
268
  onNavigateToSettings={() => router.push('/dashboard/settings')}
@@ -287,9 +292,8 @@ export default function DashboardRootLayout({
287
292
  return (
288
293
  <PrivyStackComponent
289
294
  appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID}
290
- thirdwebClientId={process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID}
291
295
  loginMethods={['email', 'google']}
292
- appearance={{ theme: 'light', accentColor: '#2563EB' }}
296
+ appearance={{ theme: 'light', accentColor: '#2563EB', logo: '/logo.svg' }}
293
297
  >
294
298
  <PrivyProtectedRoute fallback={<RedirectToLogin />}>
295
299
  <DashboardShell>{children}</DashboardShell>
@@ -9,6 +9,7 @@ import { FolderKanban, ListTodo, Users, ArrowRight } from 'lucide-react';
9
9
 
10
10
  function QuickActions() {
11
11
  const router = useRouter();
12
+
12
13
  const actions = [
13
14
  {
14
15
  label: 'New Project',
@@ -137,6 +138,7 @@ function GettingStarted({
137
138
  }
138
139
 
139
140
  export default function DashboardPage() {
141
+ const router = useRouter();
140
142
  const { name } = useCurrentUser();
141
143
  const { data: projects, loading: projectsLoading, error: projectsError, refresh: refreshProjects } = useProjects();
142
144
  const { data: tasks, loading: tasksLoading, error: tasksError, refresh: refreshTasks } = useTasks();
@@ -161,14 +163,14 @@ export default function DashboardPage() {
161
163
  </p>
162
164
  </div>
163
165
 
164
- {error && !loading && (
165
- <div className="flex items-center justify-between rounded-lg border border-amber-200 bg-amber-50 px-4 py-3">
166
- <p className="text-sm text-amber-700">Syncing your data...</p>
166
+ {error && (
167
+ <div className="flex items-center justify-between rounded-lg border border-red-200 bg-red-50 px-4 py-3">
168
+ <p className="text-sm text-red-700">Failed to load data. Please check your connection and try again.</p>
167
169
  <button
168
170
  onClick={() => { refreshProjects(); refreshTasks(); refreshTeam(); }}
169
- className="text-sm font-medium text-amber-700 hover:text-amber-800 underline"
171
+ className="text-sm font-medium text-red-700 hover:text-red-800 underline"
170
172
  >
171
- Refresh
173
+ Retry
172
174
  </button>
173
175
  </div>
174
176
  )}
@@ -606,10 +606,10 @@ export default function ProjectsPage() {
606
606
  </div>
607
607
  </Dialog>
608
608
 
609
- {error && !loading && (
610
- <div className="flex items-center justify-between rounded-lg border border-amber-200 bg-amber-50 px-4 py-3">
611
- <p className="text-sm text-amber-700">Syncing your projects...</p>
612
- <button onClick={refresh} className="text-sm font-medium text-amber-700 hover:text-amber-800 underline">Refresh</button>
609
+ {error && (
610
+ <div className="flex items-center justify-between rounded-lg border border-red-200 bg-red-50 px-4 py-3">
611
+ <p className="text-sm text-red-700">Failed to load projects. Please check your connection and try again.</p>
612
+ <button onClick={refresh} className="text-sm font-medium text-red-700 hover:text-red-800 underline">Retry</button>
613
613
  </div>
614
614
  )}
615
615
 
@@ -258,10 +258,10 @@ export default function TasksPage() {
258
258
  )}
259
259
  </div>
260
260
 
261
- {error && !loading && (
262
- <div className="flex items-center justify-between rounded-lg border border-amber-200 bg-amber-50 px-4 py-3">
263
- <p className="text-sm text-amber-700">Syncing your tasks...</p>
264
- <button onClick={refresh} className="text-sm font-medium text-amber-700 hover:text-amber-800 underline">Refresh</button>
261
+ {error && (
262
+ <div className="flex items-center justify-between rounded-lg border border-red-200 bg-red-50 px-4 py-3">
263
+ <p className="text-sm text-red-700">Failed to load tasks. Please check your connection and try again.</p>
264
+ <button onClick={refresh} className="text-sm font-medium text-red-700 hover:text-red-800 underline">Retry</button>
265
265
  </div>
266
266
  )}
267
267
 
@@ -246,10 +246,10 @@ export default function TeamPage() {
246
246
  loading={removeSubmitting}
247
247
  />
248
248
 
249
- {error && !loading && (
250
- <div className="flex items-center justify-between rounded-lg border border-amber-200 bg-amber-50 px-4 py-3">
251
- <p className="text-sm text-amber-700">Syncing your team data...</p>
252
- <button onClick={refresh} className="text-sm font-medium text-amber-700 hover:text-amber-800 underline">Refresh</button>
249
+ {error && (
250
+ <div className="flex items-center justify-between rounded-lg border border-red-200 bg-red-50 px-4 py-3">
251
+ <p className="text-sm text-red-700">Failed to load team data. Please check your connection and try again.</p>
252
+ <button onClick={refresh} className="text-sm font-medium text-red-700 hover:text-red-800 underline">Retry</button>
253
253
  </div>
254
254
  )}
255
255
 
@@ -1,23 +1,20 @@
1
1
  import type { Metadata } from 'next';
2
2
  import { Providers } from '@/components/providers';
3
- import { APP_NAME } from '@/lib/constants';
4
3
  import './globals.css';
5
4
 
6
- const description = 'Built with Varity — auth, database, and deployment included.';
7
-
8
5
  export const metadata: Metadata = {
9
- title: APP_NAME,
10
- description,
11
- metadataBase: new URL('https://varity.app'),
6
+ title: 'TaskFlow - Project Management',
7
+ description: 'Manage projects, track tasks, and collaborate with your team.',
8
+ metadataBase: new URL('https://example.com'),
12
9
  openGraph: {
13
- title: APP_NAME,
14
- description,
10
+ title: 'TaskFlow - Project Management',
11
+ description: 'Manage projects, track tasks, and collaborate with your team.',
15
12
  type: 'website',
16
13
  },
17
14
  twitter: {
18
15
  card: 'summary',
19
- title: APP_NAME,
20
- description,
16
+ title: 'TaskFlow - Project Management',
17
+ description: 'Manage projects, track tasks, and collaborate with your team.',
21
18
  },
22
19
  };
23
20
 
@@ -15,12 +15,6 @@ try {
15
15
  usePrivyHook = uiKit.usePrivy;
16
16
  } catch {}
17
17
 
18
- function loginButtonLabel(privy: { ready: boolean; authenticated: boolean }): string {
19
- if (!privy.ready) return 'Loading...';
20
- if (privy.authenticated) return 'Already Signed In';
21
- return 'Sign In with Email or Social';
22
- }
23
-
24
18
  function LoginContent() {
25
19
  const router = useRouter();
26
20
  // eslint-disable-next-line react-hooks/rules-of-hooks -- conditional on require() success, stable across renders
@@ -61,7 +55,11 @@ function LoginContent() {
61
55
  disabled={!privy.ready || privy.authenticated}
62
56
  className="w-full px-6 py-3 bg-primary-600 hover:bg-primary-700 disabled:bg-gray-300 disabled:cursor-not-allowed text-white font-medium rounded-lg transition-colors shadow-sm"
63
57
  >
64
- {loginButtonLabel(privy)}
58
+ {!privy.ready
59
+ ? 'Loading...'
60
+ : privy.authenticated
61
+ ? 'Already Signed In'
62
+ : 'Sign In with Email or Social'}
65
63
  </button>
66
64
  ) : (
67
65
  <div className="text-center space-y-4">
@@ -81,18 +79,19 @@ function LoginContent() {
81
79
  }
82
80
 
83
81
  export default function LoginPage() {
82
+ // Always wrap in PrivyStack - it uses dev credentials automatically when no appId is provided
84
83
  if (PrivyStackComponent) {
85
84
  return (
86
85
  <PrivyStackComponent
87
86
  appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID}
88
- thirdwebClientId={process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID}
89
87
  loginMethods={['email', 'google']}
90
- appearance={{ theme: 'light', accentColor: '#2563EB' }}
88
+ appearance={{ theme: 'light', accentColor: '#2563EB', logo: '/logo.svg' }}
91
89
  >
92
90
  <LoginContent />
93
91
  </PrivyStackComponent>
94
92
  );
95
93
  }
96
94
 
95
+ // Fallback if ui-kit package isn't installed
97
96
  return <LoginContent />;
98
97
  }
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import Link from 'next/link';
2
4
 
3
5
  export default function NotFound() {
@@ -2,6 +2,7 @@ import { Navbar } from '@/components/shared/Navbar';
2
2
  import { Hero } from '@/components/landing/Hero';
3
3
  import { Features } from '@/components/landing/Features';
4
4
  import { HowItWorks } from '@/components/landing/HowItWorks';
5
+ import { Testimonials } from '@/components/landing/Testimonials';
5
6
  import { Pricing } from '@/components/landing/Pricing';
6
7
  import { CTA } from '@/components/landing/CTA';
7
8
  import { Footer } from '@/components/shared/Footer';
@@ -13,7 +14,7 @@ export default function HomePage() {
13
14
  <Hero />
14
15
  <Features />
15
16
  <HowItWorks />
16
-
17
+ <Testimonials />
17
18
  <Pricing />
18
19
  <CTA />
19
20
  <Footer />
@@ -1,7 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import Link from 'next/link';
4
- import { DataTable, TaskStatusBadge, PriorityBadge } from '@varity-labs/ui-kit';
4
+ import { DataTable } from '@varity-labs/ui-kit';
5
+ import { TaskStatusBadge, PriorityBadge } from '@varity-labs/ui-kit';
5
6
  import { formatRelativeDate } from '@/lib/utils';
6
7
  import { ArrowRight } from 'lucide-react';
7
8
  import type { Task } from '@/types';
@@ -1,4 +1,3 @@
1
- import Link from 'next/link';
2
1
  import { Check } from 'lucide-react';
3
2
 
4
3
  const plans = [
@@ -53,12 +52,6 @@ const plans = [
53
52
  },
54
53
  ];
55
54
 
56
- function buttonStyle(plan: (typeof plans)[number]): string {
57
- if (plan.popular) return 'bg-primary-600 text-white hover:bg-primary-700';
58
- if (plan.price === 0) return 'bg-gray-100 text-gray-900 hover:bg-gray-200';
59
- return 'bg-gray-900 text-white hover:bg-gray-800';
60
- }
61
-
62
55
  export function Pricing() {
63
56
  return (
64
57
  <section id="pricing" className="py-24 bg-white">
@@ -109,12 +102,18 @@ export function Pricing() {
109
102
  </ul>
110
103
 
111
104
  <div className="mt-8">
112
- <Link
105
+ <a
113
106
  href="/login"
114
- className={`block w-full rounded-lg py-3 text-center font-medium transition-colors ${buttonStyle(plan)}`}
107
+ className={`block w-full rounded-lg py-3 text-center font-medium transition-colors ${
108
+ plan.popular
109
+ ? 'bg-primary-600 text-white hover:bg-primary-700'
110
+ : plan.price === 0
111
+ ? 'bg-gray-100 text-gray-900 hover:bg-gray-200'
112
+ : 'bg-gray-900 text-white hover:bg-gray-800'
113
+ }`}
115
114
  >
116
115
  {plan.cta}
117
- </Link>
116
+ </a>
118
117
  </div>
119
118
  </div>
120
119
  ))}
@@ -46,6 +46,7 @@ export function Navbar() {
46
46
  >
47
47
  Get Started
48
48
  </Link>
49
+ {/* Mobile menu toggle */}
49
50
  <button
50
51
  onClick={() => setMobileOpen(!mobileOpen)}
51
52
  className="rounded-lg p-2 text-gray-600 hover:bg-gray-100 sm:hidden"
@@ -57,6 +58,7 @@ export function Navbar() {
57
58
  </div>
58
59
  </div>
59
60
 
61
+ {/* Mobile dropdown */}
60
62
  {mobileOpen && (
61
63
  <div className="border-t border-gray-100 bg-white px-4 pb-4 pt-2 sm:hidden">
62
64
  <div className="space-y-1">
@@ -1,6 +1,6 @@
1
1
  import type { NavigationItem } from '@varity-labs/ui-kit';
2
2
 
3
- export const APP_NAME = 'My App';
3
+ export const APP_NAME = 'TaskFlow';
4
4
 
5
5
  export const NAVIGATION_ITEMS: NavigationItem[] = [
6
6
  { label: 'Dashboard', icon: 'dashboard', path: '/dashboard' },
@@ -10,18 +10,6 @@ try {
10
10
  usePrivyHook = uiKit.usePrivy;
11
11
  } catch {}
12
12
 
13
- async function fetchWithRetry<T>(fn: () => Promise<T>, retries = 3, delay = 1500): Promise<T> {
14
- for (let i = 0; i < retries; i++) {
15
- try {
16
- return await fn();
17
- } catch (err) {
18
- if (i === retries - 1) throw err;
19
- await new Promise((r) => setTimeout(r, delay));
20
- }
21
- }
22
- throw new Error('Unexpected');
23
- }
24
-
25
13
  export function useCurrentUser() {
26
14
  // eslint-disable-next-line react-hooks/rules-of-hooks -- conditional on require() success, stable across renders
27
15
  const privy = usePrivyHook ? usePrivyHook() : { user: null, authenticated: false, logout: async () => {} };
@@ -66,7 +54,7 @@ export function useProjects(): UseCollectionReturn<Project> {
66
54
  try {
67
55
  setLoading(true);
68
56
  setError(null);
69
- const result = await fetchWithRetry(() => projects().get());
57
+ const result = await projects().get();
70
58
  setData(result as Project[]);
71
59
  } catch (err) {
72
60
  setError(err instanceof Error ? err.message : 'Failed to load projects');
@@ -138,7 +126,7 @@ export function useTasks(projectId?: string): UseCollectionReturn<Task> {
138
126
  try {
139
127
  setLoading(true);
140
128
  setError(null);
141
- const result = await fetchWithRetry(() => tasks().get());
129
+ const result = await tasks().get();
142
130
  setAllTasks(result as Task[]);
143
131
  } catch (err) {
144
132
  setError(err instanceof Error ? err.message : 'Failed to load tasks');
@@ -214,7 +202,7 @@ export function useTeam(): UseCollectionReturn<TeamMember> {
214
202
  try {
215
203
  setLoading(true);
216
204
  setError(null);
217
- const result = await fetchWithRetry(() => teamMembers().get());
205
+ const result = await teamMembers().get();
218
206
  setData(result as TeamMember[]);
219
207
  } catch (err) {
220
208
  setError(err instanceof Error ? err.message : 'Failed to load team');
@@ -301,7 +289,7 @@ export function useUserSettings() {
301
289
  try {
302
290
  setLoading(true);
303
291
  setError(null);
304
- const all = await fetchWithRetry(() => userSettings().get());
292
+ const all = await userSettings().get();
305
293
  const mine = (all as UserSettings[]).find((s) => s.user_id === userId);
306
294
  if (mine) {
307
295
  setSettings(mine);
@@ -2,7 +2,7 @@
2
2
  "name": "my-saas-app",
3
3
  "version": "0.1.0",
4
4
  "framework": "nextjs",
5
- "hosting": "ipfs",
5
+ "hosting": "static",
6
6
  "build": {
7
7
  "command": "npm run build",
8
8
  "output": "out"