Inception Genealogy Begins

To begin this series for the development of Inception Genealogy program, we start with the simple shell on how we will address navigation and some level of availability for data on each page. This will use binding, logged in state and others to determine what the use will have access to when logged in and not logged in.

As the program will have two levels of access once logged in, Admin and Contributor, limited administration views will be available to contributors while full administrative access will be given to Administrators. Then there is standard access for visitors to the site.

So without wasting more time, this post will be about the beginning UI. Final colors will be determined at a later date as the project approaches closer to finished.

I’ve opted for a “universal” project, developing with Sublime Text 4. Universal is important to me in development because the amount of information I can display is extremely limited when reaching viewing on a cell phone. Tablets and desktops should see the same views since Apple has pushed the envelope by no longer recognizing a difference between an iPad and a MacPro in the Safari browser.

As a quick side note, yes; I considered using Sencha Architect, unfortunately there isn’t support for universal applications in that product, yet. And for my choice of a universal app in this case, I will leave to readers to debate the choice and reserve my decision of right/wrong until development progresses far enough to determine if it was the correct choice or not.

I am also using tools for their value to the project, not because I prefer using one over the other. Development is on Mac for the obvious reason, I can run Windows virtually on my Mac allowing me to test Windows browsers from a single machine rather than two.

The mobile platform of development will be in the form of a Progressive Web App (PWA), even though at start time the W3C is still battling over final recommendations of service workers and other technologies, and even with Apple’s chocking every time PWA is mentioned while slowly expanding their support of them. Use of a PWA over native apps, well another discussion for readers to debate. From my concept PWA makes more sense since development of this project is driven by passion to create a genealogy program making my genealogical research easier with more information at my fingertips, the thought of commercial application is quite distant in my thoughts.

Development is currently at various stages in different areas. Each having a rudimentary start, looking nothing like the final product I am envisioning. So without any more said, time to dig in. Below is the code for the main view (/classic/view/main/main.js).

Ext.define('inception.view.main.Main', {
    extend: 'Ext.tab.Panel',
    xtype: 'app-main',
    reference: 'pnlMain',
    itemId: 'mainPnl',
    bind: {
        title: '{name}'
    },
    header: {
        items: [{
            xtype: 'button',
            text: 'Logout',
            iconCls: 'fas fa-sign-out-alt',
            bind: {
                hidden: '{!loggedIn}'
            },
            handler: 'fnLogoutUser'
        }]
    },
    requires: [
        'Ext.plugin.Viewport',
        'Ext.window.MessageBox',
        'Ext.window.Toast',
        'inception.view.main.MainController',
        'inception.view.main.MainModel',
        'inception.view.main.List'
    ],
    controller: 'main',
    viewModel: 'main',
    responsiveConfig: {
        wide: {
            tabPosition: 'left',
            tabRotation: 0,
        },
        tall: {
            tabPosition: 'top',
            tabRotation: 0
        }
    },
    listeners: {
        tabchange: 'fnTabChange'
    },
    items: [{
        title: 'Home',
        iconCls: 'fas fa-home',
        xtype: 'pnlhomemain',
        newRoute: 'home'
    }, {
        title: 'Families',
        iconCls: 'fas fa-users',
        newRoute: 'families'
    }, {
        title: 'People',
        iconCls: 'fas fa-user',
        newRoute: 'people'
    }, {
        title: 'Historical',
        iconCls: 'fas fa-history',
        newRoute: 'historical',
        items: [{
            xtype: 'historyMainPnl'
        }]
    }, {
        title: 'Login',
        itemId: 'tabLogin',
        iconCls: 'fas fa-sign-in-alt',
        newRoute: 'login',
        bind: {
            disabled: '{loggedIn}'
        },
        items: [{
            xtype: 'pnlloginmain'
        }]
    }, {
        title: 'Account Info.',
        itemId: 'tabUser',
        iconCls: 'fas fa-user-alt',
        newRoute: 'user',
        hidden: true
    }, {
        title: 'Users',
        itemId: 'tabUsers',
        iconCls: 'fas fa-users',
        newRoute: 'users',
        hidden: true
    }, {
        title: 'Administration',
        itemId: 'tabAdmin',
        iconCls: 'fas fa-cog',
        newRoute: 'admin',
        hidden: true
    }]
});

I’ve opted for a Tab Panel for navigation. Simplicity is my logic, simple in having five tabs on the left side, or top of the view depending on the screen layout of the device the user entered the site with. This is handled by the responsiveConfig property.

responsiveConfig: {
        wide: {
            tabPosition: 'left',
            tabRotation: 0,
        },
        tall: {
            tabPosition: 'top',
            tabRotation: 0
        }
    },

