my-cloud-devops-consulting 5.5.9__tar.gz → 5.6.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {my_cloud_devops_consulting-5.5.9/my_cloud_devops_consulting.egg-info → my_cloud_devops_consulting-5.6.9}/PKG-INFO +1 -1
  2. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/app.py +66 -14
  3. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9/my_cloud_devops_consulting.egg-info}/PKG-INFO +1 -1
  4. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/my_cloud_devops_consulting.egg-info/SOURCES.txt +2 -0
  5. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/setup.py +1 -1
  6. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/css/index.css +0 -44
  7. my_cloud_devops_consulting-5.6.9/static/js/login.js +71 -0
  8. my_cloud_devops_consulting-5.6.9/static/js/main.js +74 -0
  9. my_cloud_devops_consulting-5.6.9/templates/base.html +100 -0
  10. my_cloud_devops_consulting-5.6.9/templates/contact-form.html +101 -0
  11. my_cloud_devops_consulting-5.6.9/templates/index.html +67 -0
  12. my_cloud_devops_consulting-5.6.9/templates/login.html +61 -0
  13. my_cloud_devops_consulting-5.6.9/templates/private-videos.html +20 -0
  14. my_cloud_devops_consulting-5.6.9/templates/register.html +63 -0
  15. my_cloud_devops_consulting-5.6.9/templates/services.html +39 -0
  16. my_cloud_devops_consulting-5.5.9/static/js/login.js +0 -42
  17. my_cloud_devops_consulting-5.5.9/static/js/main.js +0 -74
  18. my_cloud_devops_consulting-5.5.9/templates/contact-form.html +0 -103
  19. my_cloud_devops_consulting-5.5.9/templates/index.html +0 -137
  20. my_cloud_devops_consulting-5.5.9/templates/login.html +0 -72
  21. my_cloud_devops_consulting-5.5.9/templates/register.html +0 -78
  22. my_cloud_devops_consulting-5.5.9/templates/services.html +0 -80
  23. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/MANIFEST.in +0 -0
  24. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/README.md +0 -0
  25. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/my_cloud_devops_consulting.egg-info/dependency_links.txt +0 -0
  26. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/my_cloud_devops_consulting.egg-info/requires.txt +0 -0
  27. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/my_cloud_devops_consulting.egg-info/top_level.txt +0 -0
  28. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/setup.cfg +0 -0
  29. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/css/contact-form.css +0 -0
  30. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/css/login.css +0 -0
  31. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/css/main.css +0 -0
  32. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/css/register.css +0 -0
  33. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/css/services.css +0 -0
  34. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/images/image.jpg +0 -0
  35. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/js/index.js +0 -0
  36. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/js/register.js +0 -0
  37. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/static/js/services.js +0 -0
  38. {my_cloud_devops_consulting-5.5.9 → my_cloud_devops_consulting-5.6.9}/tests/test_unit_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: my-cloud-devops-consulting
3
- Version: 5.5.9
3
+ Version: 5.6.9
4
4
  Summary: This is my consulting website for Cloud & DevOps services.
5
5
  Home-page: https://github.com/Betrand1999/project-root
6
6
  Author: Betrand Mutagha
@@ -1,19 +1,41 @@
1
- from flask import Flask, render_template, request, redirect, url_for, session, flash
1
+ from flask import Flask, render_template, request, redirect, url_for, session, flash,jsonify
2
2
  from pymongo import MongoClient
3
3
  from werkzeug.security import generate_password_hash, check_password_hash
4
4
  from urllib.parse import quote_plus
5
- import os
5
+ from bson.objectid import ObjectId
6
6
 
7
+ import os
8
+ from flask_login import LoginManager,login_user,UserMixin,login_required,logout_user,current_user
9
+ from settings import SECRET_KEY,MONGO_URI, EMAIL_USER
10
+ from utils import send_email, get_videos
7
11
  app = Flask(__name__)
8
- app.secret_key = os.environ.get('SECRET_KEY', 'your_secret_key') # Secure your secret key with an environment variable
12
+ app.secret_key = SECRET_KEY # Secure your secret key with an environment variable
13
+ login_manager = LoginManager()
14
+
15
+ login_manager.init_app(app)
9
16
 
