diff --git a/CHANGELOG.md b/CHANGELOG.md
index a9522e2..b73e19f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## Unreleased
+
+### New
+* Pre-fill time played into new playevent, also tracks time since last playevent
+* Improve light theme and fix light/dark theme switcher
+* Fix purchase form logic
+
## 1.6.0 / 2025-01-15 23:13+01:00
### New
diff --git a/Dockerfile b/Dockerfile
index f727609..b3ad1cb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -19,6 +19,8 @@ RUN apt-get update && apt-get upgrade -y \
&& apt-get install --no-install-recommends -y \
bash \
curl \
+ nodejs \
+ npm \
&& curl -sSL 'https://install.python-poetry.org' | python - \
&& poetry --version \
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
@@ -33,6 +35,12 @@ RUN chown -R timetracker:timetracker /home/timetracker/app
COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
+USER timetracker
+
+# Install Node.js dependencies and build Svelte app
+RUN npm install
+RUN npm run build
+
RUN --mount=type=cache,target="$POETRY_CACHE_DIR" \
echo "$PROD" \
&& poetry version \
diff --git a/Makefile b/Makefile
index 069514e..8c3a00e 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,7 @@ initialize: npm css migrate sethookdir loadplatforms
HTMLFILES := $(shell find games/templates -type f)
PYTHON_VERSION = 3.12
+.PHONY: build frontend-dev frontend-build
npm:
npm install
@@ -24,13 +25,26 @@ init:
poetry install
npm install
+# Run Django, Tailwind, and Vite development servers concurrently
dev:
@npx concurrently \
- --names "Django,Tailwind" \
- --prefix-colors "blue,green" \
- "poetry run python -Wa manage.py runserver" \
- "npx tailwindcss -i ./common/input.css -o ./games/static/base.css --watch"
+ --names "Django,Tailwind,Vite" \
+ --prefix-colors "blue,green,yellow" \
+ "uv run python -Wa manage.py runserver" \
+ "npx tailwindcss -i ./common/input.css -o ./games/static/base.css --watch" \
+ "npm run dev"
+# Only start the Vite development server
+frontend-dev:
+ npm run dev
+
+# Build frontend assets for production
+build: frontend-build collectstatic
+
+# Only build frontend assets
+frontend-build:
+ @echo "Building frontend assets with Vite..."
+ npm run build
caddy:
caddy run --watch
diff --git a/common/input.css b/common/input.css
index 9499c59..78ab8e1 100644
--- a/common/input.css
+++ b/common/input.css
@@ -53,11 +53,11 @@
}
.responsive-table tr:nth-child(even) {
- @apply bg-slate-800
+ @apply bg-indigo-100 dark:bg-slate-800
}
.responsive-table tbody tr:nth-child(odd) {
- @apply bg-slate-900
+ @apply bg-indigo-200 dark:bg-slate-900
}
.responsive-table thead th {
diff --git a/frontend/components/CrownIcon.svelte b/frontend/components/CrownIcon.svelte
new file mode 100644
index 0000000..5ddb6ab
--- /dev/null
+++ b/frontend/components/CrownIcon.svelte
@@ -0,0 +1,6 @@
+
+{#if mastered}
+ 👑
+{/if}
diff --git a/frontend/main.js b/frontend/main.js
new file mode 100644
index 0000000..60282b1
--- /dev/null
+++ b/frontend/main.js
@@ -0,0 +1,13 @@
+import CrownIcon from './components/CrownIcon.svelte';
+
+// Expose a function to mount the CrownIcon component globally
+// This allows Django templates to easily initialize Svelte components.
+window.mountCrownIcon = (selector, props) => {
+ const target = document.querySelector(selector);
+ if (target) {
+ new CrownIcon({
+ target: target,
+ props: props,
+ });
+ }
+};
diff --git a/games/static/base.css b/games/static/base.css
index bdb24db..1724da8 100644
--- a/games/static/base.css
+++ b/games/static/base.css
@@ -2268,6 +2268,11 @@ input:checked + .toggle-bg {
color: rgb(107 114 128 / var(--tw-text-opacity, 1));
}
+.text-gray-600 {
+ --tw-text-opacity: 1;
+ color: rgb(75 85 99 / var(--tw-text-opacity, 1));
+}
+
.text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity, 1));
@@ -2288,11 +2293,6 @@ input:checked + .toggle-bg {
color: rgb(203 213 225 / var(--tw-text-opacity, 1));
}
-.text-slate-400 {
- --tw-text-opacity: 1;
- color: rgb(148 163 184 / var(--tw-text-opacity, 1));
-}
-
.text-slate-500 {
--tw-text-opacity: 1;
color: rgb(100 116 139 / var(--tw-text-opacity, 1));
@@ -2491,11 +2491,21 @@ input:checked + .toggle-bg {
}
.responsive-table tr:nth-child(even) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(229 237 255 / var(--tw-bg-opacity, 1));
+}
+
+.responsive-table tr:nth-child(even):is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(30 41 59 / var(--tw-bg-opacity, 1));
}
.responsive-table tbody tr:nth-child(odd) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(205 219 254 / var(--tw-bg-opacity, 1));
+}
+
+.responsive-table tbody tr:nth-child(odd):is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(15 23 42 / var(--tw-bg-opacity, 1));
}
@@ -3081,6 +3091,11 @@ div [type="submit"] {
color: rgb(75 85 99 / var(--tw-text-opacity, 1));
}
+.dark\:text-slate-300:is(.dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(203 213 225 / var(--tw-text-opacity, 1));
+}
+
.dark\:text-slate-400:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(148 163 184 / var(--tw-text-opacity, 1));
diff --git a/games/static/js/add_purchase.js b/games/static/js/add_purchase.js
index 46497cd..720be9b 100644
--- a/games/static/js/add_purchase.js
+++ b/games/static/js/add_purchase.js
@@ -25,18 +25,7 @@ function setupElementHandlers() {
document.addEventListener("DOMContentLoaded", setupElementHandlers);
document.addEventListener("htmx:afterSwap", setupElementHandlers);
-getEl("#id_type").onchange = () => {
+getEl("#id_type").addEventListener("change", () => {
setupElementHandlers();
-};
-
-document.body.addEventListener("htmx:beforeRequest", function (event) {
- // Assuming 'Purchase1' is the element that triggers the HTMX request
- if (event.target.id === "id_games") {
- var idEditionValue = document.getElementById("id_games").value;
-
- // Condition to check - replace this with your actual logic
- if (idEditionValue != "") {
- event.preventDefault(); // This cancels the HTMX request
- }
- }
-});
+}
+);
diff --git a/games/static/js/utils.js b/games/static/js/utils.js
index 5fc119f..ee74ecc 100644
--- a/games/static/js/utils.js
+++ b/games/static/js/utils.js
@@ -43,6 +43,7 @@ function syncSelectInputUntilChanged(syncData, parentSelector = document) {
const targetElement = document.querySelector(syncItem.target);
if (targetElement && valueToSync !== null) {
+ console.log(`Changing value of ${syncItem.target} to ${valueToSync}`)
targetElement[syncItem.target_value] = valueToSync;
}
}
@@ -184,13 +185,17 @@ function disableElementsWhenValueNotEqual(
function disableElementsWhenTrue(targetSelect, targetValue, elementList) {
return conditionalElementHandler([
() => {
+ console.log(`${disableElementsWhenTrue.name}: triggered on ${targetSelect}`)
+ console.log(`Value of ${targetSelect} is ${targetValue}: ${getEl(targetSelect).value == targetValue}`)
return getEl(targetSelect).value == targetValue;
},
elementList,
(el) => {
+ console.log(`${disableElementsWhenTrue.name}: disabling ${el.id}`)
el.disabled = "disabled";
},
(el) => {
+ console.log(`${disableElementsWhenTrue.name}: enabling ${el.id}`)
el.disabled = "";
},
]);
diff --git a/games/templates/cotton/icon/arrowdown.html b/games/templates/cotton/icon/arrowdown.html
new file mode 100644
index 0000000..26ede59
--- /dev/null
+++ b/games/templates/cotton/icon/arrowdown.html
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/games/templates/cotton/icon/play.html b/games/templates/cotton/icon/play.html
index 7f4bdc9..e51f307 100644
--- a/games/templates/cotton/icon/play.html
+++ b/games/templates/cotton/icon/play.html
@@ -2,7 +2,7 @@
x="0px"
y="0px"
viewBox="0 0 48 48"
- class="text-black dark:text-white w-4 h-4">
+ class="w-4 h-4">
diff --git a/games/templates/cotton/layouts/base.html b/games/templates/cotton/layouts/base.html
index b438e6a..d39b35d 100644
--- a/games/templates/cotton/layouts/base.html
+++ b/games/templates/cotton/layouts/base.html
@@ -39,46 +39,59 @@
{% version %} ({% version_date %})
{{ scripts }}
-