First Commit

This commit is contained in:
edwin
2026-02-03 22:03:33 -08:00
commit f886745135
8 changed files with 516 additions and 0 deletions

4
app/backend/.env Normal file
View File

@@ -0,0 +1,4 @@
MONGO_URL="mongodb://localhost:27017"
DB_NAME="test_database"
CORS_ORIGINS="*"

90
app/backend/server.py Normal file
View File

@@ -0,0 +1,90 @@
from fastapi import FastAPI, APIRouter
from dotenv import load_dotenv
from starlette.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
import os
import logging
from pathlib import Path
from pydantic import BaseModel, Field, ConfigDict
from typing import List
import uuid
from datetime import datetime, timezone
ROOT_DIR = Path(__file__).parent
load_dotenv(ROOT_DIR / '.env')
# MongoDB connection
mongo_url = os.environ['MONGO_URL']
client = AsyncIOMotorClient(mongo_url)
db = client[os.environ['DB_NAME']]
# Create the main app without a prefix
app = FastAPI()
# Create a router with the /api prefix
api_router = APIRouter(prefix="/api")
# Define Models
class StatusCheck(BaseModel):
model_config = ConfigDict(extra="ignore") # Ignore MongoDB's _id field
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
client_name: str
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
class StatusCheckCreate(BaseModel):
client_name: str
# Add your routes to the router instead of directly to app
@api_router.get("/")
async def root():
return {"message": "Hello World"}
@api_router.post("/status", response_model=StatusCheck)
async def create_status_check(input: StatusCheckCreate):
status_dict = input.model_dump()
status_obj = StatusCheck(**status_dict)
# Convert to dict and serialize datetime to ISO string for MongoDB
doc = status_obj.model_dump()
doc['timestamp'] = doc['timestamp'].isoformat()
_ = await db.status_checks.insert_one(doc)
return status_obj
@api_router.get("/status", response_model=List[StatusCheck])
async def get_status_checks():
# Exclude MongoDB's _id field from the query results
status_checks = await db.status_checks.find({}, {"_id": 0}).to_list(1000)
# Convert ISO string timestamps back to datetime objects
for check in status_checks:
if isinstance(check['timestamp'], str):
check['timestamp'] = datetime.fromisoformat(check['timestamp'])
return status_checks
# Include the router in the main app
app.include_router(api_router)
app.add_middleware(
CORSMiddleware,
allow_credentials=True,
allow_origins=os.environ.get('CORS_ORIGINS', '*').split(','),
allow_methods=["*"],
allow_headers=["*"],
)
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
@app.on_event("shutdown")
async def shutdown_db_client():
client.close()

116
app/design_guidelines.json Normal file
View File

