diff --git a/package-lock.json b/package-lock.json index 9a34cb4..0478134 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,19 +8,23 @@ "name": "newest-nxtjs-daisyui", "version": "0.1.0", "dependencies": { + "daisyui": "^5.0.43", + "dexie": "^4.0.11", "next": "15.3.4", + "papaparse": "^5.5.3", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "uuid": "^11.1.0" }, "devDependencies": { "@eslint/eslintrc": "^3", - "@tailwindcss/postcss": "^4", + "@tailwindcss/postcss": "^4.1.11", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "15.3.4", - "tailwindcss": "^4", + "tailwindcss": "^4.1.11", "typescript": "^5" } }, @@ -2409,6 +2413,15 @@ "dev": true, "license": "MIT" }, + "node_modules/daisyui": { + "version": "5.0.43", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.0.43.tgz", + "integrity": "sha512-2pshHJ73vetSpsbAyaOncGnNYL0mwvgseS1EWy1I9Qpw8D11OuBoDNIWrPIME4UFcq2xuff3A9x+eXbuFR9fUQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/saadeghi/daisyui?sponsor=1" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -2541,6 +2554,12 @@ "node": ">=8" } }, + "node_modules/dexie": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.11.tgz", + "integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==", + "license": "Apache-2.0" + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -4940,6 +4959,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6037,6 +6062,19 @@ "punycode": "^2.1.0" } }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index de93302..9677c1d 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,23 @@ "lint": "next lint" }, "dependencies": { + "daisyui": "^5.0.43", + "dexie": "^4.0.11", + "next": "15.3.4", + "papaparse": "^5.5.3", "react": "^19.0.0", "react-dom": "^19.0.0", - "next": "15.3.4" + "uuid": "^11.1.0" }, "devDependencies": { - "typescript": "^5", + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4.1.11", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4", "eslint": "^9", "eslint-config-next": "15.3.4", - "@eslint/eslintrc": "^3" + "tailwindcss": "^4.1.11", + "typescript": "^5" } } diff --git a/postcss.config.mjs b/postcss.config.mjs index c7bcb4b..25de907 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,5 +1,7 @@ +/** @type {import('postcss-load-config').Config} */ const config = { - plugins: ["@tailwindcss/postcss"], + plugins: { + '@tailwindcss/postcss': {}, + }, }; - -export default config; +export default config; \ No newline at end of file diff --git a/src/app/api/sync.ts b/src/app/api/sync.ts new file mode 100644 index 0000000..6f63e20 --- /dev/null +++ b/src/app/api/sync.ts @@ -0,0 +1,6 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; + +export default function handler(req: NextApiRequest, res: NextApiResponse) { + // stub endpoint for future sync logic + res.status(200).json({ message: 'Sync not implemented yet' }); +} diff --git a/src/app/components/ClockButton.tsx b/src/app/components/ClockButton.tsx new file mode 100644 index 0000000..b0bd9fc --- /dev/null +++ b/src/app/components/ClockButton.tsx @@ -0,0 +1,18 @@ +"use client"; +import React from 'react'; +import { useAttendance } from '@/app/hooks/useAttendance'; + +export default function ClockButton({ teacherId }: { teacherId: string }) { + const { todayRecords, clockToggle } = useAttendance(); + const last = todayRecords.filter(r => r.teacherId === teacherId).pop(); + const nextType = last?.type === 'in' ? 'out' : 'in'; + + return ( + + ); +} \ No newline at end of file diff --git a/src/app/components/DailySummary.tsx b/src/app/components/DailySummary.tsx new file mode 100644 index 0000000..a858899 --- /dev/null +++ b/src/app/components/DailySummary.tsx @@ -0,0 +1,38 @@ +"use client"; +import React from 'react'; +import { useAttendance } from '@/app/hooks/useAttendance'; +import { useTeachers } from '@/app/hooks/useTeachers'; +import { exportToCsv } from '@/app/utils/csvExport'; + +export default function DailySummary() { + const { todayRecords } = useAttendance(); + const { teachers } = useTeachers(); + + const summary = teachers.map(t => { + const recs = todayRecords.filter(r => r.teacherId === t.id); + let total = 0; + for (let i = 0; i < recs.length; i += 2) { + if (recs[i+1]) { + total += (recs[i+1].timestamp - recs[i].timestamp) / (1000*60*60); + } + } + return { name: t.name, total: total.toFixed(2) }; + }); + + return ( +