10
- # MongoDB Configuration with properly escaped credentials
11
- username = quote_plus("betrand1999") # add username and pst
12
- password = quote_plus("Cameroon@10K")
13
- client = MongoClient(f"mongodb+srv://{username}:{password}@cluster.7plpy.mongodb.net/my-database?retryWrites=true&w=majority")
17
+
18
+ client = MongoClient(MONGO_URI)
14
19
  db = client['my-database'] # Specify your database as shown in the MongoDB Atlas interface
15
20
  users_collection = db['inventory_collection'] # Collection for storing user data
16
21
 
22
+
23
+
24
+ class User(UserMixin):
25
+ def __init__(self, user_id, username):
26
+ self.id = str(user_id)
27
+ self.username = username
28
+
29
+ @staticmethod
30
+ def get(user_id):
31
+ user = users_collection.find_one({"_id": ObjectId(user_id)})
32
+ if user:
33
+ return User(user["_id"], user["username"])
34
+ return None
35
+ @login_manager.user_loader
36
+ def load_user(user_id):
37
+ return User.get(user_id)
38
+
17
39
  @app.route('/')
18
40
  def home():
19
41
  video_url = None
@@ -23,21 +45,28 @@ def home():
23
45
 
24
46
  @app.route('/login', methods=['GET', 'POST'])
25
47
  def login():
48
+ if current_user.is_authenticated:
49
+ return redirect(url_for('home'))
26
50
  if request.method == 'POST':
27
51
  username = request.form['username']
28
52
  password = request.form['password']
53
+
29
54
  user = users_collection.find_one({'username': username})
55
+ print(user)
30
56
  if user and check_password_hash(user['password'], password):
31
- session['username'] = username
32
- flash('Login successful', 'success')
33
- return redirect(url_for('home'))
57
+ user_obj =User(user["_id"], user["username"])
58
+ login_user(user_obj)
59
+ # return redirect(url_for('home'))
60
+ return jsonify({"message":"Login successful", "status": 200})
34
61
  else:
35
- flash('Invalid username or password', 'error')
36
- return redirect(url_for('login'))
62
+ # return redirect(url_for('login'))
63
+ return jsonify({"message":"Invalid username or password", "status": 400})
37
64
  return render_template('login.html')
38
65
 
39
66
  @app.route('/register', methods=['GET', 'POST'])
40
67
  def register():
68
+ if current_user.is_authenticated:
69
+ return redirect(url_for('home'))
41
70
  if request.method == 'POST':
42
71
  username = request.form['username']
43
72
  password = request.form['password']
@@ -79,14 +108,37 @@ def contact_form():
79
108
  'appointment': appointment,
80
109
  'message': message
81
110
  }
111
+ print("Contact Data: ", contact_data)
82
112
  db.contacts.insert_one(contact_data)
113
+
114
+ send_email('New Contact Form Submission', EMAIL_USER, f'Name: {name}\nEmail: {email}\nPhone: {phone}\nCategory: {category}\nAppointment: {appointment}\nMessage: {message}')
115
+ send_email('Thanks for contacting', email, "We will get back to you soon!")
116
+
117
+
83
118
 
84
119
  # Remove the email or SMS notification logic here
85
120
  # Simply flash a success message
86
121
  flash('Your message has been submitted successfully!', 'success')
87
122
 
88
- return redirect(url_for('home'))
123
+ return redirect(url_for('contact_form'))
89
124
  return render_template('contact-form.html')
90
125
 
126
+
127
+ @app.route('/videos')
128
+ # @login_required
129
+ def private_videos():
130
+ if not current_user.is_authenticated:
131
+ return redirect(url_for('login'))
132
+ videos = get_videos()
133
+ return render_template('private-videos.html', videos=videos)
134
+ @app.route("/logout")
135
+ @login_required
136
+ def logout():
137
+ logout_user()
138
+ flash("Logged out successfully!", "info")
139
+ return redirect(url_for("login"))
91
140
  if __name__ == '__main__':
