Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions noted/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
outputFileName: .gitignore
---
# Dependencies
node_modules

# Build
dist

# Environment
.env
.env.*
*.local

# Editor
.vscode
.idea
.DS_Store
*.swp

# Base44 App Config
.app.json*
1 change: 1 addition & 0 deletions noted/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22.21.1
41 changes: 41 additions & 0 deletions noted/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Noted

A simple todo list app built with React and Base44 backend.

## Structure

```
base44/ # Backend configuration
├── config.jsonc # Project settings
└── entities/ # Data schemas
└── task.jsonc # Task entity

src/ # Frontend code
├── App.jsx # Main todo app
├── api/ # Base44 client
├── components/ui/ # UI components
└── lib/ # Utilities
```

## Development

```bash
npm install
npm run dev
```

## Commands

| Command | Description |
|---------|-------------|
| `npm run dev` | Start dev server |
| `npm run build` | Build for production |
| `npm run preview` | Preview production build |

## Base44 CLI

```bash
base44 login # Authenticate
base44 entities push # Push entity schemas
base44 deploy # Deploy backend + hosting
```
11 changes: 11 additions & 0 deletions noted/base44/agents/task_manager.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "task_manager",
"description": "An AI agent that helps you manage and change your tasks",
"instructions": "You are a helpful task management assistant. You can help users create, update, mark as completed, and delete tasks using the entity tool. When a user asks to change a task, help them modify it by updating the appropriate fields. Always be conversational and helpful.",
"tool_configs": [
{
"entity_name": "Task",
"allowed_operations": ["read", "create", "update", "delete"]
}
]
}
16 changes: 16 additions & 0 deletions noted/base44/config.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// JSONC enables inline documentation and discoverability directly in config files.
// Commented-out properties show available options you can enable.

{
"name": "Noted",
"description": "A calm, no-nonsense to-do list for getting things out of your head and actually done.",

// Site/hosting configuration
// Docs: https://docs.base44.com/configuration/hosting
"site": {
"installCommand": "npm install",
"buildCommand": "npm run build",
"serveCommand": "npm run dev",
"outputDirectory": "./dist"
}
}
16 changes: 16 additions & 0 deletions noted/base44/entities/task.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Task",
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Task title"
},
"completed": {
"type": "boolean",
"default": false,
"description": "Whether the task is completed"
}
},
"required": ["title"]
}
16 changes: 16 additions & 0 deletions noted/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": false,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"ui": "@/components/ui"
}
}
13 changes: 13 additions & 0 deletions noted/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="https://base44.com/logo_v2.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Todo App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
13 changes: 13 additions & 0 deletions noted/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"jsx": "react-jsx",
"module": "esnext",
"moduleResolution": "bundler",
"target": "esnext"
},
"include": ["src"]
}
24 changes: 24 additions & 0 deletions noted/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "base44-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@base44/sdk": "^0.8.3",
"lucide-react": "^0.475.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"postcss": "^8.5.3",
"tailwindcss": "^3.4.17",
"vite": "^6.1.0"
}
}
6 changes: 6 additions & 0 deletions noted/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
148 changes: 148 additions & 0 deletions noted/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { useState, useEffect } from "react";
import { base44 } from "@/api/base44Client";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Base44Logo } from "@/components/Base44Logo";
import { Plus, Trash2, CheckCircle2 } from "lucide-react";

const Task = base44.entities.Task;