As seen above there are two options in responsiveConfig, wide, whereby if the user device screen is wider than it is tall, the tabs are shifted to the left side of the view; tall, whereby the tabs are shifted to the top of the view. This is done to make the more of the screen area for display of the information. Based on this the other views will be switched between layout hbox or vbox accordingly. In some cases, columns will be removed and possibly complete view sections, reducing the amount of information the user sees to improve the users experience.

Additional navigation for each view will be put in the specific view.

The model (/app/view/main/MainModel.js), what I will discuss next.

Ext.define('inception.view.main.MainModel', {
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.main',
    data: {
        name: 'Inception Genealogy',
        loggedIn: false,
        admin: false,
        contributor: false,
        userId: null,
        useremail: null,
        username: null,
        last_acted_on: null,
        user: null
    }
});

As I plan only information and data that will work with both the classic and modern toolkits, there is no reason to have separate files for each of the toolkits. Shared resources will grow as the project develops as I am all for reducing the number of files and lines of code, streamlining updating and reducing file sizes in loading of the site.

The controller is another shared resource, residing in /app/view/main/MainController.js.

Ext.define('inception.view.main.MainController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.main',
    routes: {
        'home': 'fnHome',
        'families': 'fnFamilies',
        'people': 'fnPeople',
        'historical': 'fnHistorical',
        'login': 'fnLogin',
        'admin': 'fnAdmin'
    },
    fnHome: function () {
        console.log('Home');
        this.fnSetActiveTab('home');
    },
    fnFamilies: function () {
        console.log('Families');
        this.fnSetActiveTab('families');
    },
    fnPeople: function () {
        console.log('People');
        this.fnSetActiveTab('people');
    },
    fnHistorical: function () {
        console.log('Historical');
        this.fnSetActiveTab('historical');
        Ext.ComponentQuery.query('#mainPnl')[0].setTitle(Ext.ComponentQuery.query('#mainPnl')[0].getTitle() + ' - Historical');
        Ext.ComponentQuery.query('#mainPnl')[0].setIconCls('fas fa-history');
    },
    fnLogin: function () {
        console.log('Login');
        this.fnSetActiveTab('login');
    },
    fnAdmin: function () {
        console.log('Administration');
        this.fnSetActiveTab('admin');
    },
    fnLoginUser: function (btn) {
        let uname = this.lookupReference().getValue();
        let pword = this.lookupReference().getValue();
        console.log('login user');
    },
    fnTabChange: function (tabPnl, newCard, oldCard, eOpts) {
        this.redirectTo(newCard.newRoute);
    },
    fnSetActiveTab: function (newRoute) {
        let tabItems = Ext.ComponentQuery.query('#mainPnl')[0].items.items;
        for (let x = 0; x < tabItems.length; x++) {
            console.log();
            if (tabItems[x].newRoute == newRoute) {
                Ext.ComponentQuery.query('#mainPnl')[0].setActiveTab(x);
            }
        }
    },
    fnLogoutUser: function () {
        console.log('logout user');
        let usr = Ext.ComponentQuery.query('#mainPnl')[0].getViewModel();
        let mainPnl = Ext.ComponentQuery.query('#mainPnl')[0];
        Ext.Ajax.request({
            url: '../resources/inception.php?method=logoutUser&username=' + usr.get('useremail'),
            success: function (response) {
                incept.getSession();
                mainPnl.getController().changeRoute('home');
                mainPnl.getComponent('tabLogin').tab.show();
                Ext.toast('You have been logged out successfully.');
            },
            failure: function (response) {
                incept.showError('serverside', 'Unexpected Server Side Error', '<p>We experienced an unexpected service side error while logging you out. Out support has been notified of the error</p>', Ext.Msg.ERROR, Ext.Msg.OK, Ext.emptyFn);
            }
        })
    },
    changeRoute: function (route) {
        this.redirectTo(route);
    },
});

You will notice there is also a utility class I created. incept.showError() resides in the file. How this class is created will be for another post where I can go into detail how to create utility classes so some top level functions can be available throughout the app without having to constantly query though the views to find what is needed. This will also help to keep the main model and especially controller uncluttered with such features as a loading mask or reused alerts for ajax failures and even some information dialogs that can be handled without using Ext.Msg.show() all over the code base.

The last bit of preliminary project details I feel important to know is the database backend is MySql, server side is PHP 7. All classes reside outside of the root web directory whereby a single file /resources/inception.php is a switch statement calling the required class function to return data requests.

My next post will cover the utility class creation.

I hope you find this series informational and helpful as you learn Sencha Ext JS programming and my perspective how to use it. As with any technology each uses it slightly different than another programmer and opinions vary. I live in the world of simple works best, programming along with everything else can become complex and convoluted in a way it doesn’t really need to be. A simple rule for my coding is the same rule I use for everything in life. “Just because we can, doesn’t mean we should”.

Author: aallord

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.