92
- app.run(debug=True, host='0.0.0.0', port=50)
141
+ app.run(debug=True, host='0.0.0.0', port=80)
142
+
143
+
144
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: my-cloud-devops-consulting
3
- Version: 5.5.9
3
+ Version: 5.6.9
4
4
  Summary: This is my consulting website for Cloud & DevOps services.
5
5
  Home-page: https://github.com/Betrand1999/project-root
6
6
  Author: Betrand Mutagha
@@ -19,9 +19,11 @@ static/js/login.js
19
19
  static/js/main.js
20
20
  static/js/register.js
21
21
  static/js/services.js
22
+ templates/base.html
22
23
  templates/contact-form.html
23
24
  templates/index.html
24
25
  templates/login.html
26
+ templates/private-videos.html
25
27
  templates/register.html
26
28
  templates/services.html
27
29
  tests/test_unit_test.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='my-cloud-devops-consulting',
5
- version='5.5.9',
5
+ version='5.6.9',
6
6
  author='Betrand Mutagha',
7
7
  author_email='mmutagha@gmail.com',
8
8
  description='This is my consulting website for Cloud & DevOps services.',
@@ -259,47 +259,3 @@ nav ul.visible {
259
259
  .scroll-to-top:hover {
260
260
  background: #e07a35;
261
261
  }
262
-
263
- /* Styling for the navigation toggle button (visible on small screens) */
264
- .nav-toggle {
265
- display: none;
266
- background: #17a2b8;
267
- color: white;
268
- border: none;
269
- padding: 10px;
270
- cursor: pointer;
271
- margin: 10px 0;
272
- }
273
-
274
- nav ul.visible {
275
- display: block;
276
- }
277
-
278
- @media (max-width: 768px) {
279
- .nav-toggle {
280
- display: block;
281
- }
282
-
283
- nav ul {
284
- display: none;
285
- flex-direction: column;
286
- }
287
- }
288
-
289
- /* Styling for the scroll-to-top button */
290
- .scroll-to-top {
291
- position: fixed;
292
- bottom: 20px;
293
- right: 20px;
294
- padding: 10px 15px;
295
- background: #ff8c42;
296
- color: white;
297
- border: none;
298
- border-radius: 5px;
299
- display: none;
300
- cursor: pointer;
301
- }
302
-
303
- .scroll-to-top:hover {
304
- background: #e07a35;
305
- }
@@ -0,0 +1,71 @@
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ const loginForm = document.getElementById('loginForm');
3
+
4
+ if (loginForm) {
5
+ loginForm.addEventListener('submit', function(event) {
6
+ event.preventDefault(); // Prevent default form submission
7
+
8
+ // Get form values
9
+ const username = document.getElementById('username').value.trim();
10
+ const password = document.getElementById('password').value.trim();
11
+
12
+ // Basic input validation
13
+ if (!username || !password) {
14
+ showAlert('Please fill out all fields.', 'danger');
15
+ return;
16
+ }
17
+
18
+ // Successful validation message
19
+ // showAlert(`Logging in with username: ${username}`, 'success');
20
+
21
+ // TODO: Implement form submission logic (e.g., AJAX request)
22
+ // Create an object with the form data
23
+ let formData = new FormData();
24
+ formData.append('username', username);
25
+ formData.append('password', password);
26
+
27
+ // Send the form data to the server using fetch
28
+ fetch('/login', {
29
+ method: 'POST',
30
+ body: formData
31
+ })
32
+ .then(response => response.json())
33
+ .then(data => {
34
+ if (data.status=="200") {
35
+ showAlert('Login successful!', 'success');
36
+ // Redirect to another page or perform other actions on success
37
+ window.location.href = '/';
38
+ } else {
39
+ showAlert(data.message || 'Login failed. Please try again.', 'danger');
40
+ }
41
+ })
42
+ .catch(error => {
43
+ console.error('Error:', error);
44
+ showAlert('An error occurred. Please try again later.', 'danger');
45
+ });
46
+
47
+ });
48
+ }
49
+
50
+ // Function to show alert messages
51
+ function showAlert(message, type) {
52
+ const existingAlert = document.querySelector('.alert');
53
+ if (existingAlert) existingAlert.remove(); // Remove existing alerts
54
+
55
+ const alert = document.createElement('div');
56
+ alert.className = `alert alert-${type} alert-dismissible fade show`;
57
+ alert.role = 'alert';
58
+ alert.innerHTML = `
59
+ ${message}
60
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
61
+ `;
62
+
63
+ // Insert alert message above the form
64
+ loginForm.parentNode.insertBefore(alert, loginForm);
65
+
66
+ // Automatically remove alert after 3 seconds
67
+ setTimeout(() => {
68
+ if (alert) alert.remove();
69
+ }, 3000);
70
+ }
71
+ });
@@ -0,0 +1,74 @@
1
+ // document.addEventListener("DOMContentLoaded", () => {
2
+ // // Smooth scrolling for all internal links
3
+ // const smoothScrollLinks = document.querySelectorAll("a[href^='#']");
4
+ // smoothScrollLinks.forEach(link => {
5
+ // link.addEventListener("click", (event) => {
6
+ // event.preventDefault();
7
+ // const targetId = link.getAttribute("href").substring(1);
8
+ // const targetElement = document.getElementById(targetId);
9
+ // if (targetElement) {
10
+ // targetElement.scrollIntoView({ behavior: "smooth" });
11
+ // }
12
+ // });
13
+ // });
14
+
15
+ // // Back to top button functionality
16
+ // const backToTopButton = document.createElement("button");
17
+ // backToTopButton.innerText = "↑ Top";
18
+ // backToTopButton.className = "scroll-to-top";
19
+ // document.body.appendChild(backToTopButton);
20
+
21
+ // backToTopButton.style.display = "none";
22
+ // backToTopButton.addEventListener("click", () => {
23
+ // window.scrollTo({ top: 0, behavior: "smooth" });
24
+ // });
25
+
26
+ // window.addEventListener("scroll", () => {
27
+ // backToTopButton.style.display = window.pageYOffset > 300 ? "block" : "none";
28
+ // });
29
+
30
+ // // Modular function to reveal elements on scroll
31
+ // const revealOnScroll = () => {
32
+ // const sections = document.querySelectorAll("section");
33
+ // const viewportHeight = window.innerHeight;
34
+ // sections.forEach(section => {
35
+ // const sectionTop = section.getBoundingClientRect().top;
36
+ // if (sectionTop < viewportHeight - 100) {
37
+ // section.classList.add("visible");
38
+ // }
39
+ // });
40
+ // };
41
+ // window.addEventListener("scroll", revealOnScroll);
42
+
43
+ // // Inject dynamic styles for section reveal effect
44
+ // const styleSheet = document.createElement("style");
45
+ // // styleSheet.type = "text/css"; // Deprecated, no longer needed
46
+ // styleSheet.innerText = `
47
+ // section {
48
+ // opacity: 0;
49
+ // transform: translateY(20px);
50
+ // transition: opacity 0.5s ease, transform 0.5s ease;
51
+ // }
52
+ // section.visible {
53
+ // opacity: 1;
54
+ // transform: translateY(0);
55
+ // }
56
+ // .scroll-to-top {
57
+ // position: fixed;
58
+ // bottom: 20px;
59
+ // right: 20px;
60
+ // padding: 10px 15px;
61
+ // background: #007bff;
62
+ // color: white;
63
+ // border: none;
64
+ // border-radius: 5px;
65
+ // display: none;
66
+ // cursor: pointer;
67
+ // }
68
+ // .scroll-to-top:hover {
69
+ // background: #0056b3;
70
+ // }
71
+ // `;
72
+ // document.head.appendChild(styleSheet);
73
+ // });
74
+
@@ -0,0 +1,100 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Consulting Homepage</title>
7
+ <link
8
+ rel="stylesheet"
9
+ href="{{ url_for('static', filename='css/main.css') }}"
10
+ />
11
+ <link
12
+ rel="stylesheet"
13
+ href="{{ url_for('static', filename='css/index.css') }}"
14
+ />
15
+ <style>
16
+ /* CSS for Moving Image */
17
+ .moving-image-container {
18
+ overflow: hidden;
19
+ position: relative;
20
+ width: 100%;
21
+ height: 300px; /* Adjust height as needed */
22
+ margin: 20px 0;
23
+ }
24
+
25
+ .moving-image {
26
+ position: absolute;
27
+ width: 1400px; /* Adjust the size as needed */
28
+ height: auto;
29
+ animation: moveImage 30s linear infinite;
30
+ }
31
+
32
+ @keyframes moveImage {
33
+ 0% {
34
+ left: -150px; /* Start off-screen (left) */
35
+ }
36
+ 50% {
37
+ left: 50%; /* Move to the center */
38
+ transform: translateX(-50%);
39
+ }
40
+ 100% {
41
+ left: 100%; /* Move off-screen (right) */
42
+ }
43
+ }
44
+ </style>
45
+ <link
46
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
47
+ rel="stylesheet"
48
+ integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
49
+ crossorigin="anonymous"
50
+ />
51
+ </head>
52
+ <body>
53
+ <header>
54
+ <div class="header-container">
55
+ <a href="{{ url_for('home') }}" style="text-decoration: none;">
56
+
57
+ <div class="logo" >
58
+ C&DC <small>Cloud & DevOps Consulting L.L.C</small>
59
+ </div>
60
+
61
+ </a>
62
+
63
+
64
+ <nav aria-label="Main Navigation">
65
+ <ul>
66
+ <li><a href="{{ url_for('home') }}">Home</a></li>
67
+ <li><a href="{{ url_for('services') }}">Services</a></li>
68
+ <li><a href="{{ url_for('contact_form') }}">Contact Form</a></li>
69
+ {% if current_user.is_authenticated %}
70
+ <li><a href="{{ url_for('private_videos') }}">Videos</a></li>
71
+ <li><a href="{{ url_for('logout') }}">Logout</a></li>
72
+ {% else %}
73
+
74
+ <li><a href="{{ url_for('register') }}">Register</a></li>
75
+ <li><a href="{{ url_for('login') }}">Login</a></li>
76
+ {% endif %}
77
+ </ul>
78
+ </nav>
79
+ </div>
80
+ </header>
81
+
82
+ {% block main %}{% endblock main %}
83
+
84
+ <footer>
85
+ <div class="contact-info">
86
+ <p>
87
+ Phone: 240 298 1823 📞🔵 answer | Email 📩 :
88
+ <a href="mailto:mmutagha@gmail.com">mmutagha@gmail.com</a>
89
+ </p>
90
+ </div>
91
+ <p>&copy; 2025 Cloud & DevOps Consulting. All rights reserved.</p>
92
+ </footer>
93
+ <script
94
+ src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
95
+ integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
96
+ crossorigin="anonymous"
97
+ ></script>
98
+ <script src="{{ url_for('static', filename='js/main.js') }}"></script>
99
+ </body>
100
+ </html>
@@ -0,0 +1,101 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block main %}
4
+ <main class="container mt-5">
5
+ <div class="row justify-content-center my-5" style="width: 100%;">
6
+ <div class="col-md-6 mx-auto">
7
+
8
+ <!-- Flash messages for feedback -->
9
+ {% with messages = get_flashed_messages(with_categories=true) %}
10
+ {% if messages %}
11
+ <div class="alert alert-dismissible fade show" role="alert">
12
+ {% for category, message in messages %}
13
+ <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
14
+ {{ message }}
15
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
16
+ </div>
17
+ {% endfor %}
18
+ </div>
19
+ {% endif %}
20
+ {% endwith %}
21
+ <h2>Get in Touch with Us</h2>
22
+
23
+ <form method="POST" action="{{ url_for('contact_form') }}" enctype="multipart/form-data">
24
+ <div class="mb-3">
25
+ <label for="name" class="form-label">Name:</label>
26
+ <input
27
+ type="text"
28
+ class="form-control"
29
+ id="name"
30
+ name="name"
31
+ placeholder="Enter your full name"
32
+ required
33
+ />
34
+ </div>
35
+
36
+ <div class="mb-3">
37
+ <label for="email" class="form-label">Email:</label>
38
+ <input
39
+ type="email"
40
+ class="form-control"
41
+ id="email"
42
+ name="email"
43
+ placeholder="Enter your email address"
44
+ required
45
+ />
46
+ </div>
47
+
48
+ <div class="mb-3">
49
+ <label for="phone" class="form-label">Phone Number:</label>
50
+ <input
51
+ type="tel"
52
+ class="form-control"
53
+ id="phone"
54
+ name="phone"
55
+ placeholder="Enter your phone number"
56
+ />
57
+ </div>
58
+
59
+ <div class="mb-3">
60
+ <label for="category" class="form-label">Category:</label>
61
+ <select id="category" name="category" class="form-select">
62
+ <option value="general">General Inquiry</option>
63
+ <option value="service">Service Request</option>
64
+ <option value="feedback">Feedback</option>
65
+ <option value="support">Technical Support</option>
66
+ </select>
67
+ </div>
68
+
69
+ <div class="mb-3">
70
+ <label for="appointment" class="form-label">Preferred Appointment Date and Time:</label>
71
+ <input type="datetime-local" class="form-control" id="appointment" name="appointment" />
72
+ </div>
73
+
74
+ <div class="mb-3">
75
+ <label for="file" class="form-label">Upload a File (if applicable):</label>
76
+ <input type="file" class="form-control" id="file" name="file" />
77
+ </div>
78
+
79
+ <div class="mb-3">
80
+ <label for="message" class="form-label">Message:</label>
81
+ <textarea
82
+ id="message"
83
+ class="form-control"
84
+ name="message"
85
+ rows="4"
86
+ placeholder="Write your message here"
87
+ required
88
+ ></textarea>
89
+ </div>
90
+
91
+ <div class="mb-3">
92
+ <input type="submit" class="btn btn-primary" value="Submit" />
93
+ </div>
94
+ </form>
95
+
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </main>
100
+ {% endblock main %}
101
+
@@ -0,0 +1,67 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block main %}
4
+
5
+ <main>
6
+ <!-- Hero Section -->
7
+ <section class="hero">
8
+ <h2>Empowering Your Cloud & DevOps Journey</h2>
9
+ <p>
10
+ We specialize in delivering Cloud and DevOps consulting services
11
+ tailored to meet your unique business needs.
12
+ </p>
13
+ <button onclick="window.location.href='{{ url_for('services') }}'">
14
+ Explore Services
15
+ </button>
16
+ </section>
17
+
18
+ <!-- Moving Image Section -->
19
+ <section class="moving-image-container">
20
+ <img
21
+ src="{{ url_for('static', filename='images/image.jpg') }}"
22
+ alt="Moving Image"
23
+ class="moving-image"
24
+ />
25
+ </section>
26
+
27
+ <!-- Services Highlights -->
28
+ <section class="services-highlight">
29
+ <h3>Our Key Services</h3>
30
+ <div class="service-cards">
31
+ <div class="card">
32
+ <h4>Cloud Consulting</h4>
33
+ <p>
34
+ Cloud migration, strategy, and optimization tailored for your
35
+ business.
36
+ </p>
37
+ <a href="{{ url_for('services') }}" class="btn-link">Learn More</a>
38
+ </div>
39
+ <div class="card">
40
+ <h4>DevOps Solutions</h4>
41
+ <p>
42
+ CI/CD pipelines, automated infrastructure, and efficient delivery.
43
+ </p>
44
+ <a href="{{ url_for('services') }}" class="btn-link">Learn More</a>
45
+ </div>
46
+ <div class="card">
47
+ <h4>Security & Compliance</h4>
48
+ <p>
49
+ Ensure security and compliance across your cloud and on-premise
50
+ environments.
51
+ </p>
52
+ <a href="{{ url_for('services') }}" class="btn-link">Learn More</a>
53
+ </div>
54
+ </div>
55
+ </section>
56
+
57
+ <!-- Quick Links Section -->
58
+ <section class="quick-links">
59
+ <h3>Quick Links</h3>
60
+ <ul>
61
+ <li><a href="{{ url_for('services') }}">View Our Services</a></li>
62
+ <li><a href="{{ url_for('contact_form') }}">Contact Form</a></li>
63
+ </ul>
64
+ </section>
65
+ </main>
66
+
67
+ {% endblock main %}