Part 4 of 7 · Visa Manager App

Bringing Outside Tools Into One Cockpit

This is the post where the cockpit becomes the business tool it was imagined as, the four real services, each in its place, one click apart, in a window designed rather than accumulated. The assembly is a services map, a sidebar, and a stack of shared-profile views, and the design work matters exactly as much as the wiring.

The heart is a plain data structure declaring the cockpit’s contents, the app’s whole purpose in one dict, and these are the real destinations it ships with:

services = {
    'visa_applications': {
        'url': 'https://cheapuaevisa.com/wp-admin/admin.php?page=fluent_forms_all_entries',
        'title': 'Visa Applications',
        'service_name': 'wordpress_admin',
    },
    'update_status': {
        'url': 'https://cheapuaevisa.com/visa-status-admin/',
        'title': 'Update Visa Status',
        'service_name': 'wordpress_admin',
    },
    'tawk_chat': {
        'url': 'https://dashboard.tawk.to/',
        'title': 'Customer Chat',
        'service_name': 'tawk_to',
    },
    'webmail': {
        'url': 'https://cheapuaevisa.com:2096/',
        'title': 'Email',
        'service_name': 'webmail',
    },
}

Details in those URLs carry real intent. The applications entry deep-links straight to Fluent Forms’ entries screen, not the WordPress dashboard, the cockpit opens where the work is, not where the tool starts. The webmail URL carries its port, 2096, the standard webmail door on this hosting. And service_name tags which credential each view belongs to, the hook the password manager will hang on, note two views share wordpress_admin, the shared profile already makes their session one.

Construction loops the map, building a sidebar button and a stacked view per service:

self.stack = QStackedWidget()
for key, cfg in services.items():
    view = make_view(cfg['url'])            # the shared-profile factory
    self.views[key] = view
    self.stack.addWidget(view)

    btn = QPushButton(cfg['title'])
    btn.clicked.connect(lambda _, k=key: self.show_service(k))
    sidebar.addWidget(btn)

One Python trap in that loop is worth naming because it silently breaks navigation, the k=key default argument in the lambda. Without it, every button’s lambda closes over the same loop variable and all four buttons open the last service, the classic late-binding closure bug, and the default-argument idiom is the standard cure. The design layer finished the transformation, a styled sidebar in the business’s green palette, generous click targets, the active service highlighted, a QStackedWidget so switching is instant with every page kept alive, no reloads, chat and mail keep their state while you work applications. The day it replaced the four browser tabs, the change was physical, the business’s whole operation in one window that looks like it was meant.

A few things people ask me about this

Why do all my dynamically-created buttons trigger the last item? The loop’s lambdas share the loop variable, evaluated late. Bind per-iteration with a default argument, lambda checked, k=key: handler(k).

Why a QStackedWidget instead of closing and opening views? State. Stacked views stay alive in the background, the chat keeps its scroll, mail keeps its draft, switching costs nothing, which is the entire feel of a cockpit.

Next

A tool this useful earns a real launch experience, an icon, a single file, no console window, no Python install required on the office machine. Packaging and polish are the next post.

Leave a Reply

Your email address will not be published. Required fields are marked *