An open API service indexing awesome lists of open source software.

https://github.com/lmcrean/coach-matrix

a multi-user blog for professionals working in education, built with Django and PostgreSQL
https://github.com/lmcrean/coach-matrix

cloudinary django fullstack-development heroku javascript

Last synced: about 2 months ago
JSON representation

a multi-user blog for professionals working in education, built with Django and PostgreSQL

Awesome Lists containing this project

README

          

# Coach Matrix

Coach Matrix is an open-source CPD platform for educators to connect and share knowledge.

## Table of Contents

- [1. Features](#1-features)
- [2. User Stories reviewed against UX Planes and Manual Testing](#2-user-stories-reviewed-against-ux-planes-and-manual-testing)
- [3. Automatic Testing](#3-automatic-testing)
- [4. Issues and Bugs](#4-issues-and-bugs)
- [5. Acknowledgement and credits](#5-acknowledgement-and-credits)

***
- [1. Features](#1-features)
- [1.1. User Posts Questions which can be answered and voted ](#11-user-posts-questions-which-can-be-answered-and-voted-)
- [1.2. Questions have tags which can be found through the filter view ](#12-questions-have-tags-which-can-be-found-through-the-filter-view-)
- [1.3. User Posts Answers to Questions](#13-user-posts-answers-to-questions)
- [1.4. User votes for both questions and answers ](#14-user-votes-for-both-questions-and-answers--)
- [1.5. Toggle Sort content by highest vote vs newest post ](#15-toggle-sort-content-by-highest-vote-vs-newest-post--)
- [1.6. Landing Page with login and sign up tabs ](#16-landing-page-with-login-and-sign-up-tabs-)
- [1.7. User creates, reads and deletes bookmarks. ](#17-user-creates-reads-and-deletes-bookmarks-)
- [1.8. User updates Authentication details ](#18-user-updates-authentication-details-)
- [1.9. Navbar ](#19-navbar-)
- [1.10. Footer ](#110-footer-)
- [1.11. About Page ](#111-about-page-)
- [1.12. Error Messages ](#112-error-messages-)
- [2. User Stories reviewed against UX Planes and Manual Testing](#2-user-stories-reviewed-against-ux-planes-and-manual-testing)
- [2.1. Strategy plane](#21-strategy-plane)
- [2.2. Scope plane](#22-scope-plane)
- [2.3. Structure plane](#23-structure-plane)
- [2.3.1. App Structure](#231-app-structure)
- [2.3.2. Models](#232-models)
- [2.3.3. Forms](#233-forms)
- [2.3.4. Views](#234-views)
- [2.3.5. Templates](#235-templates)
- [2.3.6. URLPatterns](#236-urlpatterns)
- [2.3.7. Static Files](#237-static-files)
- [2.3.8. JavaScript Front-End Logic](#238-javascript-front-end-logic)
- [2.4. Skeleton plane](#24--skeleton-plane)
- [2.4.1. Skeleton Plane Acceptance Criteria](#241-skeleton-plane-acceptance-criteria)
- [2.4.2. Early Wireframes using Figma](#242-early-wireframes-using-figma)
- [2.4.3. Feature drafting with CodePen](#243-feature-drafting-with-codepen)
- [2.4.4. CSS Skeleton Testing](#244-css-skeleton-testing)
- [2.4.5. HTML Bootstrap Testing](#245-html-bootstrap-testing)
- [2.5. Surface plane](#25-surface-plane)
- [2.5.1. Surface Plane acceptance criteria](#251-surface-plane-acceptance-criteria)
- [2.5.2. CSS Surface Testing](#252-css-surface-testing)
- [2.5.3. FontAwesome](#253-fontawesome)
- [2.5.4. Media Surface Testing](#254-media-surface-testing)
- [3. Automatic Testing and Deployment](#3-automatic-testing-and-deployment)
- [3.1. Browserstack testing](#31-browserstack-testing)
- [3.2. Lighthouse testing](#32-lighthouse-testing)
- [3.3. Deployment to Heroku](#33-deployment-to-heroku)
- [3.4. Deployment from Heroku to coachmatrix.org](#34-deployment-from-heroku-to-coachmatrixorg)
- [3.5. Python Validation with PEP8 guidelines](#35-python-validation-with-pep8-guidelines)
- [3.6. HTML Validation with W3C guidelines](#36-html-validation-with-w3c-guidelines)
- [3.7.](#37)
- [4. Issues and Bugs](#4-issues-and-bugs)
- [4.1. "Cannot Access Django-Admin Panel on Port."](#41-cannot-access-django-admin-panel-on-port)
- [4.2. "IntegrityError When Adding Social Application in Django: Null Value in Column ‘Provider\_id.’"](#42-integrityerror-when-adding-social-application-in-django-null-value-in-column-provider_id)
- [4.3. "Cannot Delete ‘Forgot Password?’ On Sign in Page. Seems Completely Unresponsive to Code."](#43-cannot-delete-forgot-password-on-sign-in-page-seems-completely-unresponsive-to-code)
- [4.4. "Cannot Access Django-Admin Panel on Port."](#44-cannot-access-django-admin-panel-on-port)
- [4.5. "Django NoReverseMatch Error for ‘questions’ View After Google OAuth Sign-In."](#45-django-noreversematch-error-for-questions-view-after-google-oauth-sign-in)
- [4.6. "Social Login Expects to Lead to Questions.Html via Django Urlpatterns, Instead Leads to Home Page."](#46-social-login-expects-to-lead-to-questionshtml-via-django-urlpatterns-instead-leads-to-home-page)
- [4.7. "Django QuestionForm in Forms.Py Not Creating Instance in Questions.Html."](#47-django-questionform-in-formspy-not-creating-instance-in-questionshtml)
- [4.8. "Django taggit view leading to FieldError: Related Field got invalid lookup: name"](#48-django-taggit-view-leading-to-fielderror-related-field-got-invalid-lookup-name)
- [4.9. "profile\_update\_form does not update field correctly, print logs suggest issue with redundant if statement](#49-profile_update_form-does-not-update-field-correctly-print-logs-suggest-issue-with-redundant-if-statement)
- [4.10. "Tag instances via django-taggit are not rendering as expected in update post form."](#410-tag-instances-via-django-taggit-are-not-rendering-as-expected-in-update-post-form)
- [5. Acknowledgement and credits](#5-acknowledgement-and-credits)
- [5.1. Coding Languages](#51-coding-languages)
- [5.2. Frameworks, Libraries and Programs](#52-frameworks-libraries-and-programs)
- [5.2.1. Front-end modules](#521-front-end-modules)
- [5.2.2. Back-end modules](#522-back-end-modules)
- [5.2.3. Large Language Models](#523-large-language-models)
- [5.3. Deployment and IDE](#53-deployment-and-ide)
- [5.4. UX Software](#54-ux-software)
- [5.5. Resources](#55-resources)
- [5.6. Personal Acknowledgements](#56-personal-acknowledgements)

# 1. Features

The following features were implemented, tested and debugged throughout the development process. Many of them are tightly interwoven e.g. Users Post Questions with Tags.

## 1.1. User Posts Questions which can be answered and voted
Users can post questions, which can be edited and deleted.

## 1.2. Questions have tags which can be found through the filter view

Tags are used throughout the programme. Users can add tags to their questions. Users can also filter for questions by tag.

## 1.3. User Posts Answers to Questions

Users can post answers in response to questions, which can be edited and deleted.

## 1.4. User votes for both questions and answers

Users can upvote and downvote both questions and answers

## 1.5. Toggle Sort content by highest vote vs newest post

The user can toggle between sorting content by highest vote vs newest post. The default is highest vote. This applies to both questions and answers. Answers also provides a toggle to sort by oldest post, so they can get a sense of the narrative.

## 1.6. Landing Page with login and sign up tabs

The landing page provides a brief description of the site and a call to action to sign up. It also provides a link to the questions page. It features appealing animations and a video background.

Dynamic Tabs are used to toggle between sign in and sign up.

For Accessibility an option to pause the video is provided.

For surface appeal dynamic animations are included, including a custom CSS logo.

## 1.7. User creates, reads and deletes bookmarks.

The user can bookmark questions. This is particularly useful for questions that the user wants to return to later.

## 1.8. User updates Authentication details

The user can update their profile. This includes updating their username and password.

## 1.9. Navbar

The navbar provides all the important links to the landing page, questions page, and the user's profile. It also provides a link to sign in and sign out.

A custom CSS Logo is included in the navbar.

## 1.10. Footer

The footer provides links to the developer's social media and a link to the developer's portfolio.

## 1.11. About Page

The About Page provides a brief description of the site and introduces the developer. It also links to this README and the developer's portfolio.

## 1.12. Error Messages

Custom error messages are used to provide feedback to the user.

# 2. User Stories reviewed against UX Planes and Manual Testing

## 2.1. Strategy plane

The key strategy in this project was to use a Manual Testing Spreadsheet that would establish
- the user stories
- the acceptance criteria that would be used to establish the feature as implemented
- the validation criteria that would be used to establish the feature as working under "high-stress" conditions e.g. a user trying to break the site.
- the design criteria that would be used to establish the feature as visually appealing and engaging to the target audience. This was generic for all features.
- Track Progress using ✅ for TRUE and ❌ for FALSE in the validation criteria.

Full testing can be reviewed here:
https://docs.google.com/spreadsheets/d/1tii97g0Q4bVVvkrn_llrX8nOc_N2EyoOecOuMOEYFz0/edit?usp=sharing

***

Manual Testing PASSED Spreadsheet

![testing](assets/media/testing.png)

![testing](assets/media/testing2.png)


Not Yet Implemented Spreadsheet
![testing](assets/media/testing3.png)

***

Discarded strategies included using a Todoist Kanban board and a large 900x1200mm whiteboard with a 3x3 grid. The spreadsheet was chosen as it was both the most efficient way to track progress while also able to carry the most accurate information for the developer.

## 2.2. Scope plane

Based on the user stories above, the following features were prioritised which you can see in the [features section](1-features) above.

## 2.3. Structure plane

The structure plane was particularly important with this project and are best summarised through the framework of Django:

Structure contents:
- [App Structure](#231-app-structure)
- [Models](#232-models)
- [Forms](#233-forms)
- [Views](#234-views)
- [Templates](#235-templates)
- [URLPatterns](#236-urlpatterns)
- [Static Files](#237-static-files)
- [JavaScript Front-End Logic](#238-javascript-front-end-logic)

### 2.3.1. App Structure

The app structure was particularly important with this project. The following app structure was concieved.

coach_matrix was the general project folder with important files being:
- settings.py holding the django settings for the project, such as installed apps and middleware
- urls.py holding the urls for the project and linking to the main_forum app

coach_matrix was the general project folder with important files being settings.py holding the django settings for the project, such as installed apps and middleware and urls.py holding the urls for the project and linking to the main_forum app.

main_forum was the django app with important files being:
- models.py holding the database schema
- forms.py holding the forms such as asking questions and answering questions
- views.py holding the views for the questions and answers. This was divided into many separate subdirectories and put together in an init.py file.
- urls.py holding the urls together within the app

main_forum was the django app with important files being models.py holding the database schema, forms.py holding the forms such as asking questions and answering questions, views.py holding the views for the questions and answers. This was divided into many separate subdirectories and put together in an init.py file, urls.py holding the urls together within the app.

users was the django app for user authentication with important files being forms.p views.py and urls.py. It included a user model for updating the profile and sign in functionality.

### 2.3.2. Models

The following diagrams were used to plan the models in `main_forum` and were tested and debugged throughout the development process.

The Diagrams were divided between User and Question point of view for better readability

**Mermaid Diagram 1: User Point of View**
Below illustrates the simplified view of the user model. The user can have a profile, ask questions, write answers, bookmark questions and vote on questions and answers.

```mermaid
erDiagram
User ||--o{ UserProfile : "has"
User ||--o{ Question : "can write Many"
User ||--o{ Answer : "can write Many"
User ||--o{ Bookmark : "can write Many, 1 per question"
User ||--o{ Vote : "can write many, 1 per question/answer"
Vote ||--o{ Upvote : ""
Vote ||--o{ Downvote : ""

UserProfile {
username string
}

Bookmark {
created_at datetime
}

Upvote {
upvotedate datetime
}
Downvote {
downvotedate datetime
}

Answer {
slug string
created_on datetime
status int
name string
email string
body text
approved boolean
answercount int
featured_image image
}

Question {
subject string
created_on datetime
status int
updated_on datetime
content text
answercount int
views int
net_votes int
}
```
***

**Mermaid Diagram 2: Question Point of View**
Below illustrates the relationship between questions and answers. A question can have many answers, and both a quesiton and an answer can have many votes.

```mermaid
erDiagram
Question ||--o{ Answer : "can recieve many from any User"
Question ||--o{ Upvote : "can receive 1 from any User"
Question ||--o{ Downvote : "can receive 1 from any User"
Question ||--o{ Bookmark : "can receive 1 from User (private)"

Answer ||--o{ Upvote : "can receive 1 from any User"
Answer ||--o{ Downvote : "can receive 1 from any User"

Bookmark {
created_at datetime
}

Upvote {
upvotedate datetime
}
Downvote {
downvotedate datetime
}

Answer {
slug string
created_on datetime
status int
name string
email string
body text
approved boolean
answercount int
featured_image image
}

Question {
subject string
created_on datetime
status int
updated_on datetime
content text
answercount int
views int
net_votes int
}
```

***

**Methods used:**

| model method | description | example | documentation-link for Django 4.1 |
| --- | --- | --- | --- |
| class-based models | models that are classes, these are particularly useful for models that require a lot of methods | Question | https://docs.djangoproject.com/en/4.1/topics/db/models/ |
| meta class | a class within a class that provides metadata for the model | Meta class for the question model | https://docs.djangoproject.com/en/4.1/ref/models/options/ |
| __str__ | a method that returns a string representation of the object | __str__ for the question model | https://docs.djangoproject.com/en/4.1/ref/models/instances/#django.db.models.Model.__str__ |
| ForeignKey | a field that links to another model | ForeignKey for the question model | https://docs.djangoproject.com/en/4.1/ref/models/fields/#foreignkey |
| ManyToManyField | a field that links to another model with a many-to-many relationship | ManyToManyField for the question model | https://docs.djangoproject.com/en/4.1/ref/models/fields/#manytomanyfield |
| CharField | a field for storing a string | CharField for the question model | https://docs.djangoproject.com/en/4.1/ref/models/fields/#charfield |
| TextField | a field for storing a large amount of text | TextField for the question model | https://docs.djangoproject.com/en/4.1/ref/models/fields/#textfield |
| DateTimeField | a field for storing a date and time | DateTimeField for the question model | https://docs.djangoproject.com/en/4.1/ref/models/fields/#datetimefield |
| IntegerField | a field for storing an integer | IntegerField for the question model | https://docs.djangoproject.com/en/4.1/ref/models/fields/#integerfield |
| SlugField | a field for storing a slug | SlugField for the question model | https://docs.djangoproject.com/en/4.1/ref/models/fields/#slugfield |
| save method | a method that saves the object | save method for the question model | https://docs.djangoproject.com/en/4.1/ref/models/instances/#django.db.models.Model.save |
| super method | a method that calls the parent class method | super method for the question model | https://docs.djangoproject.com/en/4.1/ref/models/instances/#django.db.models.Model.save |
| count method | a method that counts the number of objects | count method for the question model | https://docs.djangoproject.com/en/4.1/ref/models/querysets/#count |
| related_name | a field that provides a name for the reverse relation | related_name for the question model | https://docs.djangoproject.com/en/4.1/ref/models/fields/#django.db.models.ForeignKey.related_name |
| @reciever | a decorator that connects a signal to a function | @reciever for the question model | https://docs.djangoproject.com/en/4.1/ref/signals/#django.dispatch.receiver |
| ordering | a field that orders the queryset | ordering for the question model | https://docs.djangoproject.com/en/4.1/ref/models/options/#ordering |
| instance | a method that returns the instance of the object | instance for the question model | https://docs.djangoproject.com/en/4.1/ref/models/instances/#django.db.models.Model.instance |

### 2.3.3. Forms

The following forms were used.

| form method | description | example | documentation-link for Django 4.1 |
| --- | --- | --- | --- |
| class-based forms | forms that are classes, these are particularly useful for forms that require a lot of methods | QuestionForm | https://docs.djangoproject.com/en/4.1/topics/forms/ |
| save method | a method that saves the object | save method for the question form | https://docs.djangoproject.com/en/4.1/ref/models/instances/#django.db.models.Model.save |
| clean method | a method that cleans the form | clean method for the question form | https://docs.djangoproject.com/en/4.1/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other |
| __init__ method | a method that initialises the form | __init__ method for the question form, This is for checking if the form is bound to an existing instance, i.e. if the form is being used to update an existing question | https://docs.djangoproject.com/en/4.1/ref/forms/api/#django.forms.Form.__init__ |

### 2.3.4. Views

Numerous views were used in main_forum and users and were split into seperate folders. There were many important methods used in the views.

| function method | description | example | documentation-link for Django 4.1 |
| --- | --- | --- | --- |
| class-based views | views that are classes, these are particularly useful for views that require a lot of methods | QuestionDetail View | https://docs.djangoproject.com/en/4.1/topics/class-based-views/ |
| get_context_data | gets the context data for the view | get_context_data for the question detail view | https://docs.djangoproject.com/en/4.1/ref/class-based-views/mixins-single-object/#django.views.generic.detail.SingleObjectMixin.get_context_data |
| get_queryset | gets the queryset for the view | get_queryset for the question detail view | https://docs.djangoproject.com/en/4.1/ref/class-based-views/mixins-single-object/#django.views.generic.detail.SingleObjectMixin.get_queryset |
| get_object_or_404 | gets the object or returns a 404 error | get_object_or_404 for the question detail view | https://docs.djangoproject.com/en/4.1/topics/http/shortcuts/#get-object-or-404 |
| get | retrieves a page | get request for questions page | https://docs.djangoproject.com/en/4.1/ref/request-response/#django.http.HttpRequest.GET |
| post | sends data to the server | post request for asking a question | https://docs.djangoproject.com/en/4.1/ref/request-response/#django.http.HttpRequest.POST |
| form_valid | checks if the form is valid | form_valid for asking a question | https://docs.djangoproject.com/en/4.1/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin.form_valid |
| form_invalid | checks if the form is invalid | form_invalid for asking a question | https://docs.djangoproject.com/en/4.1/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin.form_invalid |
| get_success_url | gets the success url | get_success_url for asking a question | https://docs.djangoproject.com/en/4.1/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin.get_success_url |
| test_func | checks if the user is allowed to access the view | test_func for the question detail view | https://docs.djangoproject.com/en/4.1/ref/class-based-views/mixins-permission/#django.views.generic.detail.SingleObjectMixin.test_func |

| return method | description | example | documentation-link for Django 4.1 |
| --- | --- | --- | --- |
| return | returns a page | return for asking a question | https://docs.djangoproject.com/en/4.1/ref/request-response/#django.http.HttpResponse |
| return render() | renders a page | render for the questions page | https://docs.djangoproject.com/en/4.1/topics/http/shortcuts/#render |
| return redirect | redirects to a page | redirect for asking a question | https://docs.djangoproject.com/en/4.1/topics/http/shortcuts/#redirect |
| return Model.objects.filter() | filters the queryset | .objects.filter() for the questions page | https://docs.djangoproject.com/en/4.1/topics/db/queries/#retrieving-specific-objects-with-filters |
| return self.request.user | returns the user | self.request.user for the question detail view | https://docs.djangoproject.com/en/4.1/ref/request-response/#django.http.HttpRequest.user |
| return self.get_object() | returns the object | self.get_object() for the question detail view | https://docs.djangoproject.com/en/4.1/ref/class-based-views/mixins-single-object/#django.views.generic.detail.SingleObjectMixin.get_object |

| general method | description | example | documentation-link for Django 4.1 |
| --- | --- | --- | --- |
| class-based views | views that are classes, these are particularly useful for views that require a lot of methods | QuestionDetail View | https://docs.djangoproject.com/en/4.1/topics/class-based-views/ |

### 2.3.5. Templates

The following templates were used the hyperlinks were summarised in this diagram.

templates was the folder for seperate html templates, some of which were imported from django-allauth
- base.html was the master template with the head links, navbar and footer. It also linked to the master css file and the js files. It appears on every page.
- other file names are more self-explanatory such as questions.html and question_detail.html

| template method | description | example | documentation-link for Django 4.1 |
| --- | --- | --- | --- |
| extends | extends a template | extends for the questions page | https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#extends |
| block | defines a block | block for the questions page | https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#block |
| include | includes a template | include for the questions page | https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#include |
| url | links to a url | url for the questions page | https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#url |
| static | links to a static file | static for the questions page | https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#static |
| csrf_token | adds a csrf token | csrf_token for the questions page | https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#csrf-token |
| form.as_p | renders a form | form.as_p for the questions page | https://docs.djangoproject.com/en/4.1/ref/forms/api/#django.forms.Form.as_p |
| crispy | renders a form with crispy forms | crispy for the questions page | https://django-crispy-forms.readthedocs.io/en/latest/ |
| load socialaccount | loads the social account template | load socialaccount for the questions page | https://rdmo.readthedocs.io/en/latest/configuration/authentication/allauth.html#social-accounts |
| extra_head | adds extra head content | extra_head for the questions page | https://www.ericholscher.com/blog/2008/nov/20/gentlemans-agreement-django-templates/#block-extra-head |

### 2.3.6. URLPatterns

The following URLPatterns were used.

$$$$ INSERT CODE $$$$$

| urlpattern method | description | example | documentation-link for Django 4.1 |
| --- | --- | --- | --- |
| path | links to a view | path for the questions page | https://docs.djangoproject.com/en/4.1/ref/urls/#path |
| name | names a url | name for the questions page | https://docs.djangoproject.com/en/4.1/ref/urls/#name |
| as_view | calls a view | as_view for the questions page | https://docs.djangoproject.com/en/4.1/ref/class-based-views/base/#django.views.generic.base.View.as_view |
| int:pk | passes an integer primary key | int:pk for the question detail view | https://docs.djangoproject.com/en/4.1/topics/http/urls/#path-converters |
| slug:slug | passes a slug | slug:slug for the question detail view | https://docs.djangoproject.com/en/4.1/topics/http/urls/#path-converters |

### 2.3.7. Static Files

static was the folder for static files such as css and javascript. This was later hosted on `cloudinary` for deployment using ```dj3-cloudinary-storage```.
- CSS had a master css file then a separate css file for each html template
- JS had a separate js files for each html template
- data was a folder for json files
- media was a folder for fonts and images. In some instances media files were delegated to cloudinary URL directly, such as a video background on the login page.

### 2.3.8. JavaScript Front-End Logic

JavaScript was used to add front-end logic to the site. This was particularly important for the toggle views between compact and expanded. The following methods were used.

static/js/ask_question.js
static/js/bg-logo.js
static/js/login.js
static/js/navbar.js

## 2.4. Skeleton plane

### 2.4.1. Skeleton Plane Acceptance Criteria

THe following criteria was used to test the skeleton plane. You can refer back to the [speadsheet](#21-strategy-plane) in the strategy plane to see the progress of the acceptance criteria against the features.

- The feature can be viewed from 300px to 1920px, without unintended results such as:
- unintented text overflowing
- unintended overlapping
- unintended hidden content
- unintended large gaps
- unintended text sizing

Where appropriate, the feature is responsive to the screen size, and could split into columns or rows as the screen size decreases. A single column does not fail the acceptance criteria as the site is designed mobile-first,therefore should be responsive to mobile devices.

### 2.4.2. Early Wireframes using Figma

Figma was used to create the early wireframes during the design process, prioritising a mobile-first approach. The wireframes can be found here:

https://www.figma.com/file/jXT4Bi1WXVwYG4daO3Yczi/Portfolio-Project-4?type=design&node-id=0%3A1&mode=design&t=0DTJelaO4PzBf99a-1

$$$$$$$$$$$$ SCREENSHOT HERE $$$$$$$$$$$$$$$

The category feature was omitted from the final design as it was deemed to be too complex. Other omissions included the use of a logo backgrouund and reputation points. These plan to be added in future versions.

### 2.4.3. Feature drafting with CodePen

Codepen was used to draft code snippets in the front-end using bootstrap and css. This platform was chosen for it's efficient interface.

The codepen collection can be found here:
https://codepen.io/collection/jbEjoo

| Collection | Description | Link |
|----|---| ---|
| Master | master CSS files, e.g. colour schemes | https://codepen.io/collection/yrwrJZ |
| Components | components HTML CSS and JS files | https://codepen.io/collection/jbEjoo |

|Codepen| Description | Link |
|---|---| ---|
| Login Page | login page HTML CSS and JS | https://codepen.io/lauriecrean/pen/wvZqZyZ |
| Ask Question Page | questions page HTML CSS and JS | https://codepen.io/lauriecrean/pen/rNRYZYK |

### 2.4.4. CSS Skeleton Testing

The following files were used to test responsivity:

```
static/css/components/about.css
static/css/components/answercard.css
static/css/components/ask_question.css
static/css/components/errors.css
static/css/components/login.css
static/css/components/navbar.css
static/css/components/question_detail.css
static/css/components/votingcard.css
```

### 2.4.5. HTML Bootstrap Testing

Bootstrap was also used throughout the project as an efficient way to use CSS resources.

| bootstrap method | syntax | example | documentation-link for Bootstrap 5.3 |
| --- | --- | --- | --- |
| container | `

` | container for the questions page | https://getbootstrap.com/docs/5.3/layout/containers/ |
| row | `
` | row for the questions page | https://getbootstrap.com/docs/5.3/layout/grid/ |
| col | `
` | col for the questions page | https://getbootstrap.com/docs/5.3/layout/grid/ |
| card | `
` | card for the questions page | https://getbootstrap.com/docs/5.3/components/card/ |
| modal | `