diff --git a/frontend/__init__.py b/frontend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/admin.py b/frontend/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/frontend/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/frontend/apps.py b/frontend/apps.py new file mode 100644 index 0000000..04f7b89 --- /dev/null +++ b/frontend/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class FrontendConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'frontend' diff --git a/frontend/models.py b/frontend/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/frontend/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/frontend/static/css/index.css b/frontend/static/css/index.css new file mode 100644 index 0000000..9416823 --- /dev/null +++ b/frontend/static/css/index.css @@ -0,0 +1,37 @@ +body { + --nav-width: 200px; + margin: 0 0 0 var(--nav-width); + font-family: 'Quicksand', sans-serif; + font-size: 18px; +} + +.nav { + position: fixed; + top: 0; + left: 0; + width: var(--nav-width); + height: 100vh; + background: #222222; +} + +.nav__link { + display: block; + padding: 12px 18px; + text-decoration: none; + color: #eeeeee; + font-weight: 500; +} + +.nav__link:hover { + background: rgba(255, 255, 255, 0.05); +} + +#app { + margin: 2em; + line-height: 1.5; + font-weight: 500; +} + +a { + color: #009579; +} \ No newline at end of file diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js new file mode 100644 index 0000000..f63c27c --- /dev/null +++ b/frontend/static/js/index.js @@ -0,0 +1,63 @@ +import Dashboard from "./views/Dashboard.js"; +import Posts from "./views/Posts.js"; +import PostView from "./views/PostView.js"; +import Settings from "./views/Settings.js"; + +const pathToRegex = path => new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$"); + +const getParams = match => { + const values = match.result.slice(1); + const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map(result => result[1]); + + return Object.fromEntries(keys.map((key, i) => { + return [key, values[i]]; + })); +}; + +const navigateTo = url => { + history.pushState(null, null, url); + router(); +}; + +const router = async () => { + const routes = [ + { path: "/", view: Dashboard }, + { path: "/posts", view: Posts }, + { path: "/posts/:id", view: PostView }, + { path: "/settings", view: Settings } + ]; + + // Test each route for potential match + const potentialMatches = routes.map(route => { + return { + route: route, + result: location.pathname.match(pathToRegex(route.path)) + }; + }); + + let match = potentialMatches.find(potentialMatch => potentialMatch.result !== null); + + if (!match) { + match = { + route: routes[0], + result: [location.pathname] + }; + } + + const view = new match.route.view(getParams(match)); + + document.querySelector("#app").innerHTML = await view.getHtml(); +}; + +window.addEventListener("popstate", router); + +document.addEventListener("DOMContentLoaded", () => { + document.body.addEventListener("click", e => { + if (e.target.matches("[data-link]")) { + e.preventDefault(); + navigateTo(e.target.href); + } + }); + + router(); +}); \ No newline at end of file diff --git a/frontend/static/js/views/AbstractView.js b/frontend/static/js/views/AbstractView.js new file mode 100644 index 0000000..18bf83f --- /dev/null +++ b/frontend/static/js/views/AbstractView.js @@ -0,0 +1,13 @@ +export default class { + constructor(params) { + this.params = params; + } + + setTitle(title) { + document.title = title; + } + + async getHtml() { + return ""; + } +} \ No newline at end of file diff --git a/frontend/static/js/views/Dashboard.js b/frontend/static/js/views/Dashboard.js new file mode 100644 index 0000000..e2f4409 --- /dev/null +++ b/frontend/static/js/views/Dashboard.js @@ -0,0 +1,20 @@ +import AbstractView from "./AbstractView.js"; + +export default class extends AbstractView { + constructor(params) { + super(params); + this.setTitle("Dashboard"); + } + + async getHtml() { + return ` +
+ Fugiat voluptate et nisi Lorem cillum anim sit do eiusmod occaecat irure do. Reprehenderit anim fugiat sint exercitation consequat. Sit anim laborum sit amet Lorem adipisicing ullamco duis. Anim in do magna ea pariatur et. +
++ View recent posts. +
+ `; + } +} \ No newline at end of file diff --git a/frontend/static/js/views/PostView.js b/frontend/static/js/views/PostView.js new file mode 100644 index 0000000..24e2785 --- /dev/null +++ b/frontend/static/js/views/PostView.js @@ -0,0 +1,16 @@ +import AbstractView from "./AbstractView.js"; + +export default class extends AbstractView { + constructor(params) { + super(params); + this.postId = params.id; + this.setTitle("Viewing Post"); + } + + async getHtml() { + return ` +You are viewing post #${this.postId}.
+ `; + } +} diff --git a/frontend/static/js/views/Posts.js b/frontend/static/js/views/Posts.js new file mode 100644 index 0000000..06ff6c9 --- /dev/null +++ b/frontend/static/js/views/Posts.js @@ -0,0 +1,15 @@ +import AbstractView from "./AbstractView.js"; + +export default class extends AbstractView { + constructor(params) { + super(params); + this.setTitle("Posts"); + } + + async getHtml() { + return ` +You are viewing the posts!
+ `; + } +} \ No newline at end of file diff --git a/frontend/static/js/views/Settings.js b/frontend/static/js/views/Settings.js new file mode 100644 index 0000000..a54cf89 --- /dev/null +++ b/frontend/static/js/views/Settings.js @@ -0,0 +1,15 @@ +import AbstractView from "./AbstractView.js"; + +export default class extends AbstractView { + constructor(params) { + super(params); + this.setTitle("Settings"); + } + + async getHtml() { + return ` +Manage your privacy and configuration.
+ `; + } +} \ No newline at end of file diff --git a/frontend/templates/index.html b/frontend/templates/index.html new file mode 100644 index 0000000..6fcaec3 --- /dev/null +++ b/frontend/templates/index.html @@ -0,0 +1,19 @@ +{% load static %} + + + + + +