@@ -0,0 +1,116 @@
{
"identity": {
"persona": "E1",
"tone": "Cyber-Professional",
"archetype": "Jewel & Luxury (Deep Obsidian) + Electric & Neon (Accents)",
"mission": "Build a high-trust, tech-forward MSP & SaaS landing page that feels like a command center."
},
"typography": {
"headings": {
"font_family": "Syne, sans-serif",
"weights": ["700", "800"],
"usage": "Use for all H1-H3. Tight tracking (-0.02em). Uppercase for labels."
},
"body": {
"font_family": "Manrope, sans-serif",
"weights": ["400", "500"],
"usage": "Use for paragraphs, UI text, and buttons. Relaxed leading."
},
"installation": "Import from Google Fonts: https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;700&family=Syne:wght@700;800&display=swap"
},
"colors": {
"palette": {
"background": "#050505",
"surface": "#0A0A0A",
"surface_highlight": "#121212",
"primary": "#00F0FF",
"primary_dim": "rgba(0, 240, 255, 0.1)",
"secondary": "#7000FF",
"text_main": "#FFFFFF",
"text_muted": "#94A3B8",
"border": "#1E293B",
"border_hover": "#334155"
},
"gradients": {
"hero_glow": "radial-gradient(circle at center, rgba(0, 240, 255, 0.15) 0%, rgba(5, 5, 5, 0) 70%)",
"card_hover": "linear-gradient(45deg, rgba(0, 240, 255, 0.1), rgba(112, 0, 255, 0.1))"
}
},
"layout": {
"grid_system": "Bento Grid (Mode A: Tetris)",
"spacing": {
"section_padding": "py-24 md:py-32",
"container_padding": "px-6 md:px-12",
"element_gap": "gap-8"
},
"container_width": "max-w-7xl mx-auto"
},
"components": {
"buttons": {
"primary": "bg-primary text-black font-bold hover:shadow-[0_0_20px_rgba(0,240,255,0.5)] transition-all duration-300 rounded-none skew-x-[-10deg]",
"secondary": "border border-white/20 text-white hover:bg-white/10 transition-all duration-300 rounded-none skew-x-[-10deg]"
},
"cards": {
"style": "bg-surface border border-white/5 hover:border-primary/50 transition-colors duration-300",
"hover_effect": "Add a subtle glow or lift on hover. Use 'group' to animate inner elements."
},
"hero": {
"style": "Full screen height (min-h-screen). Background image with heavy overlay. Text centered or split. Use 'Trace Beam' effect for the main CTA."
}
},
"visual_enhancers": {
"textures": "Add a subtle noise overlay (opacity 0.03) to the entire body to kill the digital flatness.",
"borders": "Use 1px borders with very low opacity (white/10) for structure.",
"shadows": "Use colored shadows (shadow-cyan-500/20) for primary actions."
},
"media": {
"image_urls": {
"hero_bg": "https://images.pexels.com/photos/17489157/pexels-photo-17489157.jpeg",
"service_security": "https://images.unsplash.com/photo-1696013910376-c56f76dd8178?crop=entropy&cs=srgb&fm=jpg&ixid=M3w4NjAzMjV8MHwxfHNlYXJjaHwxfHxjeWJlcnNlY3VyaXR5JTIwZGlnaXRhbCUyMHNoaWVsZCUyMGxvY2slMjBjb25jZXB0fGVufDB8fHx8MTc3MDE4MzQ1Mnww&ixlib=rb-4.1.0&q=85",
"service_cloud": "https://images.unsplash.com/photo-1744868562210-fffb7fa882d9?crop=entropy&cs=srgb&fm=jpg&ixid=M3w4NjAzNTl8MHwxfHNlYXJjaHwxfHxjbG91ZCUyMGNvbXB1dGluZyUyMG5ldHdvcmslMjBjb25uZWN0aW9ufGVufDB8fHx8MTc3MDE4MzQ1N3ww&ixlib=rb-4.1.0&q=85",
"testimonial_1": "https://images.unsplash.com/photo-1689600944138-da3b150d9cb8?crop=entropy&cs=srgb&fm=jpg&ixid=M3w4NjY2NzZ8MHwxfHNlYXJjaHwxfHxwcm9mZXNzaW9uYWwlMjBidXNpbmVzcyUyMHBvcnRyYWl0JTIwaGVhZHNob3R8ZW58MHx8fHwxNzcwMDc5NDA0fDA&ixlib=rb-4.1.0&q=85",
"testimonial_2": "https://images.unsplash.com/photo-1576558656222-ba66febe3dec?crop=entropy&cs=srgb&fm=jpg&ixid=M3w4NjY2NzZ8MHwxfHNlYXJjaHwyfHxwcm9mZXNzaW9uYWwlMjBidXNpbmVzcyUyMHBvcnRyYWl0JTIwaGVhZHNob3R8ZW58MHx8fHwxNzcwMDc5NDA0fDA&ixlib=rb-4.1.0&q=85",
"partner_logo_1": "https://images.unsplash.com/photo-1662057168154-89300791ad6e?crop=entropy&cs=srgb&fm=jpg&ixid=M3w3NTY2NzF8MHwxfHNlYXJjaHwxfHx0ZWNoJTIwY29tcGFueSUyMGxvZ28lMjB2ZWN0b3IlMjB3aGl0ZXxlbnwwfHx8fDE3NzAxODM0NjJ8MA&ixlib=rb-4.1.0&q=85",
"partner_logo_2": "https://images.unsplash.com/photo-1662027044921-6febc57a0c53?crop=entropy&cs=srgb&fm=jpg&ixid=M3w3NTY2NzF8MHwxfHNlYXJjaHwyfHx0ZWNoJTIwY29tcGFueSUyMGxvZ28lMjB2ZWN0b3IlMjB3aGl0ZXxlbnwwfHx8fDE3NzAxODM0NjJ8MA&ixlib=rb-4.1.0&q=85"
}
},
"motion": {
"library": "framer-motion",
"guidelines": "Use 'staggerChildren' for lists. Hero text should slide up with a slight blur reveal. Buttons should have a 'magnetic' feel if possible, or at least a solid scale/color shift."
},
"accessibility": {
"contrast": "Ensure all text on dark backgrounds is at least WCAG AA compliant. Use lighter greys (text-gray-300) instead of dark greys.",
"focus_states": "Visible focus rings in Primary Cyan color."
},
"instructions_to_main_agent": [
"1. Install 'lucide-react' and 'framer-motion' immediately.",
"2. Create a 'Layout' component that wraps the page and handles the dark mode background and noise texture.",
"3. Build the Hero section first. It must be impressive. Use the 'Trace Beam' concept for the CTA.",
"4. For the Services section, use a Bento Grid. Do not just stack cards.",
"5. Use the provided image URLs. Do not use placeholders.",
"6. Ensure the 'Contact' form uses Shadcn components but styled to match the dark theme (no white backgrounds).",
"7. Add 'data-testid' to all interactive elements."
],
"UNIVERSAL_GUIDELINES_FOR_MAIN_AGENT": [
"Dark colors look good when used independently without gradients. DO NOT use dark colors as gradients",
"Don't make generic centered layouts, simplistic gradients, and uniform styling.",
"Create depth through layered design elements with z-index hierarchy",
"Use glass-morphism effects with backdrop filters (12-24px blur)",
"You MUST NOT apply universal transition. Eg: transition: all. This results in breaking transforms. Always add transitions for specific interactive elements like button, input excluding transforms",
"You MUST NOT center align the app container, ie do not add .App { text-align: center; } in the css file. This disrupts the human natural reading flow of text",
"Always use modern button styles like pill-shaped, or sharp button with interaction animations & color relevant to the app style",
"NEVER: use AI assistant Emoji characters like 🤖🧠💭💡🔮🎯📚🎭🎬🎪🎉🎊🎁🎀🎂🍰🎈🎨🎰💰💵💳🏦💎🪙💸🤑📊📈📉💹🔢🏆🥇 etc for icons.",
"Every interaction needs micro-animations - hover states, transitions, parallax effects, and entrance animations. Static = dead.",
"Use 2-3x more spacing than feels comfortable. Cramped designs look cheap.",
"Subtle grain textures, noise overlays, custom cursors, selection states, and loading animations: separates good from extraordinary.",
"Main agent relies on these image URLs. Missing categories (especially sponsor logos) = main agent will use placeholder text which is FORBIDDEN",
"Prioritize using pre-existing components from src/components/ui when applicable",
"Create new components that match the style and conventions of existing components when needed",
"Examine existing components to understand the project's component patterns before creating new ones",
"Components MUST use named exports (export const ComponentName = ...)",
"Pages MUST use default exports (export default function PageName() {...})",
"Use 'sonner' for toasts. Sonner component are located in /app/src/components/ui/sonner.tsx"
]
}

