diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..e8d514c8d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "git.mergeEditor": true, + "merge-conflict.autoNavigateNextConflict.enabled": true +} \ No newline at end of file diff --git a/README.md b/README.md index e03630b34..8b1378917 100644 --- a/README.md +++ b/README.md @@ -1,178 +1 @@ -# [Light Bootstrap Dashboard Angular](https://demos.creative-tim.com/light-bootstrap-dashboard-angular2/dashboard) -[![version][version-badge]][CHANGELOG] ![license][license-badge] -![alt text](src/assets/img/opt_lbd_angular_thumbnail.jpg) - -**[Light Bootstrap Dashboard Angular](https://demos.creative-tim.com/light-bootstrap-dashboard-angular2/dashboard)** is an admin dashboard template designed to be beautiful and simple. It is built on top of Bootstrap 3, using [Light Bootstrap Dashboard](https://www.creative-tim.com/product/light-bootstrap-dashboard) and it is fully responsive. It comes with a big collections of elements that will offer you multiple possibilities to create the app that best fits your needs. It can be used to create admin panels, project management systems, web applications backend, CMS or CRM. - -The product represents a big suite of front-end developer tools that can help you jump start your project. We have created it thinking about things you actually need in a dashboard. Light Bootstrap Dashboard Angular 2 contains multiple handpicked and optimized plugins. Everything is designed to fit with one another. As you will be able to see, the dashboard you can access on Creative Tim is a customization of this product. - -It comes with 6 filter colors for the sidebar (“black”, “azure”,”green”,”orange”,”red”,”purple”) and an option to have a background image. - -Special thanks go to: Robert McIntosh for the notification system Chartist for the wonderful charts We are very excited to share this dashboard with you and we look forward to hearing your feedback! - -## Links: - -+ [Live Preview](https://demos.creative-tim.com/light-bootstrap-dashboard-angular2/dashboard) -+ [Light Bootstrap Dashboard PRO Angular](https://www.creative-tim.com/product/light-bootstrap-dashboard-pro-angular2/?ref=lbd-angular-github) ($49) - -## Quick Start: - -Quick start options: - -+ [Download from Github](https://github.com/creativetimofficial/light-bootstrap-dashboard-angular2/archive/master.zip). -+ [Download from Creative Tim](https://www.creative-tim.com/product/light-bootstrap-dashboard-angular2). -+ Clone the repo: `git clone https://github.com/creativetimofficial/light-bootstrap-dashboard-angular2.git`. - -## Deploy - -:rocket: You can deploy your own version of the template to Genezio with one click: - -[![Deploy to Genezio](https://raw.githubusercontent.com/Genez-io/graphics/main/svg/deploy-button.svg)](https://app.genez.io/start/deploy?repository=https://github.com/creativetimofficial/light-bootstrap-dashboard-angular2&utm_source=github&utm_medium=referral&utm_campaign=github-creativetim&utm_term=deploy-project&utm_content=button-head) - -## Terminal Commands - -1. Install NodeJs from [NodeJs Official Page](https://nodejs.org/en). -2. Open Terminal -3. Go to your file project -4. Run in terminal: ```npm install -g @angular/cli``` -5. Then: ```npm install``` -6. And: ```ng serve``` -7. Navigate to `http://localhost:4200/` - -### What's included - -Within the download you'll find the following directories and files: -``` -light-bootstrap-dashboard-angular -├── CHANGELOG.md -├── LICENSE.md -├── README.md -├── angular.json -├── documentation -│   ├── css -│   └── tutorial-lbd-angular2.html -├── e2e -├── karma.conf.js -├── package-lock.json -├── package.json -├── protractor.conf.js -├── src -│   ├── app -│   │   ├── app.component.css -│   │   ├── app.component.html -│   │   ├── app.component.spec.ts -│   │   ├── app.component.ts -│   │   ├── app.module.ts -│   │   ├── app.routing.ts -│   │   ├── home -│   │   │   ├── home.component.css -│   │   │   ├── home.component.html -│   │   │   ├── home.component.spec.ts -│   │   │   └── home.component.ts -│   │   ├── icons -│   │   │   ├── icons.component.css -│   │   │   ├── icons.component.html -│   │   │   ├── icons.component.spec.ts -│   │   │   └── icons.component.ts -│   │   ├── layouts -│   │   │   └── admin-layout -│   │   │   ├── admin-layout.component.html -│   │   │   ├── admin-layout.component.scss -│   │   │   ├── admin-layout.component.spec.ts -│   │   │   ├── admin-layout.component.ts -│   │   │   ├── admin-layout.module.ts -│   │   │   └── admin-layout.routing.ts -│   │   ├── lbd -│   │   │   ├── lbd-chart -│   │   │   │   ├── lbd-chart.component.html -│   │   │   │   └── lbd-chart.component.ts -│   │   │   └── lbd.module.ts -│   │   ├── maps -│   │   │   ├── maps.component.css -│   │   │   ├── maps.component.html -│   │   │   ├── maps.component.spec.ts -│   │   │   └── maps.component.ts -│   │   ├── notifications -│   │   │   ├── notifications.component.css -│   │   │   ├── notifications.component.html -│   │   │   ├── notifications.component.spec.ts -│   │   │   └── notifications.component.ts -│   │   ├── shared -│   │   │   ├── footer -│   │   │   │   ├── footer.component.html -│   │   │   │   ├── footer.component.ts -│   │   │   │   └── footer.module.ts -│   │   │   └── navbar -│   │   │   ├── navbar.component.html -│   │   │   ├── navbar.component.ts -│   │   │   └── navbar.module.ts -│   │   ├── sidebar -│   │   │   ├── sidebar.component.html -│   │   │   ├── sidebar.component.ts -│   │   │   └── sidebar.module.ts -│   │   ├── tables -│   │   │   ├── tables.component.css -│   │   │   ├── tables.component.html -│   │   │   ├── tables.component.spec.ts -│   │   │   └── tables.component.ts -│   │   ├── typography -│   │   │   ├── typography.component.css -│   │   │   ├── typography.component.html -│   │   │   ├── typography.component.spec.ts -│   │   │   └── typography.component.ts -│   │   ├── upgrade -│   │   │   ├── upgrade.component.css -│   │   │   ├── upgrade.component.html -│   │   │   ├── upgrade.component.spec.ts -│   │   │   └── upgrade.component.ts -│   │   └── user -│   │   ├── user.component.css -│   │   ├── user.component.html -│   │   ├── user.component.spec.ts -│   │   └── user.component.ts -│   ├── assets -│   │   ├── css -│   │   ├── fonts -│   │   ├── img -│   │   └── sass -│   │   ├── lbd -│   │   └── light-bootstrap-dashboard.scss -│   ├── environments -│   ├── favicon.ico -│   ├── index.html -│   ├── main.ts -│   ├── polyfills.ts -│   ├── styles.css -│   ├── test.ts -│   └── tsconfig.json -├── tslint.json -└── typings.json - -``` -## Useful Links - -More products from Creative Tim: - -Tutorials: - -Freebies: - -Affiliate Program (earn money): - -Social Media: - -Twitter: - -Facebook: - -Dribbble: - -Google+: - -Instagram: - -[CHANGELOG]: ./CHANGELOG.md - -[version-badge]: https://img.shields.io/badge/version-1.9.0-blue.svg -[license-badge]: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/angular.json b/angular.json index b232b7f9f..8c8e0f410 100644 --- a/angular.json +++ b/angular.json @@ -19,20 +19,34 @@ "assets": [ "src/assets", "src/favicon.ico" + ], "styles": [ "node_modules/perfect-scrollbar/css/perfect-scrollbar.css", "node_modules/animate.css/animate.min.css", "node_modules/bootstrap/dist/css/bootstrap.min.css", "src/assets/sass/light-bootstrap-dashboard.scss", - "src/assets/css/demo.css" + "src/assets/css/demo.css", + "src/styles.css", + "src/assets/css/bootstrap.min.css", + "src/assets/css/tooplate-infinite-loop.css", + "src/assets/fontawesome-5.5/css/all.min.css", + "src/assets/magnific-popup/magnific-popup.css", + "src/assets/slick/slick.css", + "src/assets/slick/slick-theme.css" ], "scripts": [ "node_modules/jquery/dist/jquery.js", "node_modules/bootstrap/dist/js/bootstrap.js", "node_modules/bootstrap-notify/bootstrap-notify.js", - "node_modules/chartist/dist/chartist.js" - ] + "node_modules/chartist/dist/chartist.js", + "src/assets/js/jquery-1.9.1.min.js", + "src/assets/slick/slick.min.js", + "src/assets/magnific-popup/jquery.magnific-popup.min.js", + "src/assets/js/easing.min.js", + "src/assets/js/jquery.singlePageNav.min.js", + "src/assets/js/bootstrap.min.js" + ] }, "configurations": { "production": { @@ -62,7 +76,7 @@ "minify": false, "inlineCritical": true }, - "fonts": true + "fonts": false }, "outputHashing": "all" } diff --git a/package.json b/package.json index c51c02de7..2e8c06cb0 100644 --- a/package.json +++ b/package.json @@ -29,36 +29,38 @@ "@types/googlemaps": "3.43.3", "animate.css": "4.1.1", "arrive": "2.4.1", - "bootstrap": "3.3.7", + "bootstrap": "^5.3.6", "bootstrap-notify": "3.1.3", "chartist": "0.11.4", + "emailjs-com": "^3.2.0", "googleapis": "66.0.0", "jquery": "3.5.1", "perfect-scrollbar": "1.5.0", "rxjs": "~7.5.0", + "sweetalert2": "^11.22.0", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "^14.2.3", + "@angular-devkit/build-angular": "^14.2.0", "@angular/cli": "~14.2.3", "@angular/compiler-cli": "^14.2.0", + "@types/chartist": "0.11.0", "@types/jasmine": "~5.1.4", + "@types/jasminewd2": "~2.0.13", + "@types/jquery": "3.5.30", + "@types/node": "20.14.11", + "codelyzer": "^0.0.28", + "cross-env": "^7.0.3", "jasmine-core": "~4.3.0", + "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.0.0", - "typescript": "~4.7.2", - "@types/jasminewd2": "~2.0.13", - "@types/chartist": "0.11.0", - "@types/jquery": "3.5.30", - "@types/node": "20.14.11", - "codelyzer": "6.0.2", - "jasmine-spec-reporter": "~7.0.0", - "protractor": "7.0.0", + "protractor": "^7.0.0", "ts-node": "~10.7.0", - "cross-env": "^7.0.3" + "typescript": "~4.7.2" } } diff --git a/src/app/Services/scroll.service.ts b/src/app/Services/scroll.service.ts new file mode 100644 index 000000000..8a4d150c5 --- /dev/null +++ b/src/app/Services/scroll.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class ScrollService { + private anchor: string | null = null; + + setAnchor(anchor: string) { + this.anchor = anchor; + } + + getAnchor(): string | null { + const a = this.anchor; + this.anchor = null; + return a; + } +} diff --git a/src/app/about-us/about-us.component.html b/src/app/about-us/about-us.component.html new file mode 100644 index 000000000..496b1c500 --- /dev/null +++ b/src/app/about-us/about-us.component.html @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + +
+ +
+

About us

+
+
+ +
+
+
+
+ + + + +
+
+

+ Depuis plus de 20 ans, Tunav IT Group se positionne en tant que fournisseur de référence en solutions GPS, + IT et IoT. Intégrateur de systèmes à forte valeur ajoutée, Tunav conçoit et déploie des solutions technologiques + intelligentes, alliant performance et fiabilité. Nos domaines d’expertise couvrent le suivi GPS, la gestion de + flotte, la surveillance à distance en temps réel ainsi que + l’optimisation des processus métier, au service de la transformation digitale des organisations.

+ Discover more ⟩ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+

TUNAV en Action!

+
+

+ TUNAV IT GROUP a récemment marqué sa présence lors de FITA 2025, un événement majeur dédié à la coopération et + à l’investissement en Afrique. À cette occasion, nous avons mis en avant notre engagement pour la transformation + digitale à travers des solutions innovantes en IoT, cloud computing et sécurité informatique. Cet événement a + été une formidable opportunité de présenter notre savoir-faire, d’échanger avec des acteurs clés du secteur et + de renforcer notre vision : + accompagner les entreprises africaines vers une digitalisation efficace, durable et sécurisée. +

+ Learn more » +
+
+
+ +
+
+
+

Au-delà du Code : renforcer l’humain chez TUNAV

+

+Chez TUNAV IT GROUP, nous croyons que la performance naît d’une équipe soudée et épanouie. +C’est pourquoi nous organisons régulièrement des activités de team building, qui permettent à nos collaborateurs +de se retrouver dans un cadre convivial, de renforcer leur cohésion et de stimuler la créativité en dehors du cadre + professionnel. Ces moments privilégiés favorisent la communication, +la collaboration et la confiance mutuelle, tout en reflétant nos valeurs : l’humain, l’innovation et le partage. + +

+ Voir Plus » +
+
+
+ +
+
+
+
+ +
+

Les Experts derrière TUNAV


+
+
+ +

{{ membre.prenom }} {{ membre.nom }}

+

{{ membre.profession }}

+
+ linkedin + instagram + medium +
+
+
+
+ + +
+
+

Construisons ensemble des solutions d’avenir

+

Chez Tunav IT Group, nous croyons que l'innovation naît de la collaboration. Forts de plus de 20 ans d’expérience dans les technologies GPS, IT et IoT, nous accompagnons nos partenaires dans la mise en œuvre de projets sur mesure, performants et durables.

+ Collaboration Tunav IT Group +

Vous avez un projet, un besoin spécifique ou l’envie de transformer vos processus métier ? Échangeons ensemble pour construire des solutions adaptées à vos enjeux. Notre équipe est à votre écoute et prête à relever vos défis technologiques.

