diff --git "a/Vragenlijst_antwoorden\\Vragenlijst_antwoorden.md" "b/Vragenlijst_antwoorden\\Vragenlijst_antwoorden.md" new file mode 100644 index 0000000..e0c172e --- /dev/null +++ "b/Vragenlijst_antwoorden\\Vragenlijst_antwoorden.md" @@ -0,0 +1,34 @@ +**Vragenlijst Challenge 11** + +1. **Wat is het hoofddoel van uw website?**\ + (Informatie verschaffen, producten verkopen, contact genereren, enz.)\ + Ik wil beter kunnen communiceren met mijn klanten. De website zelf is best goed, maar ik mis veel informatie omdat ik mijn klantencontacten nergens kan vastleggen. Wat ik wil is een module binnen de website waarin ik elk contact dat ik met de klant heb kan vastleggen. +1. **Wie is uw belangrijkste doelgroep?**\ + (Leeftijd, beroep, interesses, technische vaardigheden, enz.)\ + Mijn klanten zijn over het algemeen mannen tussen 18 en 29 jaar die van auto’s houden en graag wat getuned willen hebben. Snelle, stoere jongens types. +1. **Wat vindt u momenteel goed aan uw bestaande website, en wat stoort u?**\ + Houden wat er is, alleen aanvullen met een CRM. +----- +4. **Hoe zou u de gewenste uitstraling van de website omschrijven?**\ + (Bijv. modern, zakelijk, creatief, vriendelijk, minimalistisch.)\ + Stoer en snel!!, Gericht op jonge mannen. +4. **Zijn er voorbeelden van websites die u aanspreken?**\ + (Wat spreekt u daarin aan: kleuren, lay-out, structuur?)\ + De huidige website is qua layout prima. +4. **Welke kleuren, lettertypes en logo’s wilt u gebruiken om uw merkidentiteit te behouden?**\ + Behoud ook in de nieuwe module de huidige stijl en kleuren. +----- +7. **Welke functies moet de nieuwe website bevatten?**\ + (Bijv. contactformulier, blog, webshop, agenda, zoekfunctie.)\ + Ik moet kunnen inloggen. Daarna een klant kunnen zoeken en kiezen. Daarna de gespreksgeschiedenis kunnen inzien van die klant met mij. Vervolgens een nieuw item als gesprekscontact kunnen toevoegen en evt. de oude wijzigen. +7. **Wilt u de website zelf kunnen aanpassen of beheren?**\ + (Zo ja: heeft u ervaring met een bepaald CMS, zoals WordPress?)\ + Zoals hierboven beschreven. Het moet zelf gecodeerd zijn. Geen wordpress. +----- +9. **Zijn er prestatie-eisen waaraan de site moet voldoen?**\ + (Bijv. laadtijd, uptime, mobiele prestaties.)\ + Uiteraard zo snel als mogelijk. +9. **Heeft u voorkeur voor een bepaalde hostingprovider of heeft u al een bestaande hostingomgeving?**\ + Er is een bestaande provider. Vraag je docent voor de inloggegevens. + + diff --git a/assets/favicon.svg b/assets/favicon.svg deleted file mode 100644 index 4f2d365..0000000 --- a/assets/favicon.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/css/styles.css b/css/styles.css deleted file mode 100644 index 2e31784..0000000 --- a/css/styles.css +++ /dev/null @@ -1,590 +0,0 @@ -/* Fallback styles in case CDN links fail */ -:root { - --primary-color: #8b5cf6; - --secondary-color: #ec4899; - --dark-bg: #4c1d95; - --light-bg: #f8f9fa; - --text-dark: #1f2937; - --text-light: #6b7280; - --white: #ffffff; - --shadow: 0 2px 4px rgba(0,0,0,0.1); - --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1); - --gradient-primary: linear-gradient(135deg, #8b5cf6, #ec4899); - --gradient-secondary: linear-gradient(135deg, #6366f1, #8b5cf6); -} - -/* Reset and base styles */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - line-height: 1.5; - background: linear-gradient(135deg, var(--light-bg), #e0e7ff); - color: var(--text-dark); - scroll-behavior: smooth; -} - -/* Container fallback */ -.container { - width: 100%; - max-width: 1200px; - margin: 0 auto; - padding: 0 15px; -} - -/* Navbar fallback */ -.navbar { - background: rgba(17, 24, 39, 0.8); - backdrop-filter: blur(10px); - transition: all 0.3s ease; -} - -.navbar.scrolled { - background: rgba(17, 24, 39, 0.95); - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); -} - -.navbar-brand { - font-size: 1.5rem; - font-weight: bold; -} - -.nav-link { - color: #fff !important; - font-weight: 500; - padding: 0.5rem 1rem !important; - transition: all 0.3s ease; -} - -.nav-link:hover { - color: #ef4444 !important; -} - -.nav-link.active { - color: #ef4444 !important; -} - -.btn-primary { - background: linear-gradient(to right, #dc2626, #ea580c); - border: none; - color: white; - padding: 0.5rem 1.5rem; - border-radius: 0.375rem; - font-weight: 500; - transition: all 0.3s ease; -} - -.btn-primary:hover { - background: linear-gradient(to right, #b91c1c, #c2410c); - transform: translateY(-2px); - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); -} - -.btn-outline-secondary { - border: 2px solid #4b5563; - color: #9ca3af; - padding: 0.5rem 1.5rem; - border-radius: 0.375rem; - font-weight: 500; - transition: all 0.3s ease; -} - -.btn-outline-secondary:hover { - border-color: #ef4444; - color: white; - transform: translateY(-2px); -} - -.form-control { - background-color: #374151; - border: 1px solid #4b5563; - color: white; - padding: 0.75rem 1rem; - border-radius: 0.375rem; - transition: all 0.3s ease; -} - -.form-control:focus { - background-color: #374151; - border-color: #ef4444; - box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.25); - color: white; -} - -.form-label { - color: #d1d5db; - font-weight: 500; - margin-bottom: 0.5rem; -} - -.glass-nav { - background: rgba(17, 24, 39, 0.8); - backdrop-filter: blur(10px); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); -} - -.glass-nav:hover { - background: rgba(17, 24, 39, 0.95); -} - -@keyframes float { - 0% { - transform: translateY(0px); - } - 50% { - transform: translateY(-10px); - } - 100% { - transform: translateY(0px); - } -} - -.animate-float { - animation: float 3s ease-in-out infinite; -} - -@keyframes pulse-slow { - 0% { - opacity: 1; - } - 50% { - opacity: 0.7; - } - 100% { - opacity: 1; - } -} - -.animate-pulse-slow { - animation: pulse-slow 3s ease-in-out infinite; -} - -@keyframes spin-slow { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -.animate-spin-slow { - animation: spin-slow 20s linear infinite; -} - -@keyframes bounce-slow { - 0%, 100% { - transform: translateY(0); - } - 50% { - transform: translateY(-10px); - } -} - -.animate-bounce-slow { - animation: bounce-slow 3s ease-in-out infinite; -} - -.fade-in { - animation: fadeIn 0.5s ease-in; -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(20px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@media (max-width: 768px) { - .navbar-collapse { - background: rgba(17, 24, 39, 0.95); - backdrop-filter: blur(10px); - padding: 1rem; - border-radius: 0.5rem; - margin-top: 0.5rem; - } - - .nav-link { - padding: 0.75rem 1rem !important; - } -} - -::-webkit-scrollbar { - width: 8px; -} - -::-webkit-scrollbar-track { - background: #1f2937; -} - -::-webkit-scrollbar-thumb { - background: #ef4444; - border-radius: 4px; -} - -::-webkit-scrollbar-thumb:hover { - background: #dc2626; -} - -/* Grid fallback */ -.row { - display: flex; - flex-wrap: wrap; - margin: -15px; -} - -.col-md-8 { - flex: 0 0 66.666667%; - max-width: 66.666667%; - padding: 15px; -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -/* Utility classes fallback */ -.py-8 { - padding-top: 2rem; - padding-bottom: 2rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.text-3xl { - font-size: 1.875rem; -} - -.font-bold { - font-weight: 700; -} - -.text-gray-600 { - color: var(--text-light); -} - -.bg-white { - background-color: var(--white); -} - -.rounded-lg { - border-radius: 0.5rem; -} - -.shadow-md { - box-shadow: var(--shadow); -} - -.p-6 { - padding: 1.5rem; -} - -/* Custom styles */ -.navbar { - box-shadow: 0 2px 4px rgba(0,0,0,0.1); -} - -.btn-primary { - transition: all 0.3s ease; -} - -.btn-primary:hover { - transform: translateY(-2px); - box-shadow: 0 4px 6px rgba(0,0,0,0.1); -} - -/* Form styles */ -.form-control { - transition: all 0.3s ease; - border: 2px solid transparent; -} - -.form-control:focus { - border-color: var(--primary-color); - box-shadow: 0 0 0 0.2rem rgba(139, 92, 246, 0.25); - transform: translateY(-1px); -} - -/* Card hover effects */ -.card { - transition: all 0.3s ease; - background: linear-gradient(135deg, #ffffff, #f9fafb); -} - -.card:hover { - transform: translateY(-5px); - box-shadow: var(--shadow-lg); - background: linear-gradient(135deg, #ffffff, #f3f4f6); -} - -/* Custom 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 pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.5; } -} - -.fade-in { - animation: fadeIn 0.5s ease-in; -} - -/* Gradient backgrounds */ -.gradient-bg { - background: var(--gradient-primary); - background-size: 200% 200%; - animation: gradient 15s ease infinite; -} - -@keyframes gradient { - 0% { background-position: 0% 50%; } - 50% { background-position: 100% 50%; } - 100% { background-position: 0% 50%; } -} - -/* Section backgrounds */ -.section-gradient-1 { - background: linear-gradient(135deg, #ffffff, #e0e7ff); -} - -.section-gradient-2 { - background: linear-gradient(135deg, #f3e8ff, #fdf2f8); -} - -.section-gradient-3 { - background: linear-gradient(135deg, #e0e7ff, #ede9fe); -} - -/* Text gradients */ -.text-gradient { - background: var(--gradient-primary); - -webkit-background-clip: text; - background-clip: text; - color: transparent; -} - -/* Border gradients */ -.border-gradient { - border: 2px solid transparent; - background: linear-gradient(white, white) padding-box, - var(--gradient-primary) border-box; -} - -/* Responsive fallback */ -@media (max-width: 768px) { - .navbar-nav { - flex-direction: column; - display: none; - } - - .navbar-nav.show { - display: flex; - } - - .col-md-8 { - flex: 0 0 100%; - max-width: 100%; - } -} - -/* Navbar Styles */ -.glass-nav { - background: rgba(17, 24, 39, 0.8); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); -} - -.glass-nav:hover { - background: rgba(17, 24, 39, 0.9); -} - -.navbar-brand img { - filter: drop-shadow(0 0 8px rgba(239, 68, 68, 0.5)); -} - -.nav-link { - position: relative; - padding: 0.5rem 1rem; - margin: 0 0.25rem; -} - -.nav-link.active { - color: #fff !important; -} - -.nav-link.active::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 2px; - background: linear-gradient(to right, #ef4444, #f97316); - transform: scaleX(1); - transition: transform 0.3s ease; -} - -.nav-link:not(.active)::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 2px; - background: linear-gradient(to right, #ef4444, #f97316); - transform: scaleX(0); - transition: transform 0.3s ease; - transform-origin: right; -} - -.nav-link:hover::after { - transform: scaleX(1); - transform-origin: left; -} - -/* Slow spin animation for logo */ -@keyframes spin-slow { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -.animate-spin-slow { - animation: spin-slow 20s linear infinite; -} - -/* Navbar scroll effect */ -.navbar { - transition: all 0.3s ease; -} - -.navbar.scrolled { - background: rgba(17, 24, 39, 0.95); - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); -} - -/* Modal Styles */ -.modal-content { - background: #1f2937; - border: 1px solid #ef4444; -} - -.modal-header { - border-bottom: 1px solid #374151; -} - -.modal-footer { - border-top: 1px solid #374151; -} - -/* Search Results */ -.search-result-item { - transition: all 0.3s ease; -} - -.search-result-item:hover { - transform: translateX(5px); -} - -/* Customer Details */ -.interaction-item { - transition: all 0.3s ease; -} - -.interaction-item:hover { - transform: translateY(-2px); - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); -} - -/* Mobile menu styles */ -@media (max-width: 991.98px) { - .navbar-collapse { - background: rgba(17, 24, 39, 0.95); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - border-radius: 0.5rem; - padding: 1rem; - margin-top: 0.5rem; - } - - .nav-link { - padding: 0.75rem 1rem; - border-radius: 0.375rem; - } - - .nav-link:hover { - background: rgba(255, 255, 255, 0.1); - } - - .nav-link::after { - display: none; - } -} - -/* Animations */ -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.fade-in { - animation: fadeIn 0.3s ease-out; -} - -/* Scrollbar Styles */ -::-webkit-scrollbar { - width: 8px; -} - -::-webkit-scrollbar-track { - background: #1f2937; -} - -::-webkit-scrollbar-thumb { - background: #ef4444; - border-radius: 4px; -} - -::-webkit-scrollbar-thumb:hover { - background: #dc2626; -} \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index dd7653d..0000000 --- a/index.html +++ /dev/null @@ -1,613 +0,0 @@ - - - - - - Auto Tuning Pro - - - - - - - - - - - - - - - - -
-
-
-
-
-

ONTKETEN JE AUTO

-

Professionele ECU Tuning & Prestatieverbetering

-
- - -
-
-
-
-
- - -
-
-

Onze Diensten

-
-
-
-
🚀
-

ECU Tuning

-

Ontgrendel het ware potentieel van je motor met onze professionele ECU-herprogrammering.

-
    -
  • • Vermogen & Koppel Verhoging
  • -
  • • Brandstofverbruik Optimalisatie
  • -
  • • Gasrespons Verbetering
  • -
-
-
-
-
-
-

Prestatie Onderdelen

-

Hoogwaardige tuning onderdelen om de prestaties van je voertuig te verbeteren.

-
    -
  • • Uitlaatsystemen
  • -
  • • Luchtfilters
  • -
  • • Vering Upgrades
  • -
-
-
-
-
-
🔧
-

Maatwerk Oplossingen

-

Op maat gemaakte aanpassingen die aansluiten bij jouw specifieke wensen.

-
    -
  • • Vermogensmeting
  • -
  • • Custom Mapping
  • -
  • • Prestatie Analyse
  • -
-
-
-
-
-
- - - - - -
-
-

Neem Contact Op

-
-
-
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
- -
- -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git "a/ingevulde behoefte analyse\\ingevulde behoefte analyse.md" "b/ingevulde behoefte analyse\\ingevulde behoefte analyse.md" new file mode 100644 index 0000000..41dca4d --- /dev/null +++ "b/ingevulde behoefte analyse\\ingevulde behoefte analyse.md" @@ -0,0 +1,68 @@ +![](Aspose.Words.168ad1cb-cb76-4af9-aba4-eba552ecdde2.001.png) + +**Sjabloon** + +**Behoefteanalyse** + +Project : + +Opdrachtgever : + +Auteur : + +Datum : + +Versie : 2.0 + +# **Inhoud** + +[**1** **Opdracht 2****](#_toc392408117)** + +[**2** **Aanleiding 2****](#_toc392408118) + +[**3** **Knelpunten 2****](#_toc392408119) + +[**4** **Eisen/Wensen 2****](#_toc392408120) + +[4.1 Must haves 2](#_toc392408121) + +[4.2 Should haves 2](#_toc392408122) + +[4.3 Could haves 2](#_toc392408123) + +[4.4 Won’t haves 2](#_toc392408124) + +[**5** **Uit te voeren werkzaamheden 2****](#_toc392408125) + + + + +1. # **Opdracht** +Website maken voor een bedrijf om een uitleg te geven over waar het bedrijf voor staat en wat het doel is. + +1. # **Aanleiding** +Beschrijf hier redenen die aanleiding waren voor het geven van de opdracht voor het opstellen van deze behoefteanalyse. + +1. # **Knelpunten** +Beschrijf hier de problemen die nu bestaan in de huidige manier van werken. + +1. # **Eisen/Wensen** +Geef hier een overzicht van de eisen en wensen. Gebruik hiervoor de MoSCoW-onderverdeling. + +1. # Must haves +Informatie over verschillende autos met de mogelijkheden die er zijn om de auto te tunen. + +1. # Should haves +Drop down menu, zoek balk, filters. + +1. # Could haves +Eisen die alleen aan bod zullen komen als er tijd genoeg is. + +1. # Won’t haves +Eisen die in dit project niet aan bod komen, maar in de toekomst bij een vervolgproject interessant kunnen zijn. + +1. # **Uit te voeren werkzaamheden** +Indien gevraagd wordt om bepaalde werkzaamheden uit te voeren, worden deze hier vermeld. +© Stichting Praktijkleren 2015 + +Sjabloon Behoefteanalyse Pagina 1 van 2 diff --git a/js/script.js b/js/script.js deleted file mode 100644 index b847340..0000000 --- a/js/script.js +++ /dev/null @@ -1,344 +0,0 @@ -// Add fade-in animation to all sections -document.addEventListener('DOMContentLoaded', () => { - const pageSections = document.querySelectorAll('section'); - pageSections.forEach(section => section.classList.add('fade-in')); - - // Car search functionality - const carSearch = document.getElementById('carSearch'); - const carSelect = document.getElementById('car'); - - if (carSearch && carSelect) { - // Set initial dropdown style - carSelect.size = 10; - carSelect.style.position = 'absolute'; - carSelect.style.width = '100%'; - carSelect.style.zIndex = '1000'; - carSelect.style.backgroundColor = '#374151'; - carSelect.style.border = '1px solid #4B5563'; - carSelect.style.borderRadius = '0.375rem'; - carSelect.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1)'; - carSelect.style.maxHeight = '300px'; - carSelect.style.overflowY = 'auto'; - - // Function to close dropdown - const closeDropdown = () => { - carSelect.size = 1; - carSelect.style.position = 'static'; - carSelect.style.width = 'auto'; - carSelect.style.zIndex = 'auto'; - carSelect.style.backgroundColor = ''; - carSelect.style.border = ''; - carSelect.style.borderRadius = ''; - carSelect.style.boxShadow = ''; - carSelect.style.maxHeight = ''; - carSelect.style.overflowY = ''; - }; - - carSearch.addEventListener('input', (e) => { - const searchTerm = e.target.value.toLowerCase().trim(); - const options = carSelect.querySelectorAll('option'); - const optgroups = carSelect.querySelectorAll('optgroup'); - - // First hide all options and optgroups - options.forEach(option => { - if (option.value !== '') { // Don't hide the placeholder - option.style.display = 'none'; - } - }); - optgroups.forEach(group => { - group.style.display = 'none'; - }); - - // If search is empty, show all options - if (searchTerm === '') { - options.forEach(option => { - option.style.display = ''; - }); - optgroups.forEach(group => { - group.style.display = ''; - }); - return; - } - - // Show matching options and their parent optgroups - let hasMatches = false; - options.forEach(option => { - if (option.value !== '') { - const optionText = option.text.toLowerCase(); - if (optionText.includes(searchTerm)) { - option.style.display = ''; - hasMatches = true; - // Show the parent optgroup - const parentGroup = option.closest('optgroup'); - if (parentGroup) { - parentGroup.style.display = ''; - } - } - } - }); - - // If no matches found, show the "Ander model" option - if (!hasMatches) { - const otherModelOption = carSelect.querySelector('option[value="Ander model"]'); - if (otherModelOption) { - otherModelOption.style.display = ''; - const parentGroup = otherModelOption.closest('optgroup'); - if (parentGroup) { - parentGroup.style.display = ''; - } - } - } - }); - - // Handle option selection - carSelect.addEventListener('change', () => { - carSearch.value = ''; - const options = carSelect.querySelectorAll('option'); - const optgroups = carSelect.querySelectorAll('optgroup'); - options.forEach(option => { - option.style.display = ''; - }); - optgroups.forEach(group => { - group.style.display = ''; - }); - closeDropdown(); - }); - - // Handle double-click on options - carSelect.addEventListener('dblclick', (e) => { - if (e.target.tagName === 'OPTION' && e.target.value !== '') { - carSelect.value = e.target.value; - carSearch.value = e.target.text; - closeDropdown(); - } - }); - - // Close the dropdown when clicking outside - document.addEventListener('click', (e) => { - if (!carSearch.contains(e.target) && !carSelect.contains(e.target)) { - closeDropdown(); - } - }); - } - - // Smooth scrolling for navigation links - document.querySelectorAll('a[href^="#"]').forEach(anchor => { - anchor.addEventListener('click', function (e) { - e.preventDefault(); - const target = document.querySelector(this.getAttribute('href')); - if (target) target.scrollIntoView({ behavior: 'smooth' }); - }); - }); - - // Get Started button functionality - const getStartedButtons = document.querySelectorAll('.btn-primary:not([type="submit"])'); - getStartedButtons.forEach(button => { - button.addEventListener('click', (e) => { - // Create and show modal - const modal = document.createElement('div'); - modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 fade-in'; - modal.innerHTML = ` -
-

Boek Je Tuning

-

Kies je gewenste dienst:

-
- - - -
- -
- `; - document.body.appendChild(modal); - - // DEBUG: Log modal HTML - console.log('Modal created. innerHTML:', modal.innerHTML); - - // Add click event to close modal when clicking outside - modal.addEventListener('click', (e) => { - if (e.target === modal) { - modal.remove(); - } - }); - - // Add click events to modal buttons - const modalButtons = modal.querySelectorAll('.space-y-4 > button'); - // DEBUG: Log number of modal buttons found - console.log('Modal buttons found:', modalButtons.length); - modalButtons.forEach(modalBtn => { - modalBtn.addEventListener('click', () => { - const action = modalBtn.textContent.trim(); - console.log('Modal button clicked:', action); // DEBUG - // Scroll to contact form and populate service type - const contactForm = document.querySelector('#contact form, #contactForm'); - const messageField = contactForm.querySelector('#message'); - messageField.value = `Ik ben geïnteresseerd in: ${action}`; - modal.remove(); - document.querySelector('#contact').scrollIntoView({ behavior: 'smooth' }); - }); - }); - }); - }); - - // Form submission handling - const contactForm = document.querySelector('#contactForm'); - if (contactForm) { - const carSelect = contactForm.querySelector('#car'); - carSelect.addEventListener('change', (e) => { - if (e.target.value === 'Ander model') { - const customInput = document.createElement('input'); - customInput.type = 'text'; - customInput.className = 'form-control bg-gray-700 border-gray-600 text-white focus:ring-2 focus:ring-red-500 transition-all duration-300 mt-2'; - customInput.placeholder = 'Voer uw voertuigmodel in'; - customInput.id = 'customCar'; - customInput.name = 'customCar'; - - const existingCustomInput = contactForm.querySelector('#customCar'); - if (!existingCustomInput) { - carSelect.parentNode.appendChild(customInput); - } - } else { - const existingCustomInput = contactForm.querySelector('#customCar'); - if (existingCustomInput) { - existingCustomInput.remove(); - } - } - }); - - contactForm.addEventListener('submit', async (e) => { - e.preventDefault(); - - const submitButton = contactForm.querySelector('button[type="submit"]'); - const originalButtonText = submitButton.textContent; - submitButton.disabled = true; - submitButton.textContent = 'Verzenden...'; - - // Hide previous success message if visible - const successMsg = document.getElementById('contactSuccess'); - if (successMsg) successMsg.classList.add('hidden'); - - try { - const formData = new FormData(contactForm); - const carModel = carSelect.value === 'Ander model' - ? contactForm.querySelector('#customCar')?.value || 'Niet gespecificeerd' - : carSelect.value; - formData.set('car', carModel); - - const response = await fetch(contactForm.action, { - method: 'POST', - body: formData, - headers: { - 'Accept': 'application/json' - } - }); - - if (response.ok) { - // Show custom confirmation message - if (successMsg) successMsg.classList.remove('hidden'); - contactForm.reset(); - const existingCustomInput = contactForm.querySelector('#customCar'); - if (existingCustomInput) { - existingCustomInput.remove(); - } - } else { - throw new Error('Er is iets misgegaan bij het verzenden van het formulier.'); - } - } catch (error) { - alert('Er is een fout opgetreden. Probeer het later opnieuw of neem telefonisch contact op.'); - console.error('Form submission error:', error); - } finally { - submitButton.disabled = false; - submitButton.textContent = originalButtonText; - } - }); - } - - // Active navigation highlighting - const navLinks = document.querySelectorAll('.nav-link'); - const sections = document.querySelectorAll('section'); - - function setActiveLink() { - let current = ''; - sections.forEach(section => { - const sectionTop = section.offsetTop; - const sectionHeight = section.clientHeight; - if (window.scrollY >= (sectionTop - sectionHeight / 3)) { - current = section.getAttribute('id'); - } - }); - - navLinks.forEach(link => { - link.classList.remove('active'); - if (link.getAttribute('href').substring(1) === current) { - link.classList.add('active'); - } - }); - } - - window.addEventListener('scroll', setActiveLink); - setActiveLink(); // Initial call - - // Mobile menu toggle - const navbarToggler = document.querySelector('.navbar-toggler'); - const navbarCollapse = document.querySelector('.navbar-collapse'); - - if (navbarToggler && navbarCollapse) { - navbarToggler.addEventListener('click', () => { - navbarCollapse.classList.toggle('show'); - }); - - // Close mobile menu when clicking outside - document.addEventListener('click', (e) => { - if (!navbarToggler.contains(e.target) && !navbarCollapse.contains(e.target)) { - navbarCollapse.classList.remove('show'); - } - }); - } - - // Add hover effects to cards - const cards = document.querySelectorAll('.card, .bg-gray-800.rounded-lg'); - cards.forEach(card => { - card.addEventListener('mouseenter', () => { - card.style.transform = 'translateY(-5px)'; - card.style.boxShadow = '0 10px 15px -3px rgba(0,0,0,0.3)'; - }); - card.addEventListener('mouseleave', () => { - card.style.transform = 'translateY(0)'; - card.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'; - }); - }); - - // Navbar scroll effect - const navbar = document.querySelector('.navbar'); - window.addEventListener('scroll', () => { - if (window.scrollY > 50) { - navbar.classList.add('scrolled'); - } else { - navbar.classList.remove('scrolled'); - } - }); - - // Gallery image hover effects - const galleryItems = document.querySelectorAll('#gallery .col-md-4'); - galleryItems.forEach(item => { - item.addEventListener('mouseenter', () => { - const image = item.querySelector('.aspect-w-16'); - image.style.transform = 'scale(1.05)'; - image.style.transition = 'transform 0.3s ease-in-out'; - }); - item.addEventListener('mouseleave', () => { - const image = item.querySelector('.aspect-w-16'); - image.style.transform = 'scale(1)'; - }); - }); -}); diff --git a/js/tailwind.config.js b/js/tailwind.config.js deleted file mode 100644 index 468ebaa..0000000 --- a/js/tailwind.config.js +++ /dev/null @@ -1,17 +0,0 @@ -tailwind.config = { - theme: { - extend: { - animation: { - 'float': 'float 3s ease-in-out infinite', - 'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite', - 'bounce-slow': 'bounce 3s infinite', - }, - keyframes: { - float: { - '0%, 100%': { transform: 'translateY(0)' }, - '50%': { transform: 'translateY(-10px)' }, - } - } - } - } -} \ No newline at end of file diff --git a/models/Contact.js b/models/Contact.js new file mode 100644 index 0000000..c7147c0 --- /dev/null +++ b/models/Contact.js @@ -0,0 +1,34 @@ +const mongoose = require('mongoose'); + +const contactSchema = new mongoose.Schema({ + customer: { + type: mongoose.Schema.Types.ObjectId, + ref: 'Customer', + required: true + }, + user: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User', + required: true + }, + type: { + type: String, + enum: ['phone', 'email', 'in-person', 'other'], + required: true + }, + notes: { + type: String, + required: true + }, + followUp: { + required: Boolean, + date: Date, + notes: String + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +module.exports = mongoose.model('Contact', contactSchema); \ No newline at end of file diff --git a/models/Customer.js b/models/Customer.js new file mode 100644 index 0000000..5648099 --- /dev/null +++ b/models/Customer.js @@ -0,0 +1,32 @@ +const mongoose = require('mongoose'); + +const customerSchema = new mongoose.Schema({ + name: { + type: String, + required: true + }, + email: { + type: String, + required: true, + unique: true + }, + phone: { + type: String + }, + carDetails: { + make: String, + model: String, + year: Number, + modifications: [String] + }, + createdAt: { + type: Date, + default: Date.now + }, + updatedAt: { + type: Date, + default: Date.now + } +}); + +module.exports = mongoose.model('Customer', customerSchema); \ No newline at end of file diff --git a/models/User.js b/models/User.js new file mode 100644 index 0000000..d751dd0 --- /dev/null +++ b/models/User.js @@ -0,0 +1,24 @@ +const mongoose = require('mongoose'); + +const userSchema = new mongoose.Schema({ + username: { + type: String, + required: true, + unique: true + }, + password: { + type: String, + required: true + }, + role: { + type: String, + enum: ['admin', 'staff'], + default: 'staff' + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +module.exports = mongoose.model('User', userSchema); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..cba8e61 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "car-tuning-crm", + "version": "1.0.0", + "description": "CRM system for car tuning business", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js", + "client": "cd client && npm start", + "dev:full": "concurrently \"npm run dev\" \"npm run client\"" + }, + "dependencies": { + "express": "^4.18.2", + "mongoose": "^7.5.0", + "bcryptjs": "^2.4.3", + "jsonwebtoken": "^9.0.1", + "cors": "^2.8.5", + "dotenv": "^16.3.1" + }, + "devDependencies": { + "nodemon": "^3.0.1", + "concurrently": "^8.2.1" + } +} \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..deba733 --- /dev/null +++ b/server.js @@ -0,0 +1,28 @@ +const express = require('express'); +const mongoose = require('mongoose'); +const cors = require('cors'); +const dotenv = require('dotenv'); + +dotenv.config(); + +const app = express(); + +// Middleware +app.use(cors()); +app.use(express.json()); + +// MongoDB Connection +mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/car-tuning-crm', { + useNewUrlParser: true, + useUnifiedTopology: true +}) +.then(() => console.log('MongoDB connected')) +.catch(err => console.log('MongoDB connection error:', err)); + +// Routes +app.use('/api/auth', require('./routes/auth')); +app.use('/api/customers', require('./routes/customers')); +app.use('/api/contacts', require('./routes/contacts')); + +const PORT = process.env.PORT || 5000; +app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); \ No newline at end of file