diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2306d99
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+node_modules
+dist
+.env
+.DS_Store
+.vscode
+.idea
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
diff --git a/README.md b/README.md
index 6d75c32..57e9d1f 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,29 @@
-Reading tracker app
\ No newline at end of file
+# Reading Goal App
+
+A React + Vite + Tailwind single-page app. Dates are DD/MM/YYYY everywhere and the calendar starts on Monday.
+
+## Local Development
+
+```bash
+npm install
+npm run dev
+```
+
+## Production Build
+
+```bash
+npm run build
+npm run preview
+```
+
+## Deploy with Coolify (Nixpacks)
+
+1. Push this repo to GitHub.
+2. In Coolify: **Create Application → Git**, select your repo/branch.
+3. **Build Pack**: choose **Nixpacks**.
+4. No base directory unless you're using a monorepo.
+5. (Optional) **Environment**: set `NODE_ENV=production`.
+6. Deploy. The container will:
+ - install dependencies
+ - build with `vite build` (output in `dist/`)
+ - run `serve -s dist -l $PORT` (Coolify injects `$PORT`)
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..26f5bf5
--- /dev/null
+++ b/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Reading Goal App
+
+
+
+
+
+
diff --git a/nixpacks.toml b/nixpacks.toml
new file mode 100644
index 0000000..bafb79f
--- /dev/null
+++ b/nixpacks.toml
@@ -0,0 +1,11 @@
+[phases.setup]
+nixPkgs = ["nodejs_20", "corepack"]
+
+[phases.install]
+cmds = ["npm ci || npm install"]
+
+[phases.build]
+cmds = ["npm run build"]
+
+[start]
+cmd = "npm run start"
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..8965c99
--- /dev/null
+++ b/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "reading-goal-app",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview --host 0.0.0.0 --port 5173",
+ "start": "serve -s dist -l ${PORT:-3000}"
+ },
+ "dependencies": {
+ "lucide-react": "^0.452.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.2.45",
+ "@types/react-dom": "^18.2.18",
+ "@vitejs/plugin-react": "^4.2.0",
+ "autoprefixer": "^10.4.18",
+ "postcss": "^8.4.33",
+ "serve": "^14.2.1",
+ "tailwindcss": "^3.4.9",
+ "typescript": "^5.5.4",
+ "vite": "^5.3.4"
+ }
+}
\ No newline at end of file
diff --git a/postcss.config.cjs b/postcss.config.cjs
new file mode 100644
index 0000000..85f717c
--- /dev/null
+++ b/postcss.config.cjs
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {}
+ }
+}
diff --git a/reading_tracker.jsx b/src/App.tsx
similarity index 99%
rename from reading_tracker.jsx
rename to src/App.tsx
index 1a11a23..b07d24c 100644
--- a/reading_tracker.jsx
+++ b/src/App.tsx
@@ -1,3 +1,4 @@
+
import React, { useEffect, useState } from 'react';
import { BookOpen, Plus, X, Edit2, Check, ChevronLeft, ChevronRight } from 'lucide-react';
@@ -465,9 +466,7 @@ const ReadingGoalApp = () => {
{safeAuthor}
= 100 ? 'bg-green-100 text-green-700' : 'bg-blue-100 text-blue-700'
- }`}
+ className={`px-2 py-1 rounded text-xs font-medium ${progressPercent >= 100 ? 'bg-green-100 text-green-700' : 'bg-blue-100 text-blue-700'}`}
>
{progressPercent >= 100 ? 'Completed' : `${goal.daysRemaining} days left`}
@@ -599,7 +598,7 @@ const ReadingGoalApp = () => {
Progress Overview
Current Progress{book.currentPage} / {book.totalPages} pages
-
+
Completion{progressPercent.toFixed(1)}%
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 0000000..8ab1d91
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,11 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+/* App-wide base styles */
+html, body, #root {
+ height: 100%;
+}
+body {
+ margin: 0;
+}
diff --git a/src/main.tsx b/src/main.tsx
new file mode 100644
index 0000000..4a1b150
--- /dev/null
+++ b/src/main.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App'
+import './index.css'
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+)
diff --git a/tailwind.config.cjs b/tailwind.config.cjs
new file mode 100644
index 0000000..f312fb3
--- /dev/null
+++ b/tailwind.config.cjs
@@ -0,0 +1,6 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: ['./index.html', './src/**/*.{ts,tsx,js,jsx}'],
+ theme: { extend: {} },
+ plugins: []
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..da1dd42
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": [
+ "ES2020",
+ "DOM",
+ "DOM.Iterable"
+ ],
+ "types": [
+ "vite/client",
+ "react",
+ "react-dom"
+ ],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true
+ },
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..eca6e2f
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ plugins: [react()]
+})