diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..38e8cda --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Sam Stevens + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 6602851..a999b23 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ -# `Daily Time Recording` +# Daily Time Recording Simple utility to calculate how much time you have left to record today. +The start time & duration are kept in browser storage. The recorded duration is also kept but cleared every day. + +# Operation + Inputs: - Start Time @@ -9,6 +13,9 @@ Inputs: - Break Duration - Break Taken Yes/No - Recorded Duration + +Output: + - Remaining Duration Calculation: @@ -21,8 +28,4 @@ Remaining = Now - Start - Break (if taken) - Recorded - `npm run build` - Builds for production, emitting to `dist/` -- `npm run preview` - Starts a server at http://localhost:4173/ to test production build locally - -# Todo - -- Save start, target & break duration in browser storage \ No newline at end of file +- `npm run preview` - Starts a server at http://localhost:4173/ to test production build locally \ No newline at end of file diff --git a/src/index.jsx b/src/index.jsx index fe14a65..880a4b0 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -10,6 +10,7 @@ import './style.css'; import {durationRe} from "./duration.js"; import Duration from "./Duration.jsx"; import calculateRemaining from "./calc.js"; +import {getStoredRecordedTime, getStoredDefaults, setStoredRecordedTime, setStoredDefaults} from "./store.js"; const schema = yup.object({ startTime: yup.string().matches(timeRe, {message: '12 or 24 hr time format required'}).required(), @@ -30,7 +31,8 @@ export function App() { startTime: '09:00', target: '7.5h', break: '1h', - recorded: '' + recorded: getStoredRecordedTime(), + ...getStoredDefaults() }, resolver: yupResolver(schema) }); @@ -49,6 +51,10 @@ export function App() { } setRemaining(calculateRemaining(currentData)); + // save new values to storage + setStoredDefaults({ startTime: currentData.startTime, target: currentData.target }); + setStoredRecordedTime(currentData.recorded); + const interval = setInterval(() => { setRemaining(calculateRemaining(currentData)); }, 60000); @@ -68,6 +74,10 @@ export function App() { Daily Time Tracking
+
diff --git a/src/store.js b/src/store.js new file mode 100644 index 0000000..3382150 --- /dev/null +++ b/src/store.js @@ -0,0 +1,65 @@ +import * as yup from "yup"; + +const DEFAULTS_KEY = 'defaults'; +const RECORDED_KEY = 'recorded'; + +const defaultsSchema = yup.object({ + startTime: yup.string().required(), + target: yup.string().required(), +}).required(); + +const recordedSchema = yup.object({ + recorded: yup.string().required(), + updated: yup.number().required().nullable() +}).required(); + +function getJsonValue(key, defaultValue = {}) { + const item = localStorage.getItem(key); + if (item != null) { + try { + const data = JSON.parse(item); + if (typeof data === 'object') { + return data; + } + } catch (e) { + localStorage.removeItem(key); + return defaultValue; + } + } + return defaultValue; +} + +export function getStoredDefaults() { + const value = getJsonValue(DEFAULTS_KEY); + try { + return defaultsSchema.validateSync(value); + } catch (e) { + return {}; + } +} + +export function setStoredDefaults(data) { + localStorage.setItem(DEFAULTS_KEY, JSON.stringify(data)); +} + +export function getStoredRecordedTime() { + const info = getJsonValue(RECORDED_KEY, { recorded: '', updated: null }); + try { + const data = recordedSchema.validateSync(info); + if (data.updated != null) { + const startOfDay = new Date(); + startOfDay.setHours(0, 0, 0, 0); + + if (data.updated < startOfDay.valueOf()) { + return ''; + } + } + return data.recorded; + } catch (e) { + return ''; + } +} + +export function setStoredRecordedTime(recorded) { + localStorage.setItem(RECORDED_KEY, JSON.stringify({ recorded, updated: new Date().valueOf() })); +} diff --git a/src/time.js b/src/time.js index 1d439ab..5a028c7 100644 --- a/src/time.js +++ b/src/time.js @@ -40,10 +40,13 @@ export function minsToTime(totalMinutes) { let hours = Math.floor(absMinutes / 60); let minutes = absMinutes % 60; if (minutes > 0) { - return `${prefix}${hours}h ${minutes}m`; + if (hours > 0) { + return `${prefix}${hours}h ${minutes}m`; + } + return `${prefix}${minutes}m`; } if (hours > 0) { - return `${hours}h`; + return `${prefix}${hours}h`; } - return '0m'; + return `${prefix}0m`; }