From 02238974aa84aa0eff6508348f7b5cf106bba196 Mon Sep 17 00:00:00 2001 From: mohamad Date: Sun, 1 Jun 2025 20:41:04 +0200 Subject: [PATCH] Refactor: Update styling and functionality in various components This commit includes several enhancements across multiple files: - In `valerie-ui.scss`, improved formatting of CSS properties and adjusted selectors for better readability and consistency. - In `CreateListModal.vue`, introduced a sentinel value for group selection and refined the logic for handling group options. - In `VModal.vue`, streamlined modal structure and removed unnecessary styles, centralizing modal styling in `valerie-ui.scss`. - In `VTextarea.vue`, adjusted aria attributes for better accessibility and improved code clarity. - Updated `api-config.ts` to switch the API base URL to a local development environment. These changes aim to enhance maintainability, accessibility, and overall user experience. --- fe/src/assets/valerie-ui.scss | 55 ++++----- fe/src/components/CreateListModal.vue | 33 +++--- fe/src/components/valerie/VModal.vue | 147 ++---------------------- fe/src/components/valerie/VTextarea.vue | 22 ++-- fe/src/config/api-config.ts | 2 +- 5 files changed, 64 insertions(+), 195 deletions(-) diff --git a/fe/src/assets/valerie-ui.scss b/fe/src/assets/valerie-ui.scss index 79ba0d8..b13bafd 100644 --- a/fe/src/assets/valerie-ui.scss +++ b/fe/src/assets/valerie-ui.scss @@ -37,13 +37,11 @@ /* Textures */ --paper-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60' viewBox='0 0 60 60'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%23a59a8a' fill-opacity='0.15'%3E%3Cpath opacity='.5' d='M36 60v-6h6v6h-6zm18-12v-6h6v6h-6zM6 0v6H0V0h6zM6 12v6H0v-6h6zM18 0v6h-6V0h6zM18 12v6h-6v-6h6zM30 0v6h-6V0h6zM30 12v6h-6v-6h6zM42 0v6h-6V0h6zM42 12v6h-6v-6h6zM54 0v6h-6V0h6zM54 12v6h-6v-6h6zM6 24v6H0v-6h6zM6 36v6H0v-6h6zM6 48v6H0v-6h6zM18 24v6h-6v-6h6zM18 36v6h-6v-6h6zM18 48v6h-6v-6h6zM30 24v6h-6v-6h6zM30 36v6h-6v-6h6zM30 48v6h-6v-6h6zM42 24v6h-6v-6h6zM42 36v6h-6v-6h6zM42 48v6h-6v-6h6zM54 24v6h-6v-6h6zM54 36v6h-6v-6h6zM54 48v6h-6v-6h6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); - --progress-texture: repeating-linear-gradient( - 45deg, - rgba(0, 0, 0, 0.05), - rgba(0, 0, 0, 0.05) 5px, - transparent 5px, - transparent 10px - ); + --progress-texture: repeating-linear-gradient(45deg, + rgba(0, 0, 0, 0.05), + rgba(0, 0, 0, 0.05) 5px, + transparent 5px, + transparent 10px); } /* Accessibility Helpers */ @@ -61,6 +59,7 @@ /* Reduced Motion Preference */ @media (prefers-reduced-motion: reduce) { + *, *::before, *::after { @@ -133,8 +132,8 @@ body { /* Higher than non-focused tabs */ } -.checkbox-label input:focus-visible ~ .checkmark, -.radio-label input:focus-visible ~ .checkmark { +.checkbox-label input:focus-visible~.checkmark, +.radio-label input:focus-visible~.checkmark { outline: var(--focus-outline); outline-offset: var(--focus-outline-offset); box-shadow: @@ -142,7 +141,7 @@ body { 0 0 0 var(--focus-outline-width) var(--focus-outline-color); } -.switch-container input:focus-visible + .switch { +.switch-container input:focus-visible+.switch { outline: var(--focus-outline); outline-offset: var(--focus-outline-offset); } @@ -211,7 +210,7 @@ h3 { margin-right: 0.5em; } -button > .icon:last-child { +button>.icon:last-child { margin-right: 0; } @@ -300,6 +299,7 @@ button > .icon:last-child { } @keyframes jiggle-subtle { + 0%, 100% { transform: translate(2px, 2px) scale(0.98) rotate(0deg); @@ -340,7 +340,7 @@ button > .icon:last-child { /* Prevent texture from interfering */ } -.card > * { +.card>* { position: relative; z-index: 1; /* Ensure content is above texture */ @@ -513,8 +513,8 @@ select.form-input { border-radius: 50%; } -.checkbox-label input:checked ~ .checkmark:after, -.radio-label input:checked ~ .checkmark:after { +.checkbox-label input:checked~.checkmark:after, +.radio-label input:checked~.checkmark:after { content: ''; position: absolute; display: block; @@ -723,12 +723,12 @@ select.form-input { /* Push to the right on larger screens */ } -.list-item-details > * { +.list-item-details>* { margin-left: 0.5rem; } /* Spacing between items in details */ -.list-item-details > :first-child { +.list-item-details> :first-child { margin-left: 0; } @@ -908,7 +908,7 @@ select.form-input { pointer-events: none; } -.tab-content > * { +.tab-content>* { position: relative; z-index: 1; } @@ -1073,7 +1073,7 @@ select.form-input { pointer-events: none; } -.alert > .alert-content { +.alert>.alert-content { /* Wrap main content */ display: flex; align-items: center; @@ -1241,11 +1241,11 @@ select.form-input { /* Vertical centering */ } -.switch-container input:checked + .switch { +.switch-container input:checked+.switch { background-color: var(--secondary-accent); } -.switch-container input:checked + .switch:before { +.switch-container input:checked+.switch:before { background-color: var(--light); border-color: var(--dark); /* Width (64) - border*2 (6) - left (2) - width (24) = 32 */ @@ -1497,8 +1497,8 @@ select.form-input { border-color: var(--dark) transparent transparent transparent; } -.tooltip .tooltip-trigger:hover + .tooltip-text, -.tooltip .tooltip-trigger:focus-visible + .tooltip-text, +.tooltip .tooltip-trigger:hover+.tooltip-text, +.tooltip .tooltip-trigger:focus-visible+.tooltip-text, .tooltip-text.visible { visibility: visible; opacity: 1; @@ -1544,6 +1544,7 @@ select.form-input { } @keyframes pulse-dot { + 0%, 80%, 100% { @@ -1678,7 +1679,7 @@ select.form-input { /* Allow badges/avatar wrap on mobile */ } - .list-item-details > * { + .list-item-details>* { margin-left: 0; margin-right: 0.5rem; margin-bottom: 0.25rem; @@ -1723,7 +1724,7 @@ select.form-input { flex-direction: column; } - .flex-layout-stack-mobile > .card { + .flex-layout-stack-mobile>.card { width: 100% !important; margin: 0 0 1.5rem 0 !important; flex-basis: auto !important; @@ -1733,12 +1734,12 @@ select.form-input { flex-direction: column; } - .form-row-wrap-mobile > .form-group { + .form-row-wrap-mobile>.form-group { margin-right: 0 !important; width: 100%; } - .form-row-wrap-mobile > .form-group:not(:last-child) { + .form-row-wrap-mobile>.form-group:not(:last-child) { margin-bottom: 1.5rem; } -} +} \ No newline at end of file diff --git a/fe/src/components/CreateListModal.vue b/fe/src/components/CreateListModal.vue index c33d4dd..ab040ed 100644 --- a/fe/src/components/CreateListModal.vue +++ b/fe/src/components/CreateListModal.vue @@ -7,7 +7,7 @@ - + @@ -52,7 +52,8 @@ const emit = defineEmits<{ const isOpen = useVModel(props, 'modelValue', emit); const listName = ref(''); const description = ref(''); -const selectedGroupId = ref(null); // Store only the ID +const SENTINEL_NO_GROUP = 0; // Using 0 to represent 'None' or 'Personal List' +const selectedGroupId = ref(SENTINEL_NO_GROUP); // Initialize with sentinel const loading = ref(false); const formErrors = ref<{ listName?: string }>({}); const notificationStore = useNotificationStore(); @@ -61,14 +62,8 @@ const listNameInput = ref | null>(null); // const modalContainerRef = ref(null); // Removed const groupOptionsForSelect = computed(() => { - const options = props.groups ? props.groups.map(g => ({ label: g.label, value: g.value })) : []; - // VSelect expects placeholder to be passed as a prop, not as an option for empty value usually - // However, if 'None' is a valid selectable option representing null, this is okay. - // The VSelect component's placeholder prop is typically for a non-selectable first option. - // Let's adjust this to provide a clear "None" option if needed, or rely on VSelect's placeholder. - // For now, assuming VSelect handles `null` modelValue with its placeholder prop. - // If selectedGroupId can be explicitly null via selection: - return [{ label: 'None (Personal List)', value: null }, ...options]; + // VSelect's placeholder should work if selectedGroupId is the sentinel value + return props.groups ? props.groups.map(g => ({ label: g.label, value: g.value })) : []; }); @@ -77,10 +72,15 @@ watch(isOpen, (newVal) => { // Reset form when opening listName.value = ''; description.value = ''; - selectedGroupId.value = null; // Default to 'None' or personal list + // If a single group is passed, pre-select it. Otherwise, default to sentinel + if (props.groups && props.groups.length === 1) { + selectedGroupId.value = props.groups[0].value; + } else { + selectedGroupId.value = SENTINEL_NO_GROUP; // Reset to sentinel + } formErrors.value = {}; nextTick(() => { - listNameInput.value?.focus?.(); + // listNameInput.value?.focus?.(); // This might still be an issue depending on VInput. Commenting out for now. }); } }); @@ -105,11 +105,12 @@ const onSubmit = async () => { } loading.value = true; try { - const response = await apiClient.post(API_ENDPOINTS.LISTS.BASE, { + const payload = { name: listName.value, description: description.value, - group_id: selectedGroupId.value, - }); + group_id: selectedGroupId.value === SENTINEL_NO_GROUP ? null : selectedGroupId.value, + }; + const response = await apiClient.post(API_ENDPOINTS.LISTS.BASE, payload); notificationStore.addNotification({ message: 'List created successfully', type: 'success' }); @@ -125,7 +126,7 @@ const onSubmit = async () => { }; - diff --git a/fe/src/components/valerie/VTextarea.vue b/fe/src/components/valerie/VTextarea.vue index daa795f..ef1113c 100644 --- a/fe/src/components/valerie/VTextarea.vue +++ b/fe/src/components/valerie/VTextarea.vue @@ -1,19 +1,10 @@