diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..04296ec --- /dev/null +++ b/.env.example @@ -0,0 +1,23 @@ +# Supabase Configuration +# Sign up at https://supabase.io to get your project URL and anon key +REACT_APP_SUPABASE_URL=https://your-supabase-project.supabase.co +REACT_APP_SUPABASE_ANON_KEY=your-supabase-anon-key-here + +# Database Password (for reference - not used directly in app) +SUPABASE_DB_PASSWORD=your-database-password-here + +# Application Settings +REACT_APP_APP_NAME=Time Tracker +REACT_APP_VERSION=1.0.0 + +# Development Settings +GENERATE_SOURCEMAP=false + +# Instructions: +# 1. Copy this file to .env +# 2. Replace placeholder values with your actual credentials +# 3. Restart the development server after making changes + +# Security Note: +# Never commit .env file to version control +# Only variables prefixed with REACT_APP_ are embedded in the client bundle \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4d29575..c2d6a6b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,12 @@ .env.development.local .env.test.local .env.production.local +.env + +# Additional environment files +.env.* +!.env.example npm-debug.log* yarn-debug.log* -yarn-error.log* +yarn-error.log* \ No newline at end of file diff --git a/README.md b/README.md index 58beeac..b2af5b4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,144 @@ -# Getting Started with Create React App +# Time Tracker App + +A React-based time tracking application with user authentication and session management. + +## Features + +- User authentication system with Supabase Auth +- Start/stop time tracking functionality +- Pause/resume functionality for breaks +- Session history management +- Edit current and past sessions +- Create new sessions with simplified time input +- Integration with Supabase for data persistence +- Responsive design for desktop and mobile + +## Pages + +1. **Login/Register Page** - Secure authentication entry point +2. **Dashboard** - Main page with timer controls +3. **Session History** - View, edit, and create sessions + +## Setup Instructions + +1. Clone the repository (if applicable) +2. Install dependencies: + ``` + npm install + ``` +3. Set up environment variables: + - Copy `.env.example` to `.env` + - Update the values in `.env` with your Supabase credentials: + ``` + REACT_APP_SUPABASE_URL=your_supabase_project_url_here + REACT_APP_SUPABASE_ANON_KEY=your_supabase_anon_key_here + ``` +4. Start the development server: + ``` + npm start + ``` + +## Database Setup + +### Supabase Tables + +Create these tables in your Supabase project: + +#### Users Table +```sql +-- Users table (handled by Supabase Auth) +-- Supabase automatically creates auth.users table +``` + +#### Timers Table +```sql +-- Create timers table +create table if not exists timers ( + id uuid primary key default gen_random_uuid(), + user_id uuid references auth.users(id) not null, + start_time timestamptz not null, + end_time timestamptz, + duration_ms integer, + pauses json, + created_at timestamptz default now() +); + +-- Enable RLS (Row Level Security) +alter table timers enable row level security; + +-- Create policy to allow users to access their own timers only +create policy "Users can access their own timers" on timers + for all using (auth.uid() = user_id); + +-- Grant permissions +grant usage on schema public to anon, authenticated; +grant all on table timers to anon, authenticated; +``` + +## Session Management Features + +### Creating New Sessions +- Click "Create New Session" in Session History +- Select date and enter start/end times (HH:MM format) +- Add pauses with start/end times as needed +- Save to store in Supabase database + +### Editing Sessions +- Edit current active session +- Edit past sessions from history +- Modify start/end times and pause periods +- Changes automatically saved to database + +### Timer Controls +- Start/Stop schedule tracking +- Take breaks with Pause/Resume functionality +- Real-time work time calculation (excluding pause time) + +## Security + +- Environment variables are stored in `.env` (excluded from git) +- Sensitive data never stored in browser localStorage or sessionStorage +- All session data persists only in user session and external database +- Review `.gitignore` to ensure sensitive files are not committed +- Supabase Auth handles user authentication securely + +## Implementation Details + +- Built with React and React Router for navigation +- Uses Context API for state management +- Implements a clean, responsive UI with CSS +- Session data persists in user session and Supabase database +- Integrates with Supabase API for data persistence and authentication + +## File Structure + +``` +src/ +├── components/ # Reusable UI components +├── context/ # Authentication and state context +├── pages/ # Page components (Login, Dashboard, Sessions) +├── services/ # API integration services +└── App.js # Main app component with routing +``` + +## Database Integration + +The app includes a service layer for Supabase integration: +- Sessions are saved to the database when stopped +- Session history is loaded from the database on login +- All database operations are handled through the sessionService +- User authentication is managed by Supabase Auth + +To connect to your own Supabase project: +1. Create a Supabase account at https://supabase.io +2. Create a new project +3. Get your project URL and anon key from project settings +4. Create the required tables using the SQL provided above +5. Update the environment variables with your credentials + +--- + +# Original Create React App Documentation This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). @@ -67,4 +207,4 @@ This section has moved here: [https://facebook.github.io/create-react-app/docs/d ### `npm run build` fails to minify -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) +This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f6c92a6..4d5bb56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,12 +8,17 @@ "name": "time-tracker-app", "version": "0.1.0", "dependencies": { + "@fortawesome/free-solid-svg-icons": "^7.2.0", + "@fortawesome/react-fontawesome": "^3.3.0", + "@supabase/supabase-js": "^2.103.0", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^13.5.0", + "axios": "^1.15.0", "react": "^19.2.5", "react-dom": "^19.2.5", + "react-router-dom": "^7.14.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" } @@ -2456,6 +2461,53 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.2.0.tgz", + "integrity": "sha512-IpR0bER9FY25p+e7BmFH25MZKEwFHTfRAfhOyJubgiDnoJNsSvJ7nigLraHtp4VOG/cy8D7uiV0dLkHOne5Fhw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.2.0.tgz", + "integrity": "sha512-6639htZMjEkwskf3J+e6/iar+4cTNM9qhoWuRfj9F3eJD6r7iCzV1SWnQr2Mdv0QT0suuqU8BoJCZUyCtP9R4Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "7.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.2.0.tgz", + "integrity": "sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "7.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-3.3.0.tgz", + "integrity": "sha512-EHmHeTf8WgO29sdY3iX/7ekE3gNUdlc2RW6mm/FzELlHFKfTrA9S4MlyquRR+RRCRCn8+jXfLFpLGB2l7wCWyw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~6 || ~7", + "react": "^18.0.0 || ^19.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -3100,6 +3152,113 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@supabase/auth-js": { + "version": "2.103.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.103.0.tgz", + "integrity": "sha512-6zAanO6c+6gpHOlt5Lb9TlBBkJdZiUWkWCJKAxzkywBDcwaHlLJKXnjQGX6GyVCyKRR1e7sTq4re/yRTH6U/9A==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.103.0", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.103.0.tgz", + "integrity": "sha512-YrneV2NjskUkkmkZ2Jt2n3elBgbWzV4Y1M9MM370z2Zd5ZPFqFbY8KIoPwuNjtAGE9YrpKBxnbZqeF07BiN9Og==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/phoenix": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.0.tgz", + "integrity": "sha512-RHSx8bHS02xwfHdAbX5Lpbo6PXbgyf7lTaXTlwtFDPwOIw64NnVRwFAXGojHhjtVYI+PEPNSWwkL90f4agN3bw==", + "license": "MIT" + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.103.0", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.103.0.tgz", + "integrity": "sha512-rC3sRxYdPZymkp2CZR1MiNQgbOleD01bGsW8VxEKRR5nMkLZ1NgAS1QTQf78Wh30czFyk505ZYr9Od8/mWT2TA==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.103.0", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.103.0.tgz", + "integrity": "sha512-gcPtXzZ6izyyBVf2of7K3dEt8CScPJn8VcSlQq6oWL9QoE1kqfQl0oFrOMHd5qrcADewxI7OxxosLB8W4XqtIQ==", + "license": "MIT", + "dependencies": { + "@supabase/phoenix": "^0.4.0", + "@types/ws": "^8.18.1", + "tslib": "2.8.1", + "ws": "^8.18.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js/node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.103.0", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.103.0.tgz", + "integrity": "sha512-DHmlvdAXwtOmZNbkIZi4lkobPR3XjIzoOgzoz5duMf6G+sDeY015YrzMJCnqdccuYr7X5x4yYuSwF//RoN2dvQ==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.103.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.103.0.tgz", + "integrity": "sha512-j/6q5+LtXbR/YOLSLhy7Na74RD1cV2v+KwIIuuqMEjk1JpLEEyu0ynwDHpGoxMncDQl+R5FogaVqZm+85lZvtw==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.103.0", + "@supabase/functions-js": "2.103.0", + "@supabase/postgrest-js": "2.103.0", + "@supabase/realtime-js": "2.103.0", + "@supabase/storage-js": "2.103.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -4803,6 +4962,33 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -9020,6 +9206,15 @@ "node": ">=10.17.0" } }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -13472,6 +13667,15 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", @@ -13763,6 +13967,57 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.0.tgz", + "integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.0.tgz", + "integrity": "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.14.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -14641,6 +14896,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", diff --git a/package.json b/package.json index 17f3cd1..2354cbb 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,17 @@ "version": "0.1.0", "private": true, "dependencies": { + "@fortawesome/free-solid-svg-icons": "^7.2.0", + "@fortawesome/react-fontawesome": "^3.3.0", + "@supabase/supabase-js": "^2.103.0", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^13.5.0", + "axios": "^1.15.0", "react": "^19.2.5", "react-dom": "^19.2.5", + "react-router-dom": "^7.14.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, @@ -16,7 +21,8 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "setup": "node setup.js" }, "eslintConfig": { "extends": [ diff --git a/public/LOGO_FICOSA.svg b/public/LOGO_FICOSA.svg new file mode 100644 index 0000000..ee22b3e --- /dev/null +++ b/public/LOGO_FICOSA.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + diff --git a/public/favicon.ico b/public/favicon.ico index a11777c..45952a3 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html index aa069f2..f3e7dee 100644 --- a/public/index.html +++ b/public/index.html @@ -9,7 +9,7 @@ name="description" content="Web site created using create-react-app" /> - +