strade-stx 1.0.0 → 1.0.1

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.
Files changed (104) hide show
  1. package/package.json +10 -1
  2. package/contracts/CoreMarketPlace.clar +0 -227
  3. package/contracts/DisputeResolution_clar.clar +0 -265
  4. package/contracts/EscrowService.clar +0 -171
  5. package/contracts/UserProfile.clar +0 -280
  6. package/contracts/ft-trait.clar +0 -24
  7. package/contracts/token.clar +0 -178
  8. package/frontend/README.md +0 -10
  9. package/frontend/components.json +0 -22
  10. package/frontend/dist/assets/index-BacuuL66.css +0 -1
  11. package/frontend/dist/assets/index-jryypd5B.js +0 -194
  12. package/frontend/dist/favicon.png +0 -0
  13. package/frontend/dist/index.html +0 -15
  14. package/frontend/dist/manifest.json +0 -15
  15. package/frontend/dist/vite.svg +0 -1
  16. package/frontend/empty-mock.js +0 -1
  17. package/frontend/eslint.config.js +0 -23
  18. package/frontend/eslint.config.mjs +0 -25
  19. package/frontend/index.html +0 -14
  20. package/frontend/next.config.ts +0 -17
  21. package/frontend/package-lock.json +0 -14740
  22. package/frontend/package.json +0 -56
  23. package/frontend/postcss.config.js +0 -5
  24. package/frontend/postcss.config.mjs +0 -5
  25. package/frontend/public/favicon.png +0 -0
  26. package/frontend/public/file.svg +0 -1
  27. package/frontend/public/globe.svg +0 -1
  28. package/frontend/public/manifest.json +0 -15
  29. package/frontend/public/next.svg +0 -1
  30. package/frontend/public/vercel.svg +0 -1
  31. package/frontend/public/vite.svg +0 -1
  32. package/frontend/public/window.svg +0 -1
  33. package/frontend/src/App.css +0 -42
  34. package/frontend/src/App.tsx +0 -177
  35. package/frontend/src/app/about/page.tsx +0 -208
  36. package/frontend/src/app/favicon.ico +0 -0
  37. package/frontend/src/app/globals.css +0 -129
  38. package/frontend/src/app/help/page.tsx +0 -167
  39. package/frontend/src/app/how-it-works/page.tsx +0 -274
  40. package/frontend/src/app/layout.tsx +0 -55
  41. package/frontend/src/app/marketplace/page.tsx +0 -324
  42. package/frontend/src/app/my-listings/page.tsx +0 -318
  43. package/frontend/src/app/page.tsx +0 -15
  44. package/frontend/src/assets/react.svg +0 -1
  45. package/frontend/src/components/ConfirmDialog.tsx +0 -54
  46. package/frontend/src/components/CreateListingForm.tsx +0 -231
  47. package/frontend/src/components/ErrorBoundary.tsx +0 -73
  48. package/frontend/src/components/FilterPanel.tsx +0 -10
  49. package/frontend/src/components/Footer.tsx +0 -100
  50. package/frontend/src/components/Header.tsx +0 -268
  51. package/frontend/src/components/ImageUpload.tsx +0 -147
  52. package/frontend/src/components/LandingPage.tsx +0 -322
  53. package/frontend/src/components/ListingCard.tsx +0 -154
  54. package/frontend/src/components/LoadingSkeleton.tsx +0 -44
  55. package/frontend/src/components/MobileNav.tsx +0 -89
  56. package/frontend/src/components/NotificationBell.tsx +0 -8
  57. package/frontend/src/components/NotificationPanel.tsx +0 -14
  58. package/frontend/src/components/README.md +0 -14
  59. package/frontend/src/components/SearchBar.tsx +0 -10
  60. package/frontend/src/components/TestnetBanner.tsx +0 -29
  61. package/frontend/src/components/ThemeToggle.tsx +0 -32
  62. package/frontend/src/components/__tests__/Header.test.tsx +0 -70
  63. package/frontend/src/components/__tests__/ListingCard.test.tsx +0 -86
  64. package/frontend/src/components/providers/ThemeProvider.tsx +0 -9
  65. package/frontend/src/components/ui/alert-dialog.tsx +0 -141
  66. package/frontend/src/components/ui/avatar.tsx +0 -53
  67. package/frontend/src/components/ui/badge.tsx +0 -46
  68. package/frontend/src/components/ui/button.tsx +0 -60
  69. package/frontend/src/components/ui/card.tsx +0 -92
  70. package/frontend/src/components/ui/dialog.tsx +0 -143
  71. package/frontend/src/components/ui/dropdown-menu.tsx +0 -257
  72. package/frontend/src/components/ui/input.tsx +0 -21
  73. package/frontend/src/components/ui/label.tsx +0 -24
  74. package/frontend/src/components/ui/select.tsx +0 -187
  75. package/frontend/src/components/ui/sonner.tsx +0 -40
  76. package/frontend/src/components/ui/textarea.tsx +0 -18
  77. package/frontend/src/context/README.md +0 -27
  78. package/frontend/src/index.css +0 -166
  79. package/frontend/src/lib/notificationEvents.ts +0 -10
  80. package/frontend/src/lib/notificationStore.ts +0 -13
  81. package/frontend/src/lib/notifications.ts +0 -13
  82. package/frontend/src/lib/search.ts +0 -28
  83. package/frontend/src/lib/stacks.ts +0 -189
  84. package/frontend/src/lib/utils.ts +0 -6
  85. package/frontend/src/main.tsx +0 -10
  86. package/frontend/src/test/setup.ts +0 -23
  87. package/frontend/src/types.d.ts +0 -9
  88. package/frontend/tsconfig.app.json +0 -28
  89. package/frontend/tsconfig.json +0 -41
  90. package/frontend/tsconfig.node.json +0 -26
  91. package/frontend/vercel.json +0 -4
  92. package/frontend/vite.config.ts +0 -6
  93. package/frontend/vitest.config.ts +0 -17
  94. package/scripts/auto-activity.sh +0 -9
  95. package/scripts/cancel-pending.ts +0 -67
  96. package/scripts/check-balances.ts +0 -23
  97. package/scripts/distribute-evenly.ts +0 -56
  98. package/scripts/drain-accounts.ts +0 -70
  99. package/scripts/fund-accounts.ts +0 -88
  100. package/scripts/fund-active.ts +0 -59
  101. package/scripts/fund-unfunded.ts +0 -88
  102. package/scripts/generate-activity.ts +0 -181
  103. package/scripts/git-activity-generator.ts +0 -154
  104. package/scripts/mobile-server.ts +0 -123
