# Membuat Website Portofolio dengan Django ## Prepare Django Project ##### 1. Buat direktori kerja baru dan masuk kedalam direktori kerjanya di visual studio code ![image](https://hackmd.io/_uploads/HJ2jcujxlx.png) ##### 2. Buat virtual environment dan aktifkan di terminal cmd ```markdown! python -m venv env .\env\Scripts\activate #output (env) C:\Dev\project-akhir> ``` ##### 3. check pip version ```markdown! python -m pip --version #upgrade pip python -m pip install --upgrade pip ``` ##### 4. install django ```markdown! py -m pip install Django ``` ##### 5. install dependency ```markdown! pip install psycopg2 pip install gunicorn dj-database-url pip install django-crispy-forms pip install pillow ``` ##### 6. Menyimpan dependency yang sudah terinstall ```markdown! pip freeze > requirements.txt ``` ## Membuat Web Portofolio ##### 1. buat proyek baru ```markdown! django-admin startproject portofolioku cd portofolioku ``` ##### 2. migrate database ```markdown! python manage.py migrate ``` ##### 3. runserver ```markdown! python manage.py runserver ``` ##### 4. Buat aplikasi ```markdown! django-admin startapp base ``` ##### 5. tambahkan apps yang telah dibuat di file settings.py ```markdown! INSTALLED_APPS = [ . . . 'base', ] ``` ##### 7. buat direktori `templates` didalam direktori `base` ![image](https://hackmd.io/_uploads/rk86kYillg.png) ##### 8. buat `index.html` didalam direktori templates ```markdown! {% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="{% static 'css/styles.css' %}" /> <link rel="stylesheet" href="{% static 'css/responsive.css' %}" /> <title>Satria Galih Setia Yudha - Telecommunication Engineer</title> <link rel="icon" href="{% static 'favian.ico' %}" type="image/x-icon"> <!-- Google Font --> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet"> <!-- Tambahan CSS untuk beautify --> <style> body { font-family: 'Poppins', sans-serif; background: #f9f9f9; color: #333; } section { padding: 80px 20px; } h2.section-title { text-align: center; font-size: 2.5rem; margin-bottom: 40px; color: #333; } .card { background: white; border-radius: 12px; padding: 20px; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08); transition: transform 0.3s ease, box-shadow 0.3s ease; } .card:hover { transform: translateY(-5px); box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12); } .portfolio .card, .experience-item, .education-item, .contact-item { margin-bottom: 30px; } .back-to-top { position: fixed; bottom: 30px; right: 30px; font-size: 1.5rem; background: #007bff; color: white; border: none; border-radius: 50%; width: 45px; height: 45px; cursor: pointer; box-shadow: 0 6px 16px rgba(0, 123, 255, 0.4); z-index: 999; display: none; transition: background 0.3s ease; } .back-to-top:hover { background: #0056b3; } nav ul li a.active { font-weight: bold; color: #007bff !important; } /* Animasi saat scroll */ [data-animate] { opacity: 0; transform: translateY(30px); transition: all 0.6s ease-out; } </style> </head> <body> <!-- Header --> <header> <div class="main-container"> <div class="nav"> <div class="logo"> <a href="/">Portofolio</a> </div> <nav> <ul> <li><a href="#services">Expertise</a></li> <li><a href="#experience">Experience</a></li> <li><a href="#portfolios">Projects</a></li> <li><a href="#education">Education</a></li> <li><a href="#skills">Skill</a></li> <li><a href="#contact">Contact</a></li> <li> <a href="{% static 'resume.pdf' %}" target="_blank" class="btn">Resume</a> </li> </ul> </nav> <div class="burger"> <div class="line-1"></div> <div class="line-2"></div> <div class="line-3"></div> </div> </div> <section id="hero"> <div class="hero-container main-container"> <!-- Tambahkan wrapper container --> <div class="hero-grid"> <!-- Tambahkan div untuk konten teks --> <div class="hero-left"> <h3 class="pre-title">Telecommunication Engineer</h3> <h1 class="hero-name">Satria Galih Setia <span>Yudha</span></h1> <p class="hero-description"> Specializing in network infrastructure, Linux administration, and MikroTik solutions. With hands-on experience at Telkom Indonesia and multiple certifications including MTCNA and CCNA, I deliver efficient and secure network solutions. </p> <div class="social-links"> <a href="https://www.linkedin.com/in/satriagalihsetiayudha/" target="_blank"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/> </svg> </a> <a href="https://www.instagram.com/sattrrrr__/" target="_blank"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/> </svg> </a> </div> </div> <div class="hero-right"> <div class="hero-image-container"> <img src="{% static 'images/hero.png' %}" alt="Satria Galih Setia Yudha" class="hero-image"/> </div> </div> </div> </div> </section> <!-- End Header --> <!-- Services --> <section id="services"> <div class="main-container"> <div class="services main-container"> <h3 class="pre-title">What I Do</h3> <h1 class="section-title services-title">My Expertise</h1> <div class="grid-3"> <div class="service"> <div class="service-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-1 17l-5-5.299 1.399-1.43 3.574 3.736 6.572-7.007 1.455 1.403-8 8.597z"/> </svg> </div> <h4>Network Infrastructure</h4> <p> Design, implement, and maintain robust network infrastructures with expertise in MikroTik, Cisco, and wireless networking solutions. </p> </div> <div class="service"> <div class="service-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path d="M12 0l-11 6v12.131l11 5.869 11-5.869v-12.066l-11-6.065zm-9 8.23l8 4.363v8.607l-8-4.268v-8.702zm10 12.97v-8.6l8-4.269v8.6l-8 4.269z"/> </svg> </div> <h4>Linux Administration</h4> <p> Proficient in Linux server management, containerization with Docker, and deploying enterprise applications in Linux environments. </p> </div> <div class="service"> <div class="service-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm6 13h-5v5h-2v-5h-5v-2h5v-5h2v5h5v2z"/> </svg> </div> <h4>Cloud Network Optimization</h4> <p> Build and optimize secure, scalable cloud networks for peak performance, leveraging hybrid and multi-cloud strategies to meet evolving business demands. </p> </div> </div> </div> </div> </section> <!-- End Services --> <!-- Experience Section --> <section id="experience"> <div class="main-container"> <div class="experience main-container"> <h3 class="pre-title">Career Journey</h3> <h1 class="section-title">Professional Experience</h1> <div class="experience-container"> <!-- Experience 1 --> <div class="experience-item card"> <div class="experience-header"> <div class="experience-title"> <h3>Student Internship</h3> <h4>Mobility & FMC Lab. DCS Telkom · PT. Telkom Indonesia</h4> </div> <div class="experience-date"> <span>Apr 2023 - Sep 2023</span> <span>6 months</span> </div> </div> <div class="experience-details"> <ul> <li>Assisted in managing and troubleshooting hardware, software, and computer network issues</li> <li>Performed hardware upgrades including servers and PCs</li> <li>Conducted research on network devices such as routers and access points</li> </ul> <div class="experience-skills"> <span>Wireless Technologies</span> <span>Wireless Networking</span> <span>Network Administration</span> <span>Hardware Maintenance</span> </div> <a href="{% static 'Sign_Sertifikat Satria Galih.pdf' %}" target="_blank" class="experience-certificate"> View Certificate </a> </div> </div> <!-- You can add more experience items here --> </div> </div> </div> </section> <!-- Portfolios --> <section id="portfolios"> <div class="main-container"> <div class="portfolios main-container"> <h3 class="pre-title">My Works</h3> <h1 class="section-title">Featured Projects</h1> <div class="grid-3"> <!-- Portfolio 1 --> <div class="portfolio"> <div class="portfolio-cover"> <img src="{% static 'images/portfolio-1.jpeg' %}" alt="Inventory Management System" /> </div> <div class="portfolio-info"> <div class="portfolio-title"> <h4>Inventory Management System</h4> </div> <div class="portfolio-tags"> <div>Docker</div> <div>Odoo</div> <div>Linux</div> </div> <p> Developed a comprehensive inventory management and POS system using Odoo in Docker containers, ensuring scalability and easy deployment. </p> </div> </div> <!-- Portfolio 2 --> <div class="portfolio"> <div class="portfolio-cover"> <img src="{% static 'images/portfolio-2.jpeg' %}" alt="AApanel Network Configuration" /> </div> <div class="portfolio-info"> <div class="portfolio-title"> <h4>AApanel Network Setup</h4> <a href="#" class="portfolio-link"> </a> </div> <div class="portfolio-tags"> <div>Linux</div> <div>Networking</div> <div>AApanel</div> </div> <p> Installed and configured AApanel on a network infrastructure, optimizing server performance and implementing security measures. </p> </div> </div> <!-- Portfolio 3 --> <div class="portfolio"> <div class="portfolio-cover"> <img src="{% static 'images/portfolio-3.jpeg' %}" alt="Netwatch Failover Configuration on MikroTik" /> </div> <div class="portfolio-info"> <div class="portfolio-title"> <h4>Captive Portal Solution</h4> <a href="#" class="portfolio-link"> </a> </div> <div class="portfolio-tags"> <div>MikroTik</div> <div>Wireless</div> <div>Iron Wifi</div> </div> <p> Failover ensures uninterrupted internet by automatically switching to a backup connection if the primary one fails. MikroTik's Netwatch monitors the connection status at set intervals to trigger this switch. </p> </div> </div> </div> </div> </div> </section> <!-- End Portfolios --> <!-- Education Section --> <section id="education"> <div class="main-container"> <div class="education-section main-container"> <h3 class="pre-title">Learning Path</h3> <h1 class="section-title">Education</h1> <div class="education-container"> <div class="education-item card"> <div class="line"> <div></div> </div> <div class="education-info"> <h4 class="education-title">Telkom University</h4> <p>Bachelor Degree - Telecommunication Engineering</p> <div class="education-meta"> <span class="education-years">2024 - Present</span> <span class="education-gpa">GPA: 3.61</span> </div> </div> </div> <div class="education-item card"> <div class="line"> <div></div> </div> <div class="education-info"> <h4 class="education-title">SMK Telkom Purwokerto</h4> <p>Computer and Network Engineering</p> <div class="education-meta"> <span class="education-years">2021 - 2024</span> <span class="education-gpa">GPA: 91.66</span> </div> </div> </div> </div> </div> </div> </section> <!-- Skills Section --> <section id="skills"> <div class="main-container"> <div class="skills main-container"> <h3 class="pre-title">Technical Expertise</h3> <h1 class="section-title">Skills & Competencies</h1> <div class="skills-grid"> <div class="skills-right"> <p> With 3+ years of hands-on experience in Network Engineering and Linux Administration, I specialize in deploying secure and efficient network solutions. My technical expertise includes: </p> <div class="skills-list"> <ul> <li>MikroTik (MTCNA Certified)</li> <li>Cisco Networking (CCNA)</li> <li>Linux Server Administration</li> <li>Docker & Containerization</li> </ul> <ul> <li>Wireless Networking</li> <li>Virtualization (VirtualBox)</li> <li>Network Troubleshooting</li> <li>Hardware Installation</li> </ul> </div> </div> </div> </div> </div> </section> <!-- Contact --> <!-- <section id="contact"> <div class="main-container"> <div class="contact main-container"> <div class="contact-left"> <form action="https://formspree.io/f/xeqwdykj" method="POST" class="contact-form" > <div> <input type="text" placeholder="Name" name="name" required /> </div> <div> <input type="email" placeholder="Email" name="email" required /> </div> <div> <textarea name="message" id="message" cols="30" rows="10" placeholder="Message" required ></textarea> </div> <div> <button class="btn-submit">Send Message</button> </div> </form> </div> --> <section id="contact"> <div class="contact-right"> <div class="main-container"> <div class="contact-item"> <div class="contact-item-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" > <path d="M12 1c-3.148 0-6 2.553-6 5.702 0 3.148 2.602 6.907 6 12.298 3.398-5.391 6-9.15 6-12.298 0-3.149-2.851-5.702-6-5.702zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2zm12 14h-24l4-8h3.135c.385.641.798 1.309 1.232 2h-3.131l-2 4h17.527l-2-4h-3.131c.435-.691.848-1.359 1.232-2h3.136l4 8z" /> </svg> </div> <div class="contact-item-detail"> <h4>Location</h4> <p>Purbalingga, Jawa Tengah, Indonesia</p> </div> </div> <div class="contact-item"> <div class="contact-item-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" > <path d="M20 22.621l-3.521-6.795c-.008.004-1.974.97-2.064 1.011-2.24 1.086-6.799-7.82-4.609-8.994l2.083-1.026-3.493-6.817-2.106 1.039c-7.202 3.755 4.233 25.982 11.6 22.615.121-.055 2.102-1.029 2.11-1.033z" /> </svg> </div> <div class="contact-item-detail"> <h4>Phone</h4> <p>+62 889 0597 4081</p> </div> </div> <div class="contact-item"> <div class="contact-item-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" > <path d="M0 3v18h24v-18h-24zm21.518 2l-9.518 7.713-9.518-7.713h19.036zm-19.518 14v-11.817l10 8.104 10-8.104v11.817h-20z" /> </svg> </div> <div class="contact-item-detail"> <h4>Email</h4> <p>iaayudha@gmail.com</p> </div> </div> </div> </div> </div> </section> <!-- End Contact --> <!-- Footer --> <footer> <div class="footer-icons"> <a href="https://www.linkedin.com/in/satriagalihsetiayudha/" target="_blank"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/> </svg> </a> <a href="https://www.instagram.com/sattrrrr__/" target="_blank"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/> </svg> </a> <a href="mailto:iaayudha@gmail.com"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path d="M0 3v18h24v-18h-24zm21.518 2l-9.518 7.713-9.518-7.713h19.036zm-19.518 14v-11.817l10 8.104 10-8.104v11.817h-20z"/> </svg> </a> <a href="tel:+6288905974081"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path d="M20 22.621l-3.521-6.795c-.008.004-1.974.97-2.064 1.011-2.24 1.086-6.799-7.82-4.609-8.994l2.083-1.026-3.493-6.817-2.106 1.039c-7.202 3.755 4.233 25.982 11.6 22.615.121-.055 2.102-1.029 2.11-1.033z"/> </svg> </a> </div> <p>&#169; 2025 Satria Galih Setia Yudha. All rights reserved.</p> </footer> <!-- End Footer --> <script src="{% static 'js/main.js' %}"></script> </body> </html> ``` ##### 9. buat folder static di dalam direktori portofolio ![image](https://hackmd.io/_uploads/Hyr3xtjeee.png) ##### 10. buat folder images, js, dan css didalam diretori static ![image](https://hackmd.io/_uploads/rkbCbFsxxx.png) ##### 11. buat file `styles.css` dan `responsive.css` di dalam direktori css responsive.css ```markdown! @media screen and (max-width: 1200px) { .main-container { width: 100%; padding: 0 15px; } .nav { padding: 1rem 15px; } #hero { padding: 4rem 15px; } section { padding-left: 15px; padding-right: 15px; } } @media screen and (max-width: 1000px) { :root { --sectionPadding: 5rem 0; } p { font-size: 0.95rem; line-height: 1.7; } .hero-name { font-size: 3rem; } .grid-3 { gap: 2rem; } .skills-list { grid-template-columns: 1fr; } .contact { gap: 2rem; } } @media screen and (max-width: 825px) { /* Burger Menu */ .burger { display: block; cursor: pointer; } .toggle-burger .line-1 { transform: rotate(-45deg) translate(-5px, 7px); } .toggle-burger .line-2 { opacity: 0; } .toggle-burger .line-3 { transform: rotate(45deg) translate(-5px, -7px); } /* Navigation */ nav { position: static; width: 60%; right: 0; top: 0; height: 100vh; flex-direction: column; background: var(--primaryBackgroundColor); border-left: 1px solid var(--borderColor); z-index: 9; transform: translateX(100%); transition: 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); padding: 1rem 0; box-shadow: -10px 0 30px rgba(0, 0, 0, 0.1); } nav ul { flex-direction: column; gap: 2.5rem; } .nav-active { transform: translateX(0); } :root { --sectionPadding: 4rem 0; } /* Titles */ .section-title { font-size: 2rem; margin: 0.6rem 0 3rem; } .pre-title { font-size: 0.85rem; } /* Hero Section */ #hero { grid-template-columns: 1fr; height: auto; padding: 3rem 0; } .hero-right { order: -1; margin-bottom: 3rem; } .hero-right img { width: 80%; max-width: 400px; } .hero-left { text-align: center; } .hero-name { font-size: 2.5rem; } .social-links { justify-content: center; } /* Skills Section */ .skills-grid { gap: 2rem; } .education { margin-bottom: 2rem; } /* Footer */ footer { padding: 3rem 0; } } @media screen and (max-width: 640px) { /* Titles */ .section-title { font-size: 1.8rem; margin-bottom: 2.5rem; } .pre-title { font-size: 0.8rem; letter-spacing: 0.2rem; } /* Hero */ .hero-name { font-size: 2.2rem; } /* Services */ .service { padding: 2rem 1.5rem; } /* Portfolio */ .portfolio-cover { height: 200px; } /* Contact */ .contact-item { flex-direction: column; align-items: flex-start; gap: 1rem; } .contact-item-icon { width: 50px; height: 50px; } /* Footer */ .footer-icons { gap: 1rem; } } @media screen and (max-width: 480px) { :root { --sectionPadding: 3.5rem 0; } /* Navigation */ nav { width: 80%; } /* Hero */ .hero-name { font-size: 2rem; } @media screen and (max-width: 768px) { .hero-image-container { width: 280px; height: 280px; margin: 0 auto; } @media screen and (max-width: 480px) { .hero-image-container { width: 220px; height: 220px; } } } /* Portfolio */ .grid-3 { grid-template-columns: 1fr; } /* Skills */ .skills-grid { grid-template-columns: 1fr; } /* Contact Form */ .contact-form input, .contact-form textarea { padding: 0.8rem 1.2rem; } /* Footer */ footer p { font-size: 0.9rem; } } /* Special height adjustments for mobile */ @media screen and (max-height: 700px) and (orientation: landscape) { #hero { height: auto; padding: 4rem 0; } .hero-right img { width: 50%; } nav ul { gap: 1.5rem; } } @media screen and (max-width: 768px) { .experience-header { flex-direction: column; } .experience-date { text-align: left; } .experience-skills span { font-size: 0.8rem; } } @media screen and (max-width: 480px) { .experience-item { padding: 1.5rem; } .education-item { margin-bottom: 2rem; } } /* Dark mode preference */ /* @media (prefers-color-scheme: dark) { :root { --primaryTextColor: #f7fafc; --secondaryTextColor: #e2e8f0; --borderColor: #2d3748; --lineColor: #4a5568; --primaryBackgroundColor: #1a202c; --secondaryBackgroundColor: #2d3748; --thirdBackgroundColor: #4a5568; } .service, .portfolio { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1); } } */ ``` styles.css ```markdown! @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"); :root { --primaryTextColor: #1F2937; --secondaryTextColor: #4B5563; --accentColor: #3B82F6; --accentHover: #2563EB; --borderColor: #E5E7EB; --lineColor: #D1D5DB; --primaryBackgroundColor: #FFFFFF; --secondaryBackgroundColor: #F9FAFB; --thirdBackgroundColor: #EFF6FF; --sectionPadding: 6rem 0; --itemBorderRadius: 0.75rem; --boxShadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); } * { margin: 0; padding: 0; box-sizing: border-box; scroll-behavior: smooth; } body { font-family: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; line-height: 1.7; color: var(--secondaryTextColor); background-color: var(--primaryBackgroundColor); } h1, h2, h3, h4, h5, h6, strong { color: var(--primaryTextColor); font-weight: 700; letter-spacing: -0.025em; } p { font-size: 1.05rem; line-height: 1.8rem; color: var(--secondaryTextColor); } a { text-decoration: none; color: inherit; transition: all 0.3s ease; } .main-container { width: 100%; max-width: none; margin: 0; padding: 0 20px; } @media screen and (max-width: 1200px) { .main-container { width: 90%; } } .btn { padding: 0.8rem 1.5rem; background: var(--accentColor); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); color: white; border: none; border-radius: var(--itemBorderRadius); cursor: pointer; transition: all 0.3s ease; font-weight: 500; display: inline-block; position: relative; overflow: hidden; z-index: 1; } .btn::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: 0.5s; z-index: -1; } .btn:hover { background: var(--accentHover); transform: translateY(-2px); box-shadow: var(--boxShadow); } .btn:hover::before { left: 100%; } .section-title { margin: 1rem 0 4rem; font-size: 2.25rem; text-align: center; position: relative; display: inline-block; font-weight: 800; letter-spacing: -0.03em; } .section-title::after { content: ''; position: absolute; width: 50%; height: 4px; background: linear-gradient(to right, var(--accentColor), transparent); bottom: -10px; left: 25%; border-radius: 2px; } .pre-title { text-transform: uppercase; letter-spacing: 0.3rem; color: var(--secondaryTextColor); position: relative; padding-left: 40px; width: fit-content; font-weight: 400; font-size: 0.9rem; display: block; margin: 0 auto 1rem; } .pre-title::before { content: ""; width: 30px; height: 1px; background: var(--lineColor); position: absolute; display: block; left: 0; top: 50%; } .grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 3rem; } .nav { display: flex; justify-content: space-between; padding: 1rem 20px; align-items: center; position: sticky; top: 0; background-color: var(--primaryBackgroundColor); z-index: 1000; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; } nav ul { list-style: none; display: flex; gap: 2.5rem; } .logo { display: flex; align-items: center; font-weight: 700; font-size: 1.8rem; color: var(--accentColor); } nav ul li { display: flex; align-items: center; } nav ul li a { font-weight: 500; position: relative; } nav ul li a::after { content: ''; position: absolute; width: 0; height: 2px; bottom: -4px; left: 0; background-color: var(--accentColor); transition: width 0.3s ease; } nav ul li a:hover::after { width: 100%; } .burger div { width: 25px; height: 2px; background-color: var(--primaryTextColor); margin: 7px; transition: all 0.3s; z-index: 99; } .burger { display: none; z-index: 1001; position: fixed; top: 33px; right: 35px; } #hero { padding: var(--sectionPadding); background: linear-gradient(135deg, #f8fafc 0%, #f0f9ff 100%); width: 100vw; margin-left: calc(-50vw + 50%); } .hero-description { text-align: justify; text-justify: inter-word; } .hero-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 3rem; align-items: center; } .hero-left { padding: 2rem; border-radius: var(--itemBorderRadius); background: linear-gradient(135deg, rgba(249, 250, 251, 0.9) 0%, rgba(239, 246, 255, 0.9) 100%); backdrop-filter: blur(5px); } .hero-name { font-size: 3.5rem; font-weight: 700; line-height: 1.1; margin: 0.5rem 0 1.5rem; background: linear-gradient(to right, var(--primaryTextColor) 60%, var(--accentColor)); -webkit-background-clip: text; background-clip: text; color: transparent; } .hero-name span { color: var(--accentColor) !important; } .hero-right img { width: 100%; max-width: 500px; border-radius: var(--itemBorderRadius); box-shadow: var(--boxShadow); transition: transform 0.3s ease; } .hero-right img:hover { transform: scale(1.02); } .hero-right { display: flex; justify-content: center; } .hero-image-container { width: 320px; height: 320px; border-radius: 50%; overflow: hidden; border: 3px solid var(--accentColor); box-shadow: 0 10px 30px rgba(79, 70, 229, 0.2); position: relative; animation: float 6s ease-in-out infinite; } .hero-image { border-radius: 8px; border: 3px solid var(--accentColor); transform: rotate(3deg); transition: transform 0.3s ease; } .hero-image:hover { transform: rotate(0); } .hero-container { padding: 0 clamp(20px, 5vw, 60px); } .social-links { display: flex; gap: 1.5rem; margin-top: 2rem; } .social-links a { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 50%; background: var(--thirdBackgroundColor); transition: all 0.3s ease; } .social-links a:hover { background: var(--accentColor); transform: translateY(-3px); } .social-links a:hover svg { fill: white; } .social-links svg { width: 20px; height: 20px; fill: var(--primaryTextColor); } #services { background-color: var(--secondaryBackgroundColor); padding: var(--sectionPadding); position: relative; overflow: hidden; } #services::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: radial-gradient(circle at 10% 20%, rgba(59, 130, 246, 0.03) 0%, transparent 20%); pointer-events: none; } .service { padding: 2.5rem; text-align: center; border-radius: 16px; background: white; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); position: relative; overflow: hidden; animation: fadeIn 0.6s ease forwards; } .service::after { content: ''; position: absolute; bottom: 0; left: 0; width: 100%; height: 4px; background: var(--accentColor); transform: scaleX(0); transform-origin: left; transition: transform 0.3s ease; } .service:hover { transform: translateY(-10px) scale(1.02); box-shadow: 0 15px 30px rgba(59, 130, 246, 0.15); } .service:hover::after { transform: scaleX(1); } .service h4 { margin: 1.5rem 0; font-weight: 600; font-size: 1.2rem; } .service-icon { background: var(--thirdBackgroundColor); width: fit-content; margin: 0 auto; padding: 1.2rem; border-radius: 50%; display: flex; align-items: center; justify-content: center; } .service-icon svg { fill: var(--accentColor); width: 30px; height: 30px; } #experience { padding: var(--sectionPadding); background-color: var(--secondaryBackgroundColor); position: relative; overflow: hidden; } #experience::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: radial-gradient(circle at 10% 20%, rgba(59, 130, 246, 0.03) 0%, transparent 20%); pointer-events: none; } .experience-container { display: flex; flex-direction: column; gap: 2.5rem; } .experience-item { background: var(--primaryBackgroundColor); border-radius: var(--itemBorderRadius); padding: 2rem; box-shadow: var(--boxShadow); transition: transform 0.3s ease; animation: fadeIn 0.6s ease forwards; } .experience-item:hover { transform: translateY(-5px); } .experience-header { display: flex; justify-content: space-between; margin-bottom: 1.5rem; flex-wrap: wrap; gap: 1rem; } .experience-title h3 { color: var(--accentColor); font-size: 1.3rem; margin-bottom: 0.5rem; } .experience-title h4 { font-weight: 500; color: var(--secondaryTextColor); } .experience-date { text-align: right; } .experience-date span { display: block; color: var(--secondaryTextColor); } .experience-date span:first-child { font-weight: 500; color: var(--primaryTextColor); } .experience-details ul { margin: 1rem 0; padding-left: 1.5rem; } .experience-details li { margin-bottom: 0.8rem; position: relative; list-style-type: none; } .experience-details li::before { content: "▹"; position: absolute; left: -1.5rem; color: var(--accentColor); } .experience-skills { display: flex; flex-wrap: wrap; gap: 0.8rem; margin: 1.5rem 0; } .experience-skills span { background: var(--thirdBackgroundColor); padding: 0.4rem 0.8rem; border-radius: 50px; font-size: 0.85rem; } .experience-certificate { display: inline-block; color: var(--accentColor); font-weight: 500; margin-top: 1rem; transition: all 0.3s ease; } .experience-certificate:hover { color: var(--accentHover); transform: translateX(5px); } #portfolios { padding: var(--sectionPadding); } .portfolio { border-radius: 16px; overflow: hidden; border: 1px solid var(--borderColor); transition: all 0.4s ease; background: white; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); position: relative; animation: fadeIn 0.6s ease forwards; } .portfolio::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(to bottom, transparent 60%, rgba(0, 0, 0, 0.7)); opacity: 0; transition: opacity 0.3s ease; z-index: 1; } .portfolio:hover { transform: translateY(-8px); box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); } .portfolio:hover::before { opacity: 1; } .portfolio-cover { height: 250px; overflow: hidden; } .portfolio img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; } .portfolio:hover img { transform: scale(1.05); } .portfolio-info { padding: 2rem 1.5rem; position: relative; z-index: 2; transition: transform 0.3s ease; } .portfolio:hover .portfolio-info { transform: translateY(-10px); } .portfolio-title { display: flex; justify-content: space-between; align-items: center; gap: 1rem; margin-bottom: 1rem; } .portfolio h4 { font-weight: 600; font-size: 1.2rem; } .portfolio-title a svg { fill: var(--secondaryTextColor); transition: all 0.3s ease; } .portfolio-title a:hover svg { fill: var(--accentColor); transform: translateX(3px); } .portfolio-tags { display: flex; gap: 0.8rem; margin: 1rem 0; flex-wrap: wrap; } .portfolio-tags div { font-size: 0.8rem; border: 1px solid var(--borderColor); padding: 0.4rem 0.8rem; color: var(--secondaryTextColor); border-radius: 50px; background: var(--secondaryBackgroundColor); } #skills { padding: var(--sectionPadding); background: var(--secondaryBackgroundColor); position: relative; overflow: hidden; } #skills::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: radial-gradient(circle at 10% 20%, rgba(59, 130, 246, 0.03) 0%, transparent 20%); pointer-events: none; } .skills-grid { display: grid; grid-template-columns: 1fr; gap: 2rem; } .skills-right { max-width: 800px; margin: 0 auto; } .skills-list { display: grid; grid-template-columns: 1fr 1fr; margin-top: 2rem; gap: 1.5rem; } .skills-list ul { list-style: none; } .skills-list li { margin-bottom: 1rem; position: relative; padding-left: 1.5rem; } .skills-list li::before { content: "▹"; position: absolute; left: 0; color: var(--accentColor); } .education { display: flex; gap: 1.5rem; margin-bottom: 3rem; animation: fadeIn 0.6s ease forwards; } .education .line { padding: 0 0.7rem; } .education .line div { width: 2px; height: 100%; background: var(--borderColor); position: relative; } .education-info p { margin: 0.6rem 0 1.4rem; color: var(--secondaryTextColor); } .education-years { margin-bottom: 1rem; color: var(--accentColor); font-weight: 500; } .education .line div:before { content: ""; width: 15px; height: 15px; background: var(--accentColor); border-radius: 50%; position: absolute; left: -6px; top: 0; } #contact { padding: var(--sectionPadding); background: var(--primaryBackgroundColor); } .contact { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 4rem; } .contact-form div { margin-bottom: 1.5rem; } .contact-form input, .contact-form textarea { width: 100%; padding: 1rem 1.5rem; font-family: "Poppins", sans-serif; background: var(--secondaryBackgroundColor); border: 1px solid var(--borderColor); border-radius: var(--itemBorderRadius); resize: none; transition: all 0.3s ease; } .contact-form input:focus, .contact-form textarea:focus { outline: none; border-color: var(--accentColor); box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1); } .btn-submit { width: 100%; padding: 1rem; background-color: var(--accentColor); color: #fff; border: none; border-radius: var(--itemBorderRadius); cursor: pointer; transition: all 0.3s ease; font-weight: 500; font-size: 1rem; } .btn-submit:hover { background-color: var(--accentHover); transform: translateY(-2px); box-shadow: var(--boxShadow); } .contact-item { display: flex; gap: 1.5rem; margin-bottom: 2.5rem; align-items: flex-start; animation: fadeIn 0.6s ease forwards; } .contact-item-icon { background: var(--thirdBackgroundColor); width: 60px; height: 60px; border-radius: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: all 0.3s ease; } .contact-item:hover .contact-item-icon { background: var(--accentColor); transform: rotate(5deg); } .contact-item:hover svg { fill: white; } .contact-item-icon svg { fill: var(--accentColor); width: 24px; height: 24px; transition: all 0.3s ease; } .contact-item-detail h4 { margin-bottom: 0.6rem; font-size: 1.1rem; } footer { padding: 4rem 0; background: var(--secondaryBackgroundColor); text-align: center; } .footer-icons { margin-bottom: 2rem; display: flex; justify-content: center; gap: 1.5rem; } .footer-icons a { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 50%; background: var(--thirdBackgroundColor); transition: all 0.3s ease; } .footer-icons a:hover { background: var(--accentColor); transform: translateY(-3px); } .footer-icons a:hover svg { fill: white; } .footer-icons svg { width: 20px; height: 20px; fill: var(--primaryTextColor); transition: all 0.3s ease; } /* Animations */ @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } @keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } } @keyframes spin { to { transform: rotate(360deg); } } /* Back to top button */ .back-to-top { position: fixed; bottom: 2rem; right: 2rem; width: 50px; height: 50px; border-radius: 50%; background: var(--accentColor); color: white; border: none; cursor: pointer; opacity: 0; visibility: hidden; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); z-index: 99; } .back-to-top.show { opacity: 1; visibility: visible; } .back-to-top:hover { background: var(--accentHover); transform: translateY(-3px); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); } /* Loading spinner */ .loading-spinner { display: inline-block; width: 1rem; height: 1rem; border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: spin 1s ease-in-out infinite; margin-right: 0.5rem; } /* Responsive Styles */ @media screen and (max-width: 1000px) { :root { --sectionPadding: 5rem 0; } p { font-size: 0.95rem; line-height: 1.7; } .hero-name { font-size: 3rem; } .grid-3 { gap: 2rem; } .skills-list { grid-template-columns: 1fr; } .contact { gap: 2rem; } } @media screen and (max-width: 825px) { /* Burger Menu */ .burger { display: block; cursor: pointer; } .toggle-burger .line-1 { transform: rotate(-45deg) translate(-5px, 7px); } .toggle-burger .line-2 { opacity: 0; } .toggle-burger .line-3 { transform: rotate(45deg) translate(-5px, -7px); } /* Navigation */ nav { position: fixed; width: 60%; right: 0; top: 0; height: 100vh; flex-direction: column; background: var(--primaryBackgroundColor); border-left: 1px solid var(--borderColor); z-index: 9; transform: translateX(100%); transition: 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); padding: 6rem 2rem; box-shadow: -10px 0 30px rgba(0, 0, 0, 0.1); } nav ul { flex-direction: column; gap: 2.5rem; } .nav-active { transform: translateX(0); } :root { --sectionPadding: 4rem 0; } /* Titles */ .section-title { font-size: 2rem; margin: 0.6rem 0 3rem; } .pre-title { font-size: 0.85rem; } /* Hero Section */ #hero { grid-template-columns: 1fr; height: auto; padding: 4rem 20px; } .hero-right { order: -1; margin-bottom: 3rem; } .hero-right img { width: 80%; max-width: 400px; } .hero-left { text-align: center; } .hero-name { font-size: 2.5rem; } .social-links { justify-content: center; } /* Skills Section */ .skills-grid { gap: 2rem; } .education { margin-bottom: 2rem; } /* Footer */ footer { padding: 3rem 0; } } @media screen and (max-width: 640px) { /* Titles */ .section-title { font-size: 1.8rem; margin-bottom: 2.5rem; } .pre-title { font-size: 0.8rem; letter-spacing: 0.2rem; } /* Hero */ .hero-name { font-size: 2.2rem; } /* Services */ .service { padding: 2rem 1.5rem; } /* Portfolio */ .portfolio-cover { height: 200px; } /* Contact */ .contact-item { flex-direction: column; align-items: flex-start; gap: 1rem; } .contact-item-icon { width: 50px; height: 50px; } /* Footer */ .footer-icons { gap: 1rem; } } @media screen and (max-width: 480px) { :root { --sectionPadding: 3.5rem 0; } /* Navigation */ nav { width: 80%; } /* Hero */ .hero-name { font-size: 2rem; } .hero-image-container { width: 220px; height: 220px; } /* Portfolio */ .grid-3 { grid-template-columns: 1fr; } /* Skills */ .skills-grid { grid-template-columns: 1fr; } /* Contact Form */ .contact-form input, .contact-form textarea { padding: 0.8rem 1.2rem; } /* Footer */ footer p { font-size: 0.9rem; } } /* Special height adjustments for mobile */ @media screen and (max-height: 700px) and (orientation: landscape) { #hero { height: auto; padding: 4rem 20px; } .hero-right img { width: 50%; } nav ul { gap: 1.5rem; } } @media screen and (max-width: 768px) { .experience-header { flex-direction: column; } .experience-date { text-align: left; } .experience-skills span { font-size: 0.8rem; } } @media screen and (max-width: 480px) { .experience-item { padding: 1.5rem; } .education-item { margin-bottom: 2rem; } } /* Dark mode preference */ /* @media (prefers-color-scheme: dark) { :root { --primaryTextColor: #f7fafc; --secondaryTextColor: #e2e8f0; --borderColor: #2d3748; --lineColor: #4a5568; --primaryBackgroundColor: #1a202c; --secondaryBackgroundColor: #2d3748; --thirdBackgroundColor: #4a5568; } .service, .portfolio { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1); } } */ ``` ##### 12. edit settings.py ```markdown! #tambahkan import os #tambahkan baris static file dibawah static url STATIC_URL = 'static/' STATICFILES_DIRS=[ os.path.join(BASE_DIR, 'static') ] ``` ##### 13. buat main.js ```markdown! document.addEventListener('DOMContentLoaded', function() { // Mobile Navigation Toggle (tetap sama) const burger = document.querySelector('.burger'); const nav = document.querySelector('nav'); burger.addEventListener('click', function() { nav.classList.toggle('active'); burger.classList.toggle('active'); document.body.classList.toggle('no-scroll'); }); // Close mobile menu when clicking on nav links (tetap sama) const navLinks = document.querySelectorAll('nav ul li a'); navLinks.forEach(link => { link.addEventListener('click', () => { nav.classList.remove('active'); burger.classList.remove('active'); document.body.classList.remove('no-scroll'); }); }); // Smooth scrolling for anchor links (diperbarui) document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function(e) { e.preventDefault(); const targetId = this.getAttribute('href'); if (targetId === '#') return; const targetElement = document.querySelector(targetId); if (targetElement) { window.scrollTo({ top: targetElement.offsetTop - 100, // Diubah dari 80 ke 100 untuk spacing lebih baik behavior: 'smooth' }); } }); }); // Active section highlighting (diperbarui) const sections = document.querySelectorAll('section[data-section]'); // Hanya section dengan atribut data-section const navItems = document.querySelectorAll('nav ul li a'); window.addEventListener('scroll', function() { let current = ''; sections.forEach(section => { const sectionTop = section.offsetTop; const sectionHeight = section.clientHeight; if (pageYOffset >= (sectionTop - 300)) { current = section.getAttribute('id'); } }); navItems.forEach(item => { item.classList.remove('active'); if (item.getAttribute('href') === `#${current}`) { item.classList.add('active'); // Tambahkan animasi untuk nav item aktif item.style.transform = 'translateY(-3px)'; setTimeout(() => { item.style.transform = ''; }, 300); } }); }); // Form submission (diperbarui dengan animasi lebih smooth) const contactForm = document.querySelector('.contact-form'); if (contactForm) { contactForm.addEventListener('submit', function(e) { e.preventDefault(); const submitBtn = this.querySelector('.btn-submit'); const originalBtnText = submitBtn.innerHTML; // Animasi loading yang lebih halus submitBtn.innerHTML = '<span class="loading-spinner"></span> Sending...'; submitBtn.disabled = true; submitBtn.style.transform = 'scale(0.98)'; // Simulasi pengiriman form setTimeout(() => { submitBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg> Sent!'; submitBtn.style.transform = 'scale(1)'; // Reset form dengan animasi setTimeout(() => { contactForm.reset(); submitBtn.innerHTML = originalBtnText; submitBtn.disabled = false; // Notifikasi sukses dengan animasi const successMsg = document.createElement('div'); successMsg.className = 'form-success'; successMsg.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> <polyline points="22 4 12 14.01 9 11.01"></polyline> </svg> <span>Message sent successfully!</span> `; contactForm.appendChild(successMsg); setTimeout(() => { successMsg.style.opacity = '0'; setTimeout(() => { successMsg.remove(); }, 500); }, 3000); }, 1500); }, 2000); }); } // Back to top button behavior (ditambahkan) const backToTopBtn = document.createElement('button'); backToTopBtn.className = 'back-to-top'; backToTopBtn.innerHTML = '↑'; backToTopBtn.setAttribute('aria-label', 'Back to top'); document.body.appendChild(backToTopBtn); // Sticky navbar on scroll window.addEventListener('scroll', function() { const nav = document.querySelector('.nav'); if (window.scrollY > 50) { nav.style.padding = '1rem 0'; nav.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.1)'; } else { nav.style.padding = '2rem 0'; nav.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.1)'; } }); backToTopBtn.addEventListener('click', function() { window.scrollTo({ top: 0, behavior: 'smooth' }); // Tambahkan efek klik this.style.transform = 'scale(0.9)'; setTimeout(() => { this.style.transform = ''; }, 200); }); document.querySelectorAll('[data-animate]').forEach((el, i) => { el.style.setProperty('--i', i); }); // Animasi scroll (diperbarui) const animateOnScroll = function() { const elements = document.querySelectorAll('[data-animate]'); const windowHeight = window.innerHeight; elements.forEach(element => { const elementPosition = element.getBoundingClientRect().top; const animationPoint = windowHeight - 150; if (elementPosition < animationPoint) { element.style.opacity = '1'; element.style.transform = 'translateY(0)'; } }); }; // Inisialisasi animasi window.addEventListener('load', animateOnScroll); window.addEventListener('scroll', animateOnScroll); // Tambahkan atribut data-animate ke elemen yang perlu dianimasikan document.querySelectorAll('.service, .portfolio, .experience-item, .education-item, .contact-item').forEach((el, i) => { el.setAttribute('data-animate', ''); el.style.transitionDelay = `${i * 0.1}s`; }); }); ``` 14. tambahkan file pdf untuk resume dan sertifikat magang ![image](https://hackmd.io/_uploads/S1aatKoxeg.png) 15. edit views.py base ```markdown! from django.shortcuts import render def index(request): return render(request, 'index.html') ``` 16.edit urls.py di portofolioku ```markdown! from django.urls import path from base import views urlpatterns = [ path('', views.index, name='index'), ] ``` ## Deploy ke docker 1. siapkan file requirenment.txt ```markdown! asgiref==3.8.1 dj-database-url==2.3.0 Django==5.2 django-crispy-forms==2.4 gunicorn==23.0.0 packaging==25.0 pillow==11.2.1 psycopg2-binary==2.9.10 sqlparse==0.5.3 typing_extensions==4.13.2 tzdata==2025.2 whitenoise==6.9.0 ``` 2. buat dockerfile di direktori yang setara dengan manage.py ```markdown! # Gunakan base image Python yang ringan FROM python:3.11-slim # Set direktori kerja di dalam container WORKDIR /app # Salin file requirements dan install dependencies terlebih dahulu COPY requirements.txt . RUN pip install --upgrade pip RUN pip install -r requirements.txt # Salin seluruh isi project ke dalam container COPY . /app # Jalankan collectstatic untuk mengumpulkan static files RUN python manage.py collectstatic --noinput # Expose port Gunicorn EXPOSE 8000 # Jalankan server Gunicorn untuk production CMD ["gunicorn", "portofolioku.wsgi:application", "--bind", "0.0.0.0:8000"] ``` 3. buat file docker-compose.yml ```markdown! services: web: build: . container_name: portofolioku_web command: gunicorn portofolioku.wsgi:application --bind 0.0.0.0:8000 ports: - "8000:8000" volumes: - .:/app env_file: - .env ``` 4. buat file .env ```markdown! DEBUG=False ``` 5. tambahkan configan berikut di settings.py ```markdown! MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', ... ] ``` ```markdown! STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'staticfiles' # penting untuk collectstatic # Optional: Enable compression and caching STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' ``` 6. build dan jalankan dengan docker compose ```markdown! docker-compose up -d --build ``` 7. akses web ```markdown! http://localhost:8000 ``` 8. mematikan dan menghapus container ```markdown! docker-compose down ```