stuff
This commit is contained in:
11
widgets/__init__.py
Normal file
11
widgets/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from .host_item import HostItem
|
||||
from .location_card import ActiveLocationCard, InactiveLocationCard
|
||||
from .customer_card import ActiveCustomerCard, InactiveCustomerCard
|
||||
|
||||
__all__ = [
|
||||
'HostItem',
|
||||
'ActiveLocationCard',
|
||||
'InactiveLocationCard',
|
||||
'ActiveCustomerCard',
|
||||
'InactiveCustomerCard'
|
||||
]
|
||||
74
widgets/customer_card.py
Normal file
74
widgets/customer_card.py
Normal file
@@ -0,0 +1,74 @@
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from .location_card import ActiveLocationCard, InactiveLocationCard
|
||||
|
||||
|
||||
class ActiveCustomerCard:
|
||||
def __init__(self, customer, callbacks):
|
||||
self.customer = customer
|
||||
self.callbacks = callbacks
|
||||
self.widget = self._create_widget()
|
||||
|
||||
def _create_widget(self):
|
||||
# GNOME-style card container
|
||||
card_frame = Gtk.Frame()
|
||||
card_frame.get_style_context().add_class("card")
|
||||
card_frame.set_shadow_type(Gtk.ShadowType.NONE) # Shadow handled by CSS
|
||||
|
||||
card_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
|
||||
card_frame.add(card_vbox)
|
||||
|
||||
# Customer header
|
||||
customer_label = Gtk.Label()
|
||||
customer_label.set_markup(f"<b><big>🏢 {self.customer.name}</big></b>")
|
||||
customer_label.set_halign(Gtk.Align.START)
|
||||
card_vbox.pack_start(customer_label, False, False, 0)
|
||||
|
||||
# Locations section
|
||||
for i, location in enumerate(self.customer.locations):
|
||||
if i > 0: # Add separator between locations
|
||||
separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
separator.set_margin_top(8)
|
||||
separator.set_margin_bottom(8)
|
||||
card_vbox.pack_start(separator, False, False, 0)
|
||||
|
||||
location_card = ActiveLocationCard(location, self.customer.name, self.callbacks)
|
||||
card_vbox.pack_start(location_card.widget, False, False, 0)
|
||||
|
||||
return card_frame
|
||||
|
||||
|
||||
class InactiveCustomerCard:
|
||||
def __init__(self, customer, callbacks):
|
||||
self.customer = customer
|
||||
self.callbacks = callbacks
|
||||
self.widget = self._create_widget()
|
||||
|
||||
def _create_widget(self):
|
||||
# GNOME-style card container
|
||||
card_frame = Gtk.Frame()
|
||||
card_frame.get_style_context().add_class("card")
|
||||
card_frame.set_shadow_type(Gtk.ShadowType.NONE) # Shadow handled by CSS
|
||||
|
||||
card_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
|
||||
card_frame.add(card_vbox)
|
||||
|
||||
# Customer header - muted
|
||||
customer_label = Gtk.Label()
|
||||
customer_label.set_markup(f"<span alpha='60%'><b><big>🏢 {self.customer.name}</big></b></span>")
|
||||
customer_label.set_halign(Gtk.Align.START)
|
||||
card_vbox.pack_start(customer_label, False, False, 0)
|
||||
|
||||
# Locations section
|
||||
for i, location in enumerate(self.customer.locations):
|
||||
if i > 0: # Add separator between locations
|
||||
separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
separator.set_margin_top(8)
|
||||
separator.set_margin_bottom(8)
|
||||
card_vbox.pack_start(separator, False, False, 0)
|
||||
|
||||
location_card = InactiveLocationCard(location, self.customer.name, self.callbacks)
|
||||
card_vbox.pack_start(location_card.widget, False, False, 0)
|
||||
|
||||
return card_frame
|
||||
81
widgets/host_item.py
Normal file
81
widgets/host_item.py
Normal file
@@ -0,0 +1,81 @@
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from models import ServiceType, HostType
|
||||
|
||||
|
||||
class HostItem:
|
||||
def __init__(self, host, open_service_callback):
|
||||
self.host = host
|
||||
self.open_service_callback = open_service_callback
|
||||
self.widget = self._create_widget()
|
||||
|
||||
def _create_widget(self):
|
||||
# Clean horizontal layout without borders
|
||||
host_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
|
||||
|
||||
# Host header
|
||||
host_header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
|
||||
host_box.pack_start(host_header, False, False, 0)
|
||||
|
||||
# Host type icon
|
||||
type_icons = {
|
||||
HostType.LINUX: "🐧",
|
||||
HostType.WINDOWS: "🪟",
|
||||
HostType.WINDOWS_SERVER: "🖥️",
|
||||
HostType.PROXMOX: "📦",
|
||||
HostType.ESXI: "📦",
|
||||
HostType.ROUTER: "🌐",
|
||||
HostType.SWITCH: "🔗"
|
||||
}
|
||||
icon = type_icons.get(self.host.host_type, "💻")
|
||||
|
||||
icon_label = Gtk.Label(label=icon)
|
||||
host_header.pack_start(icon_label, False, False, 0)
|
||||
|
||||
# Host details - compact single line
|
||||
details_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=1)
|
||||
host_header.pack_start(details_vbox, True, True, 0)
|
||||
|
||||
# Host name with IP inline
|
||||
name_label = Gtk.Label()
|
||||
name_label.set_markup(f"<b>{self.host.name}</b> <small>({self.host.host_type.value}) - <tt>{self.host.ip_address}</tt></small>")
|
||||
name_label.set_halign(Gtk.Align.START)
|
||||
details_vbox.pack_start(name_label, False, False, 0)
|
||||
|
||||
# Services section - compact button row
|
||||
if self.host.services:
|
||||
services_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
|
||||
services_box.set_margin_start(16) # Indent services
|
||||
services_box.set_margin_top(4)
|
||||
host_box.pack_start(services_box, False, False, 0)
|
||||
|
||||
for service in self.host.services:
|
||||
if service.service_type in [ServiceType.WEB_GUI, ServiceType.SSH, ServiceType.RDP]: # Only show launchable services
|
||||
service_btn = Gtk.Button(label=service.service_type.value)
|
||||
service_btn.get_style_context().add_class("suggested-action")
|
||||
service_btn.connect("clicked", lambda btn, s=service: self._on_service_clicked(s))
|
||||
services_box.pack_start(service_btn, False, False, 0)
|
||||
|
||||
# Sub-hosts (VMs) section
|
||||
if self.host.sub_hosts:
|
||||
subhost_label = Gtk.Label()
|
||||
subhost_label.set_markup(f"<b><small>Virtual Machines ({len(self.host.sub_hosts)})</small></b>")
|
||||
subhost_label.set_halign(Gtk.Align.START)
|
||||
subhost_label.set_margin_top(8)
|
||||
subhost_label.set_margin_start(16)
|
||||
host_box.pack_start(subhost_label, False, False, 0)
|
||||
|
||||
subhosts_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
|
||||
subhosts_box.set_margin_start(32) # Double indent for VMs
|
||||
host_box.pack_start(subhosts_box, False, False, 0)
|
||||
|
||||
for subhost in self.host.sub_hosts:
|
||||
subhost_item = HostItem(subhost, self.open_service_callback)
|
||||
subhosts_box.pack_start(subhost_item.widget, False, False, 0)
|
||||
|
||||
return host_box
|
||||
|
||||
|
||||
def _on_service_clicked(self, service):
|
||||
self.open_service_callback(service)
|
||||
144
widgets/location_card.py
Normal file
144
widgets/location_card.py
Normal file
@@ -0,0 +1,144 @@
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from .host_item import HostItem
|
||||
from models import VPNType
|
||||
|
||||
|
||||
class ActiveLocationCard:
|
||||
def __init__(self, location, customer_name, callbacks):
|
||||
self.location = location
|
||||
self.customer_name = customer_name
|
||||
self.callbacks = callbacks
|
||||
self.widget = self._create_widget()
|
||||
|
||||
def _create_widget(self):
|
||||
# Clean card layout - just a box with proper spacing
|
||||
location_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
|
||||
|
||||
# Location header with controls
|
||||
header_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
|
||||
location_vbox.pack_start(header_box, False, False, 0)
|
||||
|
||||
# Location info
|
||||
info_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
|
||||
header_box.pack_start(info_vbox, True, True, 0)
|
||||
|
||||
# Location name with VPN type
|
||||
location_label = Gtk.Label()
|
||||
location_label.set_markup(f"<b>📍 {self.location.name}</b>")
|
||||
location_label.set_halign(Gtk.Align.START)
|
||||
info_vbox.pack_start(location_label, False, False, 0)
|
||||
|
||||
# VPN type
|
||||
vpn_icons = {
|
||||
VPNType.OPENVPN: "🔒",
|
||||
VPNType.WIREGUARD: "⚡",
|
||||
VPNType.IPSEC: "🛡️"
|
||||
}
|
||||
vpn_icon = vpn_icons.get(self.location.vpn_type, "🔑")
|
||||
|
||||
type_label = Gtk.Label()
|
||||
type_label.set_markup(f"<small>{vpn_icon} {self.location.vpn_type.value} VPN</small>")
|
||||
type_label.set_halign(Gtk.Align.START)
|
||||
info_vbox.pack_start(type_label, False, False, 0)
|
||||
|
||||
# Status and controls
|
||||
controls_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
|
||||
header_box.pack_end(controls_box, False, False, 0)
|
||||
|
||||
# Status
|
||||
status_text = "● Connected" if self.location.connected else "○ Disconnected"
|
||||
status_color = "#4caf50" if self.location.connected else "#999"
|
||||
status_label = Gtk.Label()
|
||||
status_label.set_markup(f"<small><span color='{status_color}'>{status_text}</span></small>")
|
||||
controls_box.pack_start(status_label, False, False, 0)
|
||||
|
||||
# Connect/Disconnect button
|
||||
btn_text = "Disconnect" if self.location.connected else "Connect"
|
||||
connect_btn = Gtk.Button(label=btn_text)
|
||||
if self.location.connected:
|
||||
connect_btn.get_style_context().add_class("destructive-action")
|
||||
else:
|
||||
connect_btn.get_style_context().add_class("suggested-action")
|
||||
connect_btn.connect("clicked", self._on_connect_clicked)
|
||||
controls_box.pack_start(connect_btn, False, False, 0)
|
||||
|
||||
# X button to deactivate (close button style)
|
||||
close_btn = Gtk.Button(label="✕")
|
||||
close_btn.set_tooltip_text("Deactivate location")
|
||||
close_btn.get_style_context().add_class("circular")
|
||||
close_btn.connect("clicked", self._on_deactivate_clicked)
|
||||
controls_box.pack_start(close_btn, False, False, 0)
|
||||
|
||||
# Hosts section if available
|
||||
if self.location.hosts:
|
||||
hosts_label = Gtk.Label()
|
||||
hosts_label.set_markup("<b>Infrastructure</b>")
|
||||
hosts_label.set_halign(Gtk.Align.START)
|
||||
hosts_label.set_margin_top(8)
|
||||
location_vbox.pack_start(hosts_label, False, False, 0)
|
||||
|
||||
# Hosts box with indent
|
||||
hosts_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
|
||||
hosts_box.set_margin_start(16)
|
||||
location_vbox.pack_start(hosts_box, False, False, 0)
|
||||
|
||||
for host in self.location.hosts:
|
||||
host_item = HostItem(host, self.callbacks['open_service'])
|
||||
hosts_box.pack_start(host_item.widget, False, False, 0)
|
||||
|
||||
return location_vbox
|
||||
|
||||
def _on_connect_clicked(self, button):
|
||||
self.callbacks['toggle_connection'](self.location)
|
||||
|
||||
def _on_deactivate_clicked(self, button):
|
||||
self.callbacks['deactivate_location'](self.location, self.customer_name)
|
||||
|
||||
|
||||
class InactiveLocationCard:
|
||||
def __init__(self, location, customer_name, callbacks):
|
||||
self.location = location
|
||||
self.customer_name = customer_name
|
||||
self.callbacks = callbacks
|
||||
self.widget = self._create_widget()
|
||||
|
||||
def _create_widget(self):
|
||||
# Clean horizontal layout
|
||||
location_hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
|
||||
|
||||
# Location info
|
||||
info_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
|
||||
location_hbox.pack_start(info_vbox, True, True, 0)
|
||||
|
||||
# Location name
|
||||
location_label = Gtk.Label()
|
||||
location_label.set_markup(f"<b>📍 {self.location.name}</b>")
|
||||
location_label.set_halign(Gtk.Align.START)
|
||||
info_vbox.pack_start(location_label, False, False, 0)
|
||||
|
||||
# VPN type and host count
|
||||
vpn_icons = {
|
||||
VPNType.OPENVPN: "🔒",
|
||||
VPNType.WIREGUARD: "⚡",
|
||||
VPNType.IPSEC: "🛡️"
|
||||
}
|
||||
vpn_icon = vpn_icons.get(self.location.vpn_type, "🔑")
|
||||
host_count = len(self.location.hosts)
|
||||
|
||||
details_label = Gtk.Label()
|
||||
details_label.set_markup(f"<small>{vpn_icon} {self.location.vpn_type.value} VPN • {host_count} hosts</small>")
|
||||
details_label.set_halign(Gtk.Align.START)
|
||||
info_vbox.pack_start(details_label, False, False, 0)
|
||||
|
||||
# Activate button
|
||||
activate_btn = Gtk.Button(label="Set Active")
|
||||
activate_btn.get_style_context().add_class("suggested-action")
|
||||
activate_btn.connect("clicked", self._on_activate_clicked)
|
||||
location_hbox.pack_end(activate_btn, False, False, 0)
|
||||
|
||||
return location_hbox
|
||||
|
||||
def _on_activate_clicked(self, button):
|
||||
self.callbacks['set_location_active'](self.location, self.customer_name)
|
||||
Reference in New Issue
Block a user