export default function App() {
const [tasks, setTasks] = useState([]);
const [newTaskTitle, setNewTaskTitle] = useState("");
const [isLoading, setIsLoading] = useState(true);

const fetchTasks = async () => {
const data = await Task.list();
setTasks(data);
setIsLoading(false);
};

useEffect(() => {
fetchTasks();
}, []);

const handleSubmit = async (e) => {
e.preventDefault();
if (!newTaskTitle.trim()) return;
await Task.create({ title: newTaskTitle.trim(), completed: false });
setNewTaskTitle("");
fetchTasks();
};

const toggleTask = async (id, completed) => {
await Task.update(id, { completed });
fetchTasks();
};

const deleteTask = async (id) => {
await Task.delete(id);
fetchTasks();
};

const clearCompleted = async () => {
await Promise.all(
tasks.filter((t) => t.completed).map((t) => Task.delete(t.id))
);
fetchTasks();
};

const completedCount = tasks.filter((t) => t.completed).length;
const totalCount = tasks.length;

return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-orange-50/30">
<div className="max-w-lg mx-auto px-6 py-16">
{/* Header */}
<div className="text-center mb-12">
<h1 className="text-3xl font-semibold text-slate-900 tracking-tight">
<span className="inline-flex items-center gap-2 align-middle">
<Base44Logo className="w-9 h-9" />
<span className="font-bold">Base44</span>
<span>Tasks</span>
</span>
</h1>
{totalCount > 0 && (
<p className="text-slate-500 mt-2 text-sm">
{completedCount} of {totalCount} completed
</p>
)}
</div>

{/* Add Task Form */}
<form onSubmit={handleSubmit} className="mb-8">
<div className="flex gap-3">
<Input
type="text"
value={newTaskTitle}
onChange={(e) => setNewTaskTitle(e.target.value)}
placeholder="What needs to be done?"
className="flex-1 h-12 bg-white border-slate-200 rounded-xl shadow-sm"
/>
<Button
type="submit"
disabled={!newTaskTitle.trim()}
className="h-12 px-5 rounded-xl bg-slate-900 hover:bg-slate-800 shadow-sm"
>
<Plus className="w-5 h-5" />
</Button>
</div>
</form>

{/* Task List */}
<div className="space-y-2">
{isLoading ? (
<div className="flex items-center justify-center py-12">
<div className="w-6 h-6 border-2 border-slate-200 border-t-orange-500 rounded-full animate-spin" />
</div>
) : tasks.length === 0 ? (
<div className="text-center py-12">
<p className="text-slate-400">No tasks yet. Add one above!</p>
</div>
) : (
tasks.map((task) => (
<div
key={task.id}
className="group flex items-center gap-4 p-4 bg-white rounded-xl border border-slate-100 shadow-sm hover:shadow-md transition-all duration-200"
>
<Checkbox
checked={task.completed}
onCheckedChange={(checked) => toggleTask(task.id, checked)}
className="w-5 h-5 rounded-md border-slate-300 data-[state=checked]:bg-orange-500 data-[state=checked]:border-orange-500"
/>
<span
className={`flex-1 text-slate-700 transition-all ${
task.completed ? "line-through text-slate-400" : ""
}`}
>
{task.title}
</span>
<Button
variant="ghost"
size="icon"
onClick={() => deleteTask(task.id)}
className="opacity-0 group-hover:opacity-100 h-8 w-8 text-slate-400 hover:text-red-500 hover:bg-red-50 transition-all"
>
<Trash2 className="w-4 h-4" />
</Button>
</div>
))
)}
</div>

{/* Footer */}
{completedCount > 0 && (
<div className="mt-8 text-center">
<button
onClick={clearCompleted}
className="text-sm text-slate-400 hover:text-slate-600 transition-colors"
>
Clear completed
</button>
</div>
)}
</div>
</div>
);
}
5 changes: 5 additions & 0 deletions noted/src/api/base44Client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createClient } from '@base44/sdk';

export const base44 = createClient({
appId: import.meta.env.VITE_BASE44_APP_ID,
});
15 changes: 15 additions & 0 deletions noted/src/components/Base44Logo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function Base44Logo({ className = "w-8 h-8" }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 31 31"
className={className}
>
<path
fill="#FF631F"
d="M24.16 26.904c.04 0 .057.05.026.075a14.97 14.97 0 0 1-9.147 3.1c-3.44 0-6.612-1.156-9.146-3.1-.032-.024-.014-.075.026-.075zm3.923-4.373a15 15 0 0 1-1.842 2.544.14.14 0 0 1-.104.046H3.942a.14.14 0 0 1-.104-.046 15 15 0 0 1-1.842-2.544.056.056 0 0 1 .049-.083h25.99c.043 0 .07.046.048.083m1.698-4.5a15 15 0 0 1-.762 2.564.11.11 0 0 1-.103.07H1.163a.11.11 0 0 1-.104-.07 15 15 0 0 1-.762-2.564.056.056 0 0 1 .055-.067h29.375c.035 0 .061.032.054.067M14.938 0C23.29-.056 30.078 6.7 30.078 15.04q0 .55-.038 1.09a.056.056 0 0 1-.056.051H.094a.056.056 0 0 1-.055-.052A15 15 0 0 1 0 15.054C-.007 6.87 6.755.055 14.938 0"
></path>
</svg>
);
}
23 changes: 23 additions & 0 deletions noted/src/components/ui/button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { forwardRef } from 'react';

const Button = forwardRef(({ className = '', variant, size, ...props }, ref) => {
const baseStyles = 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50';

const variantStyles = variant === 'ghost'
? 'hover:bg-slate-100'
: 'bg-slate-900 text-white shadow hover:bg-slate-800';

const sizeStyles = size === 'icon' ? 'h-9 w-9' : 'h-9 px-4 py-2';

return (
<button
ref={ref}
className={`${baseStyles} ${variantStyles} ${sizeStyles} ${className}`}
{...props}
/>
);
});

Button.displayName = 'Button';

export { Button };
Loading