first commit
This commit is contained in:
BIN
assets/.DS_Store
vendored
Normal file
BIN
assets/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
assets/favicon.png
Normal file
BIN
assets/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
BIN
assets/icons/.DS_Store
vendored
Normal file
BIN
assets/icons/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
assets/icons/loading-animation.png
Normal file
BIN
assets/icons/loading-animation.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
BIN
assets/images/.DS_Store
vendored
Normal file
BIN
assets/images/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
assets/images/logo.png
Normal file
BIN
assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
BIN
assets/images/logo_aquila_soft_small.png
Normal file
BIN
assets/images/logo_aquila_soft_small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
469
home.py
Normal file
469
home.py
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
import flet as ft
|
||||||
|
from mail import send_gmail
|
||||||
|
|
||||||
|
class Home:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
|
||||||
|
self.web_apps_section = ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
controls=[
|
||||||
|
ft.Icon(ft.icons.WEB, size=34),
|
||||||
|
ft.Text("Aplicații web", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(
|
||||||
|
"Dashboard‑uri, aplicații interne, panouri de administrare și soluții de prezentare pentru afacerea ta.",
|
||||||
|
size=14,
|
||||||
|
color=ft.colors.BLUE_GREY_700,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=10,
|
||||||
|
),
|
||||||
|
padding=20,
|
||||||
|
border_radius=16,
|
||||||
|
bgcolor=ft.colors.WHITE,
|
||||||
|
shadow=ft.BoxShadow(
|
||||||
|
blur_radius=16,
|
||||||
|
spread_radius=1,
|
||||||
|
color=ft.colors.with_opacity(0.08, ft.colors.BLUE_GREY_900),
|
||||||
|
),
|
||||||
|
width=300
|
||||||
|
)
|
||||||
|
self.mobile_apps_section = ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
controls=[
|
||||||
|
ft.Icon(ft.icons.PHONE_ANDROID, size=34),
|
||||||
|
ft.Text("Aplicații mobile", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(
|
||||||
|
"Aplicații Android și iOS pentru clienți sau angajați, sincronizate cu serverul tău.",
|
||||||
|
size=14,
|
||||||
|
color=ft.colors.BLUE_GREY_700,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=10,
|
||||||
|
),
|
||||||
|
padding=20,
|
||||||
|
border_radius=16,
|
||||||
|
bgcolor=ft.colors.WHITE,
|
||||||
|
shadow=ft.BoxShadow(
|
||||||
|
blur_radius=16,
|
||||||
|
spread_radius=1,
|
||||||
|
color=ft.colors.with_opacity(0.08, ft.colors.BLUE_GREY_900),
|
||||||
|
),
|
||||||
|
width=300
|
||||||
|
)
|
||||||
|
|
||||||
|
self.api_section = ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
controls=[
|
||||||
|
ft.Icon(ft.icons.INTEGRATION_INSTRUCTIONS, size=34),
|
||||||
|
ft.Text("Integrare & automatizare", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(
|
||||||
|
"Integrare cu API‑uri de plăți, curieri, facturare, notificări și alte servicii esențiale.",
|
||||||
|
size=14,
|
||||||
|
color=ft.colors.BLUE_GREY_700,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=10,
|
||||||
|
),
|
||||||
|
padding=20,
|
||||||
|
border_radius=16,
|
||||||
|
bgcolor=ft.colors.WHITE,
|
||||||
|
shadow=ft.BoxShadow(
|
||||||
|
blur_radius=16,
|
||||||
|
spread_radius=1,
|
||||||
|
color=ft.colors.with_opacity(0.08, ft.colors.BLUE_GREY_900),
|
||||||
|
),
|
||||||
|
width=300
|
||||||
|
)
|
||||||
|
self.page.on_resize = self.on_resize
|
||||||
|
|
||||||
|
self.error_message = ft.Text()
|
||||||
|
|
||||||
|
self.name = ft.TextField(
|
||||||
|
label="Nume",
|
||||||
|
col=6,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.email = ft.TextField(
|
||||||
|
label="Adresă de email",
|
||||||
|
col=6,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.message = ft.TextField(
|
||||||
|
label="Mesaj",
|
||||||
|
multiline=True,
|
||||||
|
min_lines=3,
|
||||||
|
max_lines=5,
|
||||||
|
col=12,
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_resize(self, e):
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def _hero_section(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
"AquilaSoft",
|
||||||
|
size=42,
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
color=ft.colors.BLUE_GREY_900,
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
controls=[
|
||||||
|
ft.ElevatedButton(
|
||||||
|
"Vezi serviciile",
|
||||||
|
icon=ft.icons.ROCKET_LAUNCH_OUTLINED,
|
||||||
|
on_click=lambda e: self.page.scroll_to(key="services_section", duration=500),
|
||||||
|
),
|
||||||
|
ft.OutlinedButton(
|
||||||
|
"Contactează-ne",
|
||||||
|
icon=ft.icons.EMAIL_OUTLINED,
|
||||||
|
on_click=lambda e: self.page.scroll_to(key="contact_section", duration=500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=10,
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
"Aplicații custom, integrate, construite cu Python, Flutter și tehnologii moderne.",
|
||||||
|
size=14,
|
||||||
|
color=ft.colors.BLUE_GREY_500,
|
||||||
|
),
|
||||||
|
], alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Image(
|
||||||
|
src="images/logo.png",
|
||||||
|
width=220,
|
||||||
|
fit=ft.ImageFit.CONTAIN,
|
||||||
|
),
|
||||||
|
padding=20,
|
||||||
|
border_radius=20,
|
||||||
|
bgcolor=ft.colors.WHITE,
|
||||||
|
shadow=ft.BoxShadow(
|
||||||
|
blur_radius=25,
|
||||||
|
spread_radius=1,
|
||||||
|
color=ft.colors.with_opacity(0.15, ft.colors.BLUE_GREY_900),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
if self.page.width > 900 else ft.Column(
|
||||||
|
[
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Image(
|
||||||
|
src="images/logo.png",
|
||||||
|
width=220,
|
||||||
|
fit=ft.ImageFit.CONTAIN,
|
||||||
|
),
|
||||||
|
padding=20,
|
||||||
|
border_radius=20,
|
||||||
|
bgcolor=ft.colors.WHITE,
|
||||||
|
shadow=ft.BoxShadow(
|
||||||
|
blur_radius=25,
|
||||||
|
spread_radius=1,
|
||||||
|
color=ft.colors.with_opacity(0.15, ft.colors.BLUE_GREY_900),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
controls=[
|
||||||
|
ft.ElevatedButton(
|
||||||
|
"Vezi serviciile",
|
||||||
|
icon=ft.icons.ROCKET_LAUNCH_OUTLINED,
|
||||||
|
on_click=lambda e: self.page.scroll_to(key="services_section", duration=500),
|
||||||
|
),
|
||||||
|
ft.OutlinedButton(
|
||||||
|
"Contactează-ne",
|
||||||
|
icon=ft.icons.EMAIL_OUTLINED,
|
||||||
|
on_click=lambda e: self.page.scroll_to(key="contact_section", duration=500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=10,
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
"Aplicații custom, integrate, construite cu Python, Flutter și tehnologii moderne."
|
||||||
|
if self.page.width > 500 else "Aplicații custom, integrate, \nconstruite cu Python, \nFlutter și tehnologii moderne.",
|
||||||
|
size=14,
|
||||||
|
color=ft.colors.BLUE_GREY_500,
|
||||||
|
text_align=ft.TextAlign.CENTER,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
bgcolor=ft.Colors.WHITE,
|
||||||
|
padding=10
|
||||||
|
)
|
||||||
|
|
||||||
|
def _section_title(self, title: str, subtitle: str | None = None) -> ft.Control:
|
||||||
|
return ft.Column(
|
||||||
|
controls=[
|
||||||
|
ft.Text(title, size=28, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(subtitle, size=15, color=ft.colors.BLUE_GREY_900, text_align=ft.TextAlign.CENTER) if subtitle else ft.Container(),
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
spacing=5,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _about_section(self) -> ft.Control:
|
||||||
|
return ft.Row(
|
||||||
|
[
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
controls=[
|
||||||
|
self._section_title(
|
||||||
|
"Cine este AquilaSoft?",
|
||||||
|
"Suntem un studio de software, cu accent pe calitate, simplitate și soluții gândite pentru oameni ocupați.",
|
||||||
|
),
|
||||||
|
ft.Text(
|
||||||
|
"AquilaSoft este partenerul tău pentru aplicații personalizate. De la management intern și automatizări, până la prezentări online și module integrate în soluțiile existente, construim software adaptat exact modului în care lucrezi tu.",
|
||||||
|
size=15,
|
||||||
|
text_align=ft.TextAlign.CENTER,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=20,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
),
|
||||||
|
padding=ft.padding.only(top=20, bottom=20),
|
||||||
|
width=900 if self.page.width > 900 else self.page.width-50
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
|
||||||
|
def _services_section(self) -> ft.Control:
|
||||||
|
return ft.Column(
|
||||||
|
controls=[
|
||||||
|
self._section_title("Ce putem face pentru tine?"),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.web_apps_section,
|
||||||
|
self.mobile_apps_section,
|
||||||
|
self.api_section
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=25,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
expand=True,
|
||||||
|
key="services_section",
|
||||||
|
) if self.page.width > 900 else ft.Column(
|
||||||
|
controls=[
|
||||||
|
self._section_title("Ce putem face pentru tine?"),
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
self.web_apps_section,
|
||||||
|
self.mobile_apps_section,
|
||||||
|
self.api_section
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=25,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
expand=True,
|
||||||
|
key="services_section",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _technologies_section(self) -> ft.Control:
|
||||||
|
tech_chips = ["Python","Flask","Flet","Flutter"] if self.page.width > 900 else ["Python","Flask","Flet"]
|
||||||
|
tech_chips2 = ["Docker","PostgreSQL / MariaDB","Linux server"] if self.page.width > 900 else ["Flutter", "Docker"]
|
||||||
|
tech_chips3 = [] if self.page.width > 900 else ["PostgreSQL / MariaDB","Linux server"]
|
||||||
|
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
controls=[
|
||||||
|
self._section_title("Tehnologii", "Construim pe un stack modern, stabil și ușor de întreținut."),
|
||||||
|
ft.Column(
|
||||||
|
controls=[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Chip(label=ft.Text(t)) for t in tech_chips
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Chip(label=ft.Text(t)) for t in tech_chips2
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Chip(label=ft.Text(t)) for t in tech_chips3
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=10,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=20,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
padding=ft.padding.only(top=20, bottom=20),
|
||||||
|
bgcolor=ft.Colors.WHITE,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def _portfolio_teaser_section(self) -> ft.Control:
|
||||||
|
return ft.Row(
|
||||||
|
[
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
controls=[
|
||||||
|
self._section_title("Proiecte & experiență"),
|
||||||
|
ft.Text(
|
||||||
|
"Lucrăm la aplicații de gestiune, programări medicale, management transport și magazine online. "
|
||||||
|
"Site‑ul de prezentare va include în curând studii de caz și exemple concrete.",
|
||||||
|
size=15,
|
||||||
|
text_align=ft.TextAlign.CENTER,
|
||||||
|
color=ft.colors.BLUE_GREY_700,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=15,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
),
|
||||||
|
padding=ft.padding.only(top=20, bottom=20),
|
||||||
|
width=900 if self.page.width > 900 else self.page.width-50,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
|
||||||
|
def _contact_section(self) -> ft.Control:
|
||||||
|
return ft.Row(
|
||||||
|
[
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
controls=[
|
||||||
|
self._section_title("Hai să vorbim"),
|
||||||
|
ft.Text(
|
||||||
|
"Spune-ne pe scurt ce ai nevoie – o aplicație nouă, un modul pentru un sistem existent sau o idee la început de drum.",
|
||||||
|
size=15,
|
||||||
|
text_align=ft.TextAlign.CENTER,
|
||||||
|
),
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.ResponsiveRow(
|
||||||
|
controls=[
|
||||||
|
self.name,
|
||||||
|
self.email,
|
||||||
|
self.message,
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.error_message
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Container(
|
||||||
|
content=ft.ElevatedButton(
|
||||||
|
"Trimite mesajul",
|
||||||
|
icon=ft.icons.SEND,
|
||||||
|
on_click=self.on_send_message_btn_click, # de conectat la backend
|
||||||
|
),
|
||||||
|
alignment=ft.alignment.center,
|
||||||
|
col=12,
|
||||||
|
padding=ft.padding.only(top=10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
columns=12,
|
||||||
|
run_spacing=10,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
spacing=20,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
),
|
||||||
|
padding=ft.padding.only(top=30, bottom=30),
|
||||||
|
width=900 if self.page.width > 900 else self.page.width-50,
|
||||||
|
key="contact_section",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
|
||||||
|
def _footer_section(self) -> ft.Control:
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
controls=[
|
||||||
|
ft.Divider(),
|
||||||
|
ft.Text(
|
||||||
|
"© " + "2026" + " AquilaSoft. Toate drepturile rezervate.",
|
||||||
|
size=12,
|
||||||
|
color=ft.colors.BLUE_GREY_500,
|
||||||
|
text_align=ft.TextAlign.CENTER,
|
||||||
|
),
|
||||||
|
ft.Text(
|
||||||
|
"Creat cu pasiune, Python și un strop de cafea.",
|
||||||
|
size=12,
|
||||||
|
color=ft.colors.BLUE_GREY_400,
|
||||||
|
text_align=ft.TextAlign.CENTER,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
),
|
||||||
|
padding=ft.padding.only(bottom=10, top=-7),
|
||||||
|
bgcolor=ft.Colors.WHITE
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_send_message_btn_click(self, e):
|
||||||
|
name = self.name.value
|
||||||
|
email = self.email.value
|
||||||
|
message = self.message.value
|
||||||
|
found = False
|
||||||
|
if name is None or len(name)< 2:
|
||||||
|
found = True
|
||||||
|
if email is None or len(email)< 2:
|
||||||
|
found = True
|
||||||
|
if message is None or len(message)< 2:
|
||||||
|
found = True
|
||||||
|
if not found:
|
||||||
|
send_gmail('macamete.robert@gmail.com', 'Contact nou pe Aquila Soft', email+'\n'+message)
|
||||||
|
self.error_message.value = "Mesajul a fost trimis cu succes! \nVeți fi contactat în curând de unul dintre colegii noștri."
|
||||||
|
self.error_message.color = ft.Colors.GREEN
|
||||||
|
self.error_message.update()
|
||||||
|
else:
|
||||||
|
self.error_message.value = "Toate campurile sunt obligatori!"
|
||||||
|
self.error_message.color = ft.Colors.RED
|
||||||
|
self.error_message.update()
|
||||||
|
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
self._hero_section(),
|
||||||
|
self._about_section(),
|
||||||
|
self._services_section(),
|
||||||
|
self._technologies_section(),
|
||||||
|
self._portfolio_teaser_section(),
|
||||||
|
self._contact_section(),
|
||||||
|
self._footer_section()
|
||||||
|
],
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
|
expand=True,
|
||||||
|
),
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
139
mail.py
Normal file
139
mail.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import smtplib
|
||||||
|
from email.message import EmailMessage
|
||||||
|
import os
|
||||||
|
|
||||||
|
def send_email(to_email, subject, body):
|
||||||
|
smtp_host = os.environ.get("SMTP_HOST")
|
||||||
|
smtp_port = int(os.environ.get("SMTP_PORT", 587))
|
||||||
|
smtp_user = os.environ.get("SMTP_USER")
|
||||||
|
smtp_pass = os.environ.get("SMTP_PASS")
|
||||||
|
sender_email = os.environ.get("SMTP_FROM", smtp_user)
|
||||||
|
|
||||||
|
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("SMTP config incomplete in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
|
|
||||||
|
|
||||||
|
# Send email with attachment
|
||||||
|
def send_email_with_attachment(to_email, subject, body, attachment_path):
|
||||||
|
smtp_host = os.environ.get("SMTP_HOST")
|
||||||
|
smtp_port = int(os.environ.get("SMTP_PORT", 587))
|
||||||
|
smtp_user = os.environ.get("SMTP_USER")
|
||||||
|
smtp_pass = os.environ.get("SMTP_PASS")
|
||||||
|
sender_email = os.environ.get("SMTP_FROM", smtp_user)
|
||||||
|
|
||||||
|
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("SMTP config incomplete in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
if attachment_path and os.path.isfile(attachment_path):
|
||||||
|
with open(attachment_path, "rb") as f:
|
||||||
|
file_data = f.read()
|
||||||
|
file_name = os.path.basename(attachment_path)
|
||||||
|
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
|
|
||||||
|
|
||||||
|
# Send email using Gmail directly
|
||||||
|
def send_gmail(to_email, subject, body):
|
||||||
|
smtp_host = "smtp.gmail.com"
|
||||||
|
smtp_port = 587
|
||||||
|
smtp_user = 'macamete.robert@gmail.com'
|
||||||
|
smtp_pass = 'advx yqlv jkaa czvr'
|
||||||
|
sender_email = 'macamete.robert@gmail.com'
|
||||||
|
|
||||||
|
if not all([smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
|
|
||||||
|
|
||||||
|
# Send email with attachment using Gmail directly
|
||||||
|
def send_gmail_with_attachment(to_email, subject, body, attachment_path):
|
||||||
|
smtp_host = "smtp.gmail.com"
|
||||||
|
smtp_port = 587
|
||||||
|
smtp_user = 'macamete.robert@gmail.com'
|
||||||
|
smtp_pass = 'advx yqlv jkaa czvr'
|
||||||
|
sender_email = 'macamete.robert@gmail.com'
|
||||||
|
|
||||||
|
if not all([smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
if attachment_path and os.path.isfile(attachment_path):
|
||||||
|
with open(attachment_path, "rb") as f:
|
||||||
|
file_data = f.read()
|
||||||
|
file_name = os.path.basename(attachment_path)
|
||||||
|
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
|
|
||||||
|
# Send email with attachment
|
||||||
|
def send_custom_email_with_attachment(to_email, subject, body, attachment_path, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS):
|
||||||
|
smtp_host = SMTP_HOST
|
||||||
|
smtp_port = int(SMTP_PORT)
|
||||||
|
smtp_user = SMTP_USER
|
||||||
|
smtp_pass = SMTP_PASS
|
||||||
|
sender_email = smtp_user
|
||||||
|
|
||||||
|
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("SMTP config incomplete in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
if attachment_path and os.path.isfile(attachment_path):
|
||||||
|
with open(attachment_path, "rb") as f:
|
||||||
|
file_data = f.read()
|
||||||
|
file_name = os.path.basename(attachment_path)
|
||||||
|
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
25
main.py
Normal file
25
main.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import flet as ft
|
||||||
|
from home import Home
|
||||||
|
|
||||||
|
def main(page: ft.Page):
|
||||||
|
page.title = 'AquilaSoft'
|
||||||
|
page.theme = ft.Theme(color_scheme_seed=ft.Colors.BLUE)
|
||||||
|
|
||||||
|
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
|
||||||
|
page.vertical_alignment = ft.MainAxisAlignment.CENTER
|
||||||
|
page.theme_mode = ft.ThemeMode.LIGHT
|
||||||
|
page.padding = 0
|
||||||
|
|
||||||
|
def route_change(route):
|
||||||
|
page.controls.clear()
|
||||||
|
|
||||||
|
if "/" == route:
|
||||||
|
home = Home(page)
|
||||||
|
page.add(home.build())
|
||||||
|
|
||||||
|
page.update()
|
||||||
|
|
||||||
|
page.on_route_change = lambda _: route_change(page.route)
|
||||||
|
page.go('/')
|
||||||
|
|
||||||
|
ft.app(target=main, assets_dir="assets", view=ft.WEB_BROWSER, port='8550')
|
||||||
Reference in New Issue
Block a user