@digilogiclabs/create-saas-app 1.5.4 → 1.6.0
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/CHANGELOG.md +109 -49
- package/bin/index.js +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/templates/mobile/base/template/App.tsx +113 -23
- package/dist/templates/mobile/base/template/package.json +5 -2
- package/dist/templates/web/base/template/package.json +1 -1
- package/dist/templates/web/base/template/src/app/checkout/page.tsx +99 -8
- package/dist/templates/web/base/template/src/app/dashboard/page.tsx +309 -0
- package/dist/templates/web/base/template/src/app/globals.css +97 -0
- package/dist/templates/web/base/template/src/app/login/page.tsx +36 -8
- package/dist/templates/web/base/template/src/app/page.tsx +123 -83
- package/dist/templates/web/base/template/src/app/signup/page.tsx +36 -8
- package/dist/templates/web/base/template/src/components/shared/header.tsx +49 -30
- package/dist/templates/web/ui-auth/template/package.json +2 -2
- package/dist/templates/web/ui-auth/template/src/app/page.tsx +203 -61
- package/dist/templates/web/ui-auth-payments/template/package.json +2 -2
- package/dist/templates/web/ui-auth-payments/template/src/app/checkout/page.tsx +253 -87
- package/dist/templates/web/ui-auth-payments/template/src/app/globals.css +129 -0
- package/dist/templates/web/ui-auth-payments/template/src/app/page.tsx +246 -74
- package/dist/templates/web/ui-auth-payments/template/src/components/shared/header.tsx +106 -40
- package/dist/templates/web/ui-auth-payments-audio/template/package.json +2 -2
- package/dist/templates/web/ui-auth-payments-audio/template/src/app/page.tsx +221 -82
- package/dist/templates/web/ui-auth-payments-audio/template/src/components/shared/header.tsx +132 -40
- package/dist/templates/web/ui-auth-payments-video/template/package.json +2 -2
- package/dist/templates/web/ui-auth-payments-video/template/src/app/globals.css +146 -0
- package/dist/templates/web/ui-auth-payments-video/template/src/app/page.tsx +259 -85
- package/dist/templates/web/ui-auth-payments-video/template/src/components/shared/header.tsx +133 -41
- package/package.json +106 -106
- package/src/templates/mobile/base/template/App.tsx +113 -23
- package/src/templates/mobile/base/template/package.json +5 -2
- package/src/templates/web/base/template/package.json +1 -1
- package/src/templates/web/base/template/src/app/checkout/page.tsx +99 -8
- package/src/templates/web/base/template/src/app/dashboard/page.tsx +309 -0
- package/src/templates/web/base/template/src/app/globals.css +97 -0
- package/src/templates/web/base/template/src/app/login/page.tsx +36 -8
- package/src/templates/web/base/template/src/app/page.tsx +123 -83
- package/src/templates/web/base/template/src/app/signup/page.tsx +36 -8
- package/src/templates/web/base/template/src/components/shared/header.tsx +49 -30
- package/src/templates/web/ui-auth/template/package.json +2 -2
- package/src/templates/web/ui-auth/template/src/app/page.tsx +203 -61
- package/src/templates/web/ui-auth-payments/template/package.json +2 -2
- package/src/templates/web/ui-auth-payments/template/src/app/checkout/page.tsx +253 -87
- package/src/templates/web/ui-auth-payments/template/src/app/globals.css +129 -0
- package/src/templates/web/ui-auth-payments/template/src/app/page.tsx +246 -74
- package/src/templates/web/ui-auth-payments/template/src/components/shared/header.tsx +106 -40
- package/src/templates/web/ui-auth-payments-audio/template/package.json +2 -2
- package/src/templates/web/ui-auth-payments-audio/template/src/app/page.tsx +221 -82
- package/src/templates/web/ui-auth-payments-audio/template/src/components/shared/header.tsx +132 -40
- package/src/templates/web/ui-auth-payments-video/template/package.json +2 -2
- package/src/templates/web/ui-auth-payments-video/template/src/app/globals.css +146 -0
- package/src/templates/web/ui-auth-payments-video/template/src/app/page.tsx +259 -85
- package/src/templates/web/ui-auth-payments-video/template/src/components/shared/header.tsx +133 -41
- package/dist/index.js +0 -1173
- package/dist/index.js.map +0 -1
|
@@ -5,35 +5,92 @@ import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
|
5
5
|
import { AuthProvider, useAuth } from '@digilogiclabs/saas-factory-auth/native';
|
|
6
6
|
import { StripeProvider } from '@digilogiclabs/saas-factory-payments/native';
|
|
7
7
|
import { Link, Slot } from 'expo-router';
|
|
8
|
+
import {
|
|
9
|
+
NativePageTransition as PageTransition,
|
|
10
|
+
NativeMobileHero as MobileHero,
|
|
11
|
+
NativeMobileContainer as MobileContainer,
|
|
12
|
+
NativeNetworkAwareContent as NetworkAwareContent,
|
|
13
|
+
NativeOfflineWrapper as OfflineWrapper,
|
|
14
|
+
useNetworkInfo,
|
|
15
|
+
useOfflineState
|
|
16
|
+
} from '@digilogiclabs/saas-factory-ui/native';
|
|
8
17
|
|
|
9
18
|
function AppContent() {
|
|
10
19
|
const { user, signOut } = useAuth();
|
|
20
|
+
const networkInfo = useNetworkInfo();
|
|
21
|
+
const isOnline = useOfflineState();
|
|
11
22
|
|
|
12
23
|
return (
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
<PageTransition type="fade" duration={300}>
|
|
25
|
+
<OfflineWrapper
|
|
26
|
+
cacheStrategy="stale-while-revalidate"
|
|
27
|
+
showOfflineIndicator={true}
|
|
28
|
+
>
|
|
29
|
+
<MobileContainer style={styles.container}>
|
|
30
|
+
<NetworkAwareContent
|
|
31
|
+
showOnOffline={
|
|
32
|
+
<View style={styles.offlineContainer}>
|
|
33
|
+
<Text style={styles.offlineTitle}>You're offline</Text>
|
|
34
|
+
<Text style={styles.offlineText}>
|
|
35
|
+
Some features may be limited while offline.
|
|
36
|
+
</Text>
|
|
37
|
+
</View>
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
{user ? (
|
|
41
|
+
<MobileHero
|
|
42
|
+
title={{
|
|
43
|
+
text: "Welcome back!",
|
|
44
|
+
size: "lg"
|
|
45
|
+
}}
|
|
46
|
+
description="You are signed in and ready to go."
|
|
47
|
+
actions={[
|
|
48
|
+
{
|
|
49
|
+
label: "Go to Checkout",
|
|
50
|
+
href: "/checkout",
|
|
51
|
+
variant: "default"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
label: "Sign Out",
|
|
55
|
+
onPress: signOut,
|
|
56
|
+
variant: "outline"
|
|
57
|
+
}
|
|
58
|
+
]}
|
|
59
|
+
/>
|
|
60
|
+
) : (
|
|
61
|
+
<MobileHero
|
|
62
|
+
title={{
|
|
63
|
+
text: "Welcome to {{titleCaseName}}",
|
|
64
|
+
size: "xl"
|
|
65
|
+
}}
|
|
66
|
+
description="{{description}}"
|
|
67
|
+
actions={[
|
|
68
|
+
{
|
|
69
|
+
label: "Get Started",
|
|
70
|
+
href: "/signup",
|
|
71
|
+
variant: "default"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
label: "Sign In",
|
|
75
|
+
href: "/login",
|
|
76
|
+
variant: "outline"
|
|
77
|
+
}
|
|
78
|
+
]}
|
|
79
|
+
/>
|
|
80
|
+
)}
|
|
81
|
+
|
|
82
|
+
{/* Network Status Indicator */}
|
|
83
|
+
<View style={styles.statusContainer}>
|
|
84
|
+
<Text style={styles.statusText}>
|
|
85
|
+
Status: {isOnline ? '🟢 Online' : '🔴 Offline'}
|
|
86
|
+
{networkInfo?.type && ` • ${networkInfo.type}`}
|
|
87
|
+
</Text>
|
|
88
|
+
</View>
|
|
89
|
+
</NetworkAwareContent>
|
|
90
|
+
</MobileContainer>
|
|
91
|
+
</OfflineWrapper>
|
|
35
92
|
<StatusBar style="auto" />
|
|
36
|
-
</
|
|
93
|
+
</PageTransition>
|
|
37
94
|
);
|
|
38
95
|
}
|
|
39
96
|
|
|
@@ -85,4 +142,37 @@ const styles = StyleSheet.create({
|
|
|
85
142
|
textAlign: 'center',
|
|
86
143
|
width: '80%',
|
|
87
144
|
},
|
|
145
|
+
offlineContainer: {
|
|
146
|
+
padding: 20,
|
|
147
|
+
alignItems: 'center',
|
|
148
|
+
backgroundColor: '#fff3cd',
|
|
149
|
+
borderRadius: 8,
|
|
150
|
+
margin: 20,
|
|
151
|
+
},
|
|
152
|
+
offlineTitle: {
|
|
153
|
+
fontSize: 18,
|
|
154
|
+
fontWeight: 'bold',
|
|
155
|
+
color: '#856404',
|
|
156
|
+
marginBottom: 8,
|
|
157
|
+
},
|
|
158
|
+
offlineText: {
|
|
159
|
+
fontSize: 14,
|
|
160
|
+
color: '#856404',
|
|
161
|
+
textAlign: 'center',
|
|
162
|
+
},
|
|
163
|
+
statusContainer: {
|
|
164
|
+
position: 'absolute',
|
|
165
|
+
bottom: 50,
|
|
166
|
+
left: 0,
|
|
167
|
+
right: 0,
|
|
168
|
+
alignItems: 'center',
|
|
169
|
+
},
|
|
170
|
+
statusText: {
|
|
171
|
+
fontSize: 12,
|
|
172
|
+
color: '#666',
|
|
173
|
+
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
|
174
|
+
paddingHorizontal: 12,
|
|
175
|
+
paddingVertical: 6,
|
|
176
|
+
borderRadius: 20,
|
|
177
|
+
},
|
|
88
178
|
});
|
|
@@ -22,11 +22,14 @@
|
|
|
22
22
|
"react-native-screens": "~3.22.0",
|
|
23
23
|
"react-native-safe-area-context": "4.6.3",
|
|
24
24
|
"react-native-gesture-handler": "~2.12.0",
|
|
25
|
-
"@digilogiclabs/saas-factory-ui": "^0.
|
|
25
|
+
"@digilogiclabs/saas-factory-ui": "^0.13.0",
|
|
26
26
|
"@digilogiclabs/saas-factory-auth": "^0.4.3",
|
|
27
27
|
"@digilogiclabs/saas-factory-payments": "^0.2.0",
|
|
28
28
|
"firebase": "^10.0.0",
|
|
29
|
-
"@supabase/supabase-js": "^2.0.0"
|
|
29
|
+
"@supabase/supabase-js": "^2.0.0",
|
|
30
|
+
"@react-native-community/netinfo": "^11.0.0",
|
|
31
|
+
"@react-native-async-storage/async-storage": "^1.19.0",
|
|
32
|
+
"expo-haptics": "~12.4.0"
|
|
30
33
|
},
|
|
31
34
|
"devDependencies": {
|
|
32
35
|
"@babel/core": "^7.20.0",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"next": "^15.0.0",
|
|
18
18
|
"react": "^19.0.0",
|
|
19
19
|
"react-dom": "^19.0.0",
|
|
20
|
-
"@digilogiclabs/saas-factory-ui": "^0.
|
|
20
|
+
"@digilogiclabs/saas-factory-ui": "^0.13.0",
|
|
21
21
|
"@digilogiclabs/saas-factory-auth": "^0.4.3",
|
|
22
22
|
"@digilogiclabs/saas-factory-payments": "^0.2.0",
|
|
23
23
|
"tailwindcss": "^3.3.0",
|
|
@@ -3,9 +3,20 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { useStripe } from '@digilogiclabs/saas-factory-payments';
|
|
5
5
|
import { Button } from '@/components/ui/button';
|
|
6
|
+
import {
|
|
7
|
+
PageTransition,
|
|
8
|
+
MobileContainer,
|
|
9
|
+
TouchInput,
|
|
10
|
+
MobileForm,
|
|
11
|
+
SwipeableCard,
|
|
12
|
+
NetworkAwareContent,
|
|
13
|
+
useNetworkInfo
|
|
14
|
+
} from '@digilogiclabs/saas-factory-ui';
|
|
15
|
+
import { CreditCard, Zap } from 'lucide-react';
|
|
6
16
|
|
|
7
17
|
export default function CheckoutPage() {
|
|
8
18
|
const { handleCheckout } = useStripe();
|
|
19
|
+
const networkInfo = useNetworkInfo();
|
|
9
20
|
|
|
10
21
|
const onCheckout = async () => {
|
|
11
22
|
await handleCheckout({
|
|
@@ -15,14 +26,94 @@ export default function CheckoutPage() {
|
|
|
15
26
|
};
|
|
16
27
|
|
|
17
28
|
return (
|
|
18
|
-
<
|
|
19
|
-
<div className="
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
<PageTransition type="slide" direction="up" duration={350}>
|
|
30
|
+
<div className="flex items-center justify-center min-h-screen bg-gray-100">
|
|
31
|
+
<MobileContainer className="w-full max-w-md">
|
|
32
|
+
<NetworkAwareContent
|
|
33
|
+
showOnSlow={
|
|
34
|
+
<div className="p-6 bg-white rounded-lg shadow-md">
|
|
35
|
+
<div className="flex items-center gap-2 mb-4">
|
|
36
|
+
<CreditCard className="h-6 w-6 text-blue-600" />
|
|
37
|
+
<h1 className="text-xl font-bold">Checkout</h1>
|
|
38
|
+
</div>
|
|
39
|
+
<p className="mb-6 text-gray-600">
|
|
40
|
+
Simplified checkout for slower connections.
|
|
41
|
+
</p>
|
|
42
|
+
<Button
|
|
43
|
+
onClick={onCheckout}
|
|
44
|
+
className="w-full"
|
|
45
|
+
size="lg"
|
|
46
|
+
>
|
|
47
|
+
Proceed to Checkout
|
|
48
|
+
</Button>
|
|
49
|
+
</div>
|
|
50
|
+
}
|
|
51
|
+
>
|
|
52
|
+
<SwipeableCard
|
|
53
|
+
leftActions={[
|
|
54
|
+
{
|
|
55
|
+
id: 'back',
|
|
56
|
+
label: 'Back',
|
|
57
|
+
onAction: () => window.history.back(),
|
|
58
|
+
color: 'gray'
|
|
59
|
+
}
|
|
60
|
+
]}
|
|
61
|
+
rightActions={[
|
|
62
|
+
{
|
|
63
|
+
id: 'proceed',
|
|
64
|
+
label: 'Pay Now',
|
|
65
|
+
onAction: onCheckout,
|
|
66
|
+
color: 'green'
|
|
67
|
+
}
|
|
68
|
+
]}
|
|
69
|
+
threshold={60}
|
|
70
|
+
hapticFeedback={true}
|
|
71
|
+
showActionLabels={true}
|
|
72
|
+
>
|
|
73
|
+
<div className="p-8 bg-white rounded-lg shadow-md">
|
|
74
|
+
<div className="flex items-center gap-3 mb-6">
|
|
75
|
+
<div className="p-3 bg-blue-100 rounded-full">
|
|
76
|
+
<Zap className="h-6 w-6 text-blue-600" />
|
|
77
|
+
</div>
|
|
78
|
+
<div>
|
|
79
|
+
<h1 className="text-2xl font-bold">Premium Checkout</h1>
|
|
80
|
+
<p className="text-sm text-gray-500">
|
|
81
|
+
{networkInfo?.effectiveType && `${networkInfo.effectiveType.toUpperCase()} connection`}
|
|
82
|
+
</p>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div className="space-y-4 mb-6">
|
|
87
|
+
<div className="p-4 bg-blue-50 rounded-lg">
|
|
88
|
+
<h3 className="font-semibold text-blue-900">Pro Plan</h3>
|
|
89
|
+
<p className="text-blue-700">$29/month • All features included</p>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div className="text-sm text-gray-600">
|
|
93
|
+
<p>✓ Touch-optimized checkout experience</p>
|
|
94
|
+
<p>✓ Network-aware performance</p>
|
|
95
|
+
<p>✓ Secure payment processing</p>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div className="space-y-3">
|
|
100
|
+
<Button
|
|
101
|
+
onClick={onCheckout}
|
|
102
|
+
className="w-full"
|
|
103
|
+
size="lg"
|
|
104
|
+
>
|
|
105
|
+
Proceed to Checkout
|
|
106
|
+
</Button>
|
|
107
|
+
|
|
108
|
+
<p className="text-xs text-center text-gray-500">
|
|
109
|
+
Swipe left to go back • Swipe right to pay now
|
|
110
|
+
</p>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</SwipeableCard>
|
|
114
|
+
</NetworkAwareContent>
|
|
115
|
+
</MobileContainer>
|
|
25
116
|
</div>
|
|
26
|
-
</
|
|
117
|
+
</PageTransition>
|
|
27
118
|
);
|
|
28
119
|
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { useAuth } from '@digilogiclabs/saas-factory-auth';
|
|
5
|
+
import { Button } from '@/components/ui/button';
|
|
6
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
7
|
+
import {
|
|
8
|
+
PageTransition,
|
|
9
|
+
MobileContainer,
|
|
10
|
+
ResponsiveGrid,
|
|
11
|
+
PullToRefresh,
|
|
12
|
+
LazyImage,
|
|
13
|
+
VirtualScrollList,
|
|
14
|
+
SwipeableCard,
|
|
15
|
+
NetworkAwareContent,
|
|
16
|
+
OfflineWrapper,
|
|
17
|
+
useNetworkInfo,
|
|
18
|
+
useOfflineState
|
|
19
|
+
} from '@digilogiclabs/saas-factory-ui';
|
|
20
|
+
import {
|
|
21
|
+
BarChart3,
|
|
22
|
+
Users,
|
|
23
|
+
DollarSign,
|
|
24
|
+
TrendingUp,
|
|
25
|
+
Settings,
|
|
26
|
+
Archive,
|
|
27
|
+
Star,
|
|
28
|
+
Trash2,
|
|
29
|
+
Wifi,
|
|
30
|
+
WifiOff
|
|
31
|
+
} from 'lucide-react';
|
|
32
|
+
|
|
33
|
+
// Mock data for demonstration
|
|
34
|
+
const mockActivityData = Array.from({ length: 50 }, (_, i) => ({
|
|
35
|
+
id: i + 1,
|
|
36
|
+
title: `Activity ${i + 1}`,
|
|
37
|
+
description: `This is a sample activity item #${i + 1}`,
|
|
38
|
+
timestamp: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
39
|
+
type: ['user', 'sale', 'system'][Math.floor(Math.random() * 3)],
|
|
40
|
+
avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${i}`
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
export default function DashboardPage() {
|
|
44
|
+
const { user } = useAuth();
|
|
45
|
+
const networkInfo = useNetworkInfo();
|
|
46
|
+
const isOnline = useOfflineState();
|
|
47
|
+
const [activityData, setActivityData] = useState(mockActivityData);
|
|
48
|
+
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
49
|
+
|
|
50
|
+
const handleRefresh = async () => {
|
|
51
|
+
setIsRefreshing(true);
|
|
52
|
+
// Simulate API call
|
|
53
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
54
|
+
setIsRefreshing(false);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const handleArchive = (id: number) => {
|
|
58
|
+
setActivityData(prev => prev.filter(item => item.id !== id));
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const handleStar = (id: number) => {
|
|
62
|
+
console.log('Starred item:', id);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleDelete = (id: number) => {
|
|
66
|
+
setActivityData(prev => prev.filter(item => item.id !== id));
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const stats = [
|
|
70
|
+
{
|
|
71
|
+
title: 'Total Users',
|
|
72
|
+
value: '2,543',
|
|
73
|
+
change: '+12%',
|
|
74
|
+
icon: Users,
|
|
75
|
+
color: 'blue'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
title: 'Revenue',
|
|
79
|
+
value: '$12,456',
|
|
80
|
+
change: '+8%',
|
|
81
|
+
icon: DollarSign,
|
|
82
|
+
color: 'green'
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
title: 'Growth',
|
|
86
|
+
value: '23.5%',
|
|
87
|
+
change: '+2.1%',
|
|
88
|
+
icon: TrendingUp,
|
|
89
|
+
color: 'purple'
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
title: 'Analytics',
|
|
93
|
+
value: '1,234',
|
|
94
|
+
change: '+5%',
|
|
95
|
+
icon: BarChart3,
|
|
96
|
+
color: 'orange'
|
|
97
|
+
}
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<PageTransition type="slide" direction="horizontal" duration={300}>
|
|
102
|
+
<OfflineWrapper
|
|
103
|
+
cacheStrategy="stale-while-revalidate"
|
|
104
|
+
showOfflineIndicator={true}
|
|
105
|
+
backgroundSync={true}
|
|
106
|
+
>
|
|
107
|
+
<PullToRefresh
|
|
108
|
+
onRefresh={handleRefresh}
|
|
109
|
+
threshold={80}
|
|
110
|
+
loadingIndicator="spinner"
|
|
111
|
+
hapticOnTrigger={true}
|
|
112
|
+
networkAware={true}
|
|
113
|
+
>
|
|
114
|
+
<div className="min-h-screen bg-gray-50">
|
|
115
|
+
<MobileContainer className="py-6">
|
|
116
|
+
{/* Header */}
|
|
117
|
+
<div className="mb-6">
|
|
118
|
+
<div className="flex items-center justify-between">
|
|
119
|
+
<div>
|
|
120
|
+
<h1 className="text-2xl font-bold text-gray-900">
|
|
121
|
+
Welcome back, {user?.email?.split('@')[0]}
|
|
122
|
+
</h1>
|
|
123
|
+
<p className="text-gray-600 flex items-center gap-2">
|
|
124
|
+
{isOnline ? (
|
|
125
|
+
<>
|
|
126
|
+
<Wifi className="h-4 w-4 text-green-600" />
|
|
127
|
+
<span>Online</span>
|
|
128
|
+
</>
|
|
129
|
+
) : (
|
|
130
|
+
<>
|
|
131
|
+
<WifiOff className="h-4 w-4 text-red-600" />
|
|
132
|
+
<span>Offline</span>
|
|
133
|
+
</>
|
|
134
|
+
)}
|
|
135
|
+
{networkInfo?.effectiveType && (
|
|
136
|
+
<span className="text-xs bg-gray-200 px-2 py-1 rounded">
|
|
137
|
+
{networkInfo.effectiveType.toUpperCase()}
|
|
138
|
+
</span>
|
|
139
|
+
)}
|
|
140
|
+
</p>
|
|
141
|
+
</div>
|
|
142
|
+
<Button variant="outline" size="icon">
|
|
143
|
+
<Settings className="h-4 w-4" />
|
|
144
|
+
</Button>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
{/* Stats Grid */}
|
|
149
|
+
<NetworkAwareContent
|
|
150
|
+
showOnSlow={
|
|
151
|
+
<ResponsiveGrid columns={{ base: 2, md: 2 }} gap="sm" className="mb-6">
|
|
152
|
+
{stats.slice(0, 2).map((stat, index) => (
|
|
153
|
+
<Card key={index}>
|
|
154
|
+
<CardContent className="p-4">
|
|
155
|
+
<div className="flex items-center gap-2">
|
|
156
|
+
<stat.icon className={`h-5 w-5 text-${stat.color}-600`} />
|
|
157
|
+
<div>
|
|
158
|
+
<p className="text-sm font-medium text-gray-600">{stat.title}</p>
|
|
159
|
+
<p className="text-lg font-bold">{stat.value}</p>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</CardContent>
|
|
163
|
+
</Card>
|
|
164
|
+
))}
|
|
165
|
+
</ResponsiveGrid>
|
|
166
|
+
}
|
|
167
|
+
>
|
|
168
|
+
<ResponsiveGrid columns={{ base: 2, md: 4 }} gap="md" className="mb-6">
|
|
169
|
+
{stats.map((stat, index) => (
|
|
170
|
+
<Card key={index}>
|
|
171
|
+
<CardContent className="p-4">
|
|
172
|
+
<div className="flex items-center justify-between mb-2">
|
|
173
|
+
<stat.icon className={`h-5 w-5 text-${stat.color}-600`} />
|
|
174
|
+
<span className="text-sm text-green-600 font-medium">
|
|
175
|
+
{stat.change}
|
|
176
|
+
</span>
|
|
177
|
+
</div>
|
|
178
|
+
<div>
|
|
179
|
+
<p className="text-sm font-medium text-gray-600">{stat.title}</p>
|
|
180
|
+
<p className="text-2xl font-bold">{stat.value}</p>
|
|
181
|
+
</div>
|
|
182
|
+
</CardContent>
|
|
183
|
+
</Card>
|
|
184
|
+
))}
|
|
185
|
+
</ResponsiveGrid>
|
|
186
|
+
</NetworkAwareContent>
|
|
187
|
+
|
|
188
|
+
{/* Activity Feed */}
|
|
189
|
+
<Card className="mb-6">
|
|
190
|
+
<CardHeader>
|
|
191
|
+
<CardTitle className="flex items-center gap-2">
|
|
192
|
+
<BarChart3 className="h-5 w-5" />
|
|
193
|
+
Recent Activity
|
|
194
|
+
</CardTitle>
|
|
195
|
+
<CardDescription>
|
|
196
|
+
Swipe left on items to archive or star them
|
|
197
|
+
</CardDescription>
|
|
198
|
+
</CardHeader>
|
|
199
|
+
<CardContent className="p-0">
|
|
200
|
+
<VirtualScrollList
|
|
201
|
+
items={activityData}
|
|
202
|
+
itemHeight={80}
|
|
203
|
+
containerHeight={400}
|
|
204
|
+
bufferSize={5}
|
|
205
|
+
renderItem={(item) => (
|
|
206
|
+
<SwipeableCard
|
|
207
|
+
key={item.id}
|
|
208
|
+
leftActions={[
|
|
209
|
+
{
|
|
210
|
+
id: 'archive',
|
|
211
|
+
label: 'Archive',
|
|
212
|
+
onAction: () => handleArchive(item.id),
|
|
213
|
+
color: 'blue',
|
|
214
|
+
icon: Archive
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
id: 'star',
|
|
218
|
+
label: 'Star',
|
|
219
|
+
onAction: () => handleStar(item.id),
|
|
220
|
+
color: 'yellow',
|
|
221
|
+
icon: Star
|
|
222
|
+
}
|
|
223
|
+
]}
|
|
224
|
+
rightActions={[
|
|
225
|
+
{
|
|
226
|
+
id: 'delete',
|
|
227
|
+
label: 'Delete',
|
|
228
|
+
onAction: () => handleDelete(item.id),
|
|
229
|
+
color: 'red',
|
|
230
|
+
icon: Trash2
|
|
231
|
+
}
|
|
232
|
+
]}
|
|
233
|
+
threshold={50}
|
|
234
|
+
hapticFeedback={true}
|
|
235
|
+
showActionLabels={true}
|
|
236
|
+
>
|
|
237
|
+
<div className="flex items-center gap-3 p-4 border-b">
|
|
238
|
+
<LazyImage
|
|
239
|
+
src={item.avatar}
|
|
240
|
+
alt={`Activity ${item.id}`}
|
|
241
|
+
className="w-10 h-10 rounded-full"
|
|
242
|
+
placeholder="skeleton"
|
|
243
|
+
webpSupport={true}
|
|
244
|
+
mobileOptimization={{
|
|
245
|
+
quality: 80,
|
|
246
|
+
format: "webp"
|
|
247
|
+
}}
|
|
248
|
+
/>
|
|
249
|
+
<div className="flex-1 min-w-0">
|
|
250
|
+
<h4 className="font-medium text-gray-900 truncate">
|
|
251
|
+
{item.title}
|
|
252
|
+
</h4>
|
|
253
|
+
<p className="text-sm text-gray-600 truncate">
|
|
254
|
+
{item.description}
|
|
255
|
+
</p>
|
|
256
|
+
<p className="text-xs text-gray-400">
|
|
257
|
+
{new Date(item.timestamp).toLocaleDateString()}
|
|
258
|
+
</p>
|
|
259
|
+
</div>
|
|
260
|
+
<div className={`px-2 py-1 rounded-full text-xs ${
|
|
261
|
+
item.type === 'user' ? 'bg-blue-100 text-blue-800' :
|
|
262
|
+
item.type === 'sale' ? 'bg-green-100 text-green-800' :
|
|
263
|
+
'bg-gray-100 text-gray-800'
|
|
264
|
+
}`}>
|
|
265
|
+
{item.type}
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
</SwipeableCard>
|
|
269
|
+
)}
|
|
270
|
+
onEndReached={() => {
|
|
271
|
+
console.log('Loading more items...');
|
|
272
|
+
}}
|
|
273
|
+
mobileOptimized={true}
|
|
274
|
+
/>
|
|
275
|
+
</CardContent>
|
|
276
|
+
</Card>
|
|
277
|
+
|
|
278
|
+
{/* Quick Actions */}
|
|
279
|
+
<Card>
|
|
280
|
+
<CardHeader>
|
|
281
|
+
<CardTitle>Quick Actions</CardTitle>
|
|
282
|
+
<CardDescription>
|
|
283
|
+
Common tasks and shortcuts
|
|
284
|
+
</CardDescription>
|
|
285
|
+
</CardHeader>
|
|
286
|
+
<CardContent>
|
|
287
|
+
<ResponsiveGrid columns={{ base: 2, md: 3 }} gap="sm">
|
|
288
|
+
<Button variant="outline" className="h-20 flex flex-col gap-2">
|
|
289
|
+
<Users className="h-5 w-5" />
|
|
290
|
+
<span className="text-xs">Manage Users</span>
|
|
291
|
+
</Button>
|
|
292
|
+
<Button variant="outline" className="h-20 flex flex-col gap-2">
|
|
293
|
+
<DollarSign className="h-5 w-5" />
|
|
294
|
+
<span className="text-xs">View Revenue</span>
|
|
295
|
+
</Button>
|
|
296
|
+
<Button variant="outline" className="h-20 flex flex-col gap-2">
|
|
297
|
+
<BarChart3 className="h-5 w-5" />
|
|
298
|
+
<span className="text-xs">Analytics</span>
|
|
299
|
+
</Button>
|
|
300
|
+
</ResponsiveGrid>
|
|
301
|
+
</CardContent>
|
|
302
|
+
</Card>
|
|
303
|
+
</MobileContainer>
|
|
304
|
+
</div>
|
|
305
|
+
</PullToRefresh>
|
|
306
|
+
</OfflineWrapper>
|
|
307
|
+
</PageTransition>
|
|
308
|
+
);
|
|
309
|
+
}
|