diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..456751f0 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-react"] +} \ No newline at end of file diff --git a/frontend/.eslintrc.json b/.eslintrc.json similarity index 98% rename from frontend/.eslintrc.json rename to .eslintrc.json index 06489873..4d6c5b2c 100644 --- a/frontend/.eslintrc.json +++ b/.eslintrc.json @@ -1,14 +1,14 @@ { + "parser": "babel-eslint", + "env": { "browser": true }, "extends": [ - "airbnb-base", "plugin:react/recommended" ], - "parser": "babel-eslint", "plugins": [ "react" @@ -40,6 +40,7 @@ "prefer-destructuring": ["off"], "prefer-template": ["off"], "quote-props": ["error", "consistent"], + "quotes": ["warn"], "radix": ["off"], "react/no-unescaped-entities": [ "error", { "forbid": [">", "}"] }], "semi": ["error", "always"] @@ -57,5 +58,4 @@ "env": { "jest": true } } ] -} - +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7f4cd2bd..9843ef5d 100644 --- a/.gitignore +++ b/.gitignore @@ -127,7 +127,7 @@ dmypy.json .idea/runConfigurations # dependencies -frontend/node_modules +node_modules /.pnp .pnp.js diff --git a/.idea/misc.xml b/.idea/misc.xml index 6286e9aa..b68b2ef5 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/.idea/temp.iml b/.idea/temp.iml index f97b9f50..c36c4e8a 100644 --- a/.idea/temp.iml +++ b/.idea/temp.iml @@ -1,10 +1,22 @@ + + + + + + - + diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml new file mode 100644 index 00000000..fb0d65a4 --- /dev/null +++ b/.idea/watcherTasks.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index 54bbdb35..d05964f3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1 @@ -# Machine Learning Applications for Remote Language Instruction - -_Machine Learning Applications for Remote Language Instruction_ is a project by the -[MIT Programs in Digital Humanities](https://digitalhumanities.mit.edu) in -collaboration with our Spring 2021 Faculty Fellow, [Takako Aikawa](https://https://aikawa.mit.edu/), -Senior Lecturer in Japanese in the Global Languages Department at MIT. +# Boilerplate project for DH Labs diff --git a/assets/bundles/index.bundle.js b/assets/bundles/index.bundle.js new file mode 100644 index 00000000..35fba150 --- /dev/null +++ b/assets/bundles/index.bundle.js @@ -0,0 +1,32 @@ +/* + * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). + * This devtool is neither made for production nor for readable output files. + * It uses "eval()" calls to create a separate source file in the browser devtools. + * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) + * or disable the default devtool with "devtool: false". + * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). + */ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "./frontend/index.js": +/*!***************************!*\ + !*** ./frontend/index.js ***! + \***************************/ +/***/ (() => { + +eval("function component() {\n console.log('running with updates');\n const element = document.createElement('div');\n element.innerHTML = 'Hello webpack';\n return element;\n}\ndocument.body.appendChild(component());\n\n//# sourceURL=webpack://boilerplate/./frontend/index.js?"); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module can't be inlined because the eval devtool is used. +/******/ var __webpack_exports__ = {}; +/******/ __webpack_modules__["./frontend/index.js"](); +/******/ +/******/ })() +; \ No newline at end of file diff --git a/assets/img/footer/dh_logo.svg b/assets/img/dh_logo.svg similarity index 100% rename from assets/img/footer/dh_logo.svg rename to assets/img/dh_logo.svg diff --git a/assets/img/footer/mit_logo.svg b/assets/img/mit_logo.svg similarity index 100% rename from assets/img/footer/mit_logo.svg rename to assets/img/mit_logo.svg diff --git a/backend/app/common.py b/backend/app/common.py index 64494589..c5a0ceb9 100644 --- a/backend/app/common.py +++ b/backend/app/common.py @@ -14,11 +14,11 @@ def render_react_view(request, component_name=None, **url_props): :param request: Django request object to pass through :param component_name: name of the React component to render into the 'root' div - of backend/templates/index.html + of backend/templates/base.html :param url_props: props to pass into the React component, consumed from Django's url parser :return: """ - template = 'index.html' + template = 'base.html' context = { 'component_name': component_name, 'props': url_props, diff --git a/backend/app/views.py b/backend/app/views.py index 677009e6..a9861905 100644 --- a/backend/app/views.py +++ b/backend/app/views.py @@ -1,19 +1,66 @@ """ -These view functions and classes implement API endpoints +These view functions and classes implement both standard GET routes and API endpoints """ + from rest_framework.decorators import api_view from rest_framework.response import Response +from django.shortcuts import render -# from .models import () -# from .serializers import () @api_view(['GET']) def example(request, example_id): """ API example endpoint. """ + data = { 'name': 'Example', 'id': example_id, } return Response(data) + + +def index(request): + """ + Home page + """ + + context = { + 'page_metadata': { + 'title': 'Home page' + }, + } + + return render(request, 'index.html', context) + + +def example(request): + """ + Example page + """ + + context = { + 'page_metadata': { + 'title': 'Example page' + }, + } + + return render(request, 'index.html', context) + + +def example_id(request, example_id): + """ + Example ID page + """ + + context = { + 'page_metadata': { + 'title': 'Example ID page' + }, + 'app_data': { + 'id': example_id + }, + 'app_component': 'ExampleId' + } + + return render(request, 'index.html', context) diff --git a/backend/config/settings/base.py b/backend/config/settings/base.py index ee9aa7ca..1c58365a 100644 --- a/backend/config/settings/base.py +++ b/backend/config/settings/base.py @@ -153,7 +153,7 @@ # Django webpack loader settings WEBPACK_LOADER = { 'DEFAULT': { - 'BUNDLE_DIR_NAME': 'bundles/', + 'BUNDLE_DIR_NAME': './assets/bundles/', 'STATS_FILE': os.path.join(PROJECT_ROOT, 'webpack-stats.json'), } } diff --git a/backend/config/urls.py b/backend/config/urls.py index ca9caa6c..f6032489 100644 --- a/backend/config/urls.py +++ b/backend/config/urls.py @@ -17,21 +17,7 @@ from django.contrib import admin from django.urls import path -from app.common import render_react_view -from app.views import ( - example, -) - - -def react_view_path(route, component_name): - """ Convenience function for React views """ - return path( - route, - render_react_view, - { - 'component_name': component_name, - }, - ) +from app import views urlpatterns = [ @@ -39,9 +25,10 @@ def react_view_path(route, component_name): path('admin/', admin.site.urls), # API endpoints - path('api/example/', example), + path('api/example/', views.example), # View paths - react_view_path('', 'IndexView'), - react_view_path('example/', 'ExampleView'), + path('', views.index, name='index'), + path('example', views.example, name='example'), + path('example/', views.example_id, name='example_id'), ] diff --git a/backend/templates/index.html b/backend/templates/index.html index a7532e4f..ceff900d 100644 --- a/backend/templates/index.html +++ b/backend/templates/index.html @@ -4,33 +4,20 @@ - Lang Learn + {page_metadata['title']} + {% render_bundle 'index' 'css' %} -
- {% render_bundle 'undefined' %} - {% render_bundle 'runtime~main' %} - {% render_bundle 'main' %} - - - +
+

