From f886745135c5ee21edf48b57299c5be965e68876 Mon Sep 17 00:00:00 2001 From: edwin Date: Tue, 3 Feb 2026 22:03:33 -0800 Subject: [PATCH] First Commit --- app/backend/.env | 4 ++ app/backend/server.py | 90 ++++++++++++++++++++++++++ app/design_guidelines.json | 116 +++++++++++++++++++++++++++++++++ app/frontend/.env | 4 ++ app/frontend/src/App.css | 36 +++++++++++ app/frontend/src/App.js | 56 ++++++++++++++++ app/frontend/src/index.css | 117 ++++++++++++++++++++++++++++++++++ app/frontend/src/package.json | 93 +++++++++++++++++++++++++++ 8 files changed, 516 insertions(+) create mode 100644 app/backend/.env create mode 100644 app/backend/server.py create mode 100644 app/design_guidelines.json create mode 100644 app/frontend/.env create mode 100644 app/frontend/src/App.css create mode 100644 app/frontend/src/App.js create mode 100644 app/frontend/src/index.css create mode 100644 app/frontend/src/package.json diff --git a/app/backend/.env b/app/backend/.env new file mode 100644 index 0000000..3e5fa2b --- /dev/null +++ b/app/backend/.env @@ -0,0 +1,4 @@ +MONGO_URL="mongodb://localhost:27017" +DB_NAME="test_database" +CORS_ORIGINS="*" + diff --git a/app/backend/server.py b/app/backend/server.py new file mode 100644 index 0000000..36e1780 --- /dev/null +++ b/app/backend/server.py @@ -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() + diff --git a/app/design_guidelines.json b/app/design_guidelines.json new file mode 100644 index 0000000..1ebd516 --- /dev/null +++ b/app/design_guidelines.json @@ -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" + ] + } + + \ No newline at end of file diff --git a/app/frontend/.env b/app/frontend/.env new file mode 100644 index 0000000..a2272d9 --- /dev/null +++ b/app/frontend/.env @@ -0,0 +1,4 @@ +REACT_APP_BACKEND_URL=https://cloud-service-pro.preview.emergentagent.com +WDS_SOCKET_PORT=443 +ENABLE_HEALTH_CHECK=false + diff --git a/app/frontend/src/App.css b/app/frontend/src/App.css new file mode 100644 index 0000000..77906dc --- /dev/null +++ b/app/frontend/src/App.css @@ -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); + } +} + + diff --git a/app/frontend/src/App.js b/app/frontend/src/App.js new file mode 100644 index 0000000..97170b2 --- /dev/null +++ b/app/frontend/src/App.js @@ -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 ( +
+
+ + + +

Building something incredible ~!

+
+
+ ); +}; + +function App() { + return ( +
+ + + }> + } /> + + + +
+ ); +} + +export default App; + + diff --git a/app/frontend/src/index.css b/app/frontend/src/index.css new file mode 100644 index 0000000..e619509 --- /dev/null +++ b/app/frontend/src/index.css @@ -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; + } +} + + diff --git a/app/frontend/src/package.json b/app/frontend/src/package.json new file mode 100644 index 0000000..dfb4e38 --- /dev/null +++ b/app/frontend/src/package.json @@ -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" + } + + + \ No newline at end of file