4
app/frontend/.env Normal file
View File

@@ -0,0 +1,4 @@
REACT_APP_BACKEND_URL=https://cloud-service-pro.preview.emergentagent.com
WDS_SOCKET_PORT=443
ENABLE_HEALTH_CHECK=false

36
app/frontend/src/App.css Normal file
View File

@@ -0,0 +1,36 @@
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #0f0f10;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

56
app/frontend/src/App.js Normal file
View File

@@ -0,0 +1,56 @@
import { useEffect } from "react";
import "@/App.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import axios from "axios";
const BACKEND_URL = process.env.REACT_APP_BACKEND_URL;
const API = `${BACKEND_URL}/api`;
const Home = () => {
const helloWorldApi = async () => {
try {
const response = await axios.get(`${API}/`);
console.log(response.data.message);
} catch (e) {
console.error(e, `errored out requesting / api`);
}
};
useEffect(() => {
helloWorldApi();
}, []);
return (
<div>
<header className="App-header">
<a
className="App-link"
href="https://emergent.sh"
target="_blank"
rel="noopener noreferrer"
>
<img src="https://avatars.githubusercontent.com/in/1201222?s=120&u=2686cf91179bbafbc7a71bfbc43004cf9ae1acea&v=4" />
</a>
<p className="mt-5">Building something incredible ~!</p>
</header>
</div>
);
};
function App() {
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />}>
<Route index element={<Home />} />
</Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;

