Skip to content

Commit 28952b8

Browse files
authored
feat: marketplace (#345)
1 parent ddafb8f commit 28952b8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+19166
-0
lines changed

platforms/marketplace/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
dist
3+
.DS_Store
4+
server/public
5+
vite.config.ts.*
6+
*.tar.gz

platforms/marketplace/README.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Overview
2+
3+
This is a full-stack web application built as an app marketplace or directory. The platform allows users to browse and review applications, while providing administrative capabilities for managing app listings. The application features a public-facing frontend for discovering apps and submitting reviews, alongside an admin dashboard for content management.
4+
5+
# User Preferences
6+
7+
Preferred communication style: Simple, everyday language.
8+
9+
# Recent Changes (Latest First)
10+
11+
## Admin Dashboard Improvements (August 11, 2025)
12+
- Simplified admin login page to basic card - removed registration form, information section, and security notices
13+
- Fixed desktop layout for cleaner, more functional admin dashboard
14+
- Updated "View Public Site" button to open in new tab using target="_blank"
15+
- Renamed "Marketplace Apps" to "Post-Platforms" throughout admin interface
16+
- Added pagination system with 10 items per page for better data management
17+
- Implemented pagination controls with Previous/Next buttons and numbered pages
18+
- Added pagination info showing current range of displayed items
19+
- Downloaded W3DS logo locally and recolored from gray to correct MetaState brand purple (#D9B3FF)
20+
- Removed search functionality from navigation header for cleaner design
21+
- Reduced hero section and categories padding for more compact layout
22+
- Updated main description to "MetaState Post-Platforms for sovereign control of your data"
23+
24+
## Admin Interface Rebranding (August 11, 2025)
25+
- Admin login page completely rebranded with MetaState Foundation styling
26+
- Login and register buttons updated with lime green styling (HSL 85,100%,85%)
27+
- Admin dashboard header modernized with bold typography and branded colors
28+
- Stats cards redesigned with custom rounded containers using MetaState purple and green
29+
- Replaced shadcn Card components with custom styled div containers
30+
- Added hover effects and scaling micro-interactions to buttons
31+
- Fixed JSX syntax errors during rebranding process
32+
- Maintained functional admin authentication system ([email protected] / admin123)
33+
34+
# System Architecture
35+
36+
## Frontend Architecture
37+
- **Framework**: React with TypeScript using Vite as the build tool
38+
- **Routing**: Wouter for client-side navigation
39+
- **UI Components**: Radix UI primitives with shadcn/ui component library
40+
- **Styling**: Tailwind CSS with custom design tokens and CSS variables
41+
- **State Management**: TanStack Query (React Query) for server state management
42+
- **Authentication**: Context-based auth provider with session management
43+
44+
## Backend Architecture
45+
- **Runtime**: Node.js with Express.js framework
46+
- **Language**: TypeScript with ES modules
47+
- **Authentication**: Passport.js with local strategy using session-based auth
48+
- **Password Security**: Built-in crypto module with scrypt for password hashing
49+
- **Session Storage**: In-memory store for development (MemoryStore)
50+
- **API Design**: RESTful endpoints with JSON responses
51+
52+
## Database Architecture
53+
- **Database**: PostgreSQL via Neon serverless
54+
- **ORM**: Drizzle ORM with connection pooling
55+
- **Schema Management**: Drizzle migrations with schema definitions in TypeScript
56+
- **Tables**: Users, apps, and reviews with proper foreign key relationships
57+
- **Features**: UUID primary keys, automatic timestamps, cascade deletions
58+
59+
## File Storage
60+
- **Object Storage**: Google Cloud Storage integration
61+
- **File Uploads**: Uppy file uploader with drag-and-drop interface
62+
- **Access Control**: Custom ACL system for object permissions
63+
- **Storage Client**: Google Cloud Storage SDK with external account credentials via Replit sidecar
64+
65+
## Development Environment
66+
- **Hot Reload**: Vite development server with HMR
67+
- **Error Handling**: Runtime error overlay for development
68+
- **Logging**: Custom request/response logging middleware
69+
- **Build Process**: Vite for frontend bundling, esbuild for server bundling
70+
71+
# External Dependencies
72+
73+
## Core Technologies
74+
- **React**: Frontend framework with hooks and context API
75+
- **Express.js**: Web application framework for Node.js
76+
- **PostgreSQL**: Primary database via Neon serverless platform
77+
- **Drizzle ORM**: Type-safe database operations and migrations
78+
79+
## Authentication & Security
80+
- **Passport.js**: Authentication middleware with local strategy
81+
- **Express Session**: Session management with configurable stores
82+
- **Node.js Crypto**: Built-in cryptographic functions for password hashing
83+
84+
## File Management
85+
- **Google Cloud Storage**: Object storage service for file uploads
86+
- **Uppy**: Modern file uploader with multiple plugins
87+
- **AWS S3 Plugin**: Uppy plugin for S3-compatible storage uploads
88+
89+
## UI & Styling
90+
- **Radix UI**: Headless UI primitives for accessibility
91+
- **Tailwind CSS**: Utility-first CSS framework
92+
- **Lucide React**: SVG icon library
93+
- **shadcn/ui**: Pre-built component library
94+
95+
## Development Tools
96+
- **Vite**: Frontend build tool and development server
97+
- **TypeScript**: Static type checking for JavaScript
98+
- **TanStack Query**: Data fetching and caching library
99+
- **Wouter**: Minimalist routing library for React
100+
101+
## Hosting & Deployment
102+
- **Replit**: Development and hosting platform
103+
- **Neon Database**: Serverless PostgreSQL hosting
104+
- **Google Cloud Platform**: Object storage and authentication services
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
6+
<title>W3DS Marketplace - Discover the MetaState</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/src/main.tsx"></script>
11+
<!-- This is a replit script which adds a banner on the top of the page when opened in development mode outside the replit environment -->
12+
<script type="text/javascript" src="https://replit.com/public/js/replit-dev-banner.js"></script>
13+
</body>
14+
</html>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Switch, Route } from "wouter";
2+
import { queryClient } from "./lib/queryClient";
3+
import { QueryClientProvider } from "@tanstack/react-query";
4+
import { Toaster } from "@/components/ui/toaster";
5+
import { TooltipProvider } from "@/components/ui/tooltip";
6+
import { AuthProvider } from "@/hooks/use-auth";
7+
import { ProtectedRoute } from "@/lib/protected-route";
8+
import HomePage from "@/pages/home-page";
9+
import AppDetailPage from "@/pages/app-detail";
10+
import AdminDashboard from "@/pages/admin-dashboard";
11+
import AuthPage from "@/pages/auth-page";
12+
import NotFound from "@/pages/not-found";
13+
14+
function Router() {
15+
return (
16+
<Switch>
17+
<Route path="/" component={HomePage} />
18+
<Route path="/app/:id" component={AppDetailPage} />
19+
<ProtectedRoute path="/admin" component={AdminDashboard} />
20+
<Route path="/admin/auth" component={AuthPage} />
21+
<Route component={NotFound} />
22+
</Switch>
23+
);
24+
}
25+
26+
function App() {
27+
return (
28+
<QueryClientProvider client={queryClient}>
29+
<TooltipProvider>
30+
<AuthProvider>
31+
<Toaster />
32+
<Router />
33+
</AuthProvider>
34+
</TooltipProvider>
35+
</QueryClientProvider>
36+
);
37+
}
38+
39+
export default App;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { useState } from "react";
2+
import type { ReactNode } from "react";
3+
import Uppy from "@uppy/core";
4+
import { DashboardModal } from "@uppy/react";
5+
import "@uppy/core/dist/style.min.css";
6+
import "@uppy/dashboard/dist/style.min.css";
7+
import AwsS3 from "@uppy/aws-s3";
8+
import type { UploadResult } from "@uppy/core";
9+
import { Button } from "@/components/ui/button";
10+
11+
interface ObjectUploaderProps {
12+
maxNumberOfFiles?: number;
13+
maxFileSize?: number;
14+
onGetUploadParameters: () => Promise<{
15+
method: "PUT";
16+
url: string;
17+
}>;
18+
onComplete?: (
19+
result: UploadResult<Record<string, unknown>, Record<string, unknown>>
20+
) => void;
21+
buttonClassName?: string;
22+
children: ReactNode;
23+
}
24+
25+
export function ObjectUploader({
26+
maxNumberOfFiles = 1,
27+
maxFileSize = 10485760, // 10MB default
28+
onGetUploadParameters,
29+
onComplete,
30+
buttonClassName,
31+
children,
32+
}: ObjectUploaderProps) {
33+
const [showModal, setShowModal] = useState(false);
34+
const [uppy] = useState(() =>
35+
new Uppy({
36+
restrictions: {
37+
maxNumberOfFiles,
38+
maxFileSize,
39+
allowedFileTypes: ['image/*'],
40+
},
41+
autoProceed: false,
42+
})
43+
.use(AwsS3, {
44+
shouldUseMultipart: false,
45+
getUploadParameters: onGetUploadParameters,
46+
})
47+
.on("complete", (result) => {
48+
onComplete?.(result);
49+
setShowModal(false);
50+
})
51+
);
52+
53+
return (
54+
<div>
55+
<Button onClick={() => setShowModal(true)} className={buttonClassName} type="button">
56+
{children}
57+
</Button>
58+
59+
<DashboardModal
60+
uppy={uppy}
61+
open={showModal}
62+
onRequestClose={() => setShowModal(false)}
63+
proudlyDisplayPoweredByUppy={false}
64+
/>
65+
</div>
66+
);
67+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import * as React from "react"
2+
import * as AccordionPrimitive from "@radix-ui/react-accordion"
3+
import { ChevronDown } from "lucide-react"
4+
5+
import { cn } from "@/lib/utils"
6+
7+
const Accordion = AccordionPrimitive.Root
8+
9+
const AccordionItem = React.forwardRef<
10+
React.ElementRef<typeof AccordionPrimitive.Item>,
11+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
12+
>(({ className, ...props }, ref) => (
13+
<AccordionPrimitive.Item
14+
ref={ref}
15+
className={cn("border-b", className)}
16+
{...props}
17+
/>
18+
))
19+
AccordionItem.displayName = "AccordionItem"
20+
21+
const AccordionTrigger = React.forwardRef<
22+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
23+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
24+
>(({ className, children, ...props }, ref) => (
25+
<AccordionPrimitive.Header className="flex">
26+
<AccordionPrimitive.Trigger
27+
ref={ref}
28+
className={cn(
29+
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
30+
className
31+
)}
32+
{...props}
33+
>
34+
{children}
35+
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
36+
</AccordionPrimitive.Trigger>
37+
</AccordionPrimitive.Header>
38+
))
39+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
40+
41+
const AccordionContent = React.forwardRef<
42+
React.ElementRef<typeof AccordionPrimitive.Content>,
43+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
44+
>(({ className, children, ...props }, ref) => (
45+
<AccordionPrimitive.Content
46+
ref={ref}
47+
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
48+
{...props}
49+
>
50+
<div className={cn("pb-4 pt-0", className)}>{children}</div>
51+
</AccordionPrimitive.Content>
52+
))
53+
54+
AccordionContent.displayName = AccordionPrimitive.Content.displayName
55+
56+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }

0 commit comments

Comments
 (0)