@@ -1,324 +0,0 @@
1
- 'use client';
2
-
3
- import { useState, useEffect } from 'react';
4
- import Header from '@/components/Header';
5
- import Footer from '@/components/Footer';
6
- import ListingCard from '@/components/ListingCard';
7
- import CreateListingForm from '@/components/CreateListingForm';
8
- import TestnetBanner from '@/components/TestnetBanner';
9
- import { ListingGridSkeleton } from '@/components/LoadingSkeleton';
10
- import { getListings, userSession, Listing, contractAddress, contractName, getUserBalance, isWalletConnected, getConnectedAddress, network } from '@/lib/stacks';
11
- import { openContractCall } from '@stacks/connect';
12
- import { uintCV, stringUtf8CV } from '@stacks/transactions';
13
- import { toast } from 'sonner';
14
- import { Input } from '@/components/ui/input';
15
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
16
- import { Search, AlertTriangle, ArrowUpDown, Package } from 'lucide-react';
17
-
18
- export default function Home() {
19
- const [listings, setListings] = useState<Listing[]>([]);
20
- const [loading, setLoading] = useState(true);
21
- const [userAddress, setUserAddress] = useState<string>('');
22
- const [searchQuery, setSearchQuery] = useState<string>('');
23
- const [sortBy, setSortBy] = useState<'newest' | 'price-low' | 'price-high' | 'ending-soon'>('newest');
24
-
25
- useEffect(() => {
26
- loadListings();
27
- const address = getConnectedAddress();
28
- if (address) {
29
- setUserAddress(address);
30
- }
31
- }, []);
32
-
33
- const loadListings = async () => {
34
- try {
35
- const fetchedListings = await getListings();
36
- setListings(fetchedListings);
37
- } catch (error) {
38
- console.error('Error loading listings:', error);
39
- toast.error('Failed to load listings. Please try again.');
40
- } finally {
41
- setLoading(false);
42
- }
43
- };
44
-
45
- const handleCreateListing = async (data: {
46
- name: string;
47
- description: string;
48
- price: number;
49
- duration: number;
50
- imageUrl?: string;
51
- }) => {
52
- if (!isWalletConnected()) {
53
- toast.error('Please connect your wallet first');
54
- return;
55
- }
56
-
57
- const functionArgs = [
58
- stringUtf8CV(data.name),
59
- stringUtf8CV(data.description),
60
- uintCV(data.price),
61
- uintCV(data.duration),
62
- ];
63
-
64
- const options = {
65
- contractAddress,
66
- contractName,
67
- functionName: 'create-listing',
68
- functionArgs,
69
- network,
70
- appDetails: {
71
- name: 'Strade',
72
- icon: window.location.origin + '/favicon.ico',
73
- },
74
- onFinish: (result: { txId: string; stacksTransaction: unknown }) => {
75
- toast.dismiss();
76
- toast.success('Listing created successfully!', {
77
- description: `Transaction ID: ${result.txId.slice(0, 10)}...`,
78
- });
79
- // Reload listings immediately
80
- loadListings();
81
- },
82
- onCancel: () => {
83
- toast.dismiss();
84
- toast.info('Transaction cancelled');
85
- },
86
- };
87
-
88
- try {
89
- toast.loading('Waiting for transaction approval...');
90
- await openContractCall(options);
91
- } catch (error) {
92
- console.error('Error creating listing:', error);
93
- toast.dismiss();
94
- toast.error('Failed to create listing');
95
- }
96
- };
97
-
98
- const handlePurchaseListing = async (listingId: number) => {
99
- if (!isWalletConnected()) {
100
- toast.error('Please connect your wallet first');
101
- return;
102
- }
103
-
104
- // Find the listing to check price
105
- const listing = listings.find(l => l.listingId === listingId);
106
- if (!listing) {
107
- toast.error('Listing not found');
108
- return;
109
- }
110
-
111
- // Check user balance
112
- try {
113
- const address = getConnectedAddress();
114
- if (!address) {
115
- toast.error('Unable to get wallet address');
116
- return;
117
- }
118
- const balance = await getUserBalance(address);
119
-
120
- if (balance < listing.price) {
121
- toast.error('Insufficient balance', {
122
- description: `You need ${(listing.price / 1000000).toFixed(6)} STX but only have ${(balance / 1000000).toFixed(6)} STX`,
123
- icon: <AlertTriangle className="h-5 w-5" />,
124
- });
125
- return;
126
- }
127
- } catch (error) {
128
- console.error('Error checking balance:', error);
129
- }
130
-
131
- const functionArgs = [uintCV(listingId)];
132
-
133
- const options = {
134
- contractAddress,
135
- contractName,
136
- functionName: 'purchase-listing',
137
- functionArgs,
138
- network,
139
- appDetails: {
140
- name: 'Strade',
141
- icon: window.location.origin + '/favicon.ico',
142
- },
143
- onFinish: (result: { txId: string; stacksTransaction: unknown }) => {
144
- toast.dismiss();
145
- toast.success('Purchase successful!', {
146
- description: `Transaction ID: ${result.txId.slice(0, 10)}...`,
147
- });
148
- // Reload listings immediately
149
- loadListings();
150
- },
151
- onCancel: () => {
152
- toast.dismiss();
153
- toast.info('Purchase cancelled');
154
- },
155
- };
156
-
157
- try {
158
- toast.loading('Processing purchase...');
159
- await openContractCall(options);
160
- } catch (error) {
161
- console.error('Error purchasing listing:', error);
162
- toast.dismiss();
163
- toast.error('Failed to purchase listing');
164
- }
165
- };
166
-
167
- const handleCancelListing = async (listingId: number) => {
168
- if (!isWalletConnected()) {
169
- toast.error('Please connect your wallet first');
170
- return;
171
- }
172
-
173
- const functionArgs = [uintCV(listingId)];
174
-
175
- const options = {
176
- contractAddress,
177
- contractName,
178
- functionName: 'cancel-listing',
179
- functionArgs,
180
- network,
181
- appDetails: {
182
- name: 'Strade',
183
- icon: window.location.origin + '/favicon.ico',
184
- },
185
- onFinish: (result: { txId: string; stacksTransaction: unknown }) => {
186
- toast.dismiss();
187
- toast.success('Listing cancelled successfully!', {
188
- description: `Transaction ID: ${result.txId.slice(0, 10)}...`,
189
- });
190
- // Reload listings immediately
191
- loadListings();
192
- },
193
- onCancel: () => {
194
- toast.dismiss();
195
- toast.info('Cancellation cancelled');
196
- },
197
- };
198
-
199
- try {
200
- toast.loading('Cancelling listing...');
201
- await openContractCall(options);
202
- } catch (error) {
203
- console.error('Error cancelling listing:', error);
204
- toast.dismiss();
205
- toast.error('Failed to cancel listing');
206
- }
207
- };
208
-
209
- // Filter and sort listings
210
- const filteredListings = listings
211
- .filter((listing) => {
212
- if (!searchQuery) return true;
213
- const query = searchQuery.toLowerCase();
214
- return (
215
- listing.name.toLowerCase().includes(query) ||
216
- listing.description.toLowerCase().includes(query)
217
- );
218
- })
219
- .sort((a, b) => {
220
- switch (sortBy) {
221
- case 'price-low':
222
- return a.price - b.price;
223
- case 'price-high':
224
- return b.price - a.price;
225
- case 'ending-soon':
226
- return a.expiresAt - b.expiresAt;
227
- case 'newest':
228
- default:
229
- return b.listingId - a.listingId;
230
- }
231
- });
232
-
233
- return (
234
- <div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50/30 to-purple-50/30 dark:from-slate-950 dark:via-slate-900 dark:to-slate-900 flex flex-col transition-colors">
235
- <TestnetBanner />
236
- <Header />
237
-
238
- <main className="flex-1 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 w-full">
239
- <div className="mb-8">
240
- <h1 className="text-3xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent mb-2">
241
- Decentralized Marketplace
242
- </h1>
243
- <p className="text-slate-600 dark:text-slate-400">
244
- Buy and sell goods securely using smart contracts on the Stacks blockchain
245
- </p>
246
- </div>
247
-
248
- <div className="flex flex-col gap-4 mb-6">
249
- <div className="flex flex-col sm:flex-row gap-4">
250
- <CreateListingForm onCreateListing={handleCreateListing} />
251
-
252
- <div className="flex-1 relative">
253
- <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-slate-400" />
254
- <Input
255
- type="text"
256
- placeholder="Search listings..."
257
- value={searchQuery}
258
- onChange={(e) => setSearchQuery(e.target.value)}
259
- className="pl-10"
260
- />
261
- </div>
262
-
263
- <Select value={sortBy} onValueChange={(value: 'newest' | 'price-low' | 'price-high' | 'ending-soon') => setSortBy(value)}>
264
- <SelectTrigger className="w-full sm:w-[200px]">
265
- <ArrowUpDown className="h-4 w-4 mr-2" />
266
- <SelectValue placeholder="Sort by" />
267
- </SelectTrigger>
268
- <SelectContent>
269
- <SelectItem value="newest">Newest First</SelectItem>
270
- <SelectItem value="price-low">Price: Low to High</SelectItem>
271
- <SelectItem value="price-high">Price: High to Low</SelectItem>
272
- <SelectItem value="ending-soon">Ending Soon</SelectItem>
273
- </SelectContent>
274
- </Select>
275
- </div>
276
-
277
- {filteredListings.length > 0 && (
278
- <div className="text-sm text-slate-600 dark:text-slate-400">
279
- Showing {filteredListings.length} {filteredListings.length === 1 ? 'listing' : 'listings'}
280
- </div>
281
- )}
282
- </div>
283
-
284
- {loading ? (
285
- <ListingGridSkeleton count={6} />
286
- ) : filteredListings.length === 0 ? (
287
- <div className="text-center py-12 bg-white dark:bg-slate-800 rounded-lg shadow-sm">
288
- <Package className="h-16 w-16 mx-auto text-slate-300 dark:text-slate-600 mb-4" />
289
- <p className="text-slate-600 dark:text-slate-300 text-lg font-medium">
290
- {searchQuery ? 'No listings match your search.' : 'No active listings found.'}
291
- </p>
292
- <p className="text-sm text-slate-500 dark:text-slate-400 mt-2">
293
- {searchQuery ? 'Try a different search term.' : 'Be the first to create a listing!'}
294
- </p>
295
- </div>
296
- ) : (
297
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
298
- {filteredListings.map((listing) => (
299
- <ListingCard
300
- key={listing.listingId}
301
- listing={listing}
302
- onPurchase={handlePurchaseListing}
303
- onCancel={handleCancelListing}
304
- isOwner={listing.seller === userAddress}
305
- />
306
- ))}
307
- </div>
308
- )}
309
- </main>
310
-
311
- <Footer />
312
- </div>
313
- );
314
- }
315
- // import SearchBar
316
- // import FilterPanel
317
- // search state
318
- // filter state
319
- // empty state
320
- // results count
321
- // pagination
322
- // sort
323
- // debounce
324
- // useMemo
@@ -1,318 +0,0 @@
1
- 'use client';
2
-
3
- import { useState, useEffect } from 'react';
4
- import { useRouter } from 'next/navigation';
5
- import Header from '@/components/Header';
6
- import Footer from '@/components/Footer';
7
- import TestnetBanner from '@/components/TestnetBanner';
8
- import ListingCard from '@/components/ListingCard';
9
- import { ListingGridSkeleton } from '@/components/LoadingSkeleton';
10
- import { getListings, userSession, Listing, contractAddress, contractName, isWalletConnected, getConnectedAddress, network } from '@/lib/stacks';
11
- import { Card, CardContent } from '@/components/ui/card';
12
- import { Button } from '@/components/ui/button';
13
- import { Badge } from '@/components/ui/badge';
14
- import { Package, TrendingUp, Clock, CheckCircle, XCircle } from 'lucide-react';
15
- import { toast } from 'sonner';
16
- import { openContractCall } from '@stacks/connect';
17
- import { uintCV } from '@stacks/transactions';
18
-
19
- type StatusFilter = 'all' | 'active' | 'sold' | 'expired';
20
-
21
- export default function MyListings() {
22
- const router = useRouter();
23
- const [listings, setListings] = useState<Listing[]>([]);
24
- const [loading, setLoading] = useState(true);
25
-
26
- const [statusFilter, setStatusFilter] = useState<StatusFilter>('all');
27
-
28
- useEffect(() => {
29
- if (!isWalletConnected()) {
30
- // Redirect to home if not connected
31
- router.push('/');
32
- return;
33
- }
34
-
35
- const address = getConnectedAddress();
36
- if (address) {
37
- loadMyListings(address);
38
- }
39
- }, [router]);
40
-
41
- const loadMyListings = async (address: string) => {
42
- try {
43
- // Fetch all listings including sold and expired ones
44
- const allListings = await getListings(true);
45
- // Filter to show all user's listings (not just active)
46
- const myListings = allListings.filter(
47
- (listing) => listing.seller.toLowerCase() === address.toLowerCase()
48
- );
49
- setListings(myListings);
50
- } catch (error) {
51
- console.error('Error loading listings:', error);
52
- toast.error('Failed to load your listings');
53
- } finally {
54
- setLoading(false);
55
- }
56
- };
57
-
58
- const handleCancelListing = async (listingId: number) => {
59
- if (!isWalletConnected()) {
60
- toast.error('Please connect your wallet first');
61
- return;
62
- }
63
-
64
- const functionArgs = [uintCV(listingId)];
65
-
66
- const options = {
67
- contractAddress,
68
- contractName,
69
- functionName: 'cancel-listing',
70
- functionArgs,
71
- network,
72
- appDetails: {
73
- name: 'Strade',
74
- icon: window.location.origin + '/favicon.ico',
75
- },
76
- onFinish: (result: { txId: string; stacksTransaction: unknown }) => {
77
- toast.dismiss();
78
- toast.success('Listing cancelled successfully!', {
79
- description: `Transaction ID: ${result.txId.slice(0, 10)}...`,
80
- });
81
- // Reload listings immediately
82
- const address = getConnectedAddress();
83
- if (address) {
84
- loadMyListings(address);
85
- }
86
- },
87
- onCancel: () => {
88
- toast.dismiss();
89
- toast.info('Cancellation cancelled');
90
- },
91
- };
92
-
93
- try {
94
- toast.loading('Cancelling listing...');
95
- await openContractCall(options);
96
- } catch (error) {
97
- console.error('Error cancelling listing:', error);
98
- toast.dismiss();
99
- toast.error('Failed to cancel listing');
100
- }
101
- };
102
-
103
- // Calculate statistics
104
- const stats = {
105
- total: listings.length,
106
- active: listings.filter((l) => l.status === 'active' && l.expiresAt > Date.now() / 1000).length,
107
- sold: listings.filter((l) => l.status === 'sold').length,
108
- expired: listings.filter((l) => l.expiresAt < Date.now() / 1000 && l.status === 'active').length,
109
- };
110
-
111
- // Filter listings based on selected status
112
- const filteredListings = listings.filter((listing) => {
113
- const isExpired = listing.expiresAt < Date.now() / 1000;
114
-
115
- if (statusFilter === 'all') return true;
116
- if (statusFilter === 'active') return listing.status === 'active' && !isExpired;
117
- if (statusFilter === 'sold') return listing.status === 'sold';
118
- if (statusFilter === 'expired') return isExpired && listing.status === 'active';
119
- return true;
120
- });
121
-
122
- const StatCard = ({
123
- icon: Icon,
124
- label,
125
- value,
126
- color
127
- }: {
128
- icon: React.ComponentType<{ className?: string }>;
129
- label: string;
130
- value: number;
131
- color: string;
132
- }) => (
133
- <Card className="hover:shadow-lg transition-all hover:-translate-y-1 bg-white dark:bg-slate-800 border-slate-200 dark:border-slate-700">
134
- <CardContent className="p-6">
135
- <div className="flex items-center justify-between">
136
- <div>
137
- <p className="text-sm font-medium text-slate-600 dark:text-slate-400">{label}</p>
138
- <p className="text-3xl font-bold text-slate-900 dark:text-white mt-2">{value}</p>
139
- </div>
140
- <div className={`p-3 rounded-full ${color}`}>
141
- <Icon className="h-6 w-6 text-white" />
142
- </div>
143
- </div>
144
- </CardContent>
145
- </Card>
146
- );
147
-
148
- const EmptyState = () => (
149
- <div className="text-center py-16 px-4 bg-white dark:bg-slate-800 rounded-lg shadow-sm">
150
- <div className="max-w-md mx-auto">
151
- <div className="mb-6 flex justify-center">
152
- <div className="p-6 bg-slate-100 dark:bg-slate-700 rounded-full">
153
- <Package className="h-16 w-16 text-slate-400 dark:text-slate-500" />
154
- </div>
155
- </div>
156
- <h3 className="text-2xl font-bold text-slate-900 dark:text-white mb-3">
157
- No listings yet
158
- </h3>
159
- <p className="text-slate-600 dark:text-slate-400 mb-8">
160
- Start selling on the decentralized marketplace by creating your first listing.
161
- </p>
162
- <Button
163
- size="lg"
164
- onClick={() => router.push('/')}
165
- className="shadow-lg hover:shadow-xl transition-shadow bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white border-0"
166
- >
167
- Create Your First Listing
168
- </Button>
169
- </div>
170
- </div>
171
- );
172
-
173
- const FilterButton = ({
174
- filter,
175
- label,
176
- count
177
- }: {
178
- filter: StatusFilter;
179
- label: string;
180
- count: number;
181
- }) => (
182
- <button
183
- onClick={() => setStatusFilter(filter)}
184
- className={`px-4 py-2 rounded-lg font-medium transition-all ${
185
- statusFilter === filter
186
- ? 'bg-slate-900 text-white shadow-md'
187
- : 'bg-white text-slate-600 hover:bg-slate-50 border border-slate-200'
188
- }`}
189
- >
190
- {label}
191
- <Badge
192
- variant={statusFilter === filter ? 'secondary' : 'outline'}
193
- className="ml-2"
194
- >
195
- {count}
196
- </Badge>
197
- </button>
198
- );
199
-
200
- if (loading) {
201
- return (
202
- <div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50/30 to-purple-50/30 dark:from-slate-950 dark:via-slate-900 dark:to-slate-900 flex flex-col transition-colors">
203
- <TestnetBanner />
204
- <Header />
205
- <main className="flex-1 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 w-full">
206
- <div className="mb-8">
207
- <h1 className="text-3xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent mb-2">My Listings</h1>
208
- </div>
209
- <ListingGridSkeleton count={6} />
210
- </main>
211
- <Footer />
212
- </div>
213
- );
214
- }
215
-
216
- return (
217
- <div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50/30 to-purple-50/30 dark:from-slate-950 dark:via-slate-900 dark:to-slate-900 flex flex-col transition-colors">
218
- <TestnetBanner />
219
- <Header />
220
-
221
- <main className="flex-1 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 w-full">
222
- {/* Page Header */}
223
- <div className="mb-8">
224
- <h1 className="text-3xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent mb-2">
225
- My Listings
226
- </h1>
227
- <p className="text-slate-600 dark:text-slate-400">
228
- Manage and track all your marketplace listings
229
- </p>
230
- </div>
231
-
232
- {listings.length === 0 ? (
233
- <EmptyState />
234
- ) : (
235
- <>
236
- {/* Statistics Cards */}
237
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
238
- <StatCard
239
- icon={Package}
240
- label="Total Listings"
241
- value={stats.total}
242
- color="bg-blue-500"
243
- />
244
- <StatCard
245
- icon={TrendingUp}
246
- label="Active"
247
- value={stats.active}
248
- color="bg-green-500"
249
- />
250
- <StatCard
251
- icon={CheckCircle}
252
- label="Sold"
253
- value={stats.sold}
254
- color="bg-purple-500"
255
- />
256
- <StatCard
257
- icon={Clock}
258
- label="Expired"
259
- value={stats.expired}
260
- color="bg-orange-500"
261
- />
262
- </div>
263
-
264
- {/* Filter Tabs */}
265
- <div className="mb-8 flex flex-wrap gap-3">
266
- <FilterButton filter="all" label="All" count={stats.total} />
267
- <FilterButton filter="active" label="Active" count={stats.active} />
268
- <FilterButton filter="sold" label="Sold" count={stats.sold} />
269
- <FilterButton filter="expired" label="Expired" count={stats.expired} />
270
- </div>
271
-
272
- {/* Listings Grid */}
273
- {filteredListings.length === 0 ? (
274
- <div className="text-center py-12 bg-white rounded-lg border border-slate-200">
275
- <XCircle className="h-12 w-12 text-slate-400 mx-auto mb-4" />
276
- <p className="text-slate-600">
277
- No {statusFilter !== 'all' ? statusFilter : ''} listings found.
278
- </p>
279
- </div>
280
- ) : (
281
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
282
- {filteredListings.map((listing) => {
283
- const isExpired = listing.expiresAt < Date.now() / 1000;
284
- const displayStatus = isExpired && listing.status === 'active' ? 'expired' : listing.status;
285
-
286
- return (
287
- <div key={listing.listingId} className="relative">
288
- {/* Status Badge Overlay */}
289
- <div className="absolute top-4 right-4 z-10">
290
- <Badge
291
- variant={
292
- displayStatus === 'active' ? 'default' :
293
- displayStatus === 'sold' ? 'secondary' :
294
- 'destructive'
295
- }
296
- className="shadow-md"
297
- >
298
- {displayStatus.toUpperCase()}
299
- </Badge>
300
- </div>
301
- <ListingCard
302
- listing={listing}
303
- onCancel={handleCancelListing}
304
- isOwner={true}
305
- />
306
- </div>
307
- );
308
- })}
309
- </div>
310
- )}
311
- </>
312
- )}
313
- </main>
314
-
315
- <Footer />
316
- </div>
317
- );
318
- }
@@ -1,15 +0,0 @@
1
- import Header from '@/components/Header';
2
- import Footer from '@/components/Footer';
3
- import TestnetBanner from '@/components/TestnetBanner';
4
- import LandingPage from '@/components/LandingPage';
5
-
6
- export default function Home() {
7
- return (
8
- <div className="min-h-screen flex flex-col">
9
- <TestnetBanner />
10
- <Header />
11
- <LandingPage />
12
- <Footer />
13
- </div>
14
- );
15
- }
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>