From 0ddb654d99590d498520483465a5a708dd0f2516 Mon Sep 17 00:00:00 2001 From: Ian Date: Sat, 16 May 2026 11:04:49 +0300 Subject: [PATCH] feat: Initialize Team Project Tracker app Sets up the basic structure for the Team Project Tracker application. This includes: - Initializing React components for project management. - Configuring Vite for development and build processes. - Defining project metadata and environment variables. - Providing instructions for local development. --- .env.example | 9 ++ .gitignore | 8 ++ README.md | 25 ++++-- index.html | 13 +++ metadata.json | 8 ++ package.json | 35 ++++++++ src/App.tsx | 235 +++++++++++++++++++++++++++++++++++++++++++++++++ src/index.css | 1 + src/main.tsx | 10 +++ tsconfig.json | 26 ++++++ vite.config.ts | 26 ++++++ 11 files changed, 388 insertions(+), 8 deletions(-) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 index.html create mode 100644 metadata.json create mode 100644 package.json create mode 100644 src/App.tsx create mode 100644 src/index.css create mode 100644 src/main.tsx create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7a550fe --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# GEMINI_API_KEY: Required for Gemini AI API calls. +# AI Studio automatically injects this at runtime from user secrets. +# Users configure this via the Secrets panel in the AI Studio UI. +GEMINI_API_KEY="MY_GEMINI_API_KEY" + +# APP_URL: The URL where this applet is hosted. +# AI Studio automatically injects this at runtime with the Cloud Run service URL. +# Used for self-referential links, OAuth callbacks, and API endpoints. +APP_URL="MY_APP_URL" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a86d2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +build/ +dist/ +coverage/ +.DS_Store +*.log +.env* +!.env.example diff --git a/README.md b/README.md index 2241000..f220296 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,20 @@
- GHBanner - -

Built with AI Studio

- -

The fastest path from prompt to production with Gemini.

