|
211 | 211 | <div class="dropdown-menu p-2" style="width: 350px; max-height: 400px; overflow-y: auto;"> |
212 | 212 | {% for property in all_properties %} |
213 | 213 | <div class="mb-3"> |
214 | | - <label class="form-label fw-bold small">{{ property.name }} |
215 | | - <span class="text-muted">({{ property.category.name }})</span> |
216 | | - </label> |
| 214 | + <div class="d-flex justify-content-between align-items-center"> |
| 215 | + <label class="form-label fw-bold small mb-0">{{ property.name }} |
| 216 | + <span class="text-muted">({{ property.category.name }})</span> |
| 217 | + </label> |
| 218 | + <i class="fas fa-eye property-visibility-toggle property-eye-icon" |
| 219 | + style="cursor: pointer; color: #6c757d; font-size: 0.8rem;" |
| 220 | + data-property-slug="{{ property.slug }}" |
| 221 | + data-property-name="{{ property.name }}" |
| 222 | + title="Toggle property visibility in views" |
| 223 | + onclick="togglePropertyVisibility('{{ property.slug }}', '{{ property.name }}')"></i> |
| 224 | + </div> |
217 | 225 |
|
218 | 226 | {% if property.property_type == 'dropdown' %} |
219 | 227 | <div style="max-height: 120px; overflow-y: auto; border: 1px solid #ced4da; border-radius: 0.375rem; padding: 0.25rem;"> |
@@ -361,6 +369,26 @@ <h5 class="card-title mb-1">{{ asset.name }}</h5> |
361 | 369 | {% if asset.location_nr %}#{{ asset.location_nr }}{% endif %} |
362 | 370 | </p> |
363 | 371 | {% endif %} |
| 372 | + |
| 373 | + <!-- Dynamic property display --> |
| 374 | + <div class="property-display-area"> |
| 375 | + {% for property in all_properties %} |
| 376 | + <div class="property-display d-none" data-property-slug="{{ property.slug }}"> |
| 377 | + {% for prop_value in asset.property_values.all %} |
| 378 | + {% if prop_value.property.slug == property.slug %} |
| 379 | + <small class="text-muted"> |
| 380 | + <strong>{{ property.name }}:</strong> |
| 381 | + {% if property.property_type == 'number' and property.unit %} |
| 382 | + {{ prop_value.value }} {{ property.unit }} |
| 383 | + {% else %} |
| 384 | + {{ prop_value.value }} |
| 385 | + {% endif %} |
| 386 | + </small><br> |
| 387 | + {% endif %} |
| 388 | + {% endfor %} |
| 389 | + </div> |
| 390 | + {% endfor %} |
| 391 | + </div> |
364 | 392 | </div> |
365 | 393 | <div class="text-end purchase-price"> |
366 | 394 | <span class="badge |
@@ -414,6 +442,15 @@ <h4 class="text-muted">{% translate "No assets found" %}</h4> |
414 | 442 | <th>{% translate "Category" %}</th> |
415 | 443 | <th>{% translate "Location" %}</th> |
416 | 444 | <th class="purchase-price">{% translate "Status" %}</th> |
| 445 | + <!-- Dynamic property columns --> |
| 446 | + {% for property in all_properties %} |
| 447 | + <th class="property-column d-none" data-property-slug="{{ property.slug }}"> |
| 448 | + {{ property.name }} |
| 449 | + {% if property.unit %} |
| 450 | + <small class="text-muted">({{ property.unit }})</small> |
| 451 | + {% endif %} |
| 452 | + </th> |
| 453 | + {% endfor %} |
417 | 454 | <th class="purchase-price">{% translate "Purchase Price" %}</th> |
418 | 455 | <th>{% translate "Listing Price" %}</th> |
419 | 456 | <th class="purchase-price">{% translate "Created" %}</th> |
@@ -465,6 +502,20 @@ <h4 class="text-muted">{% translate "No assets found" %}</h4> |
465 | 502 | {{ asset.local_status }} |
466 | 503 | </span> |
467 | 504 | </td> |
| 505 | + <!-- Dynamic property data cells --> |
| 506 | + {% for property in all_properties %} |
| 507 | + <td class="property-column d-none" data-property-slug="{{ property.slug }}"> |
| 508 | + {% for prop_value in asset.property_values.all %} |
| 509 | + {% if prop_value.property.slug == property.slug %} |
| 510 | + {% if property.property_type == 'number' and property.unit %} |
| 511 | + {{ prop_value.value }} {{ property.unit }} |
| 512 | + {% else %} |
| 513 | + {{ prop_value.value }} |
| 514 | + {% endif %} |
| 515 | + {% endif %} |
| 516 | + {% endfor %} |
| 517 | + </td> |
| 518 | + {% endfor %} |
468 | 519 | <td class="purchase-price">€{{ asset.purchase_value|default:0|floatformat:0 }}</td> |
469 | 520 | <td><strong>€{{ asset.listing_price|default:0|floatformat:0 }}</strong></td> |
470 | 521 | <td class="purchase-price"> |
@@ -602,6 +653,96 @@ <h4 class="text-muted">{% translate "No assets found" %}</h4> |
602 | 653 | form.submit(); |
603 | 654 | } |
604 | 655 |
|
| 656 | +// Property visibility management |
| 657 | +function togglePropertyVisibility(propertySlug, propertyName) { |
| 658 | + const visibleProperties = getVisibleProperties(); |
| 659 | + const isVisible = visibleProperties.includes(propertySlug); |
| 660 | + |
| 661 | + if (isVisible) { |
| 662 | + // Hide property |
| 663 | + hideProperty(propertySlug); |
| 664 | + removeFromVisibleProperties(propertySlug); |
| 665 | + } else { |
| 666 | + // Show property |
| 667 | + showProperty(propertySlug); |
| 668 | + addToVisibleProperties(propertySlug); |
| 669 | + } |
| 670 | + |
| 671 | + updatePropertyToggleIcons(); |
| 672 | +} |
| 673 | + |
| 674 | +function showProperty(propertySlug) { |
| 675 | + // Show in table view |
| 676 | + const tableColumns = document.querySelectorAll(`th[data-property-slug="${propertySlug}"], td[data-property-slug="${propertySlug}"]`); |
| 677 | + tableColumns.forEach(col => col.classList.remove('d-none')); |
| 678 | + |
| 679 | + // Show in grid view |
| 680 | + const gridDisplays = document.querySelectorAll(`.property-display[data-property-slug="${propertySlug}"]`); |
| 681 | + gridDisplays.forEach(display => display.classList.remove('d-none')); |
| 682 | +} |
| 683 | + |
| 684 | +function hideProperty(propertySlug) { |
| 685 | + // Hide in table view |
| 686 | + const tableColumns = document.querySelectorAll(`th[data-property-slug="${propertySlug}"], td[data-property-slug="${propertySlug}"]`); |
| 687 | + tableColumns.forEach(col => col.classList.add('d-none')); |
| 688 | + |
| 689 | + // Hide in grid view |
| 690 | + const gridDisplays = document.querySelectorAll(`.property-display[data-property-slug="${propertySlug}"]`); |
| 691 | + gridDisplays.forEach(display => display.classList.add('d-none')); |
| 692 | +} |
| 693 | + |
| 694 | +function getVisibleProperties() { |
| 695 | + const stored = localStorage.getItem('visibleProperties'); |
| 696 | + return stored ? JSON.parse(stored) : []; |
| 697 | +} |
| 698 | + |
| 699 | +function addToVisibleProperties(propertySlug) { |
| 700 | + const visibleProperties = getVisibleProperties(); |
| 701 | + if (!visibleProperties.includes(propertySlug)) { |
| 702 | + visibleProperties.push(propertySlug); |
| 703 | + localStorage.setItem('visibleProperties', JSON.stringify(visibleProperties)); |
| 704 | + } |
| 705 | +} |
| 706 | + |
| 707 | +function removeFromVisibleProperties(propertySlug) { |
| 708 | + const visibleProperties = getVisibleProperties(); |
| 709 | + const index = visibleProperties.indexOf(propertySlug); |
| 710 | + if (index > -1) { |
| 711 | + visibleProperties.splice(index, 1); |
| 712 | + localStorage.setItem('visibleProperties', JSON.stringify(visibleProperties)); |
| 713 | + } |
| 714 | +} |
| 715 | + |
| 716 | +function updatePropertyToggleIcons() { |
| 717 | + const visibleProperties = getVisibleProperties(); |
| 718 | + |
| 719 | + document.querySelectorAll('.property-visibility-toggle').forEach(icon => { |
| 720 | + const propertySlug = icon.getAttribute('data-property-slug'); |
| 721 | + const isVisible = visibleProperties.includes(propertySlug); |
| 722 | + |
| 723 | + if (isVisible) { |
| 724 | + icon.style.color = '#0d6efd'; // Bootstrap primary color |
| 725 | + icon.classList.remove('fa-eye-slash'); |
| 726 | + icon.classList.add('fa-eye'); |
| 727 | + } else { |
| 728 | + icon.style.color = '#6c757d'; // Bootstrap muted color |
| 729 | + icon.classList.remove('fa-eye'); |
| 730 | + icon.classList.add('fa-eye-slash'); |
| 731 | + } |
| 732 | + }); |
| 733 | +} |
| 734 | + |
| 735 | +function initializePropertyVisibility() { |
| 736 | + const visibleProperties = getVisibleProperties(); |
| 737 | + |
| 738 | + // Apply stored visibility settings |
| 739 | + visibleProperties.forEach(propertySlug => { |
| 740 | + showProperty(propertySlug); |
| 741 | + }); |
| 742 | + |
| 743 | + updatePropertyToggleIcons(); |
| 744 | +} |
| 745 | + |
605 | 746 | // Make toggleLocationGroup available globally |
606 | 747 | function toggleLocationGroup(groupId) { |
607 | 748 | const groupCheckbox = document.getElementById(`location-group-${groupId}`); |
@@ -633,6 +774,7 @@ <h4 class="text-muted">{% translate "No assets found" %}</h4> |
633 | 774 | initializeAutocomplete(); |
634 | 775 | initializeClickableRows(); |
635 | 776 | initializeLocationGroups(); |
| 777 | + initializePropertyVisibility(); |
636 | 778 | }); |
637 | 779 |
|
638 | 780 | /** |
|
0 commit comments