Loading...

+
+ {{app_data|json_script:"app_data"}} + {{app_component|json_script:"app_component"}} + {% render_bundle 'index' 'js' %} + + \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index 8069f66e..00000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,154 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# PyCharm (but not everything!) -.idea/workspace.xml -.idea/pylint.xml -.idea/dataSources.xml -.idea/dataSources.local.xml -.idea/usage.statistics.xml -.idea/tasks.xml -.idea/runConfigurations - -# dependencies -frontend/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -build/ -static/ - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local -npm-debug.log* -yarn-debug.log* -yarn-error.log* -*.swp - -# Build byproducts -webpack-stats-dev.json -webpack-stats.json diff --git a/frontend/components/ErrorNotFound.js b/frontend/components/ErrorNotFound.js new file mode 100644 index 00000000..7e860f63 --- /dev/null +++ b/frontend/components/ErrorNotFound.js @@ -0,0 +1,13 @@ +import React from "react"; + +const ErrorNotFound = () => { + + return ( +
+

No React Component found.

+

This is likely an error in development.

+
+ ); +}; + +export default ErrorNotFound; \ No newline at end of file diff --git a/frontend/components/ExampleId.js b/frontend/components/ExampleId.js new file mode 100644 index 00000000..1be07f31 --- /dev/null +++ b/frontend/components/ExampleId.js @@ -0,0 +1,34 @@ +import React, {useState} from "react"; +import * as PropTypes from "prop-types"; +import STYLES from "./ExampleId.module.scss"; + +const ExampleId = ({id}) => { + + const [tracker, setTracker] = useState(0); + + const onButtonClick = () => { + setTracker(previousState => previousState + 1); + }; + + return ( +
+

This is the Example ID page.

+

+ This page demonstrates passing view parameters from Django to React + and a very simple state. +

+

View params:

+
    +
  • ID: {id}
  • +
+

Example state: {tracker}

+ +
+ ); +}; + +ExampleId.propTypes = { + id: PropTypes.number +}; + +export default ExampleId; diff --git a/frontend/components/ExampleId.module.scss b/frontend/components/ExampleId.module.scss new file mode 100644 index 00000000..727508b8 --- /dev/null +++ b/frontend/components/ExampleId.module.scss @@ -0,0 +1,4 @@ +.list { + list-style: none; + padding: 0.5rem; +} \ No newline at end of file diff --git a/frontend/components/global/Base.js b/frontend/components/global/Base.js new file mode 100644 index 00000000..e894ff3c --- /dev/null +++ b/frontend/components/global/Base.js @@ -0,0 +1,22 @@ +import React from "react"; +import Nav from "./Nav"; +import STYLES from "./Base.module.scss"; +import * as PropTypes from "prop-types"; + +const Base = ({children}) => { + return( + <> +