diff --git a/platforms/marketplace/assets/blabsy.svg b/platforms/marketplace/assets/blabsy.svg new file mode 100644 index 00000000..8579d246 --- /dev/null +++ b/platforms/marketplace/assets/blabsy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/platforms/marketplace/assets/charter.png b/platforms/marketplace/assets/charter.png new file mode 100644 index 00000000..25f74f45 Binary files /dev/null and b/platforms/marketplace/assets/charter.png differ diff --git a/platforms/marketplace/assets/eid-w3ds.png b/platforms/marketplace/assets/eid-w3ds.png new file mode 100644 index 00000000..66c273d2 Binary files /dev/null and b/platforms/marketplace/assets/eid-w3ds.png differ diff --git a/platforms/marketplace/assets/evoting.png b/platforms/marketplace/assets/evoting.png new file mode 100644 index 00000000..df59f603 Binary files /dev/null and b/platforms/marketplace/assets/evoting.png differ diff --git a/platforms/marketplace/assets/pictique.svg b/platforms/marketplace/assets/pictique.svg new file mode 100644 index 00000000..a8ac8a17 --- /dev/null +++ b/platforms/marketplace/assets/pictique.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms/marketplace/assets/w3dslogo.svg b/platforms/marketplace/assets/w3dslogo.svg new file mode 100644 index 00000000..7a0ad26c --- /dev/null +++ b/platforms/marketplace/assets/w3dslogo.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/platforms/marketplace/client/src/App.tsx b/platforms/marketplace/client/src/App.tsx index 9b098bbd..0251df85 100644 --- a/platforms/marketplace/client/src/App.tsx +++ b/platforms/marketplace/client/src/App.tsx @@ -4,7 +4,6 @@ import { QueryClientProvider } from "@tanstack/react-query"; import { Toaster } from "@/components/ui/toaster"; import { TooltipProvider } from "@/components/ui/tooltip"; import { AuthProvider } from "@/hooks/use-auth"; -import { ProtectedRoute } from "@/lib/protected-route"; import HomePage from "@/pages/home-page"; import AppDetailPage from "@/pages/app-detail"; import AdminDashboard from "@/pages/admin-dashboard"; @@ -16,7 +15,7 @@ function Router() { - + diff --git a/platforms/marketplace/client/src/components/ObjectUploader.tsx b/platforms/marketplace/client/src/components/ObjectUploader.tsx index c9617095..511f88bf 100644 --- a/platforms/marketplace/client/src/components/ObjectUploader.tsx +++ b/platforms/marketplace/client/src/components/ObjectUploader.tsx @@ -1,67 +1,29 @@ -import { useState } from "react"; import type { ReactNode } from "react"; -import Uppy from "@uppy/core"; -import { DashboardModal } from "@uppy/react"; -import "@uppy/core/dist/style.min.css"; -import "@uppy/dashboard/dist/style.min.css"; -import AwsS3 from "@uppy/aws-s3"; -import type { UploadResult } from "@uppy/core"; import { Button } from "@/components/ui/button"; interface ObjectUploaderProps { maxNumberOfFiles?: number; maxFileSize?: number; - onGetUploadParameters: () => Promise<{ + onGetUploadParameters?: () => Promise<{ method: "PUT"; url: string; }>; - onComplete?: ( - result: UploadResult, Record> - ) => void; + onComplete?: (result: any) => void; buttonClassName?: string; children: ReactNode; } export function ObjectUploader({ - maxNumberOfFiles = 1, - maxFileSize = 10485760, // 10MB default - onGetUploadParameters, - onComplete, buttonClassName, children, }: ObjectUploaderProps) { - const [showModal, setShowModal] = useState(false); - const [uppy] = useState(() => - new Uppy({ - restrictions: { - maxNumberOfFiles, - maxFileSize, - allowedFileTypes: ['image/*'], - }, - autoProceed: false, - }) - .use(AwsS3, { - shouldUseMultipart: false, - getUploadParameters: onGetUploadParameters, - }) - .on("complete", (result) => { - onComplete?.(result); - setShowModal(false); - }) - ); - return ( -
- - - setShowModal(false)} - proudlyDisplayPoweredByUppy={false} - /> -
+ ); } diff --git a/platforms/marketplace/client/src/data/apps.json b/platforms/marketplace/client/src/data/apps.json new file mode 100644 index 00000000..00fa9ef8 --- /dev/null +++ b/platforms/marketplace/client/src/data/apps.json @@ -0,0 +1,51 @@ +[ + { + "id": "eid-wallet", + "name": "eID for W3DS", + "description": "Secure digital identity wallet for W3DS. Maintain sovereign control over your digital identity.", + "category": "Identity", + "logoUrl": "/eid-w3ds.png", + "appStoreUrl": "https://apps.apple.com/in/app/eid-for-w3ds/id6747748667", + "playStoreUrl": "https://play.google.com/store/apps/details?id=foundation.metastate.eid_wallet" + }, + { + "id": "blabsy", + "name": "Blabsy", + "description": "Micro blogging first style application for sharing thoughts across the W3DS ecosystem.", + "category": "Social", + "logoUrl": "/blabsy.svg", + "url": "https://blabsy.w3ds.metastate.foundation" + }, + { + "id": "pictique", + "name": "Pictique", + "description": "Photo sharing first style application for sharing moments across the W3DS ecosystem.", + "category": "Social", + "logoUrl": "/pictique.svg", + "url": "https://pictique.w3ds.metastate.foundation" + }, + { + "id": "evoting", + "name": "eVoting", + "description": "Secure, transparent, and verifiable electronic voting platform with cryptographic guarantees.", + "category": "Governance", + "logoUrl": "/evoting.png", + "url": "https://evoting.w3ds.metastate.foundation" + }, + { + "id": "group-charter", + "name": "Charter Manager", + "description": "Define rules, manage memberships, and ensure transparent governance for your communities.", + "category": "Governance", + "logoUrl": "/charter.png", + "url": "https://charter.w3ds.metastate.foundation" + }, + { + "id": "dreamsync", + "name": "DreamSync", + "description": "Individual discovery platform, find people of interest across the W3DS ecosystem.", + "category": "Wellness", + "logoUrl": null, + "url": "https://dreamsync.w3ds.metastate.foundation" + } +] diff --git a/platforms/marketplace/client/src/hooks/use-auth.tsx b/platforms/marketplace/client/src/hooks/use-auth.tsx index ce4ee206..0a7f7a2a 100644 --- a/platforms/marketplace/client/src/hooks/use-auth.tsx +++ b/platforms/marketplace/client/src/hooks/use-auth.tsx @@ -1,111 +1,21 @@ import { createContext, ReactNode, useContext } from "react"; -import { - useQuery, - useMutation, - UseMutationResult, -} from "@tanstack/react-query"; -import { insertUserSchema, User as SelectUser, InsertUser } from "@shared/schema"; -import { getQueryFn, apiRequest, queryClient } from "../lib/queryClient"; -import { useToast } from "@/hooks/use-toast"; +// Simplified auth context - no actual authentication type AuthContextType = { - user: SelectUser | null; + user: null; isLoading: boolean; error: Error | null; - loginMutation: UseMutationResult; - logoutMutation: UseMutationResult; - registerMutation: UseMutationResult; -}; - -type LoginData = { - email: string; - password: string; }; export const AuthContext = createContext(null); export function AuthProvider({ children }: { children: ReactNode }) { - const { toast } = useToast(); - const { - data: user, - error, - isLoading, - } = useQuery({ - queryKey: ["/api/user"], - queryFn: getQueryFn({ on401: "returnNull" }), - }); - - const loginMutation = useMutation({ - mutationFn: async (credentials: LoginData) => { - const res = await apiRequest("POST", "/api/login", credentials); - return await res.json(); - }, - onSuccess: (user: SelectUser) => { - queryClient.setQueryData(["/api/user"], user); - toast({ - title: "Welcome back!", - description: "You have successfully signed in.", - }); - }, - onError: (error: Error) => { - toast({ - title: "Login failed", - description: error.message, - variant: "destructive", - }); - }, - }); - - const registerMutation = useMutation({ - mutationFn: async (credentials: InsertUser) => { - const res = await apiRequest("POST", "/api/register", credentials); - return await res.json(); - }, - onSuccess: (user: SelectUser) => { - queryClient.setQueryData(["/api/user"], user); - toast({ - title: "Account created!", - description: "Welcome to the marketplace admin panel.", - }); - }, - onError: (error: Error) => { - toast({ - title: "Registration failed", - description: error.message, - variant: "destructive", - }); - }, - }); - - const logoutMutation = useMutation({ - mutationFn: async () => { - await apiRequest("POST", "/api/logout"); - }, - onSuccess: () => { - queryClient.setQueryData(["/api/user"], null); - toast({ - title: "Signed out", - description: "You have been successfully signed out.", - }); - }, - onError: (error: Error) => { - toast({ - title: "Logout failed", - description: error.message, - variant: "destructive", - }); - }, - }); - return ( {children} diff --git a/platforms/marketplace/client/src/lib/protected-route.tsx b/platforms/marketplace/client/src/lib/protected-route.tsx deleted file mode 100644 index e5324238..00000000 --- a/platforms/marketplace/client/src/lib/protected-route.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useAuth } from "@/hooks/use-auth"; -import { Loader2 } from "lucide-react"; -import { Redirect, Route } from "wouter"; - -export function ProtectedRoute({ - path, - component: Component, -}: { - path: string; - component: () => React.JSX.Element; -}) { - const { user, isLoading } = useAuth(); - - if (isLoading) { - return ( - -
- -
-
- ); - } - - if (!user?.isAdmin) { - return ( - - - - ); - } - - return ; -} diff --git a/platforms/marketplace/client/src/pages/admin-dashboard.tsx b/platforms/marketplace/client/src/pages/admin-dashboard.tsx index b92961a8..638d521c 100644 --- a/platforms/marketplace/client/src/pages/admin-dashboard.tsx +++ b/platforms/marketplace/client/src/pages/admin-dashboard.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { Link } from "wouter"; -import { App, InsertApp } from "@shared/schema"; +import { App, InsertApp } from "@/types"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -19,7 +19,7 @@ import { useToast } from "@/hooks/use-toast"; import { Plus, Settings, ExternalLink, LogOut, Edit, Trash2, Star, Eye, TrendingUp, BarChart3 } from "lucide-react"; export default function AdminDashboard() { - const { user, logoutMutation } = useAuth(); + const { user } = useAuth(); const { toast } = useToast(); const [showAddModal, setShowAddModal] = useState(false); const [editingApp, setEditingApp] = useState(null); @@ -274,14 +274,7 @@ export default function AdminDashboard() {
- {user?.email} - + Admin
diff --git a/platforms/marketplace/client/src/pages/app-detail.tsx b/platforms/marketplace/client/src/pages/app-detail.tsx index 32e1798f..367c63a3 100644 --- a/platforms/marketplace/client/src/pages/app-detail.tsx +++ b/platforms/marketplace/client/src/pages/app-detail.tsx @@ -1,129 +1,43 @@ -import { useState } from "react"; -import { useQuery, useMutation } from "@tanstack/react-query"; import { useRoute, Link } from "wouter"; -import { App, Review } from "@shared/schema"; -import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; -import { Textarea } from "@/components/ui/textarea"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Separator } from "@/components/ui/separator"; -import { Progress } from "@/components/ui/progress"; -import { Star, ExternalLink, Share, ArrowLeft, Store } from "lucide-react"; -import { apiRequest, queryClient } from "@/lib/queryClient"; -import { useToast } from "@/hooks/use-toast"; +import { ExternalLink, ArrowLeft, Store } from "lucide-react"; +import appsData from "@/data/apps.json"; + +// Mock detailed descriptions for each app +const appDetails: Record = { + "eid-wallet": { + fullDescription: "eID for W3DS is a comprehensive digital identity solution that puts you in control of your personal information. Built on the W3DS framework, it provides secure authentication, verifiable credentials, and seamless integration across the MetaState ecosystem.\n\nWith advanced cryptographic protocols and user-centric design, eID for W3DS ensures your identity remains sovereign and under your control at all times.", + screenshots: [] + }, + "blabsy": { + fullDescription: "Blabsy is a decentralized micro-blogging platform where you own your content and control your data. Share your thoughts, engage with communities, and build your network while maintaining full sovereignty over your digital presence.\n\nExperience social media reimagined with privacy-first principles and community-driven governance.", + screenshots: [] + }, + "pictique": { + fullDescription: "Pictique revolutionizes photo sharing with privacy-first principles. Share your moments with complete control over who sees what, when, and for how long. All while maintaining ownership of your precious memories.\n\nBuilt on the W3DS framework, Pictique ensures your photos are stored securely and shared on your terms.", + screenshots: [] + }, + "evoting": { + fullDescription: "eVoting brings democracy to the digital age with end-to-end verifiable elections. Using advanced cryptographic techniques, every vote is counted accurately while maintaining voter privacy. Perfect for organizations, communities, and governance bodies.\n\nTransparent, secure, and verifiable - democracy as it should be.", + screenshots: [] + }, + "group-charter": { + fullDescription: "Charter Manager empowers communities to establish clear governance structures. Create charters, define rules, manage memberships, and ensure transparent decision-making processes. Build self-sovereign communities with accountable governance.\n\nFrom small groups to large organizations, Charter Manager provides the tools you need for effective community governance.", + screenshots: [] + }, + "dreamsync": { + fullDescription: "DreamSync helps you discover meaningful connections based on shared interests, values, and aspirations. Navigate the W3DS ecosystem to find individuals who resonate with your vision and collaborate on projects that matter.\n\nConnect, collaborate, and create together in the decentralized future.", + screenshots: [] + } +}; export default function AppDetailPage() { const [, params] = useRoute("/app/:id"); const appId = params?.id; - const [showReviewForm, setShowReviewForm] = useState(false); - const [reviewForm, setReviewForm] = useState({ - username: "", - rating: 5, - comment: "", - }); - const { toast } = useToast(); - - const { data: app, isLoading: isLoadingApp } = useQuery({ - queryKey: ["/api/apps", appId], - enabled: !!appId, - }); - - const { data: reviews = [], isLoading: isLoadingReviews } = useQuery({ - queryKey: ["/api/apps", appId, "reviews"], - enabled: !!appId, - }); - - const reviewMutation = useMutation({ - mutationFn: async (reviewData: typeof reviewForm) => { - const res = await apiRequest("POST", `/api/apps/${appId}/reviews`, reviewData); - return await res.json(); - }, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["/api/apps", appId, "reviews"] }); - queryClient.invalidateQueries({ queryKey: ["/api/apps", appId] }); - setShowReviewForm(false); - setReviewForm({ username: "", rating: 5, comment: "" }); - toast({ - title: "Review submitted!", - description: "Thank you for your feedback.", - }); - }, - onError: (error) => { - toast({ - title: "Failed to submit review", - description: error.message, - variant: "destructive", - }); - }, - }); - - const renderStars = (rating: number, interactive = false, onRate?: (rating: number) => void) => { - return ( -
- {[1, 2, 3, 4, 5].map((star) => ( - interactive && onRate?.(star)} - /> - ))} -
- ); - }; - const handleReviewSubmit = (e: React.FormEvent) => { - e.preventDefault(); - if (!reviewForm.username.trim()) { - toast({ - title: "Username required", - description: "Please enter your username.", - variant: "destructive", - }); - return; - } - reviewMutation.mutate(reviewForm); - }; + const app = appsData.find(a => a.id === appId); + const details = appId ? appDetails[appId] : null; - const getRatingDistribution = () => { - const distribution = { 5: 0, 4: 0, 3: 0, 2: 0, 1: 0 }; - reviews.forEach(review => { - distribution[review.rating as keyof typeof distribution]++; - }); - const total = reviews.length || 1; - return Object.entries(distribution).reverse().map(([rating, count]) => ({ - rating: parseInt(rating), - count, - percentage: (count / total) * 100, - })); - }; - - if (isLoadingApp) { - return ( -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ); - } if (!app) { return ( @@ -171,15 +85,6 @@ export default function AppDetailPage() {

{app.name}

{app.description}

-
- {renderStars(parseFloat(app.averageRating || "0"))} - - {parseFloat(app.averageRating || "0").toFixed(1)} - - - ({app.totalReviews} reviews) - -
{app.category}
@@ -188,42 +93,38 @@ export default function AppDetailPage() {
- - - - + {(app as any).appStoreUrl && (app as any).playStoreUrl ? ( + <> + + + + + + + + ) : ( + + + + )}
- {/* Screenshots */} - {app.screenshots && app.screenshots.length > 0 && ( -
-

Screenshots

-
- {app.screenshots.map((screenshot, index) => ( - {`${app.name} - ))} -
-
- )} - {/* Description */} - {app.fullDescription && ( + {details?.fullDescription && (

About this app

- {app.fullDescription.split('\n').map((paragraph, index) => ( + {details.fullDescription.split('\n').map((paragraph, index) => (

{paragraph}

@@ -232,7 +133,17 @@ export default function AppDetailPage() {
)} - {/* Reviews Section */} + {/* Reviews Section - Coming Soon */} +
+
+

Reviews & Ratings

+

Reviews feature coming soon

+
+
+ + {/* + Reviews section commented out for future implementation +

Reviews & Ratings

@@ -243,124 +154,9 @@ export default function AppDetailPage() { {showReviewForm ? "Cancel" : "Write Review"}
- - {/* Rating Summary */} -
-
-
-
- {parseFloat(app.averageRating || "0").toFixed(1)} -
-
- {renderStars(parseFloat(app.averageRating || "0"))} -
-
{app.totalReviews} reviews
-
-
-
- {getRatingDistribution().map(({ rating, count, percentage }) => ( -
- {rating} - - {Math.round(percentage)}% -
- ))} -
-
-
-
- - {/* Review Form */} - {showReviewForm && ( -
-
-
- - setReviewForm(prev => ({ ...prev, username: e.target.value }))} - placeholder="Enter your username" - className="h-12 rounded-full border-2 border-gray-200 focus:border-black font-medium" - required - /> -
-
- -
- {renderStars(reviewForm.rating, true, (rating) => - setReviewForm(prev => ({ ...prev, rating })) - )} - ({reviewForm.rating} stars) -
-
-
- -