Skip to content

Commit 4e5f5ee

Browse files
committed
Enhance form column settings and table display
- Add "Show all" and "Hide all" buttons in column settings modal - Improve column width handling and default width management - Update OpenSelect and OpenText components to support more flexible prop types - Add z-index to table header for better visual hierarchy - Refactor column width property from `cell_width` to `width`
1 parent 0d6bd1b commit 4e5f5ee

File tree

7 files changed

+189
-31
lines changed

7 files changed

+189
-31
lines changed

.cursor/rules/back-end.mdc

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
description: Laravel Back-end
3+
globs: api/**.php
4+
---
5+
You are an expert in Laravel, PHP, and related web development technologies.
6+
7+
Key Principles
8+
- Write concise, technical responses with accurate PHP examples.
9+
- Adhere to Laravel 11+ best practices and conventions.
10+
- Use object-oriented programming with a focus on SOLID principles.
11+
- Prefer iteration and modularization over duplication.
12+
- Use descriptive variable and method names.
13+
- Use lowercase with dashes for directories (e.g., app/Http/Controllers).
14+
- Favor dependency injection and service containers.
15+
16+
PHP/Laravel
17+
- Use PHP 8.2+ features when appropriate (e.g., typed properties, match expressions).
18+
- Follow PSR-12 coding standards.
19+
- Utilize Laravel's built-in features and helpers when possible.
20+
- File structure: Follow Laravel's directory structure and naming conventions.
21+
- Implement proper error handling and logging:
22+
- Use Laravel's exception handling and logging features.
23+
- Create custom exceptions when necessary.
24+
- Use try-catch blocks for expected exceptions.
25+
- Use Laravel's validation features for form and request validation.
26+
- Implement middleware for request filtering and modification.
27+
- Utilize Laravel's Eloquent ORM for database interactions.
28+
- Use Laravel's query builder for complex database queries.
29+
- Implement proper database migrations and seeders.
30+
31+
Dependencies
32+
- Laravel (latest stable version)
33+
- Composer for dependency management
34+
35+
Laravel Best Practices
36+
- Use Eloquent ORM instead of raw SQL queries when possible.
37+
- Use Laravel's built-in authentication and authorization features.
38+
- Utilize Laravel's caching mechanisms for improved performance.
39+
- Implement job queues for long-running tasks.
40+
- Use Pest for unit and feature tests.
41+
- Use Laravel's localization features for multi-language support.
42+
- Implement proper database indexing for improved query performance.
43+
- Use Laravel's built-in pagination features.
44+
- Implement proper error logging and monitoring.
45+
46+
Key Conventions
47+
1. Follow Laravel's MVC architecture.
48+
2. Use Laravel's routing system for defining application endpoints.
49+
3. Implement proper request validation using Form Requests.
50+
4. Implement proper database relationships using Eloquent.
51+
5. Use Laravel's event and listener system for decoupled code.
52+
6. Implement proper database transactions for data integrity.
53+
7. Use Laravel's built-in scheduling features for recurring tasks.

.cursor/rules/front-end.mdc

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
description: Vue and Nuxt guidelines
3+
globs: client/**.*
4+
---
5+
You are an expert in Nuxt, Vue.js, Vue Router, Pinia, VueUse, NuxtUI library and Tailwind, with a deep understanding of best practices and performance optimization techniques in these technologies.
6+
7+
Code Style and Structure
8+
- Write concise, maintainable, and technically accurate code with relevant examples.
9+
- Use functional and declarative programming patterns; avoid classes.
10+
- Favor iteration and modularization to adhere to DRY principles and avoid code duplication.
11+
- Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).
12+
- Organize files systematically: each file should contain only related content, such as exported components, subcomponents, helpers, static content, and types.
13+
14+
Naming Conventions
15+
- Use lowercase with dashes for directories (e.g., components/auth-wizard).
16+
- Favor named exports for functions.
17+
18+
We don't use typescript, javascript only (except when really required for config file for instance).
19+
20+
Syntax and Formatting
21+
- Use the "function" keyword for pure functions to benefit from hoisting and clarity.
22+
- Always use the Vue Composition API script setup style. If a legacy file still used
23+
24+
UI and Styling
25+
- Use Nuxt UI components (https://ui.nuxt.com/components) and Tailwind for components and styling.
26+
- Implement responsive design with Tailwind CSS; use a mobile-first approach.
27+
- Build UI components using atomic design principles, organizing them from smallest to largest (e.g., atoms, molecules, organisms, pages).
28+
29+
Performance Optimization
30+
- Leverage VueUse functions where applicable to enhance reactivity and performance.
31+
- Wrap asynchronous components in Suspense with a fallback UI made with <USkeleton/> components.
32+
- Use dynamic loading for non-critical components.
33+
34+
Key Conventions
35+
- Optimize Web Vitals (LCP, CLS, FID) using tools like Lighthouse or WebPageTest.
36+
- Implement proper error boundaries or try-catch mechanisms to handle errors gracefully, especially in asynchronous operations.

client/components/open/forms/components/FormColumnsSettingsModal.vue

+77-16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
compact-header
44
:show="show"
55
@close="$emit('close')"
6+
v-bind="$attrs"
67
>
78
<template #icon>
89
<Icon name="heroicons:adjustments-horizontal" class="w-8 h-8" />
@@ -21,7 +22,24 @@
2122
class="font-semibold mb-2"
2223
:class="{ 'mt-4': sectionIndex > 0 }"
2324
>
24-
{{ section.title }}
25+
<div class="flex items-center justify-between">
26+
<span>{{ section.title }}</span>
27+
<div class="flex items-center gap-2 text-xs text-gray-500">
28+
<button
29+
class="hover:text-gray-700"
30+
@click="toggleAllColumns(section.fields, true)"
31+
>
32+
Show all
33+
</button>
34+
<span class="text-gray-300">|</span>
35+
<button
36+
class="hover:text-gray-700"
37+
@click="toggleAllColumns(section.fields, false)"
38+
>
39+
Hide all
40+
</button>
41+
</div>
42+
</div>
2543
</h4>
2644
<div class="border border-gray-300">
2745
<div class="grid grid-cols-[1fr,auto,auto] gap-4 px-4 py-2 bg-gray-50 border-b border-gray-300">
@@ -47,7 +65,7 @@
4765
<div class="flex justify-center w-20">
4866
<ToggleSwitchInput
4967
v-model="displayColumns[field.id]"
50-
wrapper-class="mb-0"
68+
wrapper-class="my-0"
5169
label=""
5270
:name="`display-${field.id}`"
5371
@update:model-value="onChangeDisplayColumns"
@@ -56,7 +74,7 @@
5674
<div class="flex justify-center w-20">
5775
<ToggleSwitchInput
5876
v-model="wrapColumns[field.id]"
59-
wrapper-class="mb-0"
77+
wrapper-class="my-0"
6078
label=""
6179
:name="`wrap-${field.id}`"
6280
/>
@@ -85,6 +103,14 @@ const props = defineProps({
85103
columns: {
86104
type: Array,
87105
default: () => []
106+
},
107+
displayColumns: {
108+
type: Object,
109+
default: () => ({})
110+
},
111+
wrapColumns: {
112+
type: Object,
113+
default: () => ({})
88114
}
89115
})
90116
@@ -122,12 +148,20 @@ const sections = computed(() => [
122148
])
123149
124150
// Column preferences storage
151+
const storageKey = computed(() => `column-preferences-formid-${props.form.id}`)
152+
125153
const columnPreferences = useStorage(
126-
computed(() => props.form ? `column-preferences-formid-${props.form.id}` : null),
154+
storageKey.value,
127155
{
128156
display: {},
129157
wrap: {},
130158
widths: {}
159+
},
160+
localStorage,
161+
{
162+
onError: (error) => {
163+
console.error('Storage error:', error)
164+
}
131165
}
132166
)
133167
@@ -164,12 +198,18 @@ function preserveColumnWidths(newColumns, existingColumns = []) {
164198
// Then fallback to form properties
165199
const existing = existingColumns?.find(e => e.id === col.id)
166200
167-
const width = storedWidth || currentCol?.cell_width || currentCol?.width || existing?.cell_width || existing?.width || col.width || 150
201+
// Convert any non-numeric width to default
202+
const defaultWidth = 250
203+
let width = storedWidth || currentCol?.width || existing?.width || defaultWidth
204+
205+
// If width is not a number or is 'full', use default width
206+
if (typeof width !== 'number' || isNaN(width)) {
207+
width = defaultWidth
208+
}
168209
169210
return {
170211
...col,
171-
width,
172-
cell_width: width
212+
width
173213
}
174214
})
175215
}
@@ -187,8 +227,8 @@ watch(() => props.columns, (newColumns) => {
187227
188228
const widths = {}
189229
newColumns.forEach(col => {
190-
if (col.cell_width) {
191-
widths[col.id] = col.cell_width
230+
if (col.width) {
231+
widths[col.id] = col.width
192232
}
193233
})
194234
@@ -199,37 +239,58 @@ watch(() => props.columns, (newColumns) => {
199239
watch(() => props.form, (newForm) => {
200240
if (!newForm) return
201241
202-
const properties = newForm.properties || []
242+
const properties = candidatesProperties.value
203243
const storedPrefs = columnPreferences.value
244+
const removedProperties = newForm.removed_properties || []
204245
205246
// Initialize display columns if not set
206247
if (!Object.keys(storedPrefs.display).length) {
248+
// Set all non-removed properties to visible by default
207249
properties.forEach((field) => {
208250
storedPrefs.display[field.id] = true
209251
})
252+
// Also handle removed properties
253+
removedProperties.forEach((field) => {
254+
storedPrefs.display[field.id] = false
255+
})
210256
}
211257
212258
// Initialize wrap columns if not set
213259
if (!Object.keys(storedPrefs.wrap).length) {
214-
properties.forEach((field) => {
260+
[...properties, ...removedProperties].forEach((field) => {
215261
storedPrefs.wrap[field.id] = false
216262
})
217263
}
218264
265+
// Initialize widths if not set
266+
if (!Object.keys(storedPrefs.widths).length) {
267+
[...properties, ...removedProperties].forEach((field) => {
268+
const defaultWidth = 150
269+
storedPrefs.widths[field.id] = field.width || defaultWidth
270+
})
271+
}
272+
219273
// Emit initial values
220274
emit('update:displayColumns', storedPrefs.display)
221275
emit('update:wrapColumns', storedPrefs.wrap)
222276
223-
// Emit initial columns (all visible by default)
224-
const initialColumns = clonedeep(candidatesProperties.value)
225-
.concat(props.form?.removed_properties || [])
226-
.filter((field) => storedPrefs.display[field.id] !== false) // Show all columns by default unless explicitly hidden
277+
// Emit initial columns (all non-removed visible by default)
278+
const initialColumns = clonedeep(properties)
279+
.concat(removedProperties)
280+
.filter((field) => storedPrefs.display[field.id] !== false)
227281
228282
// Preserve any existing column widths
229-
const columnsWithWidths = preserveColumnWidths(initialColumns, props.form.properties)
283+
const columnsWithWidths = preserveColumnWidths(initialColumns, properties)
230284
emit('update:columns', columnsWithWidths)
231285
}, { immediate: true })
232286
287+
function toggleAllColumns(fields, show) {
288+
fields.forEach((field) => {
289+
displayColumns.value[field.id] = show
290+
})
291+
onChangeDisplayColumns()
292+
}
293+
233294
function onChangeDisplayColumns() {
234295
if (!import.meta.client) return
235296
const properties = clonedeep(candidatesProperties.value)

client/components/open/forms/components/FormSubmissions.vue

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010

1111
<!-- Settings Modal -->
1212
<form-columns-settings-modal
13+
v-if="form"
1314
:show="showColumnsModal"
1415
:form="form"
1516
:columns="properties"
16-
v-model:display-columns="displayColumns"
17-
v-model:wrap-columns="wrapColumns"
17+
:display-columns="displayColumns"
18+
:wrap-columns="wrapColumns"
19+
@update:display-columns="displayColumns = $event"
20+
@update:wrap-columns="wrapColumns = $event"
1821
@close="showColumnsModal = false"
1922
@update:columns="onColumnUpdated"
2023
/>

client/components/open/tables/OpenTable.vue

+7-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<thead
88
:id="'table-header-' + tableHash"
99
ref="header"
10-
class="n-table-head top-0"
10+
class="n-table-head top-0 z-10"
1111
:class="{ absolute: data.length !== 0 }"
1212
style="will-change: transform; transform: translate3d(0px, 0px, 0px)"
1313
>
@@ -18,7 +18,7 @@
1818
:key="col.id"
1919
scope="col"
2020
:allow-resize="allowResize"
21-
:width="col.cell_width ? col.cell_width + 'px' : 'auto'"
21+
:width="col.width ? col.width + 'px' : '150px'"
2222
class="n-table-cell p-0 relative"
2323
@resize-width="resizeCol(col, $event)"
2424
>
@@ -68,7 +68,7 @@
6868
<td
6969
v-for="(col, colIndex) in columns"
7070
:key="col.id"
71-
:style="{ width: col.cell_width + 'px' }"
71+
:style="{ width: col.width ? col.width + 'px' : '150px' }"
7272
class="n-table-cell border-gray-100 dark:border-gray-900 text-sm p-2 overflow-hidden"
7373
:class="[
7474
{
@@ -179,7 +179,8 @@ export default {
179179
type: Boolean,
180180
},
181181
scrollParent: {
182-
type: [Boolean]
182+
type: [Boolean, Object],
183+
default: null
183184
},
184185
},
185186
emits: ["updated", "deleted", "resize", "update-columns"],
@@ -294,7 +295,7 @@ export default {
294295
if (this.internalColumns) {
295296
this.$nextTick(() => {
296297
this.internalColumns.forEach((col) => {
297-
if (!_has(col, "cell_width")) {
298+
if (!_has(col, "width")) {
298299
if (
299300
this.allowResize &&
300301
this.internalColumns.length &&
@@ -315,7 +316,7 @@ export default {
315316
resizeCol(col, width) {
316317
if (!this.form) return
317318
const index = this.internalColumns.findIndex((c) => c.id === col.id)
318-
this.internalColumns[index].cell_width = width
319+
this.internalColumns[index].width = width
319320
this.setColumns(this.internalColumns)
320321
this.$nextTick(() => {
321322
this.$emit("resize")

client/components/open/tables/components/OpenSelect.vue

+8-5
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@ export default {
2424
components: { OpenTag },
2525
props: {
2626
value: {
27-
type: Object,
27+
type: [String, Object, Array],
28+
required: false,
29+
default: null
2830
},
31+
property: {
32+
type: Object,
33+
required: true
34+
}
2935
},
3036
3137
data() {
@@ -34,10 +40,7 @@ export default {
3440
3541
computed: {
3642
valueIsObject() {
37-
if (typeof this.value === "object" && this.value !== null) {
38-
return true
39-
}
40-
return false
43+
return Array.isArray(this.value) || (typeof this.value === "object" && this.value !== null)
4144
},
4245
},
4346
}

client/components/open/tables/components/OpenText.vue

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ export default {
77
components: {},
88
props: {
99
value: {
10-
type: String,
11-
required: true,
10+
type: [String, Number],
11+
required: false,
12+
default: null
1213
},
1314
},
1415
}

0 commit comments

Comments
 (0)