read24 - Admin UI: Managing Teachers

August 14, 2020

Roger Ngo

I have had it pretty rough the last couple of days. Lots of big changes in code, and lots of cool stuff happening. I think I’ll relax a little for this session. There’s always work in read24.

What’s something that I can cruise with for now? Well, I think a couple of Admin pages to manage teachers seems to be just fine. 😎

For instance:‌

All this is quite similar to all that classroom management stuff I had worked on a couple days ago.

As with last time, I don’t have any available routes to handle teacher administration. So, I’ll need to work on those first.

Since teachers are decorated User objects, I will first need to implement code to add a user. The route that can do that is:‌

The route implementation is found within /admin. It is implemented like:

app.post('/admin/user', async (req, res) => {
     const {
         username,
         password
     } = req.body;
    
     if(await User.findByUsername(username))
        return res.status(409).json({status: 'conflict', message: 'A user already exists with this username.'});
    
     const user = await User.create(username, password);
    
     return res.status(200).json({
         id: user.id,
         username: user.username
     });
});

And now, the User resource must implement a static async function called create that will create the user into the database and return the created resource.

public static async create(username: string, plainTextPassword: string) {
    const hashed = hashPassword(plainTextPassword);

    const user = new User({
        username,
        password: hashed.hashed,
        salt: hashed.salt
    });
    
    await user.insert();

    return user;
}

The create method exists to make it easy to create the user. The create method is implemented to handle a username and plainTextPassword being passed to create a User within the database.

Now to test, I just need to make a POST request to /admin/user with the required payload data and the following will be in the database:

"users": {
    "lastId": 1,
    "data": [
        {
            "dateCreated": 1597406744409,
            "dateUpdated": 0,
            "dateDeleted": 0,
            "username": "roger",
            "password": "$2b$07$Sp1j/tBitLw76GnDdnrHiOgk2cKcWVBZcpvT0aO5tbcTY.sbKriBu",
            "salt": "$2b$07$Sp1j/tBitLw76GnDdnrHiO",
            "id": 1
        }
    ]
}

Looks good! Now, I can add teachers tied to a user.

Administration of Teachers

Here is the API specification for the admin routes required to allow administration of teachers. Most of the parameters here are self-explanatory, so I will omit the explanations.

The specification above makes it straightforward in what I need for these additional routes. So, I won’t go into detail on their implementation here.

I will need to create a Teacher resource class, since it has not been done yet. This is the basic resource without any helper methods implemented:

import { DataType } from "../db/types";
import { BaseResource } from "../db/base_resource";

export interface TeacherType extends DataType {
    firstName: string;
    middleName: string;
    lastName: string;
    userId: number;
};

export class Teacher extends BaseResource implements TeacherType {
    public firstName: string;
    public middleName: string;
    public lastName: string; 
    public userId: number;

    constructor(initialProperties?: {[properties: string]: any}) {
        super('teachers');

        if (initialProperties) {
            this.firstName = initialProperties.firstName;
            this.middleName = initialProperties.middleName;
            this.lastName = initialProperties.lastName;
            this.userId = initialProperties.userId;
        }
    }
}

Notice that it is mandatory that a user is exists to be associated with the teacher!‌

Sometimes I want to find a teacher using the userId, so findByUserId is a useful method to have in the Teacher resource:

public static async findByUserId(userId: number) {
    const teacher = (await DatabaseConnector.select('teachers', {
        filters: [{
            column: 'userId',
            value: userId
        }]
    }))[0];

    return teacher;
}

Here is the database after adding a teacher:

"teachers": {
    "lastId": 1,
    "data": [
        {
            "dateCreated": 1597407941681,
            "dateUpdated": 0,
            "dateDeleted": 0,
            "firstName": "Roger",
            "middleName": "",
            "lastName": "Ngo",
            "userId": 1,
            "id": 1
        }
    ]
}

Okay now that the backend changes are done, let’s work on the front end!‌

First make a page to add a user. Just like adding a classroom, it is just 2 fields, and a button which makes a POST request to /admin/user.

Here is the code that will pass that information on submission:

async function submitForm() {
    const response = await (await fetch(`${API_HOST}/admin/user`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({username, password})
    })).json();

    if (response.message) {
        setAlertState({
            type: AlertBannerType.Error,
            message: `Could not add the User. Reason: ${response.message}`
        });     
    }   else {
        setAlertState({
            type: AlertBannerType.Success,
            message: 'Successfully added the User.'
        });
    } 
}

Okay, I’ll create the AddTeacher page and others next time!