+ Contactez-nous dès aujourd’hui » +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/app/about-us/about-us.component.scss b/src/app/about-us/about-us.component.scss new file mode 100644 index 000000000..61814cb58 --- /dev/null +++ b/src/app/about-us/about-us.component.scss @@ -0,0 +1,451 @@ +/* about-page.scss - Styles spécifiques à la page About */ +.about-page { + margin: 0; + padding: 0; + font-family: 'Roboto', sans-serif; + background-color: #F8F9FA; + color: #222222; + font-weight: 400; + + strong { + font-weight: 700; + } + + img { + max-width: 100%; + } + + p { + font-size: 1.125rem; + color: #222222; + margin: 0 0 15px; + line-height: 1.6; + } + + h1, h2, h3, h4, h5, h6 { + margin: 0; + } + + h1 { + color: #000000; + font-size: 36px; + @media (min-width: 768px) { + font-size: 80px; + } + } + + h2 { + color: #222222; + font-size: 30px; + @media (min-width: 768px) { + font-size: 60px; + } + } + + h3 { + color: #444444; + font-size: 24px; + @media (min-width: 768px) { + font-size: 50px; + } + } + + h4 { + color: #555555; + font-size: 22px; + @media (min-width: 768px) { + font-size: 40px; + } + } + + .about-container { + max-width: 1100px; + margin: 0 auto; + + img { + padding: 0.25rem; + border: 1px solid #bdbdbd; + border-radius: 0.25rem; + } + } + + .about-cta { + padding: 10px 30px; + text-align: center; + text-decoration: none; + background-color: #369; + border: 1px solid #369; + border-radius: 25px; + color: #FFFFFF; + text-transform: uppercase; + display: inline-block; + box-shadow: rgba(100, 100, 111, 0.8) 0px 7px 19px 0px; + transition: all 0.8s ease; + + &:hover { + background-color: #369; + border: 1px solid #3b1215; + color: #000000; + } + } + + /* Sections spécifiques */ + .about-banner { + background: linear-gradient(90deg, rgba(51, 85, 119, 0.8), rgba(0, 0, 0, 0.5)), url("/assets/img/TunavEquipe.png") no-repeat; + background-position: center; + background-size: cover; + padding: 150px 15px; + + h1 { + color: rgba($color: #FFFFFF, $alpha: 0.85); + text-transform: uppercase; + font-weight: 700; + } + + p { + color: #FFFFFF; + font-size: 1.375rem; + letter-spacing: 1.5px; + font-weight: 100; + text-shadow: 2px 2px 7px #222222; + } + } + + .about-section { + padding: 90px 15px; + } + + .about-first { + background-color: #FFFFFF; + background-image: linear-gradient(315deg, #a7a8a8 0%, #E9EBEC 74%); + .about-media-wrapper { + display: flex; + flex-direction: row; + align-items: center; + gap: 100px; + + @media (max-width: 768px) { + flex-direction: column; + gap: 20px; + } + } + + .about-video-left { + flex: 1; + min-width: 0; + position: relative; + + video { + width: 100%; + max-width: 600px; + height: auto; + display: block; + border-radius: 8px; + box-shadow: 0 5px 15px rgba(0,0,0,0.1); + } + .sound-toggle { + position: absolute; + bottom: 15px; + right: 15px; + background: rgba(0,0,0,0.5); + border: none; + color: white; + border-radius: 50%; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + z-index: 10; + font-size: 14px; + } + + } + + .about-text-right { + flex: 1; + min-width: 0; + } + + .about-container { + max-width: 800px; + } + } + +.about-second { + background-color: #fff8e6; + min-height: 100vh; + display: flex; + align-items: center; + padding: 40px 0; + + .about-container { + width: 100%; + max-width: 1400px; + margin: 0 auto; + padding: 0 20px; + display: flex; + flex-direction: column; + + @media (min-width: 992px) { + flex-direction: row; + gap: 50px; + padding: 0 40px; + } + + .about-left-img { + width: 100%; + margin-bottom: 30px; + + @media (min-width: 992px) { + flex: 0 0 45%; + margin-bottom: 0; + align-self: center; + } + + .image-slider { + position: relative; + width: 100%; + height: 300px; // Mobile first + overflow: hidden; + border-radius: 12px; + box-shadow: 0 8px 24px rgba(0,0,0,0.12); + + @media (min-width: 768px) { + height: 400px; + } + + @media (min-width: 992px) { + height: 500px; + } + + img { + position: absolute; + width: 100%; + height: 100%; + object-fit: cover; + opacity: 0; + transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1); + + &.active { + opacity: 1; + } + } + } + } + + .about-right-content { + width: 100%; + + @media (min-width: 992px) { + flex: 0 0 50%; + align-self: center; + } + + h2 { + margin: 0 0 25px; + font-size: 2.2rem; + line-height: 1.2; + color: #2c3e50; + + @media (min-width: 768px) { + font-size: 2.8rem; + } + + @media (min-width: 992px) { + margin: 0 0 30px; + font-size: 3.2rem; + } + } + + p { + font-size: 1.1rem; + line-height: 1.7; + margin-bottom: 25px; + + @media (min-width: 768px) { + font-size: 1.2rem; + } + } + + .about-cta { + display: inline-block; + margin-top: 20px; + } + } + } +} + +.about-third { + background-color: #e6f5fc; + min-height: 100vh; + display: flex; + align-items: center; + padding: 40px 0; + + .about-container { + width: 100%; + max-width: 1400px; + margin: 0 auto; + padding: 0 20px; + display: flex; + flex-direction: column; + + @media (min-width: 992px) { + flex-direction: row; + gap: 50px; + padding: 0 40px; + } + + .about-right-img { + flex-basis: 100%; + margin-bottom: 30px; + + @media (min-width: 992px) { + flex: 0 0 45%; + margin-bottom: 0; + align-self: center; + } + + .image-slider { + position: relative; + width: 100%; + height: 300px; + overflow: hidden; + border-radius: 12px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); + + @media (min-width: 768px) { + height: 400px; + } + + @media (min-width: 992px) { + height: 500px; + } + + img { + position: absolute; + width: 100%; + height: 100%; + object-fit: cover; + opacity: 0; + transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1); + + &.active { + opacity: 1; + } + } + } + } + + .about-left-content { + flex-basis: 100%; + + @media (min-width: 992px) { + flex: 0 0 50%; + align-self: center; + } + + h2 { + margin: 0 0 25px; + font-size: 2.2rem; + line-height: 1.2; + color: #2c3e50; + + @media (min-width: 768px) { + font-size: 2.8rem; + } + + @media (min-width: 992px) { + margin: 0 0 30px; + font-size: 3.2rem; + } + } + + p { + font-size: 1.1rem; + line-height: 1.7; + margin-bottom: 25px; + + @media (min-width: 768px) { + font-size: 1.2rem; + } + } + + .about-cta { + display: inline-block; + margin-top: 20px; + } + } + } +} + + + + + .about-four { + background-color: #FFFFFF; + h2 { + margin: 0 0 25px; + font-size: 2.2rem; + line-height: 1.2; + color: #2c3e50; + text-align: center; + + @media (min-width: 768px) { + font-size: 2.8rem; + } + + @media (min-width: 992px) { + margin: 0 0 30px; + font-size: 3.2rem; + } + } + .about-container { + display: block; + @media (min-width: 768px) { + display: flex; + justify-content: space-between; + } + + + .about-member { + background-color: #c0c6fa; + padding: 20px; + margin: 0 0 60px; + border-radius: 5px; + box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; + @media (min-width: 768px) { + flex-basis: 31%; + display: flex; + flex-direction: column; + justify-content: space-between; + } + + h3 { + @media (min-width: 768px) { + font-size: 32px; + } + } + + .about-social { + display: flex; + justify-content: flex-start; + + a img { + border: none; + max-width: 40px; + } + } + } + } + } + .about-six { + .about-container { + text-align: center; + max-width: 800px; + } + } + +} \ No newline at end of file diff --git a/src/app/about-us/about-us.component.spec.ts b/src/app/about-us/about-us.component.spec.ts new file mode 100644 index 000000000..fbc856a81 --- /dev/null +++ b/src/app/about-us/about-us.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AboutUsComponent } from './about-us.component'; + +describe('AboutUsComponent', () => { + let component: AboutUsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AboutUsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AboutUsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/about-us/about-us.component.ts b/src/app/about-us/about-us.component.ts new file mode 100644 index 000000000..2a1f905ee --- /dev/null +++ b/src/app/about-us/about-us.component.ts @@ -0,0 +1,147 @@ +import { AfterViewInit, Component, OnDestroy, OnInit, ElementRef, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-about-us', + templateUrl: './about-us.component.html', + styleUrls: ['./about-us.component.scss'] +}) +export class AboutUsComponent implements OnInit, AfterViewInit, OnDestroy { + constructor(private router: Router) {} + + @ViewChild('aboutVideo', { static: false }) aboutVideo!: ElementRef; + + EventImages = [ + { src: '/assets/img/FITA/FITA1.jfif', alt: 'Event 1', active: true }, + { src: '/assets/img/FITA/FITA2.jfif', alt: 'Event 2', active: false }, + { src: '/assets/img/FITA/FITA3.jfif', alt: 'Event 3', active: false }, + { src: '/assets/img/FITA/FITA4.jfif', alt: 'Event 4', active: false }, + { src: '/assets/img/FITA/FITA5.jfif', alt: 'Event 5', active: false }, + { src: '/assets/img/FITA/FITA6.jfif', alt: 'Event 6', active: false }, + { src: '/assets/img/FITA/FITA7.jfif', alt: 'Event 7', active: false } + ]; + + TeamBuildingImages = [ + { src: '/assets/img/TeamBuilding/TM1.jpeg', alt: 'TM 1', active: true }, + { src: '/assets/img/TeamBuilding/TM2.jpeg', alt: 'TM 2', active: false }, + { src: '/assets/img/TeamBuilding/TM3.jpeg', alt: 'TM 3', active: false }, + { src: '/assets/img/TeamBuilding/TM4.jpeg', alt: 'TM 4', active: false }, + { src: '/assets/img/TeamBuilding/TM5.jpeg', alt: 'TM 5', active: false }, + { src: '/assets/img/TeamBuilding/TM6.jpeg', alt: 'TM 6', active: false }, + { src: '/assets/img/TeamBuilding/TM7.jpeg', alt: 'TM 7', active: false }, + { src: '/assets/img/TeamBuilding/TM8.jpeg', alt: 'TM 8', active: false } + + ]; + membres = [ + { + nom: 'Najet', + prenom: 'Boukadi', + profession: 'Financial Manager', + image: '/assets/img/Equipe/NajetBoukadi.jfif' + }, + { + nom: 'Anis', + prenom: 'Kalel', + profession: 'PDG', + image: '/assets/img/Equipe/AnisKallel.jfif' + }, + { + nom: 'Skander', + prenom: 'Elj', + profession: 'Information System Mnager', + image: '/assets/img/Equipe/SkanderElj.jfif' + } + ]; + // Index indépendants pour chaque slider + currentIndexEvent = 0; + currentIndexTeamBuilding = 0; + + // Intervalles séparés + slideIntervalEvent: any; + slideIntervalTeamBuilding: any; + + private videoObserver?: IntersectionObserver; + + ngOnInit(): void { + this.startSlider(this.EventImages, 'event'); + this.startSlider(this.TeamBuildingImages, 'teamBuilding'); + } + + ngAfterViewInit(): void { + const videoEl = this.aboutVideo?.nativeElement; + if (!videoEl) return; + + // Lecture immédiate (autoplay) - muting obligatoire pour autoplay sans interaction + videoEl.muted = true; + videoEl.playsInline = true; + videoEl.play().catch(err => console.warn('Autoplay failed:', err)); + + // Observer pour pause/reprise au scroll avec seuil bas (0.1) + this.videoObserver = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + videoEl.play().catch(err => console.warn('Autoplay failed:', err)); + } else { + videoEl.pause(); + } + }, + { threshold: 0.1 } + ); + + this.videoObserver.observe(videoEl); + } + + startSlider(tab: any[], sliderType: 'event' | 'teamBuilding'): void { + const interval = setInterval(() => { + this.nextSlide(tab, sliderType); + }, 3000); + + if (sliderType === 'event') { + this.slideIntervalEvent = interval; + } else { + this.slideIntervalTeamBuilding = interval; + } + } + + nextSlide(tab: any[], sliderType: 'event' | 'teamBuilding'): void { + let currentIndex = sliderType === 'event' ? this.currentIndexEvent : this.currentIndexTeamBuilding; + + tab[currentIndex].active = false; + currentIndex = (currentIndex + 1) % tab.length; + tab[currentIndex].active = true; + + if (sliderType === 'event') { + this.currentIndexEvent = currentIndex; + } else { + this.currentIndexTeamBuilding = currentIndex; + } + } + + ngOnDestroy(): void { + if (this.slideIntervalEvent) { + clearInterval(this.slideIntervalEvent); + } + if (this.slideIntervalTeamBuilding) { + clearInterval(this.slideIntervalTeamBuilding); + } + if (this.videoObserver && this.aboutVideo) { + this.videoObserver.unobserve(this.aboutVideo.nativeElement); + this.videoObserver.disconnect(); + } + } + soundOn = false; + +toggleSound(): void { + const videoEl = this.aboutVideo.nativeElement; + this.soundOn = !this.soundOn; + videoEl.muted = !this.soundOn; + + if (this.soundOn) { + videoEl.play().catch(err => console.warn('Play with sound failed:', err)); + } +} +contact(event: Event){ + event.preventDefault(); + this.router.navigate(['contact']); + } +} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 21d81f5ed..0475f25a4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,18 +1,33 @@ -import { Component, OnInit } from '@angular/core'; +import { AfterViewInit, Component, OnInit } from '@angular/core'; import { LocationStrategy, PlatformLocation, Location } from '@angular/common'; - +import { Router ,NavigationEnd} from '@angular/router'; +import { ScrollService } from './Services/scroll.service'; +import { filter } from 'rxjs/operators'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) -export class AppComponent implements OnInit { - - constructor(public location: Location) {} +export class AppComponent implements OnInit,AfterViewInit { + constructor(public location: Location,private router: Router, private scrollService: ScrollService) {} ngOnInit(){ } - + ngAfterViewInit(): void { + this.router.events + .pipe(filter(event => event instanceof NavigationEnd)) + .subscribe(() => { + const anchor = this.scrollService.getAnchor(); + if (anchor) { + setTimeout(() => { + const element = document.getElementById(anchor); + if (element) { + element.scrollIntoView({ behavior: 'smooth' }); + } + }, 100); + } + }); + } isMap(path){ var titlee = this.location.prepareExternalUrl(this.location.path()); titlee = titlee.slice( 1 ); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 24da5d994..45c742d6d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,10 +1,10 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { RouterModule } from '@angular/router'; -import { AppRoutingModule } from './app.routing'; + import { NavbarModule } from './shared/navbar/navbar.module'; import { FooterModule } from './shared/footer/footer.module'; import { SidebarModule } from './sidebar/sidebar.module'; @@ -12,6 +12,31 @@ import { SidebarModule } from './sidebar/sidebar.module'; import { AppComponent } from './app.component'; import { AdminLayoutComponent } from './layouts/admin-layout/admin-layout.component'; +import { VitrineComponent } from './vitrine/vitrine.component'; +import { CarouselHomePageComponent } from './carousel-home-page/carousel-home-page.component'; +import { HeaderHomePageComponent } from './header-home-page/header-home-page.component'; +import { ClientsCarouselComponent } from './clients-carousel/clients-carousel.component'; +import { IOTCarouselComponent } from './iotcarousel/iotcarousel.component'; +import { RegisterComponent } from './register/register.component'; +import { FooterHomePageComponent } from './footer-home-page/footer-home-page.component'; +import { AboutUsComponent } from './about-us/about-us.component'; +import { ContactUsComponent } from './contact-us/contact-us.component'; +import { ChatBotComponent } from './chat-bot/chat-bot.component'; +import { BlogsComponent } from './blogs/blogs.component'; +import { FormProductsComponent } from './form-products/form-products.component'; +import { UsersListsComponent } from './users-lists/users-lists.component'; +import { BlogslistComponent } from './blogslist/blogslist.component'; +import { FormblogComponent } from './formblog/formblog.component'; +import { ProductsComponent } from './products/products.component'; +import { Carousel2Component } from './carousel2/carousel2.component'; +import { CarteProduitComponent } from './carte-produit/carte-produit.component'; +import { CarteProduitNodevisComponent } from './carte-produit-nodevis/carte-produit-nodevis.component'; +import { PetitCadreComponent } from './petit-cadre/petit-cadre.component'; +import { TitreproduitComponent } from './titreproduit/titreproduit.component'; +import { NavbarComponent } from './navbar/navbar.component'; +import { FormulaireIotItComponent } from './formulaire-iot-it/formulaire-iot-it.component'; +import { FormFranchiseComponent } from './form-franchise/form-franchise.component'; +import { AppRoutingModule } from './app.routing'; @NgModule({ imports: [ @@ -22,11 +47,36 @@ import { AdminLayoutComponent } from './layouts/admin-layout/admin-layout.compon NavbarModule, FooterModule, SidebarModule, - AppRoutingModule + AppRoutingModule, + ReactiveFormsModule ], declarations: [ AppComponent, - AdminLayoutComponent + AdminLayoutComponent, + VitrineComponent, + CarouselHomePageComponent, + HeaderHomePageComponent, + ClientsCarouselComponent, + IOTCarouselComponent, + RegisterComponent, + FooterHomePageComponent, + AboutUsComponent, + ContactUsComponent, + ChatBotComponent, + BlogsComponent, + FormProductsComponent, + UsersListsComponent, + BlogslistComponent, + FormblogComponent, + ProductsComponent, + Carousel2Component, + CarteProduitComponent, + CarteProduitNodevisComponent, + PetitCadreComponent, + TitreproduitComponent, + NavbarComponent, + FormulaireIotItComponent, + FormFranchiseComponent ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts index 993dc346d..c4bf9d603 100644 --- a/src/app/app.routing.ts +++ b/src/app/app.routing.ts @@ -1,12 +1,38 @@ -import { NgModule } from '@angular/core'; +import { Component, NgModule } from '@angular/core'; import { CommonModule, } from '@angular/common'; import { BrowserModule } from '@angular/platform-browser'; import { Routes, RouterModule } from '@angular/router'; import { AdminLayoutComponent } from './layouts/admin-layout/admin-layout.component'; +import { VitrineComponent } from './vitrine/vitrine.component'; +import { RegisterComponent } from './register/register.component'; +import { AboutUsComponent } from './about-us/about-us.component'; +import { ContactUsComponent } from './contact-us/contact-us.component'; +import { BlogsComponent } from './blogs/blogs.component'; +import { FormProductsComponent } from './form-products/form-products.component'; +import { TablesComponent } from './tables/tables.component'; +import { UsersListsComponent } from './users-lists/users-lists.component'; +import { BlogslistComponent } from './blogslist/blogslist.component'; +import { FormblogComponent } from './formblog/formblog.component'; +import {ProductsComponent} from './products/products.component'; +import {FormulaireIotItComponent} from './formulaire-iot-it/formulaire-iot-it.component'; +import { FormFranchiseComponent } from './form-franchise/form-franchise.component'; const routes: Routes =[ - { + + { path: '', component: VitrineComponent }, + { path: 'products', component: ProductsComponent }, + { path: 'home', component: VitrineComponent }, + { path: 'auth', component: RegisterComponent }, + { path: 'about', component: AboutUsComponent }, + { path: 'contact', component: ContactUsComponent }, + {path:'blogs',component:BlogsComponent}, + // { + // path: 'admin'}, + { path: 'formulaireiotit', component: FormulaireIotItComponent }, + { path: 'formulairefranchise', component: FormFranchiseComponent } + ] + /*{ path: '', redirectTo: 'dashboard', pathMatch: 'full', @@ -14,15 +40,25 @@ const routes: Routes =[ path: '', component: AdminLayoutComponent, children: [ - { - path: '', - loadChildren: () => import('./layouts/admin-layout/admin-layout.module').then(x => x.AdminLayoutModule) - }]}, + { + path: '', + loadChildren: () => import('./layouts/admin-layout/admin-layout.module').then(x => x.AdminLayoutModule) + }, + { path: 'add-product', component: FormProductsComponent }, + { path: 'update-product/:id', component: FormProductsComponent }, + { path: 'listProducts', component: TablesComponent }, + {path:'listblogs',component:BlogslistComponent}, + {path:'listusers',component:UsersListsComponent}, + { path: 'add-blog', component: FormblogComponent }, + { path: 'update-blog/:id', component: FormblogComponent }, + { path: 'admin', redirectTo: 'dashboard', pathMatch: 'full' }, + ] + }, { - path: '**', - redirectTo: 'dashboard' + path: '**',component: VitrineComponent + } -]; +];*/ @NgModule({ imports: [ diff --git a/src/app/blogs/blogs.component.html b/src/app/blogs/blogs.component.html new file mode 100644 index 000000000..3dd65b310 --- /dev/null +++ b/src/app/blogs/blogs.component.html @@ -0,0 +1,69 @@ + + +
+ + + + + + + + + +




+ +
+ +
+
+

{{ blog.author }}

+
+

{{ blog.author }}

+
+ +
+

{{ blog.title }}

+

{{ blog.summary }}

+
+ +
+
+ + + +
+



+ +
+ + + + + + + + + diff --git a/src/app/blogs/blogs.component.scss b/src/app/blogs/blogs.component.scss new file mode 100644 index 000000000..700e5de37 --- /dev/null +++ b/src/app/blogs/blogs.component.scss @@ -0,0 +1,213 @@ +.co-blog-component { + background: url('/assets/img/infinite-loop-01.jpg') no-repeat center center; + background-size: cover; + min-height: 100vh; + position: relative; + top:0; + margin: 0; + padding: 0; + .blog-container { + background: #fff; + border-radius: 5px; + box-shadow: hsla(0, 0, 0, .2) 0 4px 2px -2px; + font-family: "adelle-sans", sans-serif; + font-weight: 100; + width: 20rem; + margin: 0 auto 48px; + + @media screen and (min-width: 480px) { + width: 28rem; + } + @media screen and (min-width: 767px) { + width: 40rem; + } + @media screen and (min-width: 959px) { + width: 50rem; + } + } + + .blog-container a { + color: #4d4dff; + text-decoration: none; + transition: .25s ease; + + &:hover { + border-color: #ff4d4d; + color: #ff4d4d; + } + } + + .blog-cover { + background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/17779/yosemite-3.jpg"); + background-size: cover; + border-radius: 5px 5px 0 0; + height: 15rem; + box-shadow: inset hsla(0, 0, 0, .2) 0 64px 64px 16px; + } + + .blog-author, + .blog-author--no-cover { + margin: 0 auto; + padding-top: .125rem; + width: 80%; + } + + .blog-author h3::before, + .blog-author--no-cover h3::before { + background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/17779/russ.jpeg"); + background-size: cover; + border-radius: 50%; + content: " "; + display: inline-block; + height: 32px; + margin-right: .5rem; + position: relative; + top: 8px; + width: 32px; + } + + .blog-author h3 { + color: #fff; + font-weight: 100; + } + + .blog-author--no-cover h3 { + color: lighten(#333, 40%); + font-weight: 100; + } + + .blog-body { + margin: 0 auto; + width: 80%; + } + + .video-body { + height: 100%; + width: 100%; + } + + .blog-title h1 a { + color: #333; + font-weight: 100; + } + + .blog-summary p { + color: lighten(#333, 10%); + } + + .blog-tags ul { + display: flex; + flex-direction: row; + flex-wrap: wrap; + list-style: none; + padding-left: 0; + } + + .blog-tags li + li { + margin-left: .5rem; + } + + .blog-tags a { + border: 1px solid lighten(#333, 40%); + border-radius: 3px; + color: lighten(#333, 40%); + font-size: .75rem; + height: 1.5rem; + line-height: 1.5rem; + letter-spacing: 1px; + padding: 0 .5rem; + text-align: center; + text-transform: uppercase; + white-space: nowrap; + width: 5rem; + } + + .blog-footer { + border-top: 1px solid lighten(#333, 70%); + margin: 0 auto; + padding-bottom: .125rem; + width: 80%; + } + + .blog-footer ul { + list-style: none; + display: flex; + flex: row wrap; + justify-content: flex-end; + padding-left: 0; + } + + .blog-footer li:first-child { + margin-right: auto; + } + + .blog-footer li + li { + margin-left: .5rem; + } + + .blog-footer li { + color: lighten(#333, 40%); + font-size: .75rem; + height: 1.5rem; + letter-spacing: 1px; + line-height: 1.5rem; + text-align: center; + text-transform: uppercase; + position: relative; + white-space: nowrap; + + & a { + color: lighten(#333, 40%); + } + } + + + .published-date { + border: 1px solid lighten(#333, 40%); + border-radius: 3px; + padding: 0 .5rem; + } + + + .icon-star, + .icon-bubble { + fill: lighten(#333, 40%); + height: 35px; + margin-right: .5rem; + transition: .25s ease; + width: 35px; + + &:hover { + fill: #ff4d4d; + } + } +} +.like-button { + background: transparent; + border: none; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 16px; + padding: 0; + vertical-align: middle; +} + +.like-button svg.icon-heart { + width: 24px; + height: 24px; + transition: fill 0.3s ease; + display: block; +} + +.like-button .numero { + color: #333; + font-weight: 500; + line-height: 1; + vertical-align: middle; + margin-top: 2px; /* Ajustement fin */ + user-select: none; +} + + diff --git a/src/app/blogs/blogs.component.spec.ts b/src/app/blogs/blogs.component.spec.ts new file mode 100644 index 000000000..4803d0178 --- /dev/null +++ b/src/app/blogs/blogs.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BlogsComponent } from './blogs.component'; + +describe('BlogsComponent', () => { + let component: BlogsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BlogsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(BlogsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/blogs/blogs.component.ts b/src/app/blogs/blogs.component.ts new file mode 100644 index 000000000..57846d296 --- /dev/null +++ b/src/app/blogs/blogs.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-blogs', + templateUrl: './blogs.component.html', + styleUrls: ['./blogs.component.scss'] +}) +export class BlogsComponent implements OnInit { + blogPosts = [ + { + author: 'Russ Beye', + title: 'I Like To Make Cool Things', + summary: `I love working on fresh designs that breathe. To that end, I need to freshen up my portfolio...`, + tags: ['css', 'web design', 'codepen', 'twitter'], + publishedDate: '12/01/2025', + comments: 4, + shares: 1, + likes: 0 , + liked:false, + coverImage: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/17779/yosemite-3.jpg' + }, + { + author: 'Russ Beye', + title: 'This Post Has No Cover Image', + summary: `Here is an example of a post without a cover image. You don't always have to have a cover image.`, + tags: ['design', 'web dev', 'css'], + publishedDate: '16/06/2025', + comments: 8, + liked:false, + shares: 3,likes: 10 + } + ]; + constructor() { } + + ngOnInit(): void { + } + toggleLike(index: number): void { + const blog = this.blogPosts[index]; + blog.liked = !blog.liked; + blog.likes += blog.liked ? 1 : -1; + } +} diff --git a/src/app/blogslist/blogslist.component.html b/src/app/blogslist/blogslist.component.html new file mode 100644 index 000000000..347bc78cf --- /dev/null +++ b/src/app/blogslist/blogslist.component.html @@ -0,0 +1,30 @@ +
+
+

Liste des Blogs

+ + + +
+
+
+
+ image blog +
+
+
{{ blog.titre }}
+

{{ blog.contenu }}

+
+
+ {{ tag }} +
+
+ {{ blog.likes }} + + +
+
+
+
+
+
+
diff --git a/src/app/blogslist/blogslist.component.scss b/src/app/blogslist/blogslist.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/blogslist/blogslist.component.spec.ts b/src/app/blogslist/blogslist.component.spec.ts new file mode 100644 index 000000000..4a90aa9ab --- /dev/null +++ b/src/app/blogslist/blogslist.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BlogslistComponent } from './blogslist.component'; + +describe('BlogslistComponent', () => { + let component: BlogslistComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BlogslistComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(BlogslistComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/blogslist/blogslist.component.ts b/src/app/blogslist/blogslist.component.ts new file mode 100644 index 000000000..11c871cf4 --- /dev/null +++ b/src/app/blogslist/blogslist.component.ts @@ -0,0 +1,64 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; +export interface Blog { + id: number; + titre: string; + contenu: string; + image: string; + tags: string[]; + likes: number; +} +@Component({ + selector: 'app-blogslist', + templateUrl: './blogslist.component.html', + styleUrls: ['./blogslist.component.scss'] +}) +export class BlogslistComponent implements OnInit { +constructor(private router: Router) { } + + blogs: Blog[] = []; + + ngOnInit() { + this.blogs = [ + { + id: 1, + titre: 'Premier blog', + contenu: 'Contenu du premier blog...', + image: '/assets/img/gallery-img-03.jpg', + tags: ['tech', 'innovation'], + likes: 5 + }, + { + id: 2, + titre: 'Blog 2', + contenu: 'Contenu du blog 2...', + image: '/assets/img/gallery-img-03.jpg', + tags: ['coding', 'angular'], + likes: 10 + } + ]; + } + + editBlog(id: number) { + this.router.navigate(['/update-blog', id]); + console.log("Edit blog", id); + } + + confirmDelete(id: number) { + Swal.fire({ + title: 'Supprimer ce blog ?', + text: 'Cette action est irréversible.', + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Oui, supprimer', + cancelButtonText: 'Annuler' + }).then((result) => { + if (result.isConfirmed) { + this.blogs = this.blogs.filter(b => b.id !== id); + Swal.fire('Supprimé !', 'Le blog a été supprimé.', 'success'); + } + }); + } + +} diff --git a/src/app/carousel-home-page/carousel-home-page.component.html b/src/app/carousel-home-page/carousel-home-page.component.html new file mode 100644 index 000000000..27c9f89c7 --- /dev/null +++ b/src/app/carousel-home-page/carousel-home-page.component.html @@ -0,0 +1,42 @@ + + +
+ + + +
diff --git a/src/app/carousel-home-page/carousel-home-page.component.scss b/src/app/carousel-home-page/carousel-home-page.component.scss new file mode 100644 index 000000000..c22475f73 --- /dev/null +++ b/src/app/carousel-home-page/carousel-home-page.component.scss @@ -0,0 +1,151 @@ + +.carousel-container { + height: 100%; + width: 100%; + +background-image: url(/assets/img/HomeCarousselBackground.png); + background-repeat: no-repeat; + background-position: center center; + background-size: cover; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; + overflow: hidden; +} + +.carousel-slide { + display: flex; + justify-content: space-between; + align-items: center; + width: 90%; + max-width: 1400px; + padding: 40px; + box-sizing: border-box; + position: relative; + transition: opacity 1s ease-in-out; + opacity: 1; +} + +.fade { + animation: fadeEffect 1s; +} + +@keyframes fadeEffect { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.left-content { + color: white; + width: 50%; +} + +.title { + font-size: 2.8rem; + font-weight: bold; + margin: 0 0 15px; +} + +.subtitle { + font-size: 1.5rem; + color: #369; + margin-bottom: 10px; +} + +.description { + font-size: 1.1rem; + margin-bottom: 20px; + line-height: 1.5; +} + +.button-wrapper { + margin-top: 10px; +} + +.button-wrapper button { + padding: 10px 20px; + font-size: 1rem; + background-color: #007bff; + border: none; + border-radius: 6px; + color: white; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.button-wrapper button:hover { + background-color: #0056b3; +} + +.right-image { + width: 45%; + display: flex; + justify-content: center; + align-items: center; +} + +.right-image img { + width: 300px; + height: 300px; + object-fit: cover; + border-radius: 50%; + border: 5px solid #ffffff96; +} + +.dots { + position: absolute; + bottom: 40px; + display: flex; + gap: 10px; +} + +.dots span { + width: 10px; + height: 10px; + background-color: gray; + border-radius: 50%; + cursor: pointer; + transition: background-color 0.3s; +} + +.dots span.active { + background-color: white; + transform: scale(1.4); +} + +.scroll-arrow { + position: absolute; + right: 20px; + bottom: 20px; + background-color: rgba(255, 255, 255, 0.1); + border-radius: 50%; + padding: 10px; + cursor: pointer; + animation: saut 2s infinite; +} + +@keyframes saut{ + 0%,20%,50%,80%,100% { + transform:translateY(0); + } + 40%{ + transform: translateY(15px); + + } + 60%{ + transform: translateY(15px); + } +} + +.scroll-arrow img { + width: 40px; + height: 40px; +} \ No newline at end of file diff --git a/src/app/carousel-home-page/carousel-home-page.component.spec.ts b/src/app/carousel-home-page/carousel-home-page.component.spec.ts new file mode 100644 index 000000000..afccc81d9 --- /dev/null +++ b/src/app/carousel-home-page/carousel-home-page.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CarouselHomePageComponent } from './carousel-home-page.component'; + +describe('CarouselHomePageComponent', () => { + let component: CarouselHomePageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ CarouselHomePageComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CarouselHomePageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/carousel-home-page/carousel-home-page.component.ts b/src/app/carousel-home-page/carousel-home-page.component.ts new file mode 100644 index 000000000..b6a8a7062 --- /dev/null +++ b/src/app/carousel-home-page/carousel-home-page.component.ts @@ -0,0 +1,62 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-carousel-home-page', + templateUrl: './carousel-home-page.component.html', + styleUrls: ['./carousel-home-page.component.scss'] +}) +export class CarouselHomePageComponent implements OnInit { + + slides = [ + { + title: 'TRACKING GPS', + subtitle: "Accélérez votre transformation digitale avec le suivi GPS", + description: "Nous accompagnons les entreprises et les particuliers avec des solutions intelligentes de suivi GPS et de gestion de flotte, conçues pour optimiser leurs performances.", + image: '/assets/img/route.png' + }, + { + title: 'SOLUTION IOT', + subtitle: "Sécurisez vos données avec les solutions IoT avancées", + description: "Nos solutions IoT vous offrent une surveillance intelligente, une gestion simplifiée de vos équipements et une sécurité renforcée, pour gagner en efficacité au quotidien", + image: '/assets/img/iot.png' + }, + { + title: 'SOLUTION IT', + subtitle: "Transformez votre entreprise grâce à la gestion de flotte", + description: "Nous développons et intégrons des solutions IT qui renforcent les opérations,améliorent la performance et accélèrent la transformation digitale des entreprises.", + image: '/assets/img/service.png' + } + ]; + + currentSlide = 0; + intervalId: any; + + constructor(private router: Router) {} + + ngOnInit() { + this.intervalId = setInterval(() => this.nextSlide(), 4000); + } + + ngOnDestroy() { + clearInterval(this.intervalId); + } + + nextSlide() { + this.currentSlide = (this.currentSlide + 1) % this.slides.length; + } + + goToSlide(index: number) { + this.currentSlide = index; + } + + goToVitrine() { + this.router.navigate(['/vitrine']); + } + + goToHomeAndScroll(section: string, event: Event) { + event.preventDefault(); + this.router.navigate([''], { queryParams: { section } }); +} + +} diff --git a/src/app/carousel2/carousel2.component.css b/src/app/carousel2/carousel2.component.css new file mode 100644 index 000000000..20eb7effb --- /dev/null +++ b/src/app/carousel2/carousel2.component.css @@ -0,0 +1,150 @@ +.carousel-container { + width: 100%; + height: 400px; + /* background-image: url('assets/img/infinite-loop-01.jpg'); */ + background-repeat: no-repeat; + background-position: center center; + background-size: cover; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; + overflow: hidden; +} + +.carousel-slide { + display: flex; + justify-content: space-between; + align-items: center; + width: 90%; + max-width: 1400px; + padding: 40px; + box-sizing: border-box; + position: relative; + transition: opacity 1s ease-in-out; + opacity: 1; +} + +.fade { + animation: fadeEffect 1s; +} + +@keyframes fadeEffect { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.left-content { + color: white; + width: 50%; +} + +.title { + font-size: 2.8rem; + font-weight: bold; + margin: 0 0 15px; +} + +.subtitle { + font-size: 1.5rem; + color: #00aaff; + margin-bottom: 10px; +} + +.description { + font-size: 1.1rem; + margin-bottom: 20px; + line-height: 1.5; +} + +.button-wrapper { + margin-top: 10px; +} + +.button-wrapper button { + padding: 10px 20px; + font-size: 1rem; + background-color: #007bff; + border: none; + border-radius: 6px; + color: white; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.button-wrapper button:hover { + background-color: #0056b3; +} + +.right-image { + width: 45%; + display: flex; + justify-content: center; + align-items: center; +} + +.right-image img { + width: 300px; + height: 300px; + object-fit: cover; + border-radius: 50%; + border: 5px solid #ffffff96; +} + +.dots { + position: absolute; + bottom: 40px; + display: flex; + gap: 10px; +} + +.dots span { + width: 10px; + height: 10px; + background-color: gray; + border-radius: 50%; + cursor: pointer; + transition: background-color 0.3s; +} + +.dots span.active { + background-color: white; + transform: scale(1.4); +} + +.scroll-arrow { + position: absolute; + right: 20px; + bottom: 20px; + background-color: rgba(255, 255, 255, 0.1); + border-radius: 50%; + padding: 10px; + cursor: pointer; + animation: saut 2s infinite; +} + +@keyframes saut{ + 0%,20%,50%,80%,100% { + transform:translateY(0); + } + 40%{ + transform: translateY(15px); + + } + 60%{ + transform: translateY(15px); + } +} + +.scroll-arrow img { + width: 40px; + height: 40px; +} + diff --git a/src/app/carousel2/carousel2.component.html b/src/app/carousel2/carousel2.component.html new file mode 100644 index 000000000..97ce4ee4b --- /dev/null +++ b/src/app/carousel2/carousel2.component.html @@ -0,0 +1,30 @@ + + diff --git a/src/app/carousel2/carousel2.component.spec.ts b/src/app/carousel2/carousel2.component.spec.ts new file mode 100644 index 000000000..b04d98e68 --- /dev/null +++ b/src/app/carousel2/carousel2.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Carousel2Component } from './carousel2.component'; + +describe('Carousel2Component', () => { + let component: Carousel2Component; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Carousel2Component ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Carousel2Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/carousel2/carousel2.component.ts b/src/app/carousel2/carousel2.component.ts new file mode 100644 index 000000000..1dde1d937 --- /dev/null +++ b/src/app/carousel2/carousel2.component.ts @@ -0,0 +1,62 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-carousel2', + templateUrl: './carousel2.component.html', + styleUrls: ['./carousel2.component.css'] +}) +export class Carousel2Component implements OnInit, OnDestroy { + slides = [ + { + title: 'TRACKING GPS', + subtitle: "Accélérez votre transformation digitale avec le suivi GPS", + description: "Nous accompagnons les entreprises et les particuliers avec des solutions intelligentes de suivi GPS et de gestion de flotte, conçues pour optimiser leurs performances.", + image: 'assets/img/route.png' + }, + { + title: 'SOLUTION IOT', + subtitle: "Sécurisez vos données avec les solutions IoT avancées", + description: "Nos solutions IoT vous offrent une surveillance intelligente, une gestion simplifiée de vos équipements et une sécurité renforcée, pour gagner en efficacité au quotidien", + image: 'assets/img/iot.png' + }, + { + title: 'SOLUTION IT', + subtitle: "Transformez votre entreprise grâce à la gestion de flotte", + description: "Nous développons et intégrons des solutions IT qui renforcent les opérations,améliorent la performance et accélèrent la transformation digitale des entreprises.", + image: 'assets/img/service.png' + } + ]; + + currentSlide = 0; + intervalId: any; + + constructor(private router: Router) {} + + ngOnInit() { + this.intervalId = setInterval(() => this.nextSlide(), 4000); + } + + ngOnDestroy() { + clearInterval(this.intervalId); + } + + nextSlide() { + this.currentSlide = (this.currentSlide + 1) % this.slides.length; + } + + goToSlide(index: number) { + this.currentSlide = index; + } + + goToVitrine() { + this.router.navigate(['/vitrine']); + } + scrollToGps() { + const titreSection = document.getElementById('partietitre'); + if (titreSection) { + titreSection.scrollIntoView({ behavior: 'smooth' }); + } + } + +} diff --git a/src/app/carte-produit-nodevis/carte-produit-nodevis.component.css b/src/app/carte-produit-nodevis/carte-produit-nodevis.component.css new file mode 100644 index 000000000..33489710d --- /dev/null +++ b/src/app/carte-produit-nodevis/carte-produit-nodevis.component.css @@ -0,0 +1,297 @@ +.carte-produit { + background-color: #e9ecef; + position: relative; + width: 300px; + height: 360px; + border-radius: 12px; + overflow: hidden; + box-shadow: 0px 10px 1px #38B; + cursor: pointer; + transition: transform 0.25s ease-in-out 0s; +} + +.carte-produit:hover { + transform: scale(1.02); + transform: translateY(-3px); +} + +.image-fond { + width: 100%; + height: 90%; + object-fit: cover; + position: relative; + margin-top: 10px; + +} + +.infos-base { + position: absolute; + /* bottom: 0px; */ + left: 20px; + color: white; +} + +.categorie { + color: #01b4ff; + font-weight: bold; + font-size: 14px; + display: block; + top: 50px; +} + +.titre { + color: #01b4ff; + font-size: 14px; + font-weight: bold; + display: block; + margin-bottom: 20px; +} + +.overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.50); + color: white; + padding: 20px; + display: flex; + align-items: flex-end; + box-sizing: border-box; +} + +.infos-hover h3 { + margin: 0 0 10px; + font-size: 18px; +} + +.infos-hover p { + font-size: 14px; + margin-bottom: 15px; +} + + + +.liens { + display: flex; + gap: 12px; +} + +.voir-details { + color: white; + text-decoration: none; + font-weight: bold; + border-bottom: 3px solid white; + font-size: 14px; +} +/* //////////////// */ +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(3px); + display: flex; + justify-content: center; + align-items: center; + z-index: 999; +} + +.modal-container { + border-radius: 20px; + background-color: white; + width: 55%; + overflow-y: auto; + position: relative; + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25); + animation: fadeIn 0.3s ease-in-out; +} + +.modal-top { + background-image: url('assets/img/infinite-loop-01.jpg'); + background-repeat: no-repeat; + background-size: cover; + /* background-position: center ; */ + /* background-color: #38B; */ + padding: 30px; + color: #ffffff; + height: 160px; +} + +.categorie h5 { + margin: 0; + font-size: 16px; + background: #fff; + color: #38B; + display: inline-block; + padding: 4px 10px; + border-radius: 10px; + font-weight: bold; + +} + + +.titre-et-description h2 { + font-size: 30px; + font-weight: bold; + /* margin: 10px 0 8px; */ + color: #fff; +} + +.titre-et-description p { + margin: 0; + font-size: 10px; + color: #fff; +} + +.modal-image { + width: 100%; + height: auto; + margin-top: 20px; + max-height: 300px; + object-fit: contain; + border-radius: 10px; + margin-bottom: 20px; +} + +.modal-content { + padding: 10px; + max-height: 90vh; +} + +.modal-content h2 { + font-size: 24px; + margin-bottom: 10px; + color: #041c44; +} + +.modal-content p { + font-size: 16px; + margin-bottom: 20px; + color: #333; +} + +.modal-bottom { + display: flex; + justify-content: space-between; + gap: 20px; +} +.imagee{ + /* background-color: #f2f2f2; */ + padding: 12px 16px; + border-radius: 10px; + width: 65%; +} +.caracteristiques-et-tarif{ + display: flex; + flex-direction: column; + gap:20px; + max-width: 50%; + min-width: 40%; +} +.caracteristiques{ + height: 60%; + background-color: #f2f2f2; + padding: 12px 16px; + border-radius: 10px; + word-wrap: break-word; + overflow-wrap: break-word; + text-align: left; +} +.tarif{ + height: auto; + background-color: #f2f2f2; + padding: 12px 16px; + border-radius: 10px; + word-wrap: break-word; + overflow-wrap: break-word; +} + +.caracteristiques h4, +.tarif h4 { + margin-top: 0; + margin-bottom: 8px; + color: #38B; + font-size: 18px; + text-decoration: underline; + font-weight: bold; + text-align: left; +} +.tarif h2 { + font-weight: bold; + color: #000000; + display: block; + text-align: left; +} + +.caracteristiques ul { + padding-left: 20px; + margin: 0; +} +.caracteristiques li{ + /* font-weight: bold; */ + color: black; +} + +.tarif p { + font-weight: bold; + font-size: 20px; + margin: 10px 0; + color: orange; +} + +.btn-devis { + background-color: #01b4ff; + color: white; + padding: 8px 16px; + border-radius: 6px; + border: none; + cursor: pointer; + font-weight: bold; +} + +.modal-close { + color: #ffffff; + position: absolute; + top: 0px; + right: 16px; + font-size: 40px; + background: none; + border: none; + cursor: pointer; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +} +.btn-panier { + margin-top: 20px; + background-color: #01b4ff; + width: 100%; + color: white; + border: none; + padding: 10px 20px; + border-radius: 8px; + font-weight: bold; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s ease-in-out; + text-align: center; +} + +.btn-panier:hover { + background-color: #008fcc; +} + + + diff --git a/src/app/carte-produit-nodevis/carte-produit-nodevis.component.html b/src/app/carte-produit-nodevis/carte-produit-nodevis.component.html new file mode 100644 index 000000000..cdac75216 --- /dev/null +++ b/src/app/carte-produit-nodevis/carte-produit-nodevis.component.html @@ -0,0 +1,62 @@ + +
+ + +
+ +

{{ titre }}

+
+ +
+
+

{{ titre }}

+

{{ description }}

+ +
+
+
+ + + + + diff --git a/src/app/carte-produit-nodevis/carte-produit-nodevis.component.spec.ts b/src/app/carte-produit-nodevis/carte-produit-nodevis.component.spec.ts new file mode 100644 index 000000000..f5acb117d --- /dev/null +++ b/src/app/carte-produit-nodevis/carte-produit-nodevis.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CarteProduitNodevisComponent } from './carte-produit-nodevis.component'; + +describe('CarteProduitNodevisComponent', () => { + let component: CarteProduitNodevisComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ CarteProduitNodevisComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CarteProduitNodevisComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/carte-produit-nodevis/carte-produit-nodevis.component.ts b/src/app/carte-produit-nodevis/carte-produit-nodevis.component.ts new file mode 100644 index 000000000..31614951c --- /dev/null +++ b/src/app/carte-produit-nodevis/carte-produit-nodevis.component.ts @@ -0,0 +1,28 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'app-carte-produit-nodevis', + templateUrl: './carte-produit-nodevis.component.html', + styleUrls: ['./carte-produit-nodevis.component.css'] +}) +export class CarteProduitNodevisComponent { + @Input() titre: string = ''; + @Input() description: string = ''; + @Input() image: string = ''; + @Input() categorie: string = ''; + @Input() prix: string = ''; + @Input() caracteristiques: string[] = []; + + + hover: boolean = false; + showModal: boolean = false; + + openModal() { + this.showModal = true; + } + + closeModal() { + this.showModal = false; + } + +} diff --git a/src/app/carte-produit/carte-produit.component.css b/src/app/carte-produit/carte-produit.component.css new file mode 100644 index 000000000..27b20633b --- /dev/null +++ b/src/app/carte-produit/carte-produit.component.css @@ -0,0 +1,293 @@ +.carte-produit { + background-color: #e9ecef; + position: relative; + width: 300px; + height: 360px; + border-radius: 12px; + overflow: hidden; + box-shadow: 0px 10px 1px #38B; + cursor: pointer; + transition: transform 0.25s ease-in-out 0s; +} + +.carte-produit:hover { + transform: scale(1.02); + transform: translateY(-3px); +} + +.image-fond { + width: 100%; + height: 90%; + object-fit: cover; + position: relative; + margin-top: 10px; +} + +.infos-base { + position: absolute; + left: 20px; + color: white; +} + +.categorie { + display: inline-block; + background-color: white; + color: #01b4ff; + font-weight: bold; + font-size: 10px; + padding: 5px 10px; + border-radius: 8px; + margin-bottom: 10px; +} + +.titre { + color: #01b4ff; + font-size: 14px; + font-weight: bold; + display: block; + margin-bottom: 20px; +} + +.overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.50); + color: white; + padding: 20px; + display: flex; + align-items: flex-end; + box-sizing: border-box; +} + + +.infos-hover h3 { + margin: 0 0 10px; + font-size: 18px; +} + +.infos-hover p { + font-size: 14px; + margin-bottom: 15px; +} + +.btn-devis { + background-color: #01b4ff; + color: white; + border: none; + padding: 8px 14px; + border-radius: 4px; + font-size: 12px; + margin-bottom: 12px; + cursor: pointer; +} + +.liens { + display: flex; + gap: 12px; +} + +.voir-details { + color: white; + text-decoration: none; + font-weight: bold; + border-bottom: 3px solid white; + font-size: 14px; +} + +.demander-devis { + color: orange; + text-decoration: none; + font-weight: bold; + border-bottom: 3px solid orange; + font-size: 14px; +} +/* /////////////////////////// */ +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(3px); + display: flex; + justify-content: center; + align-items: center; + z-index: 999; +} + +.modal-container { + border-radius: 20px; + background-color: white; + width: 55%; + overflow-y: auto; + position: relative; + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25); + animation: fadeIn 0.3s ease-in-out; +} + +.modal-top { + background-image: url('assets/img/infinite-loop-01.jpg'); + background-repeat: no-repeat; + background-size: cover; + /* background-position: center ; */ + /* background-color: #38B; */ + padding: 30px; + color: #ffffff; + height: 160px; +} + +.categorie h5 { + margin: 0; + font-size: 16px; + background: #fff; + color: #38B; + display: inline-block; + padding: 4px 10px; + border-radius: 10px; + font-weight: bold; +} + +.titre-et-description h2 { + font-size: 30px; + font-weight: bold; + /* margin: 10px 0 8px; */ + color: #fff; +} + +.titre-et-description p { + margin: 0; + font-size: 10px; + color: #fff; +} + +.modal-image { + width: 100%; + height: auto; + margin-top: 20px; + max-height: 300px; + object-fit: contain; + border-radius: 10px; + margin-bottom: 20px; +} + +.modal-content { + padding: 10px; + max-height: 90vh; +} + +.modal-content h2 { + font-size: 24px; + margin-bottom: 10px; + color: #041c44; +} + +.modal-content p { + font-size: 16px; + margin-bottom: 20px; + color: #333; +} + +.modal-bottom { + display: flex; + justify-content: space-between; + gap: 20px; +} +.imagee{ + /* background-color: #f2f2f2; */ + padding: 12px 16px; + border-radius: 10px; + width: 65%; +} +.caracteristiques-et-tarif{ + display: flex; + flex-direction: column; + gap:20px; + max-width: 50%; +} + +.caracteristiques { + height: 60%; + background-color: #f2f2f2; + padding: 12px 16px; + border-radius: 10px; + word-wrap: break-word; + overflow-wrap: break-word; + text-align: left; +} +.tarif{ + height: auto; + background-color: #f2f2f2; + padding: 12px 16px; + border-radius: 10px; + word-wrap: break-word; + overflow-wrap: break-word; +} + +.caracteristiques h4, +.tarif h4 { + margin-top: 0; + margin-bottom: 8px; + color: #38B; + font-size: 18px; + text-decoration: underline; + font-weight: bold; + text-align: left; +} +.tarif h2 { + font-weight: bold; + color: #000000; + display: block; + text-align: left; +} + +.caracteristiques ul { + padding-left: 20px; + margin: 0; +} +.caracteristiques li{ + /* font-weight: bold; */ + color: black; +} + +.tarif p { + font-weight: bold; + font-size: 20px; + margin: 10px 0; + color: orange; +} + +.btn-devis { + background-color: #01b4ff; + color: white; + padding: 8px 16px; + border-radius: 6px; + border: none; + cursor: pointer; + font-weight: bold; +} + +.modal-close { + color: #ffffff; + position: absolute; + top: 0px; + right: 16px; + font-size: 40px; + background: none; + border: none; + cursor: pointer; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +} diff --git a/src/app/carte-produit/carte-produit.component.html b/src/app/carte-produit/carte-produit.component.html new file mode 100644 index 000000000..9990c9c8d --- /dev/null +++ b/src/app/carte-produit/carte-produit.component.html @@ -0,0 +1,65 @@ + + +
+ + +
+

{{ titre }}

+
+ +
+
+

{{ titre }}

+

{{ description }}

+ + +
+
+
+ + + + + diff --git a/src/app/carte-produit/carte-produit.component.spec.ts b/src/app/carte-produit/carte-produit.component.spec.ts new file mode 100644 index 000000000..c7cb1edae --- /dev/null +++ b/src/app/carte-produit/carte-produit.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CarteProduitComponent } from './carte-produit.component'; + +describe('CarteProduitComponent', () => { + let component: CarteProduitComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ CarteProduitComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CarteProduitComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/carte-produit/carte-produit.component.ts b/src/app/carte-produit/carte-produit.component.ts new file mode 100644 index 000000000..9eba88375 --- /dev/null +++ b/src/app/carte-produit/carte-produit.component.ts @@ -0,0 +1,33 @@ +import { Component, Input } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-carte-produit', + templateUrl: './carte-produit.component.html', + styleUrls: ['./carte-produit.component.css'] +}) +export class CarteProduitComponent { + @Input() titre: string = ''; + @Input() description: string = ''; + @Input() image: string = ''; + @Input() categorie: string = ''; + @Input() caracteristiques: string[] = []; + + constructor(private router: Router) {} + + hover: boolean = false; + showModal: boolean = false; + + openModal() { + this.showModal = true; + } + + closeModal() { + this.showModal = false; + } + allerAuFormulaire() { + this.closeModal(); + this.router.navigate(['/formulaireiotit']); + } +} + diff --git a/src/app/chat-bot/chat-bot.component.html b/src/app/chat-bot/chat-bot.component.html new file mode 100644 index 000000000..e0b8c8ac7 --- /dev/null +++ b/src/app/chat-bot/chat-bot.component.html @@ -0,0 +1,2 @@ +
+
\ No newline at end of file diff --git a/src/app/chat-bot/chat-bot.component.scss b/src/app/chat-bot/chat-bot.component.scss new file mode 100644 index 000000000..5f062f9e8 --- /dev/null +++ b/src/app/chat-bot/chat-bot.component.scss @@ -0,0 +1,23 @@ +.chatbot-float-button { + position: fixed; + bottom: 20px; + right: 20px; + background: #4e8cff; + color: white; + border-radius: 50%; + width: 60px; + height: 60px; + font-size: 24px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + transition: all 0.3s ease; + z-index: 9999; +} + +.chatbot-float-button:hover { + background: #356eea; + transform: scale(1.05); +} diff --git a/src/app/chat-bot/chat-bot.component.spec.ts b/src/app/chat-bot/chat-bot.component.spec.ts new file mode 100644 index 000000000..2b9bd93d1 --- /dev/null +++ b/src/app/chat-bot/chat-bot.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChatBotComponent } from './chat-bot.component'; + +describe('ChatBotComponent', () => { + let component: ChatBotComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ChatBotComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ChatBotComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/chat-bot/chat-bot.component.ts b/src/app/chat-bot/chat-bot.component.ts new file mode 100644 index 000000000..6d9e88e39 --- /dev/null +++ b/src/app/chat-bot/chat-bot.component.ts @@ -0,0 +1,58 @@ +import { Component, OnInit ,Renderer2} from '@angular/core'; + +@Component({ + selector: 'app-chat-bot', + templateUrl: './chat-bot.component.html', + styleUrls: ['./chat-bot.component.scss'] +}) +export class ChatBotComponent implements OnInit { + showChat = false; + + constructor(private renderer: Renderer2) {} + + ngOnInit(): void { + const script = this.renderer.createElement('script'); + script.innerHTML = ` + (function(){ + if(!window.chatbase || window.chatbase("getState") !== "initialized") { + window.chatbase=(...arguments)=>{ + if(!window.chatbase.q){ + window.chatbase.q=[] + } + window.chatbase.q.push(arguments) + }; + window.chatbase=new Proxy(window.chatbase,{ + get(target,prop){ + if(prop==="q"){ return target.q } + return (...args)=>target(prop,...args) + } + }) + } + + const onLoad=function(){ + const script=document.createElement("script"); + script.src="https://www.chatbase.co/embed.min.js"; + script.id="f0d3oyMrl3n7digplpnOD"; + script.setAttribute("chatbotId", "f0d3oyMrl3n7digplpnOD"); + document.body.appendChild(script); + }; + + if(document.readyState==="complete"){ + onLoad() + }else{ + window.addEventListener("load",onLoad) + } + })(); + `; + this.renderer.appendChild(document.body, script); + } + + toggleChat(): void { + const iframe = document.querySelector('iframe[src*="chatbase"]') as HTMLElement; + if (iframe) { + this.showChat = !this.showChat; + iframe.style.display = this.showChat ? 'block' : 'none'; + } + } + +} diff --git a/src/app/clients-carousel/clients-carousel.component.html b/src/app/clients-carousel/clients-carousel.component.html new file mode 100644 index 000000000..9e61e687a --- /dev/null +++ b/src/app/clients-carousel/clients-carousel.component.html @@ -0,0 +1,25 @@ +
+
+

Nos Clients

+

+ Découvrez les entreprises qui nous font confiance. Notre portfolio inclut des partenariats + avec des leaders industriels et des innovateurs de premier plan, unis par notre engagement + commun envers l'excellence et la qualité. +

+
+
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/src/app/clients-carousel/clients-carousel.component.scss b/src/app/clients-carousel/clients-carousel.component.scss new file mode 100644 index 000000000..03e4a4e34 --- /dev/null +++ b/src/app/clients-carousel/clients-carousel.component.scss @@ -0,0 +1,20 @@ +.carousel-container { + width: 100%; + margin: 0 auto; +} + +.carousel-content { + display: flex; + transition: transform 0.01s linear; /* Transition très courte pour fluidité */ + will-change: transform; /* Optimisation performance */ +} + +/* Masquer la scrollbar */ +.carousel-container::-webkit-scrollbar { + display: none; +} + +.carousel-container { + -ms-overflow-style: none; + scrollbar-width: none; +} \ No newline at end of file diff --git a/src/app/clients-carousel/clients-carousel.component.spec.ts b/src/app/clients-carousel/clients-carousel.component.spec.ts new file mode 100644 index 000000000..cb45a9bb0 --- /dev/null +++ b/src/app/clients-carousel/clients-carousel.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClientsCarouselComponent } from './clients-carousel.component'; + +describe('ClientsCarouselComponent', () => { + let component: ClientsCarouselComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ClientsCarouselComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ClientsCarouselComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/clients-carousel/clients-carousel.component.ts b/src/app/clients-carousel/clients-carousel.component.ts new file mode 100644 index 000000000..d041117bb --- /dev/null +++ b/src/app/clients-carousel/clients-carousel.component.ts @@ -0,0 +1,58 @@ +import { Component, OnInit,OnDestroy } from '@angular/core'; + +@Component({ + selector: 'app-clients-carousel', + templateUrl: './clients-carousel.component.html', + styleUrls: ['./clients-carousel.component.scss'] +}) +export class ClientsCarouselComponent implements OnInit , OnDestroy { + clients = [ + { name: 'Client 1', logo: '/assets/img/logos/pgs.jpeg' }, + { name: 'Client 2', logo: '/assets/img/logos/pharmaderm.jpeg' }, + { name: 'Client 3', logo: '/assets/img/logos/sagemcom.jpeg' }, + { name: 'Client 4', logo: '/assets/img/logos/TT.jpeg' }, + { name: 'Client 5', logo: '/assets/img/logos/pharmaservice.jpeg' }, + { name: 'Client 6', logo: '/assets/img/logos/cftp.jpeg' }, + { name: 'Client 7', logo: '/assets/img/logos/cnam.jpeg' }, + { name: 'Client 8', logo: '/assets/img/logos/etap.jpeg' }, + { name: 'Client 9', logo: '/assets/img/logos/smtf.jpeg' }, + { name: 'Client 10', logo: '/assets/img/logos/TLS.jpeg' }, + + + ]; + + duplicatedClients = [...this.clients, ...this.clients]; // Pour l'effet infini + private animationFrameId: number; + private scrollSpeed = 1; // Ajustez la vitesse ici + private carouselElement: HTMLElement; + + ngOnInit(): void { + this.carouselElement = document.querySelector('.carousel-content'); + this.startAnimation(); + } + + ngOnDestroy(): void { + this.stopAnimation(); + } + + startAnimation(): void { + let position = 0; + const animate = () => { + position += this.scrollSpeed; + + // Réinitialiser la position quand on arrive à la moitié (grâce aux éléments dupliqués) + if (position >= this.carouselElement.scrollWidth / 2) { + position = 0; + } + + this.carouselElement.style.transform = `translateX(-${position}px)`; + this.animationFrameId = requestAnimationFrame(animate); + }; + + this.animationFrameId = requestAnimationFrame(animate); + } + + stopAnimation(): void { + cancelAnimationFrame(this.animationFrameId); + } +} \ No newline at end of file diff --git a/src/app/contact-us/contact-us.component.html b/src/app/contact-us/contact-us.component.html new file mode 100644 index 000000000..ddf805ee1 --- /dev/null +++ b/src/app/contact-us/contact-us.component.html @@ -0,0 +1,112 @@ + + +
+
+
+
+

Contact Us

+

+ Vous pouvez nous contacter à tout moment par e-mail ou par téléphone. Vous pouvez également remplir directement ce formulaire, et notre équipe vous répondra dans les plus brefs délais. + +


+
+ +
+
+ + + + + +
+ Veuillez entrer un email valide. +
+ + + + +
+ +
+ {{ messageText }} +
+ +
+ + + + +
+ +
+ + +
+ \ No newline at end of file diff --git a/src/app/contact-us/contact-us.component.scss b/src/app/contact-us/contact-us.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/contact-us/contact-us.component.spec.ts b/src/app/contact-us/contact-us.component.spec.ts new file mode 100644 index 000000000..ecc463a0d --- /dev/null +++ b/src/app/contact-us/contact-us.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ContactUsComponent } from './contact-us.component'; + +describe('ContactUsComponent', () => { + let component: ContactUsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ContactUsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ContactUsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contact-us/contact-us.component.ts b/src/app/contact-us/contact-us.component.ts new file mode 100644 index 000000000..4226b71a1 --- /dev/null +++ b/src/app/contact-us/contact-us.component.ts @@ -0,0 +1,91 @@ +import { Component, OnInit } from '@angular/core'; +import emailjs from 'emailjs-com'; +import Swal from 'sweetalert2'; + +declare var Email: any; +@Component({ + selector: 'app-contact-us', + templateUrl: './contact-us.component.html', + styleUrls: ['./contact-us.component.scss'] +}) +export class ContactUsComponent implements OnInit { + + constructor() { } + form = { + name: '', + email: '', + message: '' + }; + + ngOnInit(): void { + const script = document.createElement('script'); + script.src = 'https://smtpjs.com/v3/smtp.js'; + script.type = 'text/javascript'; + document.body.appendChild(script); + } + messageBox = document.getElementById('messageBox'); + + showMessage(text, isError = false) { + this.messageBox.textContent = text; + this.messageBox.style.color = isError ? 'red' : 'green'; + this.messageBox.style.display = 'block'; +} +emailInvalid = false; +messageText = ''; +messageError = false; + +isValidEmail(email: string): boolean { + const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return re.test(email); +} + + sendEmail() { + if (!this.isValidEmail(this.form.email)) { + Swal.fire({ + icon: 'error', + title: 'Email invalide', + text: 'Veuillez entrer un email valide.', + }); + return; + } + + const serviceID = 'service_x3dvh7z'; + const template = 'template_al9cbrs'; + const publicKey = 'sDFgBbOfrGRnnhc0V'; + + const templateParams = { + from_name: this.form.name, + from_email: this.form.email, + message: this.form.message, + to_name: 'Tunav Team', + reply_to: this.form.email, + }; + + emailjs.send(serviceID, template, templateParams, publicKey) + .then(() => { + Swal.fire({ + icon: 'success', + title: 'Message envoyé ✅', + html: ` +

+ Un e-mail de confirmation automatique va vous être envoyé immédiatement.

+ ⚠️ Si vous ne le recevez pas dans quelques minutes, cela signifie que vous avez saisi une adresse e-mail inexistante ou incorrecte. + `, + confirmButtonColor: '#3085d6' + }); + + this.form = { name: '', email: '', message: '' }; + }) + .catch((err) => { + Swal.fire({ + icon: 'error', + title: 'Erreur', + text: 'L\'adresse email est incorrecte .', + }); + console.error(err); + }); +} + + + +} diff --git a/src/app/footer-home-page/footer-home-page.component.html b/src/app/footer-home-page/footer-home-page.component.html new file mode 100644 index 000000000..1e239d8b7 --- /dev/null +++ b/src/app/footer-home-page/footer-home-page.component.html @@ -0,0 +1,32 @@ + + + diff --git a/src/app/footer-home-page/footer-home-page.component.scss b/src/app/footer-home-page/footer-home-page.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/footer-home-page/footer-home-page.component.spec.ts b/src/app/footer-home-page/footer-home-page.component.spec.ts new file mode 100644 index 000000000..8eb44750a --- /dev/null +++ b/src/app/footer-home-page/footer-home-page.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FooterHomePageComponent } from './footer-home-page.component'; + +describe('FooterHomePageComponent', () => { + let component: FooterHomePageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FooterHomePageComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FooterHomePageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/footer-home-page/footer-home-page.component.ts b/src/app/footer-home-page/footer-home-page.component.ts new file mode 100644 index 000000000..df8b1b75f --- /dev/null +++ b/src/app/footer-home-page/footer-home-page.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-footer-home-page', + templateUrl: './footer-home-page.component.html', + styleUrls: ['./footer-home-page.component.scss'] +}) +export class FooterHomePageComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/form-franchise/form-franchise.component.css b/src/app/form-franchise/form-franchise.component.css new file mode 100644 index 000000000..d174b242e --- /dev/null +++ b/src/app/form-franchise/form-franchise.component.css @@ -0,0 +1,115 @@ +.body { + background-image: url('assets/img/infinite-loop-01.jpg'); + background-size: cover; + background-repeat: no-repeat; + background-position: center; +} + +.container { + display: flex; + flex-wrap: wrap; + gap: 40px; + padding: 2rem; + max-width: 1200px; + margin: auto; + color: white; +} + +.partie-informations { + margin-top: 50px; + flex-grow: 1; + flex-shrink: 1; + flex-basis: 40%; +} + +.partie-formulaire { + flex-grow: 1; + flex-shrink: 1; + flex-basis: 60%; +} + +h1 { + text-align: center; + color: #01b4ff; +} + +.subtitle { + text-align: center; + color: #ffffff; + margin-bottom: 1.5rem; +} + +.form-container { + background: white; + padding: 2rem; + border-radius: 12px; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); + color: rgba(0, 0, 0, 0.766); +} + +.form-container h2 { + font-size: 1.5rem; + margin-bottom: 0.8rem; + color: #01b4ff; + font-weight: 600; +} + +.form-container p { + font-size: 0.9rem; + margin-bottom: 1rem; +} + +.form-container form { + display: flex; + flex-direction: column; + gap: 0.8rem; +} + +.form-container label { + font-size: 0.9rem; +} + +.form-container input:not([type="radio"]), +.form-container textarea { + padding: 0.6rem; + border-radius: 6px; + border: 1px solid #ccc; + font-size: 0.9rem; +} + +textarea { + resize: vertical; + height: 80px; +} + +.form-container button { + margin-top: 1rem; + background-color: #01b4ff; + border: none; + color: white; + padding: 0.75rem; + font-size: 0.95rem; + font-weight: bold; + border-radius: 6px; + cursor: pointer; + transition: background 0.3s ease; +} + +.form-container button:hover { + background: linear-gradient(to right, #1e88e5, #1565c0); +} + +@media (max-width: 768px) { + .container { + flex-direction: column; + padding: 1rem; + } + + .form-container { + padding: 1.2rem; + } +} +input[type="radio"]{ + border: #01b4ff; + gap: 30px; +} diff --git a/src/app/form-franchise/form-franchise.component.html b/src/app/form-franchise/form-franchise.component.html new file mode 100644 index 000000000..facc3ef18 --- /dev/null +++ b/src/app/form-franchise/form-franchise.component.html @@ -0,0 +1,75 @@ +
+ +
+
+
+

Franchise TUNAV

+

+ Rejoignez notre réseau de partenaires et développez votre activité dans les technologies IT, IoT et GPS Tracking avec le support de TUNAV IT GROUP. +

+
+ +
+
+
+

Demande de Franchise

+

+ Nos solutions de franchise vous permettent de développer votre activité avec le support technologique et commercial de TUNAV IT GROUP. +

+ + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + +
+ + +
+ + +
+ +
+ + +
+ + + + + +
+
+
+
+


+ +
diff --git a/src/app/form-franchise/form-franchise.component.spec.ts b/src/app/form-franchise/form-franchise.component.spec.ts new file mode 100644 index 000000000..60243b7bd --- /dev/null +++ b/src/app/form-franchise/form-franchise.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FormFranchiseComponent } from './form-franchise.component'; + +describe('FormFranchiseComponent', () => { + let component: FormFranchiseComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FormFranchiseComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FormFranchiseComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/form-franchise/form-franchise.component.ts b/src/app/form-franchise/form-franchise.component.ts new file mode 100644 index 000000000..aa8025b7b --- /dev/null +++ b/src/app/form-franchise/form-franchise.component.ts @@ -0,0 +1,36 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +@Component({ + selector: 'app-form-franchise', + templateUrl: './form-franchise.component.html', + styleUrls: ['./form-franchise.component.css'] +}) +export class FormFranchiseComponent implements OnInit { + formFranchise!: FormGroup; + + constructor(private fb: FormBuilder) {} + + ngOnInit(): void { + this.formFranchise = this.fb.group({ + nom: ['Tapez vote nom', Validators.required], + prenom: ['Tapez votre prénom', Validators.required], + telephone: ['+216 xx xxx xxx', Validators.required], + email: ['test@test.test', [Validators.required, Validators.email]], + profession: ['Tapez votre profession'], + experienceIT: ['', Validators.required], + precisionsExp: [''], + dirigeEntreprise: ['', Validators.required], + secteurDuree: [''], + motivation: ['Quels sont vos motivations pour rejoindre La franchise à Tunav', Validators.required], + }); + } + + onSubmit(): void { + if (this.formFranchise.valid) { + console.log(this.formFranchise.value); + } else { + this.formFranchise.markAllAsTouched(); + } + } +} diff --git a/src/app/form-products/form-products.component.html b/src/app/form-products/form-products.component.html new file mode 100644 index 000000000..b8a5def0a --- /dev/null +++ b/src/app/form-products/form-products.component.html @@ -0,0 +1,39 @@ +
+

{{ isEditMode ? 'Modifier un Produit' : 'Ajouter un Produit' }}

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+

Glissez-déposez une image ici ou cliquez

+ Image + +
+ +
+ + +
+
diff --git a/src/app/form-products/form-products.component.scss b/src/app/form-products/form-products.component.scss new file mode 100644 index 000000000..ee11ce7fb --- /dev/null +++ b/src/app/form-products/form-products.component.scss @@ -0,0 +1,11 @@ +.drag-drop-area { + border: 2px dashed #aaa; + padding: 20px; + text-align: center; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.drag-drop-area:hover { + background-color: #f1f1f1; +} diff --git a/src/app/form-products/form-products.component.spec.ts b/src/app/form-products/form-products.component.spec.ts new file mode 100644 index 000000000..250095eaa --- /dev/null +++ b/src/app/form-products/form-products.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FormProductsComponent } from './form-products.component'; + +describe('FormProductsComponent', () => { + let component: FormProductsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FormProductsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FormProductsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/form-products/form-products.component.ts b/src/app/form-products/form-products.component.ts new file mode 100644 index 000000000..a0c02d114 --- /dev/null +++ b/src/app/form-products/form-products.component.ts @@ -0,0 +1,98 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ReactiveFormsModule } from '@angular/forms'; +@Component({ + selector: 'app-form-products', + templateUrl: './form-products.component.html', + styleUrls: ['./form-products.component.scss'] +}) +export class FormProductsComponent implements OnInit { + + productForm!: FormGroup; + isEditMode = false; + productId: number | null = null; + previewUrl: string | ArrayBuffer | null = null; + selectedFile: File | null = null; + + constructor( + private fb: FormBuilder, + private route: ActivatedRoute, + private router: Router + ) {} + + ngOnInit(): void { + this.productForm = this.fb.group({ + nom: [''], + description: [''], + prix: [''], + image: [null] + }); + + this.productId = Number(this.route.snapshot.paramMap.get('id')); + if (this.productId) { + this.isEditMode = true; + const product = this.getProductById(this.productId); + if (product) { + this.productForm.patchValue({ + nom: product.nom, + description: product.description, + prix: product.prix + }); + this.previewUrl = product.image; + } + } + } + + onSubmit(): void { + const formValue = this.productForm.value; + if (this.isEditMode) { + console.log('Mettre à jour le produit :', this.productId, formValue); + } else { + console.log('Ajouter un nouveau produit :', formValue); + } + this.router.navigate(['/listProducts']); + } + + onDragOver(event: DragEvent) { + event.preventDefault(); + } + + onDragLeave(event: DragEvent) { + event.preventDefault(); + } + + onFileDrop(event: DragEvent) { + event.preventDefault(); + if (event.dataTransfer?.files.length) { + this.handleFile(event.dataTransfer.files[0]); + } + } + + onFileSelect(event: any) { + if (event.target.files.length) { + this.handleFile(event.target.files[0]); + } + } + + handleFile(file: File) { + this.selectedFile = file; + this.productForm.patchValue({ image: file }); + const reader = new FileReader(); + reader.onload = () => { + this.previewUrl = reader.result; + }; + reader.readAsDataURL(file); + } + + getProductById(id: number) { + return { + id, + nom: 'Produit Exemple', + description: 'Description exemple', + prix: 99.99, + image: 'https://via.placeholder.com/150' + }; + } + +} diff --git a/src/app/formblog/formblog.component.html b/src/app/formblog/formblog.component.html new file mode 100644 index 000000000..745537138 --- /dev/null +++ b/src/app/formblog/formblog.component.html @@ -0,0 +1,51 @@ +
+
+

{{ isEditMode ? 'Modifier le blog' : 'Ajouter un blog' }}

+
+ +
+ +
+ + +
+ + +
+ + +
+ + +
+ +
+ + {{ tag }} + + +
+ +
+ + +
+ +
+

Glissez-déposez une image ici ou sélectionnez un fichier.

+ Aperçu de l'image + +
+
+ + + +
+
diff --git a/src/app/formblog/formblog.component.scss b/src/app/formblog/formblog.component.scss new file mode 100644 index 000000000..f5272583a --- /dev/null +++ b/src/app/formblog/formblog.component.scss @@ -0,0 +1,20 @@ +.image-upload-container { + border: 2px dashed #3377AA; + padding: 20px; + text-align: center; + border-radius: 10px; + background-color: #f8f9fa; + transition: background-color 0.2s ease-in-out; + + &.drag-over { + background-color: #e0f3ff; + } + + .image-preview { + max-width: 200px; + max-height: 200px; + margin-top: 10px; + border-radius: 10px; + object-fit: cover; + } +} diff --git a/src/app/formblog/formblog.component.spec.ts b/src/app/formblog/formblog.component.spec.ts new file mode 100644 index 000000000..edfef247f --- /dev/null +++ b/src/app/formblog/formblog.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FormblogComponent } from './formblog.component'; + +describe('FormblogComponent', () => { + let component: FormblogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FormblogComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FormblogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/formblog/formblog.component.ts b/src/app/formblog/formblog.component.ts new file mode 100644 index 000000000..f088d112e --- /dev/null +++ b/src/app/formblog/formblog.component.ts @@ -0,0 +1,91 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Blog } from 'app/blogslist/blogslist.component'; + +@Component({ + selector: 'app-formblog', + templateUrl: './formblog.component.html', + styleUrls: ['./formblog.component.scss'] +}) +export class FormblogComponent implements OnInit { + + blog: Blog = { + id: 0, + titre: '', + contenu: '', + image: '', + tags: [], + likes: 0 + }; + + newTag: string = ''; + isEditMode = false; + isDragging = false; + + constructor(private route: ActivatedRoute, private router: Router) {} + + ngOnInit() { + const blogId = this.route.snapshot.paramMap.get('id'); + if (blogId) { + this.isEditMode = true; + + const storedBlog = null; + if (storedBlog) { + this.blog = storedBlog; + } + } + } + + onFileSelected(event: any) { + const file = event.target.files[0]; + if (file && file.type.startsWith('image/')) { + const reader = new FileReader(); + reader.onload = e => this.blog.image = (e.target as FileReader).result as string; + reader.readAsDataURL(file); + } + } + + onDragOver(event: DragEvent) { + event.preventDefault(); + this.isDragging = true; + } + + onDragLeave(event: DragEvent) { + event.preventDefault(); + this.isDragging = false; + } + + onDrop(event: DragEvent) { + event.preventDefault(); + this.isDragging = false; + const file = event.dataTransfer?.files[0]; + if (file && file.type.startsWith('image/')) { + const reader = new FileReader(); + reader.onload = () => { + this.blog.image = reader.result as string; + }; + reader.readAsDataURL(file); + } + } + + addTag(event: KeyboardEvent) { + event.preventDefault(); + if (this.newTag.trim() && !this.blog.tags.includes(this.newTag.trim())) { + this.blog.tags.push(this.newTag.trim()); + this.newTag = ''; + } + } + + removeTag(index: number) { + this.blog.tags.splice(index, 1); + } + + onSubmit() { + if (this.isEditMode) { + // blogService.update(this.blog); + } else { + // blogService.create(this.blog); + } + this.router.navigate(['listblogs']); + } +} diff --git a/src/app/formulaire-iot-it/formulaire-iot-it.component.css b/src/app/formulaire-iot-it/formulaire-iot-it.component.css new file mode 100644 index 000000000..102e441d7 --- /dev/null +++ b/src/app/formulaire-iot-it/formulaire-iot-it.component.css @@ -0,0 +1,129 @@ +.body{ + background-image: url('assets/img/infinite-loop-01.jpg'); +} +.container { + /* margin-top: 20px; */ + display: flex; + /* padding: 2rem; */ + min-width: 100%; + background: transparent; + font-family: 'Segoe UI', sans-serif; + box-shadow: 0 5px 12px rgba(0, 0, 0, 0.1); + gap: 30px; + justify-content: center; + align-items: center; + height: 695px; +} + +.partie-informations { + width: auto; +} + +.partie-formulaire { + width: 65%; + margin-right: 30px; +} +h1 { + text-align: center; + color: #01b4ff; +} + +.subtitle { + text-align: center; + color: #ffffff; + margin-bottom: 1.5rem; +} +.tabs { + display: flex; + justify-content: center; + margin-bottom: 2rem; + gap: 1rem; +} +.tabs .tab { + padding: 0.6rem 1.5rem; + border-radius: 30px; + border: none; + background: #e0e0e0; + cursor: pointer; + transition: all 0.3s ease; + font-weight: 500; +} + +.tabs .tab.active { + background-color:#01b4ff; + color: white; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); +} + +.form-container { + margin-top: 20px; + margin-bottom: 20px; + background: white; + padding: 1rem; + border-radius: 12px; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); + height: 600px; + max-height: 100%; + margin: auto 0; + display: flex; + flex-direction: column; + justify-content: space-between; + overflow: hidden; +} + +.form-container .description { + font-size: 0.85rem; + margin-bottom: 0.6rem; +} + +.form-container h2 { + font-size: 1.3rem; + margin-bottom: 0.3rem; +} + +.form-container strong { + font-size: 0.9rem; +} + +.form-container form { + display: flex; + flex-direction: column; + flex-grow: 1; + justify-content: space-evenly; +} + +.form-container form label { + font-size: 0.85rem; + margin-bottom: 0.2rem; +} + +.form-container form input, +.form-container form textarea { + margin: 0.3rem 0; + padding: 0.6rem; + border-radius: 6px; + border: 1px solid #ccc; + font-size: 0.85rem; +} + +.form-container form textarea { + resize: vertical; + height: 70px; +} + +.form-container form button { + margin-top: 0.6rem; + background-color: #01b4ff; + border: none; + color: white; + padding: 0.7rem; + font-size: 0.85rem; + font-weight: bold; + border-radius: 6px; + cursor: pointer; + transition: background 0.3s; +} + +.form-container form button:hover { + background: linear-gradient(to right, #1e88e5, #1565c0); +} diff --git a/src/app/formulaire-iot-it/formulaire-iot-it.component.html b/src/app/formulaire-iot-it/formulaire-iot-it.component.html new file mode 100644 index 000000000..4a8b3fac0 --- /dev/null +++ b/src/app/formulaire-iot-it/formulaire-iot-it.component.html @@ -0,0 +1,90 @@ +
+ +
+
+
+

📡 Solutions GPS Avancées

+

Découvrez nos solutions IoT et IT pour vos besoins GPS

+ +
+ + +
+
+ + +
+ +
+

🌐 Solution IoT GPS

+

+ Solution Internet des Objets
+ Nos solutions IoT permettent la connectivité et le suivi en temps réel de vos dispositifs GPS avec une intégration cloud avancée. +

+ +
+ + + + + + + + + + + + + + + + +
+
+ + +
+

💻 Solution IT GPS

+

+ Solutions Informatiques
+ Nos solutions IT offrent des systèmes GPS robustes avec intégration logicielle, bases de données et architectures d’entreprise. +

+ +
+ + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+ +
diff --git a/src/app/formulaire-iot-it/formulaire-iot-it.component.spec.ts b/src/app/formulaire-iot-it/formulaire-iot-it.component.spec.ts new file mode 100644 index 000000000..1fb027ef9 --- /dev/null +++ b/src/app/formulaire-iot-it/formulaire-iot-it.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FormulaireIotItComponent } from './formulaire-iot-it.component'; + +describe('FormulaireIotItComponent', () => { + let component: FormulaireIotItComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FormulaireIotItComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FormulaireIotItComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/formulaire-iot-it/formulaire-iot-it.component.ts b/src/app/formulaire-iot-it/formulaire-iot-it.component.ts new file mode 100644 index 000000000..13940a6a9 --- /dev/null +++ b/src/app/formulaire-iot-it/formulaire-iot-it.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +@Component({ + selector: 'app-formulaire-iot-it', + templateUrl: './formulaire-iot-it.component.html', + styleUrls: ['./formulaire-iot-it.component.css'] +}) +export class FormulaireIotItComponent implements OnInit { + selectedTab: 'iot' | 'it' = 'iot'; + formIot!: FormGroup; + formIt!: FormGroup; + + constructor(private fb: FormBuilder) {} + + ngOnInit(): void { + this.formIot = this.fb.group({ + nom: ['', Validators.required], + prenom: ['', Validators.required], + email: ['', [Validators.required, Validators.email]], + entreprise: [''], + message: ['', Validators.required] + }); + + this.formIt = this.fb.group({ + nom: ['', Validators.required], + prenom: ['', Validators.required], + email: ['', [Validators.required, Validators.email]], + entreprise: [''], + message: ['', Validators.required] + }); + } + + selectTab(tab: 'iot' | 'it') { + this.selectedTab = tab; + } + + submitForm(type: 'iot' | 'it') { + const form = type === 'iot' ? this.formIot : this.formIt; + if (form.valid) { + console.log(`Formulaire ${type.toUpperCase()} :`, form.value); + alert(`Formulaire ${type.toUpperCase()} soumis avec succès !`); + form.reset(); + } else { + form.markAllAsTouched(); + } + } +} diff --git a/src/app/header-home-page/header-home-page.component.html b/src/app/header-home-page/header-home-page.component.html new file mode 100644 index 000000000..6ebca3e1f --- /dev/null +++ b/src/app/header-home-page/header-home-page.component.html @@ -0,0 +1,37 @@ + + diff --git a/src/app/header-home-page/header-home-page.component.scss b/src/app/header-home-page/header-home-page.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/header-home-page/header-home-page.component.spec.ts b/src/app/header-home-page/header-home-page.component.spec.ts new file mode 100644 index 000000000..67de4addc --- /dev/null +++ b/src/app/header-home-page/header-home-page.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HeaderHomePageComponent } from './header-home-page.component'; + +describe('HeaderHomePageComponent', () => { + let component: HeaderHomePageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HeaderHomePageComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(HeaderHomePageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/header-home-page/header-home-page.component.ts b/src/app/header-home-page/header-home-page.component.ts new file mode 100644 index 000000000..31be6685d --- /dev/null +++ b/src/app/header-home-page/header-home-page.component.ts @@ -0,0 +1,76 @@ +import { Component, OnInit } from '@angular/core'; +import { NavigationEnd, Router } from '@angular/router'; +import { ScrollService } from 'app/Services/scroll.service'; + +@Component({ + selector: 'app-header-home-page', + templateUrl: './header-home-page.component.html', + styleUrls: ['./header-home-page.component.scss'] +}) +export class HeaderHomePageComponent implements OnInit { + pendingSection: string | null = null; + + constructor(private router: Router) { + // Quand la navigation finit, scroll vers la section demandée si besoin + this.router.events.subscribe(event => { + if (event instanceof NavigationEnd && this.pendingSection) { + setTimeout(() => { + const el = document.getElementById(this.pendingSection!); + if (el) { + el.scrollIntoView({ behavior: 'smooth' }); + } + this.pendingSection = null; + }, 100); // délai pour que la page ait le temps de s'afficher + } + }); + } + + navigateToSection(sectionId: string, event: Event) { + event.preventDefault(); // Empêche le comportement par défaut du lien + + // Vérifie si on est sur la page d'accueil (adapter le chemin selon ton routing) + const currentUrl = this.router.url.split('#')[0]; + if (currentUrl !== '/' && currentUrl !== '/home') { + // Pas sur la home => redirige vers home, puis scroll + this.pendingSection = sectionId; + this.router.navigate(['/']); + } else { + // Déjà sur la home => scroll direct + const el = document.getElementById(sectionId); + if (el) { + el.scrollIntoView({ behavior: 'smooth' }); + } + } + } + ngOnInit(): void { + window.addEventListener('scroll', this.onScroll); +} + +onScroll = () => { + const scrollY = window.scrollY; + const logoBlanc = document.getElementById('logoBlanc'); + const logoCouleur = document.getElementById('logoCouleur'); + + if (scrollY <= 50) { + logoBlanc!.style.display = 'block'; + logoCouleur!.style.display = 'none'; + } else { + logoBlanc!.style.display = 'none'; + logoCouleur!.style.display = 'block'; + } +}; + + + Login(event: Event){ + event.preventDefault(); + this.router.navigate(['auth']); + } + About(event: Event){ + event.preventDefault(); + this.router.navigate(['about']); + } + Blogs(event: Event){ + event.preventDefault(); + this.router.navigate(['blogs']); + } +} diff --git a/src/app/iotcarousel/iotcarousel.component.html b/src/app/iotcarousel/iotcarousel.component.html new file mode 100644 index 000000000..42191aa88 --- /dev/null +++ b/src/app/iotcarousel/iotcarousel.component.html @@ -0,0 +1,26 @@ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/src/app/iotcarousel/iotcarousel.component.scss b/src/app/iotcarousel/iotcarousel.component.scss new file mode 100644 index 000000000..b4592278a --- /dev/null +++ b/src/app/iotcarousel/iotcarousel.component.scss @@ -0,0 +1,89 @@ +$poussin: url("http://images.metmuseum.org/CRDImages/ep/original/46_160.jpg"); +$picasso: url("http://uploads6.wikiart.org/images/pablo-picasso/the-abduction-of-sabines-1962-1.jpg"); +$rubens: url("https://upload.wikimedia.org/wikipedia/commons/7/72/Peter_Paul_Rubens_(taller)_-_Rapto_de_las_Sabinas.jpg"); + +html, body { + margin: 0; + height: 100%; + background: #F4F1E9; +} + +.arrow { + font-size: 2em; + color: #363f85; + position: absolute; + top: 50%; + left: 50%; + transition: 0.2s; + + &.left-arrow { + transform: translate(-11em, -50%); + } + + &.right-arrow { + transform: translate(10em, -50%); + } + + &:hover { + color: #B3B2AD; + } +} + +.slider { + position: relative; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + width: 600px; + height: 200px; +} + +.slide { + float: left; + position: relative; + width: 33.3333%; + height: 100%; + overflow-x: hidden; + border-radius: 50%; + box-shadow: 0px 0px 15px rgba(0,0,0,0.5); + + &.slide-center { + z-index: 1; + box-shadow: 0px 0px 15px rgba(0,0,0,0.75); + transform: scale(1.3); + } +} + +.slide-holder { + width: 300%; + height: 100%; + position: relative; + top: 0; + transform: translateX(-33.3333%); + display: flex; +} + +.slide-bg { + width: 33.3333%; + height: 100%; + background-size: cover; + background-position: center center; + background-repeat: no-repeat; + display: inline-block; +} + +#slide-left { + .bg-previous { background-image: $rubens; } + .bg-current { background-image: $poussin; } + .bg-next { background-image: $picasso; } +} +#slide-center { + .bg-previous { background-image: $poussin; } + .bg-current { background-image: $picasso; } + .bg-next { background-image: $rubens; } +} +#slide-right { + .bg-previous { background-image: $picasso; } + .bg-current { background-image: $rubens; } + .bg-next { background-image: $poussin; } +} diff --git a/src/app/iotcarousel/iotcarousel.component.spec.ts b/src/app/iotcarousel/iotcarousel.component.spec.ts new file mode 100644 index 000000000..cd2555c1e --- /dev/null +++ b/src/app/iotcarousel/iotcarousel.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IOTCarouselComponent } from './iotcarousel.component'; + +describe('IOTCarouselComponent', () => { + let component: IOTCarouselComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ IOTCarouselComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(IOTCarouselComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/iotcarousel/iotcarousel.component.ts b/src/app/iotcarousel/iotcarousel.component.ts new file mode 100644 index 000000000..9d542fe75 --- /dev/null +++ b/src/app/iotcarousel/iotcarousel.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-iotcarousel', + templateUrl: './iotcarousel.component.html', + styleUrls: ['./iotcarousel.component.scss'] +}) +export class IOTCarouselComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/layouts/admin-layout/admin-layout.routing.ts b/src/app/layouts/admin-layout/admin-layout.routing.ts index e09417d82..54555076b 100644 --- a/src/app/layouts/admin-layout/admin-layout.routing.ts +++ b/src/app/layouts/admin-layout/admin-layout.routing.ts @@ -5,8 +5,6 @@ import { UserComponent } from '../../user/user.component'; import { TablesComponent } from '../../tables/tables.component'; import { TypographyComponent } from '../../typography/typography.component'; import { IconsComponent } from '../../icons/icons.component'; -import { MapsComponent } from '../../maps/maps.component'; -import { NotificationsComponent } from '../../notifications/notifications.component'; import { UpgradeComponent } from '../../upgrade/upgrade.component'; export const AdminLayoutRoutes: Routes = [ @@ -15,7 +13,5 @@ export const AdminLayoutRoutes: Routes = [ { path: 'table', component: TablesComponent }, { path: 'typography', component: TypographyComponent }, { path: 'icons', component: IconsComponent }, - { path: 'maps', component: MapsComponent }, - { path: 'notifications', component: NotificationsComponent }, { path: 'upgrade', component: UpgradeComponent }, ]; diff --git a/src/app/navbar/navbar.component.css b/src/app/navbar/navbar.component.css new file mode 100644 index 000000000..250ad7968 --- /dev/null +++ b/src/app/navbar/navbar.component.css @@ -0,0 +1,63 @@ + nav { + position: fixed; + width: 100%; + top: 0; + left: 0; + transition: background-color 0.3s ease, transform 0.5s ease; + z-index: 2; + height: 80px; + display: flex; + align-items: center; +} + +.navbar-container { + width: 100%; + max-width: 1300px; + margin: 0 auto; + padding: 0 30px; + display: flex; + justify-content: space-between; + /* align-items: center; */ +} + +.logo img { + height: 60px; +} + +.nav-links { + list-style: none; + display: flex; + gap: 25px; + justify-content: center; + align-items: center; + text-align: center; + +} + +.nav-links li a { + text-decoration: none; + color: white; + font-weight: 500; + text-align: center; + cursor: pointer; + +} + +.navbar-transparent { + background-color: transparent; +} + +.navbar-dark { + background-color: white; +} +.navbar-dark .nav-links a{ + color: #38B; +} +.nav-links a { + transition: color 0.3s ease; +} + +.navbar-hidden { + transform: translateY(-100%); + transition: transform 0.4s ease-in-out; +} \ No newline at end of file diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html new file mode 100644 index 000000000..7245f54c5 --- /dev/null +++ b/src/app/navbar/navbar.component.html @@ -0,0 +1,20 @@ + diff --git a/src/app/navbar/navbar.component.spec.ts b/src/app/navbar/navbar.component.spec.ts new file mode 100644 index 000000000..505cc2ffb --- /dev/null +++ b/src/app/navbar/navbar.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NavbarComponent } from './navbar.component'; + +describe('NavbarComponent', () => { + let component: NavbarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ NavbarComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NavbarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts new file mode 100644 index 000000000..0e40215e8 --- /dev/null +++ b/src/app/navbar/navbar.component.ts @@ -0,0 +1,42 @@ +import { Component, HostListener, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-navbar', + templateUrl: './navbar.component.html', + styleUrls: ['./navbar.component.css'] +}) +export class NavbarComponent implements OnInit { + isScrolled = false; + + constructor(private router: Router) {} + + ngOnInit(): void { + this.onScroll(); + } + + @HostListener('window:scroll', []) + onScroll(): void { + const scrollY = window.scrollY || document.documentElement.scrollTop; + this.isScrolled = scrollY > 10; + } + + navigateToSection(sectionId: string): void { + const element = document.getElementById(sectionId); + if (element) { + element.scrollIntoView({ behavior: 'smooth' }); + } else { + this.router.navigate(['/home']).then(() => { + setTimeout(() => { + const el = document.getElementById(sectionId); + if (el) el.scrollIntoView({ behavior: 'smooth' }); + }, 50); + }); + } + } + + login(event: Event): void { + event.preventDefault(); + this.router.navigate(['/auth']); + } +} diff --git a/src/app/petit-cadre/petit-cadre.component.css b/src/app/petit-cadre/petit-cadre.component.css new file mode 100644 index 000000000..7fa7c1271 --- /dev/null +++ b/src/app/petit-cadre/petit-cadre.component.css @@ -0,0 +1,21 @@ +button { + border: 2px solid #01b4ff; + background-color: transparent; + color: #01b4ff; + font-weight: 600; + padding: 10px 25px; + border-radius: 25px; + font-size: 16px; + cursor: pointer; + transition: background-color 0.3s ease, color 0.3s ease; +} + +button.active { + background-color: #01b4ff; + color: white; +} + +button:hover { + background-color: #01b4ff; + color: white; +} diff --git a/src/app/petit-cadre/petit-cadre.component.html b/src/app/petit-cadre/petit-cadre.component.html new file mode 100644 index 000000000..b82941cec --- /dev/null +++ b/src/app/petit-cadre/petit-cadre.component.html @@ -0,0 +1,5 @@ + diff --git a/src/app/petit-cadre/petit-cadre.component.spec.ts b/src/app/petit-cadre/petit-cadre.component.spec.ts new file mode 100644 index 000000000..e272db61b --- /dev/null +++ b/src/app/petit-cadre/petit-cadre.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PetitCadreComponent } from './petit-cadre.component'; + +describe('PetitCadreComponent', () => { + let component: PetitCadreComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PetitCadreComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(PetitCadreComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/petit-cadre/petit-cadre.component.ts b/src/app/petit-cadre/petit-cadre.component.ts new file mode 100644 index 000000000..2d79dbe42 --- /dev/null +++ b/src/app/petit-cadre/petit-cadre.component.ts @@ -0,0 +1,17 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; + +@Component({ + selector: 'app-petit-cadre', + templateUrl: './petit-cadre.component.html', + styleUrls: ['./petit-cadre.component.css'] +}) +export class PetitCadreComponent { + @Input() label: string = ''; + @Input() active: boolean = false; + @Output() clicked = new EventEmitter(); + + onClick() { + this.clicked.emit(); + } +} + diff --git a/src/app/products/products.component.css b/src/app/products/products.component.css new file mode 100644 index 000000000..355c05214 --- /dev/null +++ b/src/app/products/products.component.css @@ -0,0 +1,66 @@ +.nav{ + width: 100%; + margin-bottom: 50px; +} +.products-grid { + /* width: 85%; jai rajoute hethy pour le truc de 3 containers */ + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 20px; + padding: 20px; + + /* margin-left: 100px; jai rajoute hethy pour le truc de 3 containers */ +} +.carousel-et-nav{ + background-image: url('assets/img/infinite-loop-03.jpg'); +} + +.body { + margin-bottom: 100px; + padding: 0; + background-color: #ffffff; + background-repeat: no-repeat; + background-size: cover; + background-position:center center; + height: 100%; +} +.filter-buttons { + display: flex; + gap: 15px; + justify-content: center; + margin-top: 20px; + margin-bottom: 40px; +} +.description-it { + max-width: 800px; + margin: 0 auto 40px auto; + padding: 20px; + text-align: center; + font-size: 1.1rem; + color: #333; + background-color: #f4f9ff; + border-radius: 12px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); +} + +.btn-formulaire-it { + display: inline-block; + margin-top: 20px; + padding: 12px 24px; + background: linear-gradient(to right, #42a5f5, #1e88e5); + color: white; + text-decoration: none; + font-weight: 600; + border-radius: 30px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + transition: background 0.3s ease; +} + +.btn-formulaire-it:hover { + transform: translateY(-5px); + transition: transform 0.25s ease-in-out; + background: linear-gradient(to right, #1e88e5, #1565c0); +} + + diff --git a/src/app/products/products.component.html b/src/app/products/products.component.html new file mode 100644 index 000000000..f6508d2eb --- /dev/null +++ b/src/app/products/products.component.html @@ -0,0 +1,66 @@ + +
+
+ + +
+ +
+ +
+ + +
+
+

+ Nous développons et intégrons des solutions IT qui renforcent les opérations, + améliorent la performance et accélèrent la transformation digitale des entreprises. +

+ + 👉 Demander une consultation IT + +
+ + +
+ + +
+ +
+ + +
+ +
+
+
+ + + diff --git a/src/app/products/products.component.spec.ts b/src/app/products/products.component.spec.ts new file mode 100644 index 000000000..623e5feff --- /dev/null +++ b/src/app/products/products.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProductsComponent } from './products.component'; + +describe('ProductsComponent', () => { + let component: ProductsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ProductsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ProductsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/products.component.ts b/src/app/products/products.component.ts new file mode 100644 index 000000000..af75d2975 --- /dev/null +++ b/src/app/products/products.component.ts @@ -0,0 +1,208 @@ +import { Component ,HostListener} from '@angular/core'; + +@Component({ + selector: 'app-products', + templateUrl: './products.component.html', + styleUrls: ['./products.component.css'] +}) +export class ProductsComponent { + isScrolled = false; + + @HostListener('window:scroll', []) + onWindowScroll() { + const offset = window.pageYOffset || document.documentElement.scrollTop; + this.isScrolled = offset > 300; + } + allProduitsNodevis = [ + { + id: 1, + titre: 'ET6_KIT', + description: 'Ce produit est un traceur GPS de haute précision, idéal pour la géolocalisation de véhicules.', + image: 'assets/img/ET6_KIT.png', + categorie: 'GPS TRACKER', + caracteristiques: [ + 'KIT TRACKING GPS', + 'GPRS', + 'SMS - ADVANCED AUTOMOTIVE' + ], + prix:'495,000DT' + }, + { + id: 2, + titre: 'ET8_KIT', + description: 'Un modèle avancé de traceur GPS, avec connectivité améliorée et autonomie prolongée.', + image: 'assets/img/ET8_KIT.png', + categorie: 'GPS TRACKER', + caracteristiques: [ + 'KIT TRACKING GPS', + 'GPRS', + 'SMS - ETANCHE AUTOMOTIVE' + + ], + prix:'435,000DT' + }, + { + id: 3, + titre: 'ETBLE_KIT', + description: 'Traceur GPS avec technologie BLE pour un suivi en temps réel via smartphone.', + image: 'assets/img/ETBLE_KIT.png', + categorie: 'GPS TRACKER', + caracteristiques: [ + 'KIT TRACKING GPS', + 'GPRS', + 'SMS - AUTOMOTIVE' + ], + prix: '345,000 DT' + }, + { + id: 4, + titre: 'ETX_KIT', + description: 'Traceur GPS robuste conçu pour les environnements difficiles et les flottes.', + image: 'assets/img/ETX_KIT.png', + categorie: 'GPS TRACKER', + caracteristiques: [ + 'KIT TRACKING GPS', + 'GPRS', + 'SMS - AUTOMOTIVE' + ], + prix: '195,000DT' + }, + { + id: 5, + titre: 'ETCAN_KIT', + description: 'Traceur GPS avancé conçu pour les véhicules lourds et la gestion de flotte, avec surveillance du carburant et connectivité BUSCAN.', + image: 'assets/img/ETCAN_KIT.png', + categorie: 'GPS TRACKER', + caracteristiques: [ + 'KIT TRACKING GPS', + 'GPRS', + 'SMS - BUSCAN', + 'CARBURANT AUTOMOTIVE' + ], + prix: '695,000DT' + }, + { + id: 6, + titre: 'MiniTrace_KIT', + description: 'Un mini traceur GPS compact pour les objets personnels ou petits véhicules.', + image: 'assets/img/MiniTrace_ KIT.png', + categorie: 'GPS TRACKER', + caracteristiques: [ + 'KIT TRACKING GPS', + 'GPRS', + 'SMS - PORTABLE' + ], + prix: '695,000DT' + }, + { + id: 7, + titre: 'Camtrack', + description: 'Caméra de surveillance avec GPS intégré pour le suivi vidéo et géographique.', + image: 'assets/img/Camtrack.png', + categorie: 'GPS TRACKER', + caracteristiques: [ + 'DASHCAM GPS 4G 3CH', + 'FRONT CAM INSIDE CAM', + 'WIFI,MICRO&SPEAKER, SOS, ADAS, DMS - OPTIONAL EXT CAM, OBD, RFID, ', + 'TF CARD' + ], + prix: '1 195,000DT' + }, + { + id: 8, + titre: 'SMART_LOCK', + description: 'Serrure connectée avec traçabilité GPS pour les containers et accès sécurisés.', + image: 'assets/img/SMART_lo.png', + categorie: 'GPS TRACKER', + caracteristiques: [ + 'KIT TRACKING GPS', + 'GRPS', + 'SMS - TRAILER SMART LOCK', + ], + prix: '1 195,000DT' + } + + ]; + + allProduitsAvecDevis = [ + { + id: 9, + titre: 'DISJONCTEUR INTELLIGENT', + description: 'Système de disjoncteur connecté permettant la surveillance et le contrôle à distance.', + image: 'assets/img/disjoncteur_intelligent.png', + categorie: 'IOT', + caracteristiques: [ + 'Fermer à distance n importe quel disjoncteur.', + 'Planifier l arrêt et le démarrage automatiques des machines.', + 'Mesurer la tension électronique à distance.', + ], + }, + { + id: 10, + titre: 'TAGIT RFID', + description: 'Solution RFID intelligente pour la gestion et l’identification automatisée des actifs.', + image: 'assets/img/tagit_rfid.png', + categorie: 'IOT', + caracteristiques: [ + 'traçabilité et sécurisation de vos biens', + 'Données en temps réel' + ], + }, + { + id: 11, + titre: 'EASY 360', + description: 'Système IoT complet pour le suivi énergétique et la gestion des bâtiments intelligents.', + image: 'assets/img/easy_360.png', + categorie: 'IOT', + caracteristiques: [ + 'surveillance en temps réel des niveaux d’oxygène, de l’humidité, de la température, de la pression, intégrant le GPS, le WiFi, le Bluetooth', + 'compatible avec les protocoles de communication Modbus RS485 et RS422' + ], + }, + { + id: 12, + titre: 'FUEL RESCUE', + description: 'Système IoT complet pour le suivi énergétique et la gestion des bâtiments intelligents.', + image: 'assets/img/FUEL_RESCUE.png', + categorie: 'IOT', + caracteristiques: [ + 'Détecte rapidement les anomalies telles que le vol de carburant, envossie des alertes', + 'suit l’activité du réservoir', 'enregistre les pleins et les retraits', + 'simulation et reconstitution de trajets' + ], + } + ]; + + produits_nodevis = [...this.allProduitsNodevis]; + produits_avecdevis = [...this.allProduitsAvecDevis]; + + petitsCadres = [ + { label: 'Tous', active: true }, + { label: 'GPS Trackers', active: false }, + { label: 'Solutions IoT', active: false }, + { label: 'Solutions IT', active: false } + ]; + + setActive(label: string) { + this.petitsCadres.forEach(c => c.active = (c.label === label)); + + if (label === 'Tous') { + this.produits_nodevis = [...this.allProduitsNodevis]; + this.produits_avecdevis = [...this.allProduitsAvecDevis]; + } else if (label === 'GPS Trackers') { + this.produits_nodevis = this.allProduitsNodevis.filter(p => p.categorie.toUpperCase().includes('GPS')); + this.produits_avecdevis = []; + } else if (label === 'Solutions IoT') { + this.produits_avecdevis = this.allProduitsAvecDevis.filter(p => p.categorie.toUpperCase().includes('IOT')); + this.produits_nodevis = []; + } else if (label === 'Solutions IT') { + this.produits_avecdevis = []; + this.produits_nodevis = []; + } + } + isSolutionsITSelected(): boolean { + return this.petitsCadres.find(c => c.label === 'Solutions IT')?.active || false; +} + +} + diff --git a/src/app/register/register.component.html b/src/app/register/register.component.html new file mode 100644 index 000000000..7a918323f --- /dev/null +++ b/src/app/register/register.component.html @@ -0,0 +1,62 @@ + + + + + Auth Component + + + + + + +
+
+ + +
+
+
+

Welcome Back!

+

To keep connected with us please login with your personal info

+ +
+
+

Hello, Friend!

+

Enter your personal details and start journey with us

+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/src/app/register/register.component.scss b/src/app/register/register.component.scss new file mode 100644 index 000000000..466cc8a25 --- /dev/null +++ b/src/app/register/register.component.scss @@ -0,0 +1,280 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat:400,800'); + +.auth-component * { + box-sizing: border-box; + +} +.auth-component { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: white; +} + +.auth-box { + width: 1000px; + max-width: 95%; + min-height: 600px; + background-color: #fff; + border-radius: 20px; + box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), + 0 10px 10px rgba(0, 0, 0, 0.22); + display: flex; + position: relative; + overflow: hidden; +} + +.auth-component body { + background:white; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + font-family: 'Montserrat', sans-serif; + height: 200vh; + margin: -20px 0 50px; + +} + +.auth-component h1 { + font-weight: bold; + margin: 0; +} + +.auth-component h2 { + text-align: center; +} + +.auth-component p { + font-size: 14px; + font-weight: 100; + line-height: 20px; + letter-spacing: 0.5px; + margin: 20px 0 30px; +} + +.auth-component span { + font-size: 12px; +} + +.auth-component a { + color: #333; + font-size: 14px; + text-decoration: none; + margin: 15px 0; +} + +.auth-component button { + border-radius: 20px; + border: 1px solid #369; + background-color: #369; + color: #FFFFFF; + font-size: 12px; + font-weight: bold; + padding: 12px 45px; + letter-spacing: 1px; + text-transform: uppercase; + transition: transform 80ms ease-in; +} + +.auth-component .overlay { + background: #38B; + background: -webkit-linear-gradient(to right, #357, #37A); + background: linear-gradient(to right, #357, #37A); + background-repeat: no-repeat; + background-size: cover; + background-position: 0 0; + color: #FFFFFF; + position: relative; + left: -100%; + height: 100%; + width: 200%; + transform: translateX(0); + transition: transform 0.6s ease-in-out; +} + + +.auth-component button:active { + transform: scale(0.95); +} + +.auth-component button:focus { + outline: none; +} + +.auth-component button.ghost { + background-color: transparent; + border-color: #FFFFFF; +} + +.auth-component form { + background-color: #FFFFFF; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 0 50px; + height: 100%; + text-align: center; +} + +.auth-component input { + background-color: #eee; + border: none; + padding: 12px 15px; + margin: 8px 0; + width: 100%; +} + +.auth-component .auth-box { + background-color: #fff; + border-radius: 10px; + box-shadow: 0 14px 28px rgba(0,0,0,0.25), + 0 10px 10px rgba(0,0,0,0.22); + position: relative; + overflow: hidden; + width: 768px; + max-width: 100%; + min-height: 480px; +} + +.auth-component .form-auth-box { + position: absolute; + top: 0; + height: 100%; + transition: all 0.6s ease-in-out; +} + +.auth-component .sign-in-auth-box { + left: 0; + width: 50%; + z-index: 2; +} + +.auth-component .auth-box.right-panel-active .sign-in-auth-box { + transform: translateX(100%); +} + +.auth-component .sign-up-auth-box { + left: 0; + width: 50%; + opacity: 1; + z-index: 1; +} + +.auth-component .auth-box.right-panel-active .sign-up-auth-box { + transform: translateX(100%); + opacity: 1; + z-index: 5; + animation: show 0.6s; +} + +@keyframes show { + 0%, 49.99% { + opacity: 0; + z-index: 1; + } + + 50%, 100% { + opacity: 1; + z-index: 5; + } +} + +.auth-component .overlay-auth-box { + position: absolute; + top: 0; + left: 50%; + width: 50%; + height: 100%; + overflow: hidden; + transition: transform 0.6s ease-in-out; + z-index: 100; +} + +.auth-component .auth-box.right-panel-active .overlay-auth-box{ + transform: translateX(-100%); +} + + +.auth-component .auth-box.right-panel-active .overlay { + transform: translateX(50%); +} + +.auth-component .overlay-panel { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 0 40px; + text-align: center; + top: 0; + height: 100%; + width: 50%; + transform: translateX(0); + transition: transform 0.6s ease-in-out; +} + +.auth-component .overlay-left { + transform: translateX(-20%); +} + +.auth-component .auth-box.right-panel-active .overlay-left { + transform: translateX(0); +} + +.auth-component .overlay-right { + right: 0; + transform: translateX(0); +} + +.auth-component .auth-box.right-panel-active .overlay-right { + transform: translateX(20%); +} + +.auth-component .social-auth-box { + margin: 20px 0; +} + +.auth-component .social-auth-box a { + border: 1px solid #DDDDDD; + border-radius: 50%; + display: inline-flex; + justify-content: center; + align-items: center; + margin: 0 5px; + height: 40px; + width: 40px; +} +.back-arrow { + position: fixed; + top: 60px; + left: 20px; + font-size: 24px; + color: #007bff; + text-decoration: none; + z-index: 1000; + background: rgba(255, 255, 255, 0.8); + padding: 10px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + + @media (max-width: 768px) { + top: 40px; + left: 10px; + font-size: 20px; + width: 35px; + height: 35px; + } + + &:hover { + color: #0056b3; + background: rgba(255, 255, 255, 1); + } +} \ No newline at end of file diff --git a/src/app/register/register.component.spec.ts b/src/app/register/register.component.spec.ts new file mode 100644 index 000000000..f97553378 --- /dev/null +++ b/src/app/register/register.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterComponent } from './register.component'; + +describe('RegisterComponent', () => { + let component: RegisterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RegisterComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/register/register.component.ts b/src/app/register/register.component.ts new file mode 100644 index 000000000..5a99f15e9 --- /dev/null +++ b/src/app/register/register.component.ts @@ -0,0 +1,30 @@ +import { Component, OnInit, AfterViewInit } from '@angular/core'; + +@Component({ + selector: 'app-register', + templateUrl: './register.component.html', + styleUrls: ['./register.component.scss'] +}) +export class RegisterComponent implements OnInit, AfterViewInit { + + constructor() { } + + ngOnInit(): void { + } + + ngAfterViewInit(): void { + const signUpButton = document.getElementById('signUp'); + const signInButton = document.getElementById('signIn'); + const container = document.getElementById('auth-box'); + + if (signUpButton && signInButton && container) { + signUpButton.addEventListener('click', () => { + container.classList.add("right-panel-active"); + }); + + signInButton.addEventListener('click', () => { + container.classList.remove("right-panel-active"); + }); + } + } +} diff --git a/src/app/shared/navbar/navbar.module.ts b/src/app/shared/navbar/navbar.module.ts index 0a76b80ed..375b2849c 100644 --- a/src/app/shared/navbar/navbar.module.ts +++ b/src/app/shared/navbar/navbar.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { NavbarComponent } from './navbar.component'; - @NgModule({ imports: [ RouterModule, CommonModule ], declarations: [ NavbarComponent ], diff --git a/src/app/sidebar/sidebar.component.html b/src/app/sidebar/sidebar.component.html index 817b194c6..18fdf2204 100644 --- a/src/app/sidebar/sidebar.component.html +++ b/src/app/sidebar/sidebar.component.html @@ -1,78 +1,14 @@ +