This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

update admin styling

+306 -100
+223 -84
src/components/Admin/AdminPanel.css
··· 26 26 font-weight: 800; 27 27 } 28 28 29 + /* Tab navigation - Updated */ 30 + .nav-tabs { 31 + display: flex; 32 + margin: 0 auto; 33 + } 34 + 35 + .nav-tab { 36 + padding: 10px 20px; 37 + background-color: var(--navbar-bg); 38 + border: 1px solid var(--card-border); 39 + border-bottom: 2px solid transparent; 40 + cursor: pointer; 41 + font-size: 16px; 42 + margin: 0 5px; 43 + color: var(--text); 44 + transition: all 0.3s ease; 45 + } 46 + 47 + .nav-tab:hover { 48 + background-color: var(--background); 49 + border-color: var(--button-bg); 50 + } 51 + 52 + .nav-tab.active { 53 + border-bottom: 2px solid var(--button-bg); 54 + color: var(--button-bg); 55 + font-weight: bold; 56 + } 57 + 29 58 /* Container layout */ 30 59 .admin-container { 31 60 display: grid; ··· 451 480 transition: background-color 0.2s, opacity 0.2s; 452 481 } 453 482 483 + button:disabled { 484 + opacity: 0.5; 485 + cursor: not-allowed; 486 + } 487 + 454 488 .add-new-button { 455 489 background-color: var(--button-bg); 456 490 color: var(--button-text); ··· 570 604 animation: spin 1s linear infinite; 571 605 margin-bottom: 15px; 572 606 } 573 - 574 - /* Tab navigation */ 575 - .nav-tabs { 576 - display: flex; 577 - margin: 0 auto; 578 - } 579 607 580 - .nav-tab { 581 - padding: 10px 20px; 582 - background: #f5f5f5; 583 - border: none; 584 - border-bottom: 2px solid transparent; 585 - cursor: pointer; 586 - font-size: 16px; 587 - } 588 - 589 - .nav-tab.active { 590 - border-bottom: 2px solid #4a90e2; 591 - font-weight: bold; 592 - } 593 - 594 - /* Reorder container */ 608 + /* Reorder container - Updated */ 595 609 .reorder-container { 596 610 max-width: 1200px; 597 611 margin: 0 auto; 598 612 padding: 20px; 613 + background: var(--navbar-bg); 614 + border: 5px solid var(--card-border); 615 + border-radius: 12px; 616 + color: var(--text); 599 617 } 600 618 601 619 .reorder-header { ··· 618 636 619 637 .mode-button { 620 638 padding: 8px 16px; 621 - background: #f5f5f5; 622 - border: 1px solid #ddd; 639 + background-color: var(--navbar-bg); 640 + border: 1px solid var(--card-border); 623 641 border-radius: 4px; 624 642 cursor: pointer; 643 + color: var(--text); 644 + transition: all 0.3s ease; 645 + } 646 + 647 + .mode-button:hover { 648 + background-color: var(--background); 649 + border-color: var(--button-bg); 625 650 } 626 651 627 652 .mode-button.active { 628 - background: #4a90e2; 629 - color: white; 630 - border-color: #4a90e2; 653 + background-color: var(--button-bg); 654 + color: var(--button-text); 655 + border-color: var(--button-bg); 631 656 } 632 657 633 658 .category-select { 634 659 padding: 8px; 635 660 min-width: 200px; 636 - border: 1px solid #ddd; 661 + border: 1px solid var(--card-border); 662 + border-radius: 4px; 663 + background-color: var(--navbar-bg); 664 + color: var(--text); 665 + transition: all 0.3s ease; 666 + } 667 + 668 + .category-select:hover, 669 + .category-select:focus { 670 + border-color: var(--button-bg); 671 + background-color: var(--background); 672 + } 673 + 674 + /* Position input - New */ 675 + .position-control { 676 + display: flex; 677 + align-items: center; 678 + gap: 8px; 679 + margin-left: 10px; 680 + } 681 + 682 + .position-input { 683 + width: 60px; 684 + padding: 3px 5px; 685 + border: 1px solid var(--card-border); 637 686 border-radius: 4px; 687 + background-color: var(--navbar-bg); 688 + color: var(--text); 689 + font-size: 13px; 690 + transition: all 0.3s ease; 691 + } 692 + 693 + .position-input:hover, 694 + .position-input:focus { 695 + border-color: var(--button-bg); 696 + background-color: var(--background); 638 697 } 639 698 640 699 /* Sortable resources list */ ··· 649 708 justify-content: space-between; 650 709 align-items: center; 651 710 padding: 15px; 652 - background: white; 653 - border: 1px solid #eee; 711 + background-color: var(--background); 712 + border: 1px solid var(--card-border); 654 713 border-radius: 4px; 655 714 box-shadow: 0 1px 3px rgba(0,0,0,0.1); 715 + transition: all 0.3s ease; 656 716 } 657 717 658 718 .resource-info { ··· 662 722 .resource-name { 663 723 font-weight: bold; 664 724 margin-bottom: 5px; 725 + color: var(--text); 665 726 } 666 727 667 728 .resource-meta { 668 729 display: flex; 669 730 gap: 10px; 670 731 font-size: 0.9rem; 671 - } 672 - 673 - .position-indicator { 674 - color: #666; 732 + align-items: center; 733 + flex-wrap: wrap; 675 734 } 676 735 677 736 .reorder-actions { ··· 682 741 .move-button { 683 742 width: 32px; 684 743 height: 32px; 685 - background: #f5f5f5; 686 - border: 1px solid #ddd; 744 + background-color: var(--navbar-bg); 745 + border: 1px solid var(--card-border); 687 746 border-radius: 4px; 688 747 cursor: pointer; 689 748 display: flex; 690 749 align-items: center; 691 750 justify-content: center; 692 751 font-size: 18px; 752 + color: var(--text); 753 + transition: all 0.3s ease; 693 754 } 694 755 695 - .move-button:hover { 696 - background: #e5e5e5; 756 + .move-button:hover:not(:disabled) { 757 + background-color: var(--background); 758 + border-color: var(--button-bg); 697 759 } 698 760 699 761 .no-resources-message, 700 762 .select-category-message { 701 763 padding: 20px; 702 764 text-align: center; 703 - color: #666; 765 + color: var(--text); 766 + background-color: var(--navbar-bg); 767 + border: 1px solid var(--card-border); 768 + border-radius: 4px; 769 + margin-top: 20px; 770 + } 771 + 772 + .reorder-list h3 { 773 + color: var(--text); 774 + margin-top: 20px; 775 + margin-bottom: 15px; 776 + font-size: 18px; 704 777 } 705 778 706 779 @keyframes spin { ··· 745 818 margin-top: 10px; 746 819 font-weight: 700; 747 820 } 748 - 821 + 749 822 .admin-container form { 750 823 border: 0px; 751 824 padding: 0px; ··· 764 837 margin-bottom: 15px; 765 838 font-size: 14px; 766 839 } 767 - 840 + 768 841 .completeness-badge { 769 842 background-color: #f0f0f0; 770 843 color: #333; ··· 773 846 border-radius: 3px; 774 847 margin-left: 5px; 775 848 } 776 - 849 + 777 850 .filter-group { 778 851 margin-bottom: 10px; 779 852 } ··· 793 866 font-size: 16px; 794 867 color: #666; 795 868 } 796 - 869 + 797 870 .featured-filter { 798 871 width: 100%; 799 872 padding: 8px; 800 - border: 1px solid #ddd; 873 + border: 1px solid var(--card-border); 801 874 border-radius: 4px; 802 875 margin-bottom: 5px; 876 + background-color: var(--navbar-bg); 877 + color: var(--text); 878 + transition: all 0.3s ease; 879 + } 880 + 881 + .featured-filter:hover, 882 + .featured-filter:focus { 883 + border-color: var(--button-bg); 884 + background-color: var(--background); 803 885 } 804 886 805 887 .reset-filters-button:hover { ··· 809 891 .resources-summary { 810 892 margin: 5px 0; 811 893 font-size: 0.9rem; 812 - color: #666; 894 + color: var(--text); 895 + opacity: 0.8; 813 896 padding: 0 10px; 814 897 } 815 898 ··· 817 900 .tag-filter { 818 901 width: 100%; 819 902 padding: 8px; 820 - border: 1px solid #ddd; 903 + border: 1px solid var(--card-border); 821 904 border-radius: 4px; 822 905 margin-bottom: 5px; 906 + background-color: var(--navbar-bg); 907 + color: var(--text); 908 + transition: all 0.3s ease; 909 + } 910 + 911 + .category-filter:hover, 912 + .category-filter:focus, 913 + .tag-filter:hover, 914 + .tag-filter:focus { 915 + border-color: var(--button-bg); 916 + background-color: var(--background); 823 917 } 824 918 825 919 /* Accessibility */ ··· 840 934 .admin-container { 841 935 grid-template-columns: 1fr; 842 936 } 843 - 937 + 844 938 .floating-actions { 845 - align-items: center; 846 - display: flex; 847 - gap: 15px; 848 - justify-content: space-between; 849 - flex-direction: column; 850 - } 851 - 852 - .admin-container form { 853 - border: 0px; 854 - padding: 0px; 855 - align-content: center; 856 - display: block; 857 - } 858 - 859 - .form-group textarea { 860 - min-height: 100px; 861 - resize: vertical; 862 - font-family: articulat-cf; 863 - width: unset; 864 - } 865 - 866 - .admin-container { 867 - display: grid; 868 - gap: 0px; 869 - } 870 - 871 - .resource-editor { 872 - height: 235%; 873 - } 874 - 875 - .resources-sidebar { 876 - max-height: 80%; 877 - overflow-y: scroll; 878 - } 879 - 880 - .admin-panel { 881 - height: 2121.5px; 882 - } 939 + align-items: center; 940 + display: flex; 941 + gap: 15px; 942 + justify-content: space-between; 943 + flex-direction: column; 944 + } 945 + 946 + .admin-container form { 947 + border: 0px; 948 + padding: 0px; 949 + align-content: center; 950 + display: block; 951 + } 952 + 953 + .form-group textarea { 954 + min-height: 100px; 955 + resize: vertical; 956 + font-family: articulat-cf; 957 + width: unset; 958 + } 959 + 960 + .admin-container { 961 + display: grid; 962 + gap: 0px; 963 + } 964 + 965 + .resource-editor { 966 + height: 235%; 967 + } 968 + 969 + .resources-sidebar { 970 + max-height: 80%; 971 + overflow-y: scroll; 972 + } 973 + 974 + .admin-panel { 975 + height: 2121.5px; 976 + } 883 977 884 978 .form-row { 885 979 grid-template-columns: 1fr; 980 + } 981 + 982 + .reorder-header { 983 + flex-direction: column; 984 + align-items: start; 985 + gap: 15px; 986 + } 987 + 988 + .reorder-controls { 989 + flex-direction: column; 990 + align-items: start; 991 + width: 100%; 992 + } 993 + 994 + .reorder-mode-selector { 995 + width: 100%; 996 + } 997 + 998 + .mode-button { 999 + flex: 1; 1000 + text-align: center; 1001 + } 1002 + 1003 + .category-selector { 1004 + width: 100%; 1005 + } 1006 + 1007 + .category-select { 1008 + width: 100%; 1009 + } 1010 + 1011 + .sortable-resource-item { 1012 + flex-direction: column; 1013 + align-items: start; 1014 + gap: 10px; 1015 + } 1016 + 1017 + .resource-meta { 1018 + flex-direction: column; 1019 + align-items: start; 1020 + gap: 5px; 1021 + } 1022 + 1023 + .reorder-actions { 1024 + align-self: flex-end; 886 1025 } 887 1026 }
+83 -16
src/components/Admin/AdminPanel.js
··· 23 23 const [activeView, setActiveView] = useState('resources'); // 'resources', 'reorder' 24 24 const [reorderMode, setReorderMode] = useState('featured'); // 'featured', 'category' 25 25 const [selectedCategoryForReorder, setSelectedCategoryForReorder] = useState(null); 26 + const [updatingPositions, setUpdatingPositions] = useState(false); 26 27 27 28 // Login state 28 29 const [email, setEmail] = useState(''); ··· 261 262 }); 262 263 }; 263 264 265 + // Update a single resource position without full page refresh 266 + const updateResourcePosition = async (resourceId, newPosition) => { 267 + if (updatingPositions) return; 268 + setUpdatingPositions(true); 269 + 270 + try { 271 + // Update the resource position in the database 272 + const { error } = await supabase 273 + .from('resources') 274 + .update({ position: newPosition }) 275 + .eq('id', resourceId); 276 + 277 + if (error) throw error; 278 + 279 + // Update local state without fetching all data again 280 + setResources(prevResources => { 281 + return prevResources.map(resource => { 282 + if (resource.id === resourceId) { 283 + return { ...resource, position: newPosition }; 284 + } 285 + return resource; 286 + }); 287 + }); 288 + 289 + showAlert(`Position updated successfully!`); 290 + } catch (error) { 291 + console.error('Error updating position:', error); 292 + showAlert(`Error: ${error.message}`, 'error'); 293 + } finally { 294 + setUpdatingPositions(false); 295 + } 296 + }; 297 + 298 + // Handle position input change and update 299 + const handlePositionChange = (resourceId, e) => { 300 + const newPosition = parseInt(e.target.value); 301 + if (isNaN(newPosition) || newPosition < 0) return; 302 + 303 + updateResourcePosition(resourceId, newPosition); 304 + }; 305 + 264 306 // Reorder resources (move up or down in list) 265 307 const handleReorderResource = async (resourceId, direction) => { 266 308 const resourceIndex = resources.findIndex(r => r.id === resourceId); 267 - 268 309 if (resourceIndex === -1) return; 269 310 270 311 let filteredResources = resources; ··· 279 320 } 280 321 281 322 const resourceToMoveIndex = filteredResources.findIndex(r => r.id === resourceId); 282 - 283 323 if (resourceToMoveIndex === -1) return; 284 324 285 325 const adjacentIndex = direction === 'up' ··· 291 331 const resourceToMove = filteredResources[resourceToMoveIndex]; 292 332 const adjacentResource = filteredResources[adjacentIndex]; 293 333 294 - setIsLoading(true); 334 + if (updatingPositions) return; 335 + setUpdatingPositions(true); 295 336 296 337 try { 297 - // Swap positions 298 - const tempPosition = resourceToMove.position; 299 - 338 + // Swap positions in database 300 339 await supabase 301 340 .from('resources') 302 341 .update({ position: adjacentResource.position }) ··· 304 343 305 344 await supabase 306 345 .from('resources') 307 - .update({ position: tempPosition }) 346 + .update({ position: resourceToMove.position }) 308 347 .eq('id', adjacentResource.id); 309 348 310 - // Refresh data 311 - await fetchAllData(); 349 + // Update local state without fetching all data again 350 + setResources(prevResources => { 351 + return prevResources.map(resource => { 352 + if (resource.id === resourceToMove.id) { 353 + return { ...resource, position: adjacentResource.position }; 354 + } 355 + if (resource.id === adjacentResource.id) { 356 + return { ...resource, position: resourceToMove.position }; 357 + } 358 + return resource; 359 + }); 360 + }); 312 361 313 362 showAlert(`Resources reordered successfully!`); 314 363 } catch (error) { 315 364 console.error('Error reordering resources:', error); 316 365 showAlert(`Error: ${error.message}`, 'error'); 317 366 } finally { 318 - setIsLoading(false); 367 + setUpdatingPositions(false); 319 368 } 320 369 }; 321 370 ··· 1123 1172 <span className={`status-badge status-${resource.status}`}> 1124 1173 {resource.status} 1125 1174 </span> 1126 - <span className="position-indicator"> 1127 - Position: {resource.position} 1128 - </span> 1175 + <div className="position-control"> 1176 + <span>Position:</span> 1177 + <input 1178 + type="number" 1179 + value={resource.position} 1180 + onChange={(e) => handlePositionChange(resource.id, e)} 1181 + className="position-input" 1182 + min="1" 1183 + /> 1184 + </div> 1129 1185 </div> 1130 1186 </div> 1131 1187 <div className="reorder-actions"> ··· 1133 1189 onClick={() => handleReorderResource(resource.id, 'up')} 1134 1190 className="move-button move-up" 1135 1191 title="Move up" 1192 + disabled={updatingPositions} 1136 1193 > 1137 1194 1138 1195 </button> ··· 1140 1197 onClick={() => handleReorderResource(resource.id, 'down')} 1141 1198 className="move-button move-down" 1142 1199 title="Move down" 1200 + disabled={updatingPositions} 1143 1201 > 1144 1202 1145 1203 </button> ··· 1178 1236 {resource.status} 1179 1237 </span> 1180 1238 {resource.featured && <span className="featured-badge">Featured</span>} 1181 - <span className="position-indicator"> 1182 - Position: {resource.position} 1183 - </span> 1239 + <div className="position-control"> 1240 + <span>Position:</span> 1241 + <input 1242 + type="number" 1243 + value={resource.position} 1244 + onChange={(e) => handlePositionChange(resource.id, e)} 1245 + className="position-input" 1246 + min="1" 1247 + /> 1248 + </div> 1184 1249 </div> 1185 1250 </div> 1186 1251 <div className="reorder-actions"> ··· 1188 1253 onClick={() => handleReorderResource(resource.id, 'up')} 1189 1254 className="move-button move-up" 1190 1255 title="Move up" 1256 + disabled={updatingPositions} 1191 1257 > 1192 1258 1193 1259 </button> ··· 1195 1261 onClick={() => handleReorderResource(resource.id, 'down')} 1196 1262 className="move-button move-down" 1197 1263 title="Move down" 1264 + disabled={updatingPositions} 1198 1265 > 1199 1266 1200 1267 </button>