Zlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i
zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7
zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG
z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S
zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr
z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S
zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er
zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa
zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc-
zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V
zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I
zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc
z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E(
zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef
LrJugUA?W`A8`#=m
literal 0
HcmV?d00001
diff --git a/pomodoro-timer-by-jules/src/app/globals.css b/pomodoro-timer-by-jules/src/app/globals.css
new file mode 100644
index 0000000..6b717ad
--- /dev/null
+++ b/pomodoro-timer-by-jules/src/app/globals.css
@@ -0,0 +1,21 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+ --background: #ffffff;
+ --foreground: #171717;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0a0a0a;
+ --foreground: #ededed;
+ }
+}
+
+body {
+ color: var(--foreground);
+ background: var(--background);
+ font-family: Arial, Helvetica, sans-serif;
+}
diff --git a/pomodoro-timer-by-jules/src/app/layout.tsx b/pomodoro-timer-by-jules/src/app/layout.tsx
new file mode 100644
index 0000000..f7fa87e
--- /dev/null
+++ b/pomodoro-timer-by-jules/src/app/layout.tsx
@@ -0,0 +1,34 @@
+import type { Metadata } from "next";
+import { Geist, Geist_Mono } from "next/font/google";
+import "./globals.css";
+
+const geistSans = Geist({
+ variable: "--font-geist-sans",
+ subsets: ["latin"],
+});
+
+const geistMono = Geist_Mono({
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
+});
+
+export const metadata: Metadata = {
+ title: "Create Next App",
+ description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/pomodoro-timer-by-jules/src/app/page.tsx b/pomodoro-timer-by-jules/src/app/page.tsx
new file mode 100644
index 0000000..393c8eb
--- /dev/null
+++ b/pomodoro-timer-by-jules/src/app/page.tsx
@@ -0,0 +1,118 @@
+"use client"; // Required for hooks
+
+import React, { useState, useEffect } from 'react';
+import TimerDisplay from '@/app/components/TimerDisplay';
+import HistorySidebar from '@/app/components/HistorySidebar'; // Import the sidebar
+
+const WORK_DURATION_MINUTES = 25;
+const BREAK_DURATION_MINUTES = 5;
+const WORK_DURATION_SECONDS = WORK_DURATION_MINUTES * 60;
+const BREAK_DURATION_SECONDS = BREAK_DURATION_MINUTES * 60;
+
+export default function Home() {
+ const [timeLeft, setTimeLeft] = useState(WORK_DURATION_SECONDS);
+ const [isActive, setIsActive] = useState(false);
+ const [isWorkSession, setIsWorkSession] = useState(true);
+
+ const saveSession = async (session_type: 'work' | 'break', duration_minutes: number) => {
+ try {
+ const response = await fetch('/api/sessions', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ session_type, duration_minutes }),
+ });
+ const data = await response.json();
+ if (response.ok) {
+ console.log('Session saved successfully:', data);
+ window.dispatchEvent(new CustomEvent('sessionSaved')); // Dispatch event
+ } else {
+ console.error('Failed to save session:', data.message);
+ }
+ } catch (error) {
+ console.error('Error POSTing session data:', error);
+ }
+ };
+
+ useEffect(() => {
+ let interval: NodeJS.Timeout | null = null;
+
+ if (isActive && timeLeft > 0) {
+ interval = setInterval(() => {
+ setTimeLeft((prevTime) => prevTime - 1);
+ }, 1000);
+ } else if (timeLeft === 0 && isActive) { // Only trigger if timer was active and reached zero
+ const completedSessionType = isWorkSession ? 'work' : 'break';
+ const completedSessionDuration = isWorkSession ? WORK_DURATION_MINUTES : BREAK_DURATION_MINUTES;
+
+ saveSession(completedSessionType, completedSessionDuration);
+
+ // Prepare for next session
+ const nextIsWorkSession = !isWorkSession;
+ setIsWorkSession(nextIsWorkSession);
+ setTimeLeft(nextIsWorkSession ? WORK_DURATION_SECONDS : BREAK_DURATION_SECONDS);
+ // Optionally play a sound here or notify user
+ }
+
+ return () => {
+ if (interval) {
+ clearInterval(interval);
+ }
+ };
+ }, [isActive, timeLeft, isWorkSession]);
+
+ const handleStart = () => {
+ setIsActive(true);
+ };
+
+ const handleStop = () => {
+ setIsActive(false);
+ };
+
+ const handleReset = () => {
+ setIsActive(false);
+ setTimeLeft(isWorkSession ? WORK_DURATION_SECONDS : BREAK_DURATION_SECONDS);
+ };
+
+ return (
+
+ {/* Main Timer Area */}
+
+
+
+ {isWorkSession ? 'Work Session' : 'Break Time'}
+
+
+
+
+
+
+
+
+
+
+ {/* History Sidebar */}
+
+
+
+
+ );
+}
diff --git a/pomodoro-timer-by-jules/src/lib/db.ts b/pomodoro-timer-by-jules/src/lib/db.ts
new file mode 100644
index 0000000..e47aa09
--- /dev/null
+++ b/pomodoro-timer-by-jules/src/lib/db.ts
@@ -0,0 +1,91 @@
+import sqlite3 from 'sqlite3';
+import { open, Database } from 'sqlite';
+import path from 'path';
+
+// Define the database file path.
+const dbPath = process.env.NODE_ENV === 'production'
+ ? path.join(process.cwd(), 'pomodoro.db')
+ : path.join(process.cwd(), 'pomodoro.db');
+
+let db: Database | null = null;
+
+export interface PomodoroSession {
+ id: number;
+ session_type: 'work' | 'break';
+ start_time: string; // Stored as ISO string
+ end_time: string; // Stored as ISO string
+ duration_minutes: number;
+}
+
+export async function initDb(): Promise {
+ if (db) {
+ return db;
+ }
+ try {
+ const newDbInstance = await open({
+ filename: dbPath,
+ driver: sqlite3.Database,
+ });
+
+ await newDbInstance.exec(`
+ CREATE TABLE IF NOT EXISTS pomodoro_sessions (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ session_type TEXT NOT NULL CHECK(session_type IN ('work', 'break')),
+ start_time DATETIME NOT NULL,
+ end_time DATETIME NOT NULL,
+ duration_minutes INTEGER NOT NULL
+ )
+ `);
+ console.log('Database initialized and table ensured:', dbPath);
+ db = newDbInstance;
+ return db;
+ } catch (error) {
+ console.error('Failed to initialize database:', error);
+ throw error;
+ }
+}
+
+export async function addPomodoroSession(
+ session_type: 'work' | 'break',
+ start_time: Date,
+ end_time: Date,
+ duration_minutes: number
+): Promise<{ id: number }> {
+ const currentDb = await initDb();
+ if (!currentDb) {
+ throw new Error("Database not initialized, cannot add session.");
+ }
+
+ const result = await currentDb.run(
+ 'INSERT INTO pomodoro_sessions (session_type, start_time, end_time, duration_minutes) VALUES (?, ?, ?, ?)',
+ session_type,
+ start_time.toISOString(),
+ end_time.toISOString(),
+ duration_minutes
+ );
+
+ if (result.lastID === undefined) {
+ throw new Error('Failed to insert session or retrieve lastID.');
+ }
+ return { id: result.lastID };
+}
+
+export async function getPomodoroSessions(): Promise {
+ const currentDb = await initDb();
+ if (!currentDb) {
+ throw new Error("Database not initialized, cannot get sessions.");
+ }
+
+ const sessions = await currentDb.all(
+ 'SELECT id, session_type, start_time, end_time, duration_minutes FROM pomodoro_sessions ORDER BY start_time DESC'
+ );
+ return sessions;
+}
+
+export async function closeDb() {
+ if (db) {
+ await db.close();
+ db = null;
+ console.log('Database connection closed.');
+ }
+}
diff --git a/pomodoro-timer-by-jules/tailwind.config.ts b/pomodoro-timer-by-jules/tailwind.config.ts
new file mode 100644
index 0000000..109807b
--- /dev/null
+++ b/pomodoro-timer-by-jules/tailwind.config.ts
@@ -0,0 +1,18 @@
+import type { Config } from "tailwindcss";
+
+export default {
+ content: [
+ "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
+ "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
+ "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ background: "var(--background)",
+ foreground: "var(--foreground)",
+ },
+ },
+ },
+ plugins: [],
+} satisfies Config;
diff --git a/pomodoro-timer-by-jules/tsconfig.json b/pomodoro-timer-by-jules/tsconfig.json
new file mode 100644
index 0000000..c133409
--- /dev/null
+++ b/pomodoro-timer-by-jules/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/src/app/api/sessions/route.ts b/src/app/api/sessions/route.ts
new file mode 100644
index 0000000..465d114
--- /dev/null
+++ b/src/app/api/sessions/route.ts
@@ -0,0 +1,49 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { initDb, addPomodoroSession } from '@/lib/db'; // Using import alias
+
+// Ensure the database is initialized when this module is loaded or when the API route is first hit.
+// initDb is designed to be idempotent.
+initDb().catch(err => {
+ console.error("Failed to initialize database on API route load:", err);
+ // Depending on the strategy, you might want to prevent the app from running
+ // or handle this more gracefully. For now, we log the error.
+});
+
+export async function POST(request: NextRequest) {
+ try {
+ const body = await request.json();
+ const { session_type, duration_minutes } = body;
+
+ if (!session_type || typeof duration_minutes !== 'number') {
+ return NextResponse.json({ message: 'Missing session_type or duration_minutes' }, { status: 400 });
+ }
+
+ if (session_type !== 'work' && session_type !== 'break') {
+ return NextResponse.json({ message: 'Invalid session_type' }, { status: 400 });
+ }
+
+ const start_time = new Date(); // Current time as start time
+ const end_time = new Date(start_time.getTime() + duration_minutes * 60000);
+
+ const sessionData = {
+ session_type,
+ start_time,
+ end_time,
+ duration_minutes,
+ };
+
+ const result = await addPomodoroSession(
+ sessionData.session_type as 'work' | 'break',
+ sessionData.start_time,
+ sessionData.end_time,
+ sessionData.duration_minutes
+ );
+
+ return NextResponse.json({ message: 'Pomodoro session saved successfully', data: result }, { status: 201 });
+ } catch (error) {
+ console.error('Error saving pomodoro session:', error);
+ // Check if the error is an instance of Error to safely access its message property
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
+ return NextResponse.json({ message: 'Failed to save pomodoro session', error: errorMessage }, { status: 500 });
+ }
+}
diff --git a/src/app/components/TimerDisplay.tsx b/src/app/components/TimerDisplay.tsx
new file mode 100644
index 0000000..972d639
--- /dev/null
+++ b/src/app/components/TimerDisplay.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+
+interface TimerDisplayProps {
+ time: string;
+}
+
+const TimerDisplay: React.FC = ({ time }) => {
+ return (
+
+ {time}
+
+ );
+};
+
+export default TimerDisplay;
diff --git a/src/lib/db.ts b/src/lib/db.ts
new file mode 100644
index 0000000..6c19896
--- /dev/null
+++ b/src/lib/db.ts
@@ -0,0 +1,75 @@
+import sqlite3 from 'sqlite3';
+import { open, Database } from 'sqlite';
+import path from 'path';
+
+// Define the database file path.
+// __dirname will be /pomodoro-timer-by-jules/.next/server/app/api/sessions in production after build
+// or /pomodoro-timer-by-jules/src/lib when running in a local dev context for this file itself.
+// For consistency, especially for the DB file location, we aim for the project root.
+const dbPath = process.env.NODE_ENV === 'production'
+ ? path.join(process.cwd(), 'pomodoro.db') // process.cwd() should be /pomodoro-timer-by-jules
+ : path.join(process.cwd(), 'pomodoro.db'); // In dev, cwd is usually /pomodoro-timer-by-jules
+
+let db: Database | null = null;
+
+export async function initDb() {
+ if (db) {
+ return db;
+ }
+ try {
+ const newDbInstance = await open({
+ filename: dbPath,
+ driver: sqlite3.Database,
+ });
+
+ await newDbInstance.exec(`
+ CREATE TABLE IF NOT EXISTS pomodoro_sessions (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ session_type TEXT NOT NULL CHECK(session_type IN ('work', 'break')),
+ start_time DATETIME NOT NULL,
+ end_time DATETIME NOT NULL,
+ duration_minutes INTEGER NOT NULL
+ )
+ `);
+ console.log('Database initialized and table ensured:', dbPath);
+ db = newDbInstance;
+ return db;
+ } catch (error) {
+ console.error('Failed to initialize database:', error);
+ throw error; // Re-throw to indicate failure
+ }
+}
+
+export async function addPomodoroSession(
+ session_type: 'work' | 'break',
+ start_time: Date,
+ end_time: Date,
+ duration_minutes: number
+): Promise<{ id: number }> {
+ const currentDb = await initDb(); // Ensure DB is initialized
+ if (!currentDb) {
+ throw new Error("Database not initialized, cannot add session.");
+ }
+
+ const result = await currentDb.run(
+ 'INSERT INTO pomodoro_sessions (session_type, start_time, end_time, duration_minutes) VALUES (?, ?, ?, ?)',
+ session_type,
+ start_time.toISOString(),
+ end_time.toISOString(),
+ duration_minutes
+ );
+
+ if (result.lastID === undefined) {
+ throw new Error('Failed to insert session or retrieve lastID.');
+ }
+ return { id: result.lastID };
+}
+
+// Optional: A way to close the database if needed, though often not necessary for serverless functions
+export async function closeDb() {
+ if (db) {
+ await db.close();
+ db = null;
+ console.log('Database connection closed.');
+ }
+}
|