117
app/frontend/src/index.css Normal file
View File

@@ -0,0 +1,117 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
margin: 0;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family:
source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
@layer base {
[data-debug-wrapper="true"] {
display: contents !important;
}
[data-debug-wrapper="true"] > * {
margin-left: inherit;
margin-right: inherit;
margin-top: inherit;
margin-bottom: inherit;
padding-left: inherit;
padding-right: inherit;
padding-top: inherit;
padding-bottom: inherit;
column-gap: inherit;
row-gap: inherit;
gap: inherit;
border-left-width: inherit;
border-right-width: inherit;
border-top-width: inherit;
border-bottom-width: inherit;
border-left-style: inherit;
border-right-style: inherit;
border-top-style: inherit;
border-bottom-style: inherit;
border-left-color: inherit;
border-right-color: inherit;
border-top-color: inherit;
border-bottom-color: inherit;
}
}

View File

@@ -0,0 +1,93 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@hookform/resolvers": "^5.0.1",
"@radix-ui/react-accordion": "^1.2.8",
"@radix-ui/react-alert-dialog": "^1.1.11",
"@radix-ui/react-aspect-ratio": "^1.1.4",
"@radix-ui/react-avatar": "^1.1.7",
"@radix-ui/react-checkbox": "^1.2.3",
"@radix-ui/react-collapsible": "^1.1.8",
"@radix-ui/react-context-menu": "^2.2.12",
"@radix-ui/react-dialog": "^1.1.11",
"@radix-ui/react-dropdown-menu": "^2.1.12",
"@radix-ui/react-hover-card": "^1.1.11",
"@radix-ui/react-label": "^2.1.4",
"@radix-ui/react-menubar": "^1.1.12",
"@radix-ui/react-navigation-menu": "^1.2.10",
"@radix-ui/react-popover": "^1.1.11",
"@radix-ui/react-progress": "^1.1.4",
"@radix-ui/react-radio-group": "^1.3.4",
"@radix-ui/react-scroll-area": "^1.2.6",
"@radix-ui/react-select": "^2.2.2",
"@radix-ui/react-separator": "^1.1.4",
"@radix-ui/react-slider": "^1.3.2",
"@radix-ui/react-slot": "^1.2.0",
"@radix-ui/react-switch": "^1.2.2",
"@radix-ui/react-tabs": "^1.1.9",
"@radix-ui/react-toast": "^1.2.11",
"@radix-ui/react-toggle": "^1.1.6",
"@radix-ui/react-toggle-group": "^1.1.7",
"@radix-ui/react-tooltip": "^1.2.4",
"axios": "^1.8.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"cra-template": "1.2.0",
"date-fns": "^4.1.0",
"embla-carousel-react": "^8.6.0",
"input-otp": "^1.4.2",
"lucide-react": "^0.507.0",
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-day-picker": "8.10.1",
"react-dom": "^19.0.0",
"react-hook-form": "^7.56.2",
"react-resizable-panels": "^3.0.1",
"react-router-dom": "^7.5.1",
"react-scripts": "5.0.1",
"recharts": "^3.6.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.2.0",
"tailwindcss-animate": "^1.0.7",
"vaul": "^1.1.2",
"zod": "^3.24.4"
},
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@craco/craco": "^7.1.0",
"@eslint/js": "9.23.0",
"autoprefixer": "^10.4.20",
"eslint": "9.23.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-react": "7.37.4",
"eslint-plugin-react-hooks": "5.2.0",
"globals": "15.15.0",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}