@@ -829,63 +829,77 @@ <h4 class="font-medium mb-4">Follow Me</h4>
829829 </ p >
830830 </ footer >
831831
832- < script >
832+ < script >
833833 gsap . registerPlugin ( ScrollTrigger ) ;
834834
835- // Certificate data
836- const certificates = [
837- { path : 'data/UdacityCertificate.pdf' , title : 'Udacity Certificate' , category : 'education' } ,
838- { path : 'data/CertificateOfCompletion_Microsoft Azure AI Essentials Professional Certificate by Microsoft and LinkedIn.pdf' , title : 'Microsoft Azure AI' , category : 'education' } ,
839- { path : 'data/Coursera E4WZRUCIBAMB.pdf' , title : 'Coursera Certificate' , category : 'education' } ,
840- { path : 'data/Coursera Cert Data Engineering.pdf' , title : 'Data Engineering' , category : 'education' } ,
841- { path : 'data/CertificateOfCompletion_Economics Quantitative Demand Analysis.pdf' , title : 'Quantitative Demand Analysis' , category : 'education' } ,
842- { path : 'data/CertificateOfCompletion_Marketing Strategy Competitive Intelligence.pdf' , title : 'Marketing' , category : 'education' } ,
843-
844- // JavaScript Certificates
845- { path : 'data/CertificateOfCompletion_Master JavaScript.pdf' , title : 'Master JavaScript' , category : 'javascript' } ,
846- { path : 'data/Gabrielius Pocevicius_JavaScript.pdf' , title : 'JavaScript Certificate' , category : 'javascript' } ,
847- { path : 'data/CertificateOfCompletion_EndtoEnd JavaScript Testing with Cypress.io.pdf' , title : 'Cypress.io Testing' , category : 'javascript' } ,
848- { path : 'data/CertificateOfCompletion_Ethical Hacking with JavaScript.pdf' , title : 'Ethical Hacking' , category : 'javascript' } ,
849- { path : 'data/CertificateOfCompletion_JavaScript Best Practices for Code Formatting.pdf' , title : 'JS Code Formatting' , category : 'javascript' } ,
850- { path : 'data/CertificateOfCompletion_JavaScript Best Practices for Data.pdf' , title : 'JS Data Practices' , category : 'javascript' } ,
851- { path : 'data/CertificateOfCompletion_JavaScript Security Essentials.pdf' , title : 'JS Security' , category : 'javascript' } ,
852- { path : 'data/CertificateOfCompletion_JavaScript TestDriven Development ES6.pdf' , title : 'Test-Driven Development' , category : 'javascript' } ,
853-
854- // Node.js Certificates
855- { path : 'data/CertificateOfCompletion_Advanced Node.js.pdf' , title : 'Advanced Node.js' , category : 'node' } ,
856- { path : 'data/CertificateOfCompletion_Node.js Design Patterns.pdf' , title : 'Node.js Design Patterns' , category : 'node' } ,
857- { path : 'data/CertificateOfCompletion_Node.js Security.pdf' , title : 'Node.js Security' , category : 'node' } ,
858-
859- // CSS Certificates
860- { path : 'data/CertificateOfCompletion_CSS Essential Training.pdf' , title : 'CSS Essential Training' , category : 'css' } ,
861- { path : 'data/CertificateOfCompletion_Responsive Layout.pdf' , title : 'Responsive Layout' , category : 'css' } ,
862-
863- // AI Certificates
864- { path : 'data/CertificateOfCompletion_Career Essentials in Generative AI by Microsoft and LinkedIn.pdf' , title : 'Generative AI Essentials' , category : 'ai' } ,
865- { path : 'data/CertificateOfCompletion_Unboxing AI Build a Remote MCP Server from Zero to Deployed with OAuth.pdf' , title : 'MCP Server Certificate' , category : 'ai' } ,
866- { path : 'data/CertificateOfCompletion_Artificial Intelligence Foundations Machine Learning.pdf' , title : 'Machine Learning' , category : 'ai' } ,
867-
868- // Development Certificates
869- { path : 'data/CertificateOfCompletion_Search Techniques for Web Developers.pdf' , title : 'Search Techniques' , category : 'development' } ,
870- { path : 'data/CertificateOfCompletion_Essential New Skills in Software Engineering.pdf' , title : 'Software Engineering Skills' , category : 'development' } ,
871- { path : 'data/CertificateOfCompletion_Unix Essential Training.pdf' , title : 'Unix Shell' , category : 'development' } ,
872- { path : 'data/CertificateOfCompletion_Introduction to Career Skills in Software Development.pdf' , title : 'Career Skills in Software Development' , category : 'development' } ,
873- { path : 'data/CertificateOfCompletion_Career Essentials in Software Development by Microsoft and LinkedIn.pdf' , title : 'Career Essentials in Software Development' , category : 'development' } ,
874- { path : 'data/CertificateOfCompletion_Programming Foundations Software TestingQA.pdf' , title : 'Software Testing & QA' , category : 'development' } ,
875- { path : 'data/CertificateOfCompletion_Programming Foundations Beyond the Fundamentals.pdf' , title : 'Programming Beyond Fundamentals' , category : 'development' } ,
876- { path : 'data/CertificateOfCompletion_Programming Foundations Fundamentals.pdf' , title : 'Programming Fundamentals' , category : 'development' } ,
877-
878- // Other Certificates
879- { path : 'data/CertificateOfCompletion_Learning MongoDB.pdf' , title : 'MongoDB' , category : 'database' } ,
880- { path : 'data/Gabrielius Pocevicius_Python.pdf' , title : 'Python Certificate' , category : 'python' }
881- ] ;
835+ // Certificate data - Deduplicated and organized
836+ const certificates = [
837+ { path : 'data/UdacityCertificate.pdf' , title : 'Udacity Certificate' , category : 'education' } ,
838+ { path : 'data/CertificateOfCompletion_Microsoft Azure AI Essentials Professional Certificate by Microsoft and LinkedIn.pdf' , title : 'Microsoft Azure AI' , category : 'education' } ,
839+ // Removed duplicate: 'data/Coursera E4WZRUCIBAMB.pdf' - same as Data Engineering below
840+ { path : 'data/Coursera Cert Data Engineering.pdf' , title : 'DeepLearning.AI Data Engineering' , category : 'education' } ,
841+ { path : 'data/CertificateOfCompletion_Economics Quantitative Demand Analysis.pdf' , title : 'Quantitative Demand Analysis' , category : 'education' } ,
842+ { path : 'data/CertificateOfCompletion_Marketing Strategy Competitive Intelligence.pdf' , title : 'Marketing' , category : 'education' } ,
843+
844+ // JavaScript Certificates
845+ { path : 'data/CertificateOfCompletion_Master JavaScript.pdf' , title : 'Master JavaScript' , category : 'javascript' } ,
846+ { path : 'data/Gabrielius Pocevicius_JavaScript.pdf' , title : 'JavaScript Certificate' , category : 'javascript' } ,
847+ { path : 'data/CertificateOfCompletion_EndtoEnd JavaScript Testing with Cypress.io.pdf' , title : 'Cypress.io Testing' , category : 'javascript' } ,
848+ { path : 'data/CertificateOfCompletion_Ethical Hacking with JavaScript.pdf' , title : 'Ethical Hacking' , category : 'javascript' } ,
849+ { path : 'data/CertificateOfCompletion_JavaScript Best Practices for Code Formatting.pdf' , title : 'JS Code Formatting' , category : 'javascript' } ,
850+ { path : 'data/CertificateOfCompletion_JavaScript Best Practices for Data.pdf' , title : 'JS Data Practices' , category : 'javascript' } ,
851+ { path : 'data/CertificateOfCompletion_JavaScript Security Essentials.pdf' , title : 'JS Security' , category : 'javascript' } ,
852+ { path : 'data/CertificateOfCompletion_JavaScript TestDriven Development ES6.pdf' , title : 'Test-Driven Development' , category : 'javascript' } ,
853+
854+ // Node.js Certificates
855+ { path : 'data/CertificateOfCompletion_Advanced Node.js.pdf' , title : 'Advanced Node.js' , category : 'node' } ,
856+ { path : 'data/CertificateOfCompletion_Node.js Design Patterns.pdf' , title : 'Node.js Design Patterns' , category : 'node' } ,
857+ { path : 'data/CertificateOfCompletion_Node.js Security.pdf' , title : 'Node.js Security' , category : 'node' } ,
858+
859+ // CSS Certificates
860+ { path : 'data/CertificateOfCompletion_CSS Essential Training.pdf' , title : 'CSS Essential Training' , category : 'css' } ,
861+ { path : 'data/CertificateOfCompletion_Responsive Layout.pdf' , title : 'Responsive Layout' , category : 'css' } ,
862+
863+ // AI Certificates
864+ { path : 'data/CertificateOfCompletion_Career Essentials in Generative AI by Microsoft and LinkedIn.pdf' , title : 'Generative AI Essentials' , category : 'ai' } ,
865+ { path : 'data/CertificateOfCompletion_Unboxing AI Build a Remote MCP Server from Zero to Deployed with OAuth.pdf' , title : 'MCP Server Certificate' , category : 'ai' } ,
866+ { path : 'data/CertificateOfCompletion_Artificial Intelligence Foundations Machine Learning.pdf' , title : 'Machine Learning' , category : 'ai' } ,
867+
868+ // Development Certificates
869+ { path : 'data/CertificateOfCompletion_Search Techniques for Web Developers.pdf' , title : 'Search Techniques' , category : 'development' } ,
870+ { path : 'data/CertificateOfCompletion_Essential New Skills in Software Engineering.pdf' , title : 'Software Engineering Skills' , category : 'development' } ,
871+ { path : 'data/CertificateOfCompletion_Unix Essential Training.pdf' , title : 'Unix Shell' , category : 'development' } ,
872+ { path : 'data/CertificateOfCompletion_Introduction to Career Skills in Software Development.pdf' , title : 'Career Skills in Software Development' , category : 'development' } ,
873+ { path : 'data/CertificateOfCompletion_Career Essentials in Software Development by Microsoft and LinkedIn.pdf' , title : 'Career Essentials in Software Development' , category : 'development' } ,
874+ { path : 'data/CertificateOfCompletion_Programming Foundations Software TestingQA.pdf' , title : 'Software Testing & QA' , category : 'development' } ,
875+ { path : 'data/CertificateOfCompletion_Programming Foundations Beyond the Fundamentals.pdf' , title : 'Programming Beyond Fundamentals' , category : 'development' } ,
876+ { path : 'data/CertificateOfCompletion_Programming Foundations Fundamentals.pdf' , title : 'Programming Fundamentals' , category : 'development' } ,
877+
878+ // Other Certificates
879+ { path : 'data/CertificateOfCompletion_Learning MongoDB.pdf' , title : 'MongoDB' , category : 'database' } ,
880+ { path : 'data/Gabrielius Pocevicius_Python.pdf' , title : 'Python Certificate' , category : 'python' }
881+ ] ;
882+
883+ // Remove duplicates based on path (primary key)
884+ const uniqueCertificates = certificates . filter ( ( cert , index , self ) =>
885+ index === self . findIndex ( c => c . path === cert . path )
886+ ) ;
882887
883888 let loadedCount = 0 ;
884- const totalCerts = certificates . length ;
889+ const totalCerts = uniqueCertificates . length ; // Use unique certificates count
885890 let currentFilter = 'all' ;
891+ const loadedPaths = new Set ( ) ; // Track loaded certificate paths to prevent duplicates
886892
887893 // Initialize all functionality when DOM is loaded
888894 document . addEventListener ( 'DOMContentLoaded' , function ( ) {
895+ // Clear any existing certificates first
896+ const certContainer = document . getElementById ( 'cert-container' ) ;
897+ if ( certContainer ) {
898+ certContainer . innerHTML = '' ;
899+ loadedPaths . clear ( ) ;
900+ loadedCount = 0 ;
901+ }
902+
889903 // Initialize certificates
890904 initializeCertificates ( ) ;
891905
@@ -976,12 +990,23 @@ <h4 class="font-medium mb-4">Follow Me</h4>
976990 }
977991 }
978992
979- // Create certificate card
993+ // Create certificate card with duplicate prevention
980994 function createCertificateCard ( cert , index ) {
981995 return new Promise ( ( resolve ) => {
996+ // Check if this certificate path has already been loaded
997+ if ( loadedPaths . has ( cert . path ) ) {
998+ console . warn ( `Duplicate certificate prevented: ${ cert . title } (${ cert . path } )` ) ;
999+ resolve ( ) ;
1000+ return ;
1001+ }
1002+
1003+ // Add to loaded paths set
1004+ loadedPaths . add ( cert . path ) ;
1005+
9821006 const card = document . createElement ( 'div' ) ;
9831007 card . className = 'opacity-0 transform translate-y-4 transition-all duration-300 ease-in-out cert-card' ;
9841008 card . dataset . categories = cert . category ;
1009+ card . dataset . certPath = cert . path ; // Add unique identifier
9851010
9861011 card . innerHTML = `
9871012 <div class="card rounded-lg overflow-hidden">
@@ -1031,10 +1056,33 @@ <h4 class="font-medium mb-4">Follow Me</h4>
10311056 } ) ;
10321057 }
10331058
1034- // Load all certificates
1059+ // Load all certificates with duplicate prevention
10351060 async function loadAllCertificates ( ) {
1036- const loadPromises = certificates . map ( ( cert , index ) => createCertificateCard ( cert , index ) ) ;
1061+ // Use unique certificates and filter out any potential runtime duplicates
1062+ const loadPromises = uniqueCertificates . map ( ( cert , index ) => createCertificateCard ( cert , index ) ) ;
10371063 await Promise . all ( loadPromises ) ;
1064+
1065+ // Final check: remove any duplicate DOM elements that might have been created
1066+ removeDuplicateCards ( ) ;
1067+ }
1068+
1069+ // Remove duplicate cards from DOM if any exist
1070+ function removeDuplicateCards ( ) {
1071+ const certContainer = document . getElementById ( 'cert-container' ) ;
1072+ if ( ! certContainer ) return ;
1073+
1074+ const cards = certContainer . querySelectorAll ( '.cert-card' ) ;
1075+ const seenPaths = new Set ( ) ;
1076+
1077+ cards . forEach ( card => {
1078+ const certPath = card . dataset . certPath ;
1079+ if ( seenPaths . has ( certPath ) ) {
1080+ console . warn ( `Removing duplicate card: ${ certPath } ` ) ;
1081+ card . remove ( ) ;
1082+ } else {
1083+ seenPaths . add ( certPath ) ;
1084+ }
1085+ } ) ;
10381086 }
10391087
10401088 // Certificate loading and filtering
0 commit comments