From cee0c922d2ead3f4d732400623f1016085749669 Mon Sep 17 00:00:00 2001 From: Ryan Kegel Date: Thu, 18 Dec 2025 16:44:25 -0500 Subject: [PATCH] Made changes to CSS and merged together. --- web/vue-app/src/assets/child-list-shared.css | 214 +++++++++++++++++ web/vue-app/src/assets/global.css | 78 +++++++ .../src/components/ChildrenListView.vue | 2 +- web/vue-app/src/components/ImagePicker.vue | 22 +- web/vue-app/src/components/LoginButton.vue | 23 -- .../src/components/child/ChildDetailCard.vue | 12 +- .../src/components/child/ChildEditView.vue | 11 +- .../src/components/child/ChildView.vue | 35 +-- .../src/components/child/ParentView.vue | 31 --- .../src/components/child/RewardAssignView.vue | 44 +--- .../src/components/child/TaskAssignView.vue | 43 +--- .../notification/NotificationList.vue | 7 +- .../notification/NotificationView.vue | 23 -- .../src/components/reward/ChildRewardList.vue | 216 +----------------- .../src/components/task/ChildTaskList.vue | 198 +--------------- 15 files changed, 352 insertions(+), 607 deletions(-) create mode 100644 web/vue-app/src/assets/child-list-shared.css diff --git a/web/vue-app/src/assets/child-list-shared.css b/web/vue-app/src/assets/child-list-shared.css new file mode 100644 index 0000000..bd6c071 --- /dev/null +++ b/web/vue-app/src/assets/child-list-shared.css @@ -0,0 +1,214 @@ +.child-list-container { + background: var(--child-list-bg, rgba(255, 255, 255, 0.1)); + border-radius: 12px; + padding: 1rem; + color: var(--child-list-title-color, #fff); + width: 100%; + box-sizing: border-box; +} + +.child-list-container h3 { + margin: 0; + font-size: 1.1rem; + font-weight: 600; + color: var(--child-list-title-color, #fff); +} + +.loading, +.empty { + text-align: center; + padding: 1rem; + font-size: 0.9rem; + opacity: 0.8; + color: var(--child-list-loading-color, #fff); +} + +.scroll-wrapper { + overflow-x: auto; + overflow-y: hidden; + scroll-behavior: smooth; + width: 100%; + -webkit-overflow-scrolling: touch; +} + +.scroll-wrapper::-webkit-scrollbar { + height: 8px; +} + +.scroll-wrapper::-webkit-scrollbar-track { + background: var(--child-list-scrollbar-track, rgba(255, 255, 255, 0.05)); + border-radius: 10px; +} + +.scroll-wrapper::-webkit-scrollbar-thumb { + background: var( + --child-list-scrollbar-thumb, + linear-gradient(180deg, rgba(102, 126, 234, 0.8), rgba(118, 75, 162, 0.8)) + ); + border-radius: 10px; + border: var(--child-list-scrollbar-thumb-border, 2px solid rgba(255, 255, 255, 0.08)); +} + +.scroll-wrapper::-webkit-scrollbar-thumb:hover { + background: var( + --child-list-scrollbar-thumb-hover, + linear-gradient(180deg, rgba(102, 126, 234, 1), rgba(118, 75, 162, 1)) + ); + box-shadow: 0 0 8px rgba(102, 126, 234, 0.4); +} + +.item-scroll { + display: flex; + gap: 0.75rem; + min-width: min-content; + padding: 0.5rem 0; +} + +/* Fallback for browsers that don't support flex gap */ +.item-card + .item-card { + margin-left: 0.75rem; +} + +.item-card { + position: relative; + background: var(--item-card-bg, rgba(255, 255, 255, 0.12)); + border-radius: 8px; + padding: 0.75rem; + min-width: 140px; + max-width: 220px; + width: 100%; + text-align: center; + flex-shrink: 0; + transition: transform 0.18s ease; + border: var(--item-card-border, 1px solid rgba(255, 255, 255, 0.08)); + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; +} + +.item-card:hover { + transform: translateY(-4px); + box-shadow: var(--item-card-hover-shadow, 0 8px 20px rgba(0, 0, 0, 0.12)); +} + +.item-card.ready { + box-shadow: var(--item-card-ready-shadow, 0 0 0 3px #667eea88, 0 0 12px #667eea44); + border-color: var(--item-card-ready-border, #667eea); + animation: ready-glow 0.7s; +} + +.item-card.disabled { + opacity: 0.5; + pointer-events: none; + filter: grayscale(0.7); +} + +.item-card.good { + border-color: var(--item-card-good-border, rgba(46, 204, 113, 0.9)); /* green */ + background: var(--item-card-good-bg, rgba(46, 204, 113, 0.06)); +} + +.item-card.bad { + border-color: var(--item-card-bad-border, rgba(255, 99, 71, 0.95)); /* red */ + background: var(--item-card-bad-bg, rgba(255, 99, 71, 0.03)); +} + +@keyframes ready-glow { + 0% { + box-shadow: 0 0 0 0 #667eea00; + border-color: inherit; + } + 100% { + box-shadow: var(--item-card-ready-shadow, 0 0 0 3px #667eea88, 0 0 12px #667eea44); + border-color: var(--item-card-ready-border, #667eea); + } +} + +.item-name { + font-size: 0.95rem; + font-weight: 700; + margin-bottom: 0.4rem; + color: var(--item-name-color, #fff); + line-height: 1.2; + word-break: break-word; +} + +/* Image styling */ +.item-image { + width: 70px; + height: 70px; + object-fit: cover; + border-radius: 6px; + margin: 0 auto 0.4rem auto; + display: block; +} + +.item-points { + font-size: 0.82rem; + font-weight: 600; + color: var(--item-points-color, #ffd166); + font-size: 1rem; + font-weight: 900; + text-shadow: var( + --item-points-shadow, + -1px -1px 0 #1a3d1f, + 1px -1px 0 #1a3d1f, + -1px 1px 0 #1a3d1f, + 1px 1px 0 #1a3d1f + ); +} + +.item-points.ready { + color: var(--item-points-ready-color, #38c172); /* a nice green */ + font-weight: 700; + letter-spacing: 0.5px; +} + +/* Mobile tweaks */ +@media (max-width: 480px) { + .item-card { + min-width: 110px; + max-width: 150px; + padding: 0.6rem; + } + .item-name { + font-size: 0.86rem; + } + .item-image { + width: 50px; + height: 50px; + margin: 0 auto 0.3rem auto; + } + .item-points { + font-size: 0.78rem; + } + .scroll-wrapper::-webkit-scrollbar { + height: 10px; + } + .scroll-wrapper::-webkit-scrollbar-thumb { + border-width: 1px; + } +} + +.pending-block { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80%; + background: var(--pending-block-bg, #222b); + color: var(--pending-block-color, #62ff7a); + font-weight: 700; + font-size: 1.05rem; + text-align: center; + border-radius: 6px; + padding: 0.4rem 0; + letter-spacing: 2px; + display: flex; + align-items: center; + justify-content: center; + z-index: 2; + opacity: 0.95; + pointer-events: none; +} diff --git a/web/vue-app/src/assets/global.css b/web/vue-app/src/assets/global.css index 29a94c1..d77f504 100644 --- a/web/vue-app/src/assets/global.css +++ b/web/vue-app/src/assets/global.css @@ -57,6 +57,7 @@ --card-bg: #fff; --card-title: #333; --card-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + --kebab-icon-color: #666; --kebab-menu-bg: #f7fafc; --kebab-menu-border: #bcc1c9; --kebab-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), 0 1.5px 6px rgba(102, 126, 234, 0.08); @@ -80,4 +81,81 @@ --child-image-bg: #fff; --age-color: #666; --points-color: #444; + + /* Image Picker styles */ + --icon-btn-bg: #f3f3f3; + --icon-btn-color: #667eea; + --icon-btn-shadow: 0 2px 8px rgba(102, 126, 234, 0.07); + --selectable-image-border: #e6e6e6; + --selectable-image-bg: #fafbff; + --selectable-image-selected: #667eea; + --loading-text: #888; + --modal-bg: #fff; + --modal-shadow: 0 12px 40px rgba(0, 0, 0, 0.2); + + /* ChildDetailCard color variables */ + --detail-card-bg: #fff; + --detail-card-shadow: 0 8px 32px rgba(0, 0, 0, 0.13); + + --child-name-color: #333; + --child-age-color: #666; + + --points-label-color: #888; + --points-value-color: #667eea; + + /* ChildEditView styles */ + --form-label-color: #333; + --form-input-bg: #fafbff; + --form-input-border: #e6e6e6; + --form-input-color: #222; + --form-input-focus: #667eea; + + /*Reward/Task Assignment styles*/ + --assign-heading-color: #ffffff; + --assign-no-items-color: #fdfdfd; + --assign-sub-message-color: #b5ccff; + --assign-create-btn-bg: #fff; + --assign-create-btn-color: #2563eb; + --assign-create-btn-border: #2563eb; + --assign-create-btn-hover-bg: #2563eb; + --assign-create-btn-hover-color: #fff; + + --notification-reward-name: #ef4444; + + /* ChildLists styles */ + --child-list-bg: rgba(255, 255, 255, 0.1); + --child-list-title-color: #fff; + --child-list-loading-color: #fff; + --child-list-scrollbar-track: rgba(255, 255, 255, 0.05); + --child-list-scrollbar-thumb: linear-gradient( + 180deg, + rgba(102, 126, 234, 0.8), + rgba(118, 75, 162, 0.8) + ); + --child-list-scrollbar-thumb-hover: linear-gradient( + 180deg, + rgba(102, 126, 234, 1), + rgba(118, 75, 162, 1) + ); + --child-list-scrollbar-thumb-border: 2px solid rgba(255, 255, 255, 0.08); + + --item-card-bg: rgba(255, 255, 255, 0.12); + --item-card-border: 1px solid rgba(255, 255, 255, 0.08); + --item-card-good-border: rgba(46, 204, 113, 0.9); + --item-card-good-bg: rgba(46, 204, 113, 0.06); + --item-card-bad-border: rgba(255, 99, 71, 0.95); + --item-card-bad-bg: rgba(255, 99, 71, 0.03); + --item-card-ready-shadow: 0 0 0 3px #667eea88, 0 0 12px #667eea44; + --item-card-ready-border: #667eea; + --item-card-hover-shadow: 0 8px 20px rgba(0, 0, 0, 0.12); + + --item-name-color: #fff; + + --item-points-color: #ffd166; + --item-points-shadow: + -1px -1px 0 #1a3d1f, 1px -1px 0 #1a3d1f, -1px 1px 0 #1a3d1f, 1px 1px 0 #1a3d1f; + --item-points-ready-color: #38c172; + + --pending-block-bg: #222b; + --pending-block-color: #62ff7a; } diff --git a/web/vue-app/src/components/ChildrenListView.vue b/web/vue-app/src/components/ChildrenListView.vue index 459008c..f0b919f 100644 --- a/web/vue-app/src/components/ChildrenListView.vue +++ b/web/vue-app/src/components/ChildrenListView.vue @@ -451,7 +451,7 @@ h1 { border: 0; padding: 0; cursor: pointer; - color: #666; + color: var(--kebab-icon-color); border-radius: 6px; box-sizing: border-box; font-size: 1.5rem; diff --git a/web/vue-app/src/components/ImagePicker.vue b/web/vue-app/src/components/ImagePicker.vue index 6dc45cd..5f2f037 100644 --- a/web/vue-app/src/components/ImagePicker.vue +++ b/web/vue-app/src/components/ImagePicker.vue @@ -286,20 +286,18 @@ function updateLocalImage(url: string, file: File) { height: 64px; object-fit: cover; border-radius: 8px; - border: 2px solid #e6e6e6; - background: #fafbff; + border: 2px solid var(--selectable-image-border); + background: var(--selectable-image-bg); cursor: pointer; transition: border 0.18s; } -.selectable-image:hover { - border-color: #667eea; -} +.selectable-image:hover, .selectable-image.selected { - border-color: #667eea; + border-color: var(--selectable-image-selected); box-shadow: 0 0 0 2px #667eea55; } .loading-images { - color: #888; + color: var(--loading-text); font-size: 0.98rem; padding: 0.5rem 0; text-align: center; @@ -313,7 +311,7 @@ function updateLocalImage(url: string, file: File) { } .icon-btn { - background: #f3f3f3; + background: var(--icon-btn-bg); border: none; border-radius: 50%; width: 56px; /* Increased size */ @@ -324,8 +322,8 @@ function updateLocalImage(url: string, file: File) { cursor: pointer; transition: background 0.18s; font-size: 2.2rem; /* Bigger + icon */ - color: #667eea; - box-shadow: 0 2px 8px rgba(102, 126, 234, 0.07); + color: var(--icon-btn-color); + box-shadow: var(--icon-btn-shadow); } .icon-btn svg { width: 32px; /* Bigger camera icon */ @@ -344,9 +342,9 @@ function updateLocalImage(url: string, file: File) { top: 50%; left: 50%; transform: translate(-50%, -50%); - background: #fff; + background: var(--modal-bg); border-radius: 12px; - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2); + box-shadow: var(--modal-shadow); z-index: 1300; width: 380px; max-width: calc(100vw - 32px); diff --git a/web/vue-app/src/components/LoginButton.vue b/web/vue-app/src/components/LoginButton.vue index 0174b7a..7a61515 100644 --- a/web/vue-app/src/components/LoginButton.vue +++ b/web/vue-app/src/components/LoginButton.vue @@ -89,29 +89,6 @@ onUnmounted(() => { diff --git a/web/vue-app/src/components/child/ParentView.vue b/web/vue-app/src/components/child/ParentView.vue index 6e81ec9..f11c532 100644 --- a/web/vue-app/src/components/child/ParentView.vue +++ b/web/vue-app/src/components/child/ParentView.vue @@ -478,35 +478,4 @@ const childId = computed(() => child.value?.id ?? null) justify-content: center; margin: 2rem 0; } -.assign-task-btn, -.assign-bad-btn, -.assign-reward-btn { - font-weight: 600; - border: none; - border-radius: 8px; - padding: 0.7rem 1.5rem; - font-size: 1.1rem; - cursor: pointer; - box-shadow: 0 2px 8px rgba(102, 126, 234, 0.08); - transition: background 0.18s; - color: #fff; - background: #667eea; -} -.assign-task-btn:hover, -.assign-bad-btn:hover, -.assign-reward-btn:hover { - background: #5a67d8; -} -.assign-bad-btn { - background: #ef4444; -} -.assign-bad-btn:hover { - background: #dc2626; -} -.assign-reward-btn { - background: #38c172; -} -.assign-reward-btn:hover { - background: #2f855a; -} diff --git a/web/vue-app/src/components/child/RewardAssignView.vue b/web/vue-app/src/components/child/RewardAssignView.vue index 7308628..ab7b756 100644 --- a/web/vue-app/src/components/child/RewardAssignView.vue +++ b/web/vue-app/src/components/child/RewardAssignView.vue @@ -74,7 +74,7 @@ function onCancel() { } h2 { font-size: 1.15rem; - color: #ffffff; + color: var(--assign-heading-color); font-weight: 700; text-align: center; margin: 0.2rem; @@ -89,33 +89,7 @@ h2 { overflow-y: auto; margin-bottom: 2rem; } -.actions { - display: flex; - gap: 3rem; /* Increased gap for more space between buttons */ - justify-content: center; - margin-top: 0.5rem; -} -.actions button { - padding: 1rem 2.2rem; /* Bigger touch area */ - border-radius: 12px; - border: 0; - cursor: pointer; - font-weight: 700; - font-size: 1.25rem; /* Larger text */ - transition: background 0.18s; - min-width: 120px; /* Ensures buttons are wide enough */ -} -.btn.cancel { - background: #f3f3f3; - color: #666; -} -.btn.submit { - background: #667eea; - color: #fff; -} -.btn.submit:hover { - background: #5a67d8; -} + .reward-view { display: flex; flex-direction: column; @@ -132,19 +106,19 @@ h2 { font-size: 1.15rem; font-weight: 600; text-align: center; - color: #fdfdfd; + color: var(--assign-no-items-color); line-height: 1.5; } .sub-message { margin-top: 0.3rem; font-size: 1rem; font-weight: 400; - color: #b5ccff; + color: var(--assign-sub-message-color); } .create-btn { - background: #fff; - color: #2563eb; - border: 2px solid #2563eb; + background: var(--assign-create-btn-bg); + color: var(--assign-create-btn-color); + border: 2px solid var(--assign-create-btn-border); border-radius: 6px; font-size: 0.85rem; font-weight: 600; @@ -156,7 +130,7 @@ h2 { color 0.18s; } .create-btn:hover { - background: #2563eb; - color: #fff; + background: var(--assign-create-btn-hover-bg); + color: var(--assign-create-btn-hover-color); } diff --git a/web/vue-app/src/components/child/TaskAssignView.vue b/web/vue-app/src/components/child/TaskAssignView.vue index 1107fae..a60d082 100644 --- a/web/vue-app/src/components/child/TaskAssignView.vue +++ b/web/vue-app/src/components/child/TaskAssignView.vue @@ -81,7 +81,7 @@ function onCancel() { } h2 { font-size: 1.15rem; - color: #ffffff; + color: var(--assign-heading-color); font-weight: 700; text-align: center; margin: 0.2rem; @@ -96,33 +96,6 @@ h2 { overflow-y: auto; margin-bottom: 2rem; } -.actions { - display: flex; - gap: 3rem; /* Increased gap for more space between buttons */ - justify-content: center; - margin-top: 0.5rem; -} -.actions button { - padding: 1rem 2.2rem; /* Bigger touch area */ - border-radius: 12px; - border: 0; - cursor: pointer; - font-weight: 700; - font-size: 1.25rem; /* Larger text */ - transition: background 0.18s; - min-width: 120px; /* Ensures buttons are wide enough */ -} -.btn.cancel { - background: #f3f3f3; - color: #666; -} -.btn.submit { - background: #667eea; - color: #fff; -} -.btn.submit:hover { - background: #5a67d8; -} .task-view { display: flex; @@ -140,19 +113,19 @@ h2 { font-size: 1.15rem; font-weight: 600; text-align: center; - color: #fdfdfd; + color: var(--assign-no-items-color); line-height: 1.5; } .sub-message { margin-top: 0.3rem; font-size: 1rem; font-weight: 400; - color: #b5ccff; + color: var(--assign-sub-message-color); } .create-btn { - background: #fff; - color: #2563eb; - border: 2px solid #2563eb; + background: var(--assign-create-btn-bg); + color: var(--assign-create-btn-color); + border: 2px solid var(--assign-create-btn-border); border-radius: 6px; font-size: 0.85rem; font-weight: 600; @@ -164,7 +137,7 @@ h2 { color 0.18s; } .create-btn:hover { - background: #2563eb; - color: #fff; + background: var(--assign-create-btn-hover-bg); + color: var(--assign-create-btn-hover-color); } diff --git a/web/vue-app/src/components/notification/NotificationList.vue b/web/vue-app/src/components/notification/NotificationList.vue index 9fdf0f3..2a35999 100644 --- a/web/vue-app/src/components/notification/NotificationList.vue +++ b/web/vue-app/src/components/notification/NotificationList.vue @@ -120,21 +120,22 @@ function handleItemClick(item: PendingReward) { } .child-name { font-weight: 600; - color: #667eea; + color: var(--dialog-child-name); } .reward-info { display: flex; align-items: center; gap: 0.7rem; + margin-bottom: 0rem; } .reward-name { font-weight: 600; - color: #ef4444; + color: var(--notification-reward-name); } .requested-text { margin: 0 0.7rem; font-weight: 500; - color: #444; + color: var(--dialog-message); font-size: 1.05rem; white-space: nowrap; } diff --git a/web/vue-app/src/components/notification/NotificationView.vue b/web/vue-app/src/components/notification/NotificationView.vue index f9cfaa0..cfaa5fb 100644 --- a/web/vue-app/src/components/notification/NotificationView.vue +++ b/web/vue-app/src/components/notification/NotificationView.vue @@ -29,27 +29,4 @@ function handleNotificationClick(item: PendingReward) { padding: 0; min-height: 0; } - -/* Optional: Floating Action Button styles if you add one */ -.fab { - position: fixed; - bottom: 2rem; - right: 2rem; - background: #ef4444; - color: #fff; - border: none; - border-radius: 50%; - width: 56px; - height: 56px; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); - cursor: pointer; - font-size: 24px; - z-index: 1300; -} -.fab:hover { - background: #dc2626; -} diff --git a/web/vue-app/src/components/reward/ChildRewardList.vue b/web/vue-app/src/components/reward/ChildRewardList.vue index 5f6e480..af5f98a 100644 --- a/web/vue-app/src/components/reward/ChildRewardList.vue +++ b/web/vue-app/src/components/reward/ChildRewardList.vue @@ -3,6 +3,7 @@ import { ref, onBeforeUnmount, watch, nextTick, computed } from 'vue' import { defineProps, defineEmits, defineExpose } from 'vue' import { getCachedImageUrl, revokeAllImageUrls } from '../../common/imageCache' import type { RewardStatus } from '@/common/models' +import '@/assets/child-list-shared.css' const imageCacheName = 'images-v1' @@ -143,7 +144,7 @@ const isAnyPending = computed(() => rewards.value.some((r) => r.redeeming)) - + diff --git a/web/vue-app/src/components/task/ChildTaskList.vue b/web/vue-app/src/components/task/ChildTaskList.vue index 27c17c2..c2507e2 100644 --- a/web/vue-app/src/components/task/ChildTaskList.vue +++ b/web/vue-app/src/components/task/ChildTaskList.vue @@ -3,6 +3,7 @@ import { ref, watch, onBeforeUnmount, nextTick, computed } from 'vue' import { defineProps, defineEmits } from 'vue' import { getCachedImageUrl, revokeAllImageUrls } from '../../common/imageCache' import type { Task } from '@/common/models' +import '@/assets/child-list-shared.css' const imageCacheName = 'images-v1' @@ -138,26 +139,26 @@ onBeforeUnmount(() => { - +