- - Start building -
+ +# Run and deploy your AI Studio app + +This contains everything you need to run your app locally. + +View your app in AI Studio: https://ai.studio/apps/a086d74b-a8b6-4b42-ae0d-08271ccd7b66 + +## Run Locally + +**Prerequisites:** Node.js + + +1. Install dependencies: + `npm install` +2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key +3. Run the app: + `npm run dev` diff --git a/index.html b/index.html new file mode 100644 index 0000000..21dfe69 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + My Google AI Studio App + + +
+ + + + diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..256fd68 --- /dev/null +++ b/metadata.json @@ -0,0 +1,8 @@ +{ + "name": "Team Project Tracker", + "description": "A streamlined collaborative tool for tracking team projects, owners, and real-time status updates.", + "requestFramePermissions": [], + "majorCapabilities": [ + "MAJOR_CAPABILITY_SERVER_SIDE_GEMINI_API" + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9748aee --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "react-example", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --port=3000 --host=0.0.0.0", + "build": "vite build", + "preview": "vite preview", + "clean": "rm -rf dist server.js", + "lint": "tsc --noEmit" + }, + "dependencies": { + "@google/genai": "^1.29.0", + "@tailwindcss/vite": "^4.1.14", + "@vitejs/plugin-react": "^5.0.4", + "lucide-react": "^0.546.0", + "react": "^19.0.1", + "react-dom": "^19.0.1", + "vite": "^6.2.3", + "express": "^4.21.2", + "dotenv": "^17.2.3", + "motion": "^12.23.24" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "autoprefixer": "^10.4.21", + "esbuild": "^0.25.0", + "tailwindcss": "^4.1.14", + "tsx": "^4.21.0", + "typescript": "~5.8.2", + "vite": "^6.2.3", + "@types/express": "^4.17.21" + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..a91002e --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,235 @@ +/** + * @license + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @license + * SPDX-License-Identifier: Apache-2.0 + */ + +import { useState, useEffect, FormEvent } from 'react'; +import { Plus, Trash2, LayoutGrid, CheckCircle2, Circle, Clock } from 'lucide-react'; +import { motion, AnimatePresence } from 'motion/react'; + +type ProjectStatus = 'Not Started' | 'In Progress' | 'Done'; + +interface Project { + id: string; + name: string; + owner: string; + status: ProjectStatus; + createdAt: number; +} + +export default function App() { + const [projects, setProjects] = useState(() => { + const saved = localStorage.getItem('team-projects'); + return saved ? JSON.parse(saved) : []; + }); + + const [formData, setFormData] = useState({ + name: '', + owner: '', + status: 'Not Started' as ProjectStatus, + }); + + useEffect(() => { + localStorage.setItem('team-projects', JSON.stringify(projects)); + }, [projects]); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + if (!formData.name || !formData.owner) return; + + const newProject: Project = { + id: crypto.randomUUID(), + ...formData, + createdAt: Date.now(), + }; + + setProjects([newProject, ...projects]); + setFormData({ name: '', owner: '', status: 'Not Started' }); + }; + + const deleteProject = (id: string) => { + setProjects(projects.filter(p => p.id !== id)); + }; + + const statusColors = { + 'Not Started': 'text-red-500', + 'In Progress': 'text-amber-500', + 'Done': 'text-emerald-500', + }; + + const statusIcons = { + 'Not Started': , + 'In Progress': , + 'Done': , + }; + + return ( +
+
+ + {/* Header */} +
+
+
+ + Workspace +
+

Team Project Tracker

+

+ Manage your team's workload with a simple, high-visibility tracking system. +

+
+
+
+ Total Projects + {projects.length} +
+
+ Completion + + {projects.length > 0 ? Math.round((projects.filter(p => p.status === 'Done').length / projects.length) * 100) : 0}% + +
+
+
+ + {/* Input Form Section */} + +
+
+ + setFormData({ ...formData, name: e.target.value })} + className="w-full bg-white border border-neutral-200 rounded-xl px-4 py-3 text-neutral-600 placeholder:text-neutral-300 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all font-medium" + /> +
+ +
+ + setFormData({ ...formData, owner: e.target.value })} + className="w-full bg-white border border-neutral-200 rounded-xl px-4 py-3 text-neutral-600 placeholder:text-neutral-300 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all font-medium" + /> +
+ +
+ + +
+ + +
+
+ + {/* Projects List Table */} +
+
+ + + + + + + + + + + + {projects.length > 0 ? ( + projects.map((project) => ( + + + + + + + )) + ) : ( + + + + )} + + +
Project DetailTeam MemberStatus UpdateActions
+ {project.name} + Created {new Date(project.createdAt).toLocaleDateString()} + +
+
+ {project.owner.charAt(0).toUpperCase()} +
+ {project.owner} +
+
+ + {statusIcons[project.status]} + {project.status} + + + +
+
+
+ +
+

No projects found. Use the form above to get started.

+
+
+
+
+ + {/* Footer Info */} +
+

© 2026 Team Project Tracker. Precision engineering for teams.

+
+ Documentation + Support +
+
+
+
+ ); +} + diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..f1d8c73 --- /dev/null +++ b/src/index.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..080dac3 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import {StrictMode} from 'react'; +import {createRoot} from 'react-dom/client'; +import App from './App.tsx'; +import './index.css'; + +createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d88f175 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable" + ], + "skipLibCheck": true, + "moduleResolution": "bundler", + "isolatedModules": true, + "moduleDetection": "force", + "allowJs": true, + "jsx": "react-jsx", + "paths": { + "@/*": [ + "./*" + ] + }, + "allowImportingTsExtensions": true, + "noEmit": true + } +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..52ac87c --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,26 @@ +import tailwindcss from '@tailwindcss/vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; +import {defineConfig, loadEnv} from 'vite'; + +export default defineConfig(({mode}) => { + const env = loadEnv(mode, '.', ''); + return { + plugins: [react(), tailwindcss()], + define: { + 'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY), + }, + resolve: { + alias: { + '@': path.resolve(__dirname, '.'), + }, + }, + server: { + // HMR is disabled in AI Studio via DISABLE_HMR env var. + // Do not modify—file watching is disabled to prevent flickering during agent edits. + hmr: process.env.DISABLE_HMR !== 'true', + // Disable file watching when DISABLE_HMR is true to save CPU during agent edits. + watch: process.env.DISABLE_HMR === 'true' ? null : {}, + }, + }; +});