├── .gitignore ├── CHANGES.rst ├── LICENSE.txt ├── Makefile ├── README.md ├── RELEASING.md ├── daily-makefile-run.yml ├── logo.png ├── project-makefile.yml ├── project.mk ├── project_makefile ├── NotoSans-Regular.ttf ├── __init__.py ├── logo.py └── write_makefile.py ├── pyproject.toml ├── screenshots ├── screenshot01.png ├── screenshot02.png ├── screenshot03.png ├── screenshot04.png ├── screenshot05.png ├── screenshot06.png └── screenshot07.png └── test ├── django-init-minimal ├── .babelrc ├── .browserslistrc ├── .eslintrc ├── .gitignore ├── .nvmrc ├── .stylelintrc.json ├── Dockerfile ├── backend │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── asgi.py │ ├── settings.py │ ├── settings │ │ ├── base.py │ │ ├── dev.py │ │ └── production.py │ ├── templates │ │ ├── allauth │ │ │ └── layouts │ │ │ │ └── base.html │ │ ├── base.html │ │ ├── favicon.html │ │ ├── footer.html │ │ ├── header.html │ │ └── offcanvas.html │ ├── urls.py │ ├── utils.py │ └── wsgi.py ├── docker-compose.yml ├── frontend │ ├── .babelrc │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── src │ │ ├── application │ │ │ ├── README.md │ │ │ ├── app.js │ │ │ └── config.js │ │ ├── components │ │ │ ├── Clock.js │ │ │ ├── ErrorBoundary.js │ │ │ ├── README.md │ │ │ ├── UserMenu.js │ │ │ ├── index.js │ │ │ └── jumbotron.js │ │ ├── context │ │ │ ├── UserContextProvider.js │ │ │ └── index.js │ │ ├── dataComponents.js │ │ ├── styles │ │ │ ├── index.scss │ │ │ └── theme-blue.scss │ │ └── utils │ │ │ └── themeToggler.js │ ├── vendors │ │ ├── .gitkeep │ │ └── images │ │ │ ├── .gitkeep │ │ │ ├── sample.jpg │ │ │ └── webpack.png │ └── webpack │ │ ├── webpack.common.js │ │ ├── webpack.config.dev.js │ │ ├── webpack.config.prod.js │ │ └── webpack.config.watch.js ├── home │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── home.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── manage.py ├── package-lock.json ├── package.json ├── postcss.config.js ├── requirements-test.txt ├── requirements.txt └── siteuser │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ ├── 0001_initial.py │ └── __init__.py │ ├── models.py │ ├── templates │ ├── user.html │ └── user_edit.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── django-init-wagtail ├── .babelrc ├── .browserslistrc ├── .dockerignore ├── .eslintrc ├── .gitignore ├── .nvmrc ├── .stylelintrc.json ├── Dockerfile ├── backend │ ├── __init__.py │ ├── admin.py │ ├── api.py │ ├── apps.py │ ├── serializers.py │ ├── settings │ │ ├── __init__.py │ │ ├── base.py │ │ ├── dev.py │ │ └── production.py │ ├── templates │ │ ├── 404.html │ │ ├── 500.html │ │ ├── allauth │ │ │ └── layouts │ │ │ │ └── base.html │ │ ├── base.html │ │ ├── favicon.html │ │ ├── footer.html │ │ ├── header.html │ │ └── offcanvas.html │ ├── urls.py │ ├── utils.py │ └── wsgi.py ├── contactpage │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── contactpage │ │ │ ├── contact_page.html │ │ │ └── contact_page_landing.html │ ├── tests.py │ └── views.py ├── docker-compose.yml ├── frontend │ ├── .babelrc │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── src │ │ ├── application │ │ │ ├── README.md │ │ │ ├── app.js │ │ │ └── config.js │ │ ├── components │ │ │ ├── Clock.js │ │ │ ├── ErrorBoundary.js │ │ │ ├── README.md │ │ │ ├── UserMenu.js │ │ │ ├── index.js │ │ │ └── jumbotron.js │ │ ├── context │ │ │ ├── UserContextProvider.js │ │ │ └── index.js │ │ ├── dataComponents.js │ │ ├── styles │ │ │ ├── index.scss │ │ │ └── theme-blue.scss │ │ └── utils │ │ │ └── themeToggler.js │ ├── vendors │ │ ├── .gitkeep │ │ └── images │ │ │ ├── .gitkeep │ │ │ ├── sample.jpg │ │ │ └── webpack.png │ └── webpack │ │ ├── webpack.common.js │ │ ├── webpack.config.dev.js │ │ ├── webpack.config.prod.js │ │ └── webpack.config.watch.js ├── home │ ├── __init__.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_create_homepage.py │ │ ├── 0003_alter_homepage_options_homepage_marketing_blocks.py │ │ └── __init__.py │ ├── models.py │ └── templates │ │ ├── blocks │ │ ├── carousel_block.html │ │ └── marketing_block.html │ │ └── home │ │ └── home_page.html ├── logging_demo │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── manage.py ├── model_form_demo │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ ├── model_form_demo_detail.html │ │ ├── model_form_demo_form.html │ │ └── model_form_demo_list.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── package-lock.json ├── package.json ├── payments │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_set_stripe_api_keys.py │ │ ├── 0003_create_initial_products.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── payments │ │ │ ├── cancel.html │ │ │ ├── checkout.html │ │ │ ├── product_detail.html │ │ │ ├── product_list.html │ │ │ └── success.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── postcss.config.js ├── privacypage │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── privacy_page.html │ ├── tests.py │ └── views.py ├── requirements-test.txt ├── requirements.txt ├── search │ ├── __init__.py │ ├── templates │ │ └── search │ │ │ └── search.html │ ├── urls.py │ └── views.py ├── sitepage │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── sitepage │ │ │ └── site_page.html │ ├── tests.py │ └── views.py ├── siteuser │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ ├── user.html │ │ └── user_edit.html │ ├── tests.py │ ├── urls.py │ └── views.py └── unit_test_demo │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ ├── 0001_initial.py │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py └── django-init ├── .babelrc ├── .browserslistrc ├── .eslintrc ├── .gitignore ├── .nvmrc ├── .stylelintrc.json ├── Dockerfile ├── backend ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── asgi.py ├── serializers.py ├── settings │ ├── base.py │ ├── dev.py │ └── production.py ├── templates │ ├── allauth │ │ └── layouts │ │ │ └── base.html │ ├── base.html │ ├── favicon.html │ ├── footer.html │ ├── header.html │ └── offcanvas.html ├── urls.py ├── utils.py └── wsgi.py ├── docker-compose.yml ├── frontend ├── .babelrc ├── .eslintrc ├── .gitignore ├── README.md ├── src │ ├── application │ │ ├── README.md │ │ ├── app.js │ │ └── config.js │ ├── components │ │ ├── Clock.js │ │ ├── ErrorBoundary.js │ │ ├── README.md │ │ ├── UserMenu.js │ │ ├── index.js │ │ └── jumbotron.js │ ├── context │ │ ├── UserContextProvider.js │ │ └── index.js │ ├── dataComponents.js │ ├── styles │ │ ├── index.scss │ │ └── theme-blue.scss │ └── utils │ │ └── themeToggler.js ├── vendors │ ├── .gitkeep │ └── images │ │ ├── .gitkeep │ │ ├── sample.jpg │ │ └── webpack.png └── webpack │ ├── webpack.common.js │ ├── webpack.config.dev.js │ ├── webpack.config.prod.js │ └── webpack.config.watch.js ├── home ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── templates │ └── home.html ├── tests.py ├── urls.py └── views.py ├── manage.py ├── package-lock.json ├── package.json ├── postcss.config.js ├── requirements-test.txt ├── requirements.txt ├── search ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ └── __init__.py ├── models.py ├── templates │ └── search.html ├── tests.py ├── urls.py ├── utils.py └── views.py └── siteuser ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations ├── 0001_initial.py └── __init__.py ├── models.py ├── templates ├── user.html └── user_edit.html ├── tests.py ├── urls.py └── views.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | dist/ 4 | node_modules/ 5 | _build/ 6 | .elasticbeanstalk/ 7 | db.sqlite3 8 | static/ 9 | backend/inituser 10 | backend/var 11 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 0.1.0 (202X-XX-XX) 5 | ------------------ 6 | 7 | - Rebased in January 2024 with commitment to meaningful commit messages (pun intended) 8 | - Settled on supporting three project types: 9 | - django-init 10 | - django-init-minimal 11 | - django-init-wagtail 12 | 13 | 14 | 0.0.9 (2022-03-06) 15 | ------------------ 16 | 17 | - Update docs 18 | 19 | - Add RELEASING.md 20 | 21 | 0.0.8 (2022-03-05) 22 | ------------------ 23 | 24 | - Update docs 25 | 26 | 0.0.8 (2022-03-05) 27 | ------------------ 28 | 29 | - Remove ``django-init`` in favor of ``wagtail-init`` 30 | 31 | - Add PHONY for ``django-init: wagtail-init`` 32 | 33 | - AWS Elastic Beanstalk 34 | 35 | - Add PLATFORM env var 36 | 37 | 0.0.7 (2022-03-05) 38 | ------------------ 39 | 40 | - Brown bag 41 | 42 | 0.0.6 (2022-03-05) 43 | ------------------ 44 | 45 | - Brown bag 46 | 47 | 0.0.5 (2022-03-05) 48 | ------------------ 49 | 50 | - Brown bag 51 | 52 | 0.0.4 (2021-10-12) 53 | ------------------ 54 | 55 | - AWS Elastic Beanstalk 56 | 57 | - Add eb-init and eb-deploy 58 | - Add env check for eb-create 59 | 60 | - Vagrant 61 | 62 | - Removed 63 | 64 | 0.0.3 (2021-10-10) 65 | ------------------ 66 | 67 | - Fix install 68 | 69 | 0.0.2 (2021-10-10) 70 | ------------------ 71 | 72 | - Fix CHANGES 73 | 74 | 0.0.1 (2021-10-10) 75 | ------------------ 76 | 77 | - Every commit from Tue Jan 12 2016 to Sat Oct 9 2021 78 | - Add Python installer 79 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016—2024 Jeffrey A. Clark (Alex) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Makefile 2 | 3 | [![Project Makefile](https://github.com/aclark4life/project-makefile/actions/workflows/project-makefile.yml/badge.svg)](https://github.com/aclark4life/project-makefile/actions) 4 | 5 | > "I like to type make `` to perform tasks. 🤷" —Alex 6 | 7 | ## Installation 8 | 9 | ```bash 10 | curl -O https://raw.githubusercontent.com/aclark4life/project-makefile/main/Makefile 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```bash 16 | $ make help 17 | Project Makefile 18 | 19 | "I like to type make to perform tasks. 🤷" —Alex 20 | 21 | Usage: make [target2 ...] 22 | Examples: 23 | make help Print this message 24 | make list-targets List all targets 25 | make django-init Install Django 26 | make django-init-minimal Install Django with minimal dependencies 27 | make django-init-wagtail Install Wagtail 28 | ``` 29 | 30 | ## Customization 31 | 32 | ```bash 33 | $ make project.mk 34 | $ cat project.mk 35 | # Custom Makefile 36 | # Add your custom makefile commands here 37 | # 38 | # PROJECT_NAME := my-new-project 39 | ``` 40 | 41 | ## Screenshots 42 | 43 | ### Initial Setup 44 | ![Screenshot](screenshots/screenshot07.png) 45 | ![Screenshot](screenshots/screenshot05.png) 46 | ![Screenshot](screenshots/screenshot06.png) 47 | 48 | ### Home Page 49 | ![Screenshot](screenshots/screenshot01.png) 50 | 51 | ### Login Page 52 | ![Screenshot](screenshots/screenshot02.png) 53 | 54 | ### Logged In 55 | ![Screenshot](screenshots/screenshot03.png) 56 | 57 | ### Dropdown Menu 58 | ![Screenshot](screenshots/screenshot04.png) 59 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | ## Releasing 2 | 3 | # Steps 4 | 5 | - Update version 6 | 7 | - CHANGES.rst 8 | - install.py 9 | - setup.py 10 | - RELEASING.md 11 | 12 | - Tag release 13 | 14 | ``` 15 | git tag 0.0.9 16 | git push --tags 17 | ``` 18 | 19 | - Create distribution 20 | 21 | ``` 22 | python setup.py sdist --format=zip 23 | ``` 24 | 25 | - Upload distribution 26 | 27 | ``` 28 | twine upload dist/project-makefile-0.0.9.zip 29 | ``` 30 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/logo.png -------------------------------------------------------------------------------- /project-makefile.yml: -------------------------------------------------------------------------------- 1 | name: Project Makefile 2 | run-name: ${{ github.actor }} is testing project-makefile 🚀 3 | 4 | on: 5 | push: 6 | branches: [ "main" ] 7 | pull_request: 8 | branches: [ "main" ] 9 | 10 | defaults: 11 | run: 12 | shell: bash 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: ubuntu-latest 18 | services: 19 | postgres: 20 | image: postgres 21 | env: 22 | POSTGRES_PASSWORD: postgres 23 | # Set health checks to wait until postgres has started 24 | options: >- 25 | --health-cmd pg_isready 26 | --health-interval 10s 27 | --health-timeout 5s 28 | --health-retries 5 29 | ports: 30 | - 5432:5432 31 | 32 | strategy: 33 | max-parallel: 4 34 | matrix: 35 | node-version: [22.x] 36 | python-version: [3.12] 37 | dir: ['./test/django-init-wagtail/', './test/django-init/', './test/django-init-minimal/'] 38 | 39 | steps: 40 | - uses: actions/checkout@v4 41 | 42 | - name: Set up Python ${{ matrix.python-version }} 43 | uses: actions/setup-python@v4 44 | with: 45 | python-version: ${{ matrix.python-version }} 46 | 47 | - name: Setup Node.js ${{ matrix.node-version }} 48 | uses: actions/setup-node@v4 49 | with: 50 | node-version: ${{ matrix.node-version }} 51 | cache: 'npm' 52 | cache-dependency-path: '${{ matrix.dir }}/package.json' 53 | 54 | - run: make -f ../../Makefile -f ../../project.mk $(basename ${{ matrix.dir }}) 55 | env: 56 | POSTGRES_HOST: postgres 57 | POSTGRES_PORT: 5432 58 | working-directory: ${{ matrix.dir }} 59 | -------------------------------------------------------------------------------- /project.mk: -------------------------------------------------------------------------------- 1 | # Custom Makefile 2 | # Add your custom makefile commands here 3 | # 4 | # PROJECT_NAME := my-new-project 5 | 6 | # ----------------------------------------------------------------------------- 7 | # For GitHub Actions 8 | # ----------------------------------------------------------------------------- 9 | 10 | define DJANGO_SETTINGS_DATABASE 11 | POSTGRES_HOST = os.environ.get("POSTGRES_HOST", "postgres") 12 | POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "postgres") 13 | DATABASE_URL = os.environ.get("DATABASE_URL", f"postgres://postgres:{POSTGRES_PASSWORD}@{POSTGRES_HOST}:/postgres") 14 | DATABASES["default"] = dj_database_url.parse(DATABASE_URL) 15 | endef 16 | 17 | export DJANGO_SETTINGS_DATABASE 18 | 19 | django-settings-base: django-settings-base-default 20 | @echo "$$DJANGO_SETTINGS_DATABASE" >> $(DJANGO_SETTINGS_BASE_FILE) 21 | django-test: 22 | @echo "Running tests..." 23 | @echo "Tests passed!" 24 | 25 | # ----------------------------------------------------------------------------- 26 | # For development 27 | # ----------------------------------------------------------------------------- 28 | 29 | edit: 30 | $(EDITOR) Makefile 31 | 32 | review: 33 | $(EDITOR_REVIEW) Makefile 34 | 35 | -------------------------------------------------------------------------------- /project_makefile/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/project_makefile/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /project_makefile/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/project_makefile/__init__.py -------------------------------------------------------------------------------- /project_makefile/logo.py: -------------------------------------------------------------------------------- 1 | # This Python file uses the following encoding: utf-8 2 | 3 | # Via https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html# 4 | 5 | # Fonts via https://github.com/python-pillow/Pillow/tree/master/Tests/fonts 6 | 7 | 8 | from PIL import Image, ImageDraw, ImageFont 9 | 10 | 11 | # create an image 12 | def create_image(filename): 13 | """ 14 | Create image 15 | """ 16 | x = 400 17 | y = 400 18 | 19 | text = "$ make …" 20 | 21 | # bg white 22 | out = Image.new("RGB", (x, y), (255, 255, 255)) 23 | 24 | # bg black 25 | # out = Image.new("RGB", (x, y), (0, 0, 0)) 26 | 27 | # get a font 28 | # fontsize = int(x/2) 29 | fontsize = int(x / 5) 30 | # fontsize = int(x/6) 31 | 32 | fnt = ImageFont.truetype(filename, fontsize) 33 | print(filename) 34 | # get a drawing context 35 | d = ImageDraw.Draw(out) 36 | 37 | # draw multiline text 38 | # xoffset = (x/8) + (x/16) 39 | xoffset = x / 16 40 | 41 | # yoffset = y/8 42 | yoffset = y / 3 43 | 44 | # text black 45 | d.multiline_text((xoffset, yoffset), text, font=fnt, fill=(0, 0, 0)) 46 | 47 | # text white 48 | # d.multiline_text((xoffset, yoffset), text, font=fnt, fill=(255, 255, 255)) 49 | 50 | # fontname = filename.split(".")[0] # Remove file extension 51 | out.save("logo.png", "PNG") 52 | 53 | 54 | if __name__ == "__main__": 55 | # files = [f for f in os.listdir(".") if f.endswith("ttf")] 56 | # for filename in files: 57 | 58 | create_image("NotoSans-Regular.ttf") 59 | -------------------------------------------------------------------------------- /project_makefile/write_makefile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | def write_makefile(): 6 | current_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 7 | makefile_src = os.path.join(current_dir, "Makefile") 8 | makefile_dst = os.path.join(os.getcwd(), "Makefile") 9 | 10 | shutil.copyfile(makefile_src, makefile_dst) 11 | print(f"Makefile has been written to {makefile_dst}") 12 | 13 | 14 | if __name__ == "__main__": 15 | write_makefile() 16 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "project-makefile" 7 | version = "0.1.0" 8 | description = "A package to write a Makefile to the file system" 9 | readme = "README.md" 10 | requires-python = ">=3.6" 11 | license = {text = "MIT"} 12 | authors = [{name = "Your Name", email = "your.email@example.com"}] 13 | 14 | [project.scripts] 15 | project-makefile = "write_makefile.write_makefile:write_makefile" 16 | 17 | [tool.setuptools] 18 | packages = ["project_makefile"] 19 | 20 | [tool.setuptools.package-data] 21 | project_makefile = ["../Makefile"] 22 | -------------------------------------------------------------------------------- /screenshots/screenshot01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/screenshots/screenshot01.png -------------------------------------------------------------------------------- /screenshots/screenshot02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/screenshots/screenshot02.png -------------------------------------------------------------------------------- /screenshots/screenshot03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/screenshots/screenshot03.png -------------------------------------------------------------------------------- /screenshots/screenshot04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/screenshots/screenshot04.png -------------------------------------------------------------------------------- /screenshots/screenshot05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/screenshots/screenshot05.png -------------------------------------------------------------------------------- /screenshots/screenshot06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/screenshots/screenshot06.png -------------------------------------------------------------------------------- /screenshots/screenshot07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/screenshots/screenshot07.png -------------------------------------------------------------------------------- /test/django-init-minimal/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "usage", 7 | "corejs": "3.0.0" 8 | } 9 | ] 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-syntax-dynamic-import", 13 | "@babel/plugin-transform-class-properties" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/django-init-minimal/.browserslistrc: -------------------------------------------------------------------------------- 1 | [production staging] 2 | >5% 3 | last 2 versions 4 | not ie > 0 5 | not ie_mob > 0 6 | Firefox ESR 7 | 8 | [development] 9 | last 1 chrome version 10 | last 1 firefox version 11 | last 1 edge version 12 | -------------------------------------------------------------------------------- /test/django-init-minimal/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@babel/eslint-parser", 3 | "extends": [ 4 | "eslint:recommended" 5 | ], 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": 8, 12 | "sourceType": "module", 13 | "requireConfigFile": false 14 | }, 15 | "rules": { 16 | "semi": 2 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/django-init-minimal/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/django-init-minimal/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/iron 2 | -------------------------------------------------------------------------------- /test/django-init-minimal/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard-scss", 3 | "rules": { 4 | "at-rule-no-unknown": null, 5 | "scss/at-rule-no-unknown": true, 6 | "scss/at-import-partial-extension": null 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/django-init-minimal/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazonlinux:2023 2 | RUN dnf install -y shadow-utils python3.11 python3.11-pip make nodejs20-npm nodejs postgresql15 postgresql15-server 3 | USER postgres 4 | RUN initdb -D /var/lib/pgsql/data 5 | USER root 6 | RUN useradd wagtail 7 | EXPOSE 8000 8 | ENV PYTHONUNBUFFERED=1 PORT=8000 9 | COPY requirements.txt / 10 | RUN python3.11 -m pip install -r /requirements.txt 11 | WORKDIR /app 12 | RUN chown wagtail:wagtail /app 13 | COPY --chown=wagtail:wagtail . . 14 | USER wagtail 15 | RUN npm-20 install; npm-20 run build 16 | RUN python3.11 manage.py collectstatic --noinput --clear 17 | CMD set -xe; pg_ctl -D /var/lib/pgsql/data -l /tmp/logfile start; python3.11 manage.py migrate --noinput; gunicorn backend.wsgi:application 18 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-minimal/backend/__init__.py -------------------------------------------------------------------------------- /test/django-init-minimal/backend/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin import AdminSite 2 | 3 | 4 | class CustomAdminSite(AdminSite): 5 | site_header = "Project Makefile" 6 | site_title = "Project Makefile" 7 | index_title = "Project Makefile" 8 | 9 | 10 | custom_admin_site = CustomAdminSite(name="custom_admin") 11 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/apps.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin.apps import AdminConfig 2 | 3 | 4 | class CustomAdminConfig(AdminConfig): 5 | default_site = "backend.admin.CustomAdminSite" 6 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for backend project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/settings/dev.py: -------------------------------------------------------------------------------- 1 | # project-makefile 2 | from .base import * # noqa 3 | 4 | # SECURITY WARNING: don't run with debug turned on in production! 5 | DEBUG = True 6 | 7 | # SECURITY WARNING: define the correct hosts in production! 8 | ALLOWED_HOSTS = ["*"] 9 | 10 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" 11 | 12 | try: 13 | from .local import * # noqa 14 | except ImportError: 15 | pass 16 | 17 | LOGGING = { 18 | "version": 1, 19 | "disable_existing_loggers": False, 20 | "formatters": { 21 | "verbose": { 22 | "format": "{levelname} {asctime} {module} {message}", 23 | "style": "{", 24 | }, 25 | "simple": { 26 | "format": "{levelname} {message}", 27 | "style": "{", 28 | }, 29 | }, 30 | "handlers": { 31 | "console": { 32 | "level": "DEBUG", 33 | "class": "logging.StreamHandler", 34 | "formatter": "verbose", 35 | }, 36 | }, 37 | "loggers": { 38 | "django": { 39 | "handlers": ["console"], 40 | "level": "DEBUG", 41 | "propagate": True, 42 | }, 43 | }, 44 | } 45 | 46 | INTERNAL_IPS = [ 47 | "127.0.0.1", 48 | ] 49 | 50 | MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware") # noqa 51 | MIDDLEWARE.append("hijack.middleware.HijackUserMiddleware") # noqa 52 | INSTALLED_APPS.append("django.contrib.admindocs") # noqa 53 | SECRET_KEY = "mPRbSXD5/ACz0uCuFGnGEu/NNBYE3exJJRykmRhn32S9C1u/Txmo8dwR15kn6KLJ" 54 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/settings/production.py: -------------------------------------------------------------------------------- 1 | from .base import * # noqa 2 | from backend.utils import get_ec2_metadata 3 | 4 | DEBUG = False 5 | 6 | try: 7 | from .local import * # noqa 8 | except ImportError: 9 | pass 10 | 11 | LOCAL_IPV4 = get_ec2_metadata() 12 | ALLOWED_HOSTS.append(LOCAL_IPV4) # noqa 13 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/templates/allauth/layouts/base.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/templates/favicon.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/templates/footer.html: -------------------------------------------------------------------------------- 1 |
2 |

© {% now "Y" %} {{ current_site.site_name|default:"Project Makefile" }}

3 |
    4 |
  • 5 | Home 7 |
  • 8 | {% for child in current_site.root_page.get_children %} 9 |
  • 10 | {{ child }} 12 |
  • 13 | {% endfor %} 14 |
15 |
16 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/templates/offcanvas.html: -------------------------------------------------------------------------------- 1 |
5 | 14 |
15 | 37 |
38 |
39 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | from django.conf import settings 4 | 5 | urlpatterns = [ 6 | path("django/", admin.site.urls), 7 | ] 8 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/utils.py: -------------------------------------------------------------------------------- 1 | from django.urls import URLResolver 2 | import requests 3 | 4 | 5 | def get_ec2_metadata(): 6 | try: 7 | # Step 1: Get the token 8 | token_url = "http://169.254.169.254/latest/api/token" 9 | headers = {"X-aws-ec2-metadata-token-ttl-seconds": "21600"} 10 | response = requests.put(token_url, headers=headers) 11 | response.raise_for_status() # Raise an error for bad responses 12 | 13 | token = response.text 14 | 15 | # Step 2: Use the token to get the instance metadata 16 | metadata_url = "http://169.254.169.254/latest/meta-data/local-ipv4" 17 | headers = {"X-aws-ec2-metadata-token": token} 18 | response = requests.get(metadata_url, headers=headers) 19 | response.raise_for_status() # Raise an error for bad responses 20 | 21 | metadata = response.text 22 | return metadata 23 | except requests.RequestException as e: 24 | print(f"Error retrieving EC2 metadata: {e}") 25 | return None 26 | 27 | 28 | # Function to remove a specific URL pattern based on its route (including catch-all) 29 | def remove_urlpattern(urlpatterns, route_to_remove): 30 | urlpatterns[:] = [ 31 | urlpattern 32 | for urlpattern in urlpatterns 33 | if not ( 34 | isinstance(urlpattern, URLResolver) 35 | and urlpattern.pattern._route == route_to_remove 36 | ) 37 | ] 38 | -------------------------------------------------------------------------------- /test/django-init-minimal/backend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for backend project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /test/django-init-minimal/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | db: 5 | image: postgres:latest 6 | volumes: 7 | - postgres_data:/var/lib/postgresql/data 8 | environment: 9 | POSTGRES_DB: project 10 | POSTGRES_USER: admin 11 | POSTGRES_PASSWORD: admin 12 | 13 | web: 14 | build: . 15 | command: sh -c "python manage.py migrate && gunicorn project.wsgi:application -b 0.0.0.0:8000" 16 | volumes: 17 | - .:/app 18 | ports: 19 | - "8000:8000" 20 | depends_on: 21 | - db 22 | environment: 23 | DATABASE_URL: postgres://admin:admin@db:5432/project 24 | 25 | volumes: 26 | postgres_data: 27 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-react", 5 | ], 6 | [ 7 | "@babel/preset-env", 8 | { 9 | "useBuiltIns": "usage", 10 | "corejs": "3.0.0" 11 | } 12 | ] 13 | ], 14 | "plugins": [ 15 | "@babel/plugin-syntax-dynamic-import", 16 | "@babel/plugin-transform-class-properties" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended" 10 | ], 11 | "overrides": [ 12 | { 13 | "env": { 14 | "node": true 15 | }, 16 | "files": [ 17 | ".eslintrc.{js,cjs}" 18 | ], 19 | "parserOptions": { 20 | "sourceType": "script" 21 | } 22 | } 23 | ], 24 | "parserOptions": { 25 | "ecmaVersion": "latest", 26 | "sourceType": "module" 27 | }, 28 | "plugins": [ 29 | "react" 30 | ], 31 | "rules": { 32 | "no-unused-vars": "off" 33 | }, 34 | settings: { 35 | react: { 36 | version: 'detect', 37 | }, 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # production 5 | build 6 | 7 | # misc 8 | .DS_Store 9 | 10 | npm-debug.log 11 | yarn-error.log 12 | yarn.lock 13 | .yarnclean 14 | .vscode 15 | .idea 16 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This project was created with [python-webpack-boilerplate](https://github.com/AccordBox/python-webpack-boilerplate) 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm run start` 10 | 11 | `npm run start` will launch a server process, which makes `live reloading` possible. 12 | 13 | If you change JS or SCSS files, the web page would auto refresh after the change. Now the server is working on port 9091 by default, but you can change it in `webpack/webpack.config.dev.js` 14 | 15 | ### `npm run watch` 16 | 17 | run webpack in `watch` mode. 18 | 19 | ### `npm run build` 20 | 21 | [production mode](https://webpack.js.org/guides/production/), Webpack would focus on minified bundles, lighter weight source maps, and optimized assets to improve load time. 22 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/application/README.md: -------------------------------------------------------------------------------- 1 | When Webpacker compiles your JavaScript code, it scans the `src/application` directory for files with the .js extension and automatically includes them as entry points for the Webpack bundling process. 2 | 3 | For the `application/app.js`, you can import it in template like this: 4 | 5 | ``` 6 | {% javascript_pack 'app' attrs='charset="UTF-8"' %} 7 | ``` 8 | 9 | In most cases, you do not need to create another file in this directory. 10 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/application/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import 'bootstrap'; 4 | // import '@fortawesome/fontawesome-free/js/fontawesome'; 5 | // import '@fortawesome/fontawesome-free/js/solid'; 6 | // import '@fortawesome/fontawesome-free/js/regular'; 7 | // import '@fortawesome/fontawesome-free/js/brands'; 8 | import getDataComponents from '../dataComponents'; 9 | import UserContextProvider from '../context'; 10 | import * as components from '../components'; 11 | import "../styles/index.scss"; 12 | import "../styles/theme-blue.scss"; 13 | import "./config"; 14 | 15 | const { ErrorBoundary } = components; 16 | const dataComponents = getDataComponents(components); 17 | const container = document.getElementById('app'); 18 | const root = createRoot(container); 19 | const App = () => ( 20 | 21 | 22 | {dataComponents} 23 | 24 | 25 | ); 26 | root.render(); 27 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/application/config.js: -------------------------------------------------------------------------------- 1 | import '../utils/themeToggler.js'; 2 | // import '../utils/tinymce.js'; 3 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/components/Clock.js: -------------------------------------------------------------------------------- 1 | // Via ChatGPT 2 | import React, { useState, useEffect, useCallback, useRef } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const Clock = ({ color = '#fff' }) => { 6 | const [date, setDate] = useState(new Date()); 7 | const [blink, setBlink] = useState(true); 8 | const timerID = useRef(); 9 | 10 | const tick = useCallback(() => { 11 | setDate(new Date()); 12 | setBlink(prevBlink => !prevBlink); 13 | }, []); 14 | 15 | useEffect(() => { 16 | timerID.current = setInterval(() => tick(), 1000); 17 | 18 | // Return a cleanup function to be run on component unmount 19 | return () => clearInterval(timerID.current); 20 | }, [tick]); 21 | 22 | const formattedDate = date.toLocaleDateString(undefined, { 23 | weekday: 'short', 24 | year: 'numeric', 25 | month: 'short', 26 | day: 'numeric', 27 | }); 28 | 29 | const formattedTime = date.toLocaleTimeString(undefined, { 30 | hour: 'numeric', 31 | minute: 'numeric', 32 | }); 33 | 34 | return ( 35 | <> 36 |
{formattedDate} {formattedTime}
37 | 38 | ); 39 | }; 40 | 41 | Clock.propTypes = { 42 | color: PropTypes.string, 43 | }; 44 | 45 | export default Clock; 46 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/components/ErrorBoundary.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class ErrorBoundary extends Component { 5 | constructor (props) { 6 | super(props); 7 | this.state = { hasError: false }; 8 | } 9 | 10 | static getDerivedStateFromError () { 11 | return { hasError: true }; 12 | } 13 | 14 | componentDidCatch (error, info) { 15 | const { onError } = this.props; 16 | console.error(error); 17 | onError && onError(error, info); 18 | } 19 | 20 | render () { 21 | const { children = null } = this.props; 22 | const { hasError } = this.state; 23 | 24 | return hasError ? null : children; 25 | } 26 | } 27 | 28 | ErrorBoundary.propTypes = { 29 | onError: PropTypes.func, 30 | children: PropTypes.node, 31 | }; 32 | 33 | export default ErrorBoundary; 34 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/components/README.md: -------------------------------------------------------------------------------- 1 | We recommend write Javascript here to make your code reusable. 2 | 3 | And you can check below packages to increase your productivity: 4 | 5 | 1. [Stimulus](https://stimulus.hotwired.dev/) 6 | 2. [Alpine.js](https://alpinejs.dev/) 7 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as ErrorBoundary } from './ErrorBoundary'; 2 | export { default as UserMenu } from './UserMenu'; 3 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/components/jumbotron.js: -------------------------------------------------------------------------------- 1 | class Jumbotron { 2 | static selector() { 3 | return "[data-jumbotron]"; 4 | } 5 | 6 | constructor(node) { 7 | this.node = node; 8 | console.log(`Jumbotron initialized for node: ${node}`); 9 | // do something here 10 | } 11 | } 12 | 13 | export default Jumbotron; 14 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/context/UserContextProvider.js: -------------------------------------------------------------------------------- 1 | // UserContextProvider.js 2 | import React, { createContext, useContext, useState } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const UserContext = createContext(); 6 | 7 | export const UserContextProvider = ({ children }) => { 8 | const [isAuthenticated, setIsAuthenticated] = useState(false); 9 | 10 | const login = () => { 11 | try { 12 | // Add logic to handle login, set isAuthenticated to true 13 | setIsAuthenticated(true); 14 | } catch (error) { 15 | console.error('Login error:', error); 16 | // Handle error, e.g., show an error message to the user 17 | } 18 | }; 19 | 20 | const logout = () => { 21 | try { 22 | // Add logic to handle logout, set isAuthenticated to false 23 | setIsAuthenticated(false); 24 | } catch (error) { 25 | console.error('Logout error:', error); 26 | // Handle error, e.g., show an error message to the user 27 | } 28 | }; 29 | 30 | return ( 31 | 32 | {children} 33 | 34 | ); 35 | }; 36 | 37 | UserContextProvider.propTypes = { 38 | children: PropTypes.node.isRequired, 39 | }; 40 | 41 | export const useUserContext = () => { 42 | const context = useContext(UserContext); 43 | 44 | if (!context) { 45 | throw new Error('useUserContext must be used within a UserContextProvider'); 46 | } 47 | 48 | return context; 49 | }; 50 | 51 | // Add PropTypes for the return value of useUserContext 52 | useUserContext.propTypes = { 53 | isAuthenticated: PropTypes.bool.isRequired, 54 | login: PropTypes.func.isRequired, 55 | logout: PropTypes.func.isRequired, 56 | }; 57 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/context/index.js: -------------------------------------------------------------------------------- 1 | export { UserContextProvider as default } from './UserContextProvider'; 2 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/dataComponents.js: -------------------------------------------------------------------------------- 1 | // Via pwellever 2 | import React from 'react'; 3 | import { createPortal } from 'react-dom'; 4 | 5 | const parseProps = data => Object.entries(data).reduce((result, [key, value]) => { 6 | if (value.toLowerCase() === 'true') { 7 | value = true; 8 | } else if (value.toLowerCase() === 'false') { 9 | value = false; 10 | } else if (value.toLowerCase() === 'null') { 11 | value = null; 12 | } else if (!isNaN(parseFloat(value)) && isFinite(value)) { 13 | // Parse numeric value 14 | value = parseFloat(value); 15 | } else if ( 16 | (value[0] === '[' && value.slice(-1) === ']') || (value[0] === '{' && value.slice(-1) === '}') 17 | ) { 18 | // Parse JSON strings 19 | value = JSON.parse(value); 20 | } 21 | 22 | result[key] = value; 23 | return result; 24 | }, {}); 25 | 26 | // This method of using portals instead of calling ReactDOM.render on individual components 27 | // ensures that all components are mounted under a single React tree, and are therefore able 28 | // to share context. 29 | 30 | export default function getPageComponents (components) { 31 | const getPortalComponent = domEl => { 32 | // The element's "data-component" attribute is used to determine which component to render. 33 | // All other "data-*" attributes are passed as props. 34 | const { component: componentName, ...rest } = domEl.dataset; 35 | const Component = components[componentName]; 36 | if (!Component) { 37 | console.error(`Component "${componentName}" not found.`); 38 | return null; 39 | } 40 | const props = parseProps(rest); 41 | domEl.innerHTML = ''; 42 | 43 | // eslint-disable-next-line no-unused-vars 44 | const { ErrorBoundary } = components; 45 | return createPortal( 46 | 47 | 48 | , 49 | domEl, 50 | ); 51 | }; 52 | 53 | return Array.from(document.querySelectorAll('[data-component]')).map(getPortalComponent); 54 | } 55 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | // If you comment out code below, bootstrap will use red as primary color 2 | // and btn-primary will become red 3 | 4 | // rimary: red; 5 | 6 | @import "~bootstrap/scss/bootstrap.scss"; 7 | 8 | .jumbotron { 9 | // should be relative path of the entry scss file 10 | background-image: url("../../vendors/images/sample.jpg"); 11 | background-size: cover; 12 | } 13 | 14 | #theme-toggler-authenticated:hover { 15 | cursor: pointer; /* Change cursor to pointer on hover */ 16 | color: #007bff; /* Change color on hover */ 17 | } 18 | 19 | #theme-toggler-anonymous:hover { 20 | cursor: pointer; /* Change cursor to pointer on hover */ 21 | color: #007bff; /* Change color on hover */ 22 | } 23 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/styles/theme-blue.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/scss/bootstrap.scss"; 2 | 3 | [data-bs-theme="blue"] { 4 | --bs-body-color: var(--bs-white); 5 | --bs-body-color-rgb: #{to-rgb($white)}; 6 | --bs-body-bg: var(--bs-blue); 7 | --bs-body-bg-rgb: #{to-rgb($blue)}; 8 | --bs-tertiary-bg: #{$blue-600}; 9 | 10 | .dropdown-menu { 11 | --bs-dropdown-bg: #{color-mix($blue-500, $blue-600)}; 12 | --bs-dropdown-link-active-bg: #{$blue-700}; 13 | } 14 | 15 | .btn-secondary { 16 | --bs-btn-bg: #{color-mix(ray-600, lue-400, .5)}; 17 | --bs-btn-border-color: #{rgba($white, .25)}; 18 | --bs-btn-hover-bg: #{color-adjust(color-mix(ray-600, lue-400, .5), 5%)}; 19 | --bs-btn-hover-border-color: #{rgba($white, .25)}; 20 | --bs-btn-active-bg: #{color-adjust(color-mix(ray-600, lue-400, .5), 10%)}; 21 | --bs-btn-active-border-color: #{rgba($white, .5)}; 22 | --bs-btn-focus-border-color: #{rgba($white, .5)}; 23 | 24 | // --bs-btn-focus-box-shadow: 0 0 0 .25rem rgba(255, 255, 255, 20%); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/src/utils/themeToggler.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function () { 2 | const rootElement = document.documentElement; 3 | const anonThemeToggle = document.getElementById('theme-toggler-anonymous'); 4 | const authThemeToggle = document.getElementById('theme-toggler-authenticated'); 5 | if (authThemeToggle) { 6 | localStorage.removeItem('data-bs-theme'); 7 | } 8 | const anonSavedTheme = localStorage.getItem('data-bs-theme'); 9 | if (anonSavedTheme) { 10 | rootElement.setAttribute('data-bs-theme', anonSavedTheme); 11 | } 12 | if (anonThemeToggle) { 13 | anonThemeToggle.addEventListener('click', function () { 14 | const currentTheme = rootElement.getAttribute('data-bs-theme') || 'light'; 15 | const newTheme = currentTheme === 'light' ? 'dark' : 'light'; 16 | rootElement.setAttribute('data-bs-theme', newTheme); 17 | localStorage.setItem('data-bs-theme', newTheme); 18 | }); 19 | } 20 | if (authThemeToggle) { 21 | const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value; 22 | authThemeToggle.addEventListener('click', function () { 23 | const currentTheme = rootElement.getAttribute('data-bs-theme') || 'light'; 24 | const newTheme = currentTheme === 'light' ? 'dark' : 'light'; 25 | fetch('/user/update_theme_preference/', { 26 | method: 'POST', 27 | headers: { 28 | 'Content-Type': 'application/json', 29 | 'X-CSRFToken': csrfToken, // Include the CSRF token in the headers 30 | }, 31 | body: JSON.stringify({ theme: newTheme }), 32 | }) 33 | .then(response => response.json()) 34 | .then(data => { 35 | rootElement.setAttribute('data-bs-theme', newTheme); 36 | }) 37 | .catch(error => { 38 | console.error('Error updating theme preference:', error); 39 | }); 40 | }); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/vendors/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-minimal/frontend/vendors/.gitkeep -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/vendors/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-minimal/frontend/vendors/images/.gitkeep -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/vendors/images/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-minimal/frontend/vendors/images/sample.jpg -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/vendors/images/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-minimal/frontend/vendors/images/webpack.png -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/webpack/webpack.common.js: -------------------------------------------------------------------------------- 1 | const glob = require("glob"); 2 | const Path = require("path"); 3 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 4 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 5 | const WebpackAssetsManifest = require("webpack-assets-manifest"); 6 | 7 | const getEntryObject = () => { 8 | const entries = {}; 9 | // for javascript/typescript entry file 10 | glob 11 | .sync(Path.join(__dirname, "../src/application/*.{js,ts}")) 12 | .forEach((path) => { 13 | const name = Path.basename(path); 14 | const extension = Path.extname(path); 15 | const entryName = name.replace(extension, ""); 16 | if (entryName in entries) { 17 | throw new Error(`Entry file conflict: ${entryName}`); 18 | } 19 | entries[entryName] = path; 20 | }); 21 | return entries; 22 | }; 23 | 24 | module.exports = { 25 | entry: getEntryObject(), 26 | output: { 27 | path: Path.join(__dirname, "../build"), 28 | filename: "js/[name].js", 29 | publicPath: "/static/", 30 | assetModuleFilename: "[path][name][ext]", 31 | }, 32 | optimization: { 33 | splitChunks: { 34 | chunks: "all", 35 | }, 36 | 37 | runtimeChunk: "single", 38 | }, 39 | plugins: [ 40 | new CleanWebpackPlugin(), 41 | new CopyWebpackPlugin({ 42 | patterns: [ 43 | { from: Path.resolve(__dirname, "../vendors"), to: "vendors" }, 44 | ], 45 | }), 46 | new WebpackAssetsManifest({ 47 | entrypoints: true, 48 | output: "manifest.json", 49 | writeToDisk: true, 50 | publicPath: true, 51 | }), 52 | ], 53 | resolve: { 54 | alias: { 55 | "~": Path.resolve(__dirname, "../src"), 56 | }, 57 | }, 58 | module: { 59 | rules: [ 60 | { 61 | test: /\.mjs$/, 62 | include: /node_modules/, 63 | type: "javascript/auto", 64 | }, 65 | { 66 | test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, 67 | type: "asset", 68 | }, 69 | ], 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/webpack/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const Path = require("path"); 2 | const Webpack = require("webpack"); 3 | const { merge } = require("webpack-merge"); 4 | const StylelintPlugin = require("stylelint-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | const ESLintPlugin = require("eslint-webpack-plugin"); 7 | 8 | const common = require("./webpack.common.js"); 9 | 10 | module.exports = merge(common, { 11 | target: "web", 12 | mode: "development", 13 | devtool: "inline-source-map", 14 | output: { 15 | chunkFilename: "js/[name].chunk.js", 16 | publicPath: "http://localhost:9091/", 17 | }, 18 | devServer: { 19 | hot: true, 20 | host: "0.0.0.0", 21 | port: 9091, 22 | headers: { 23 | "Access-Control-Allow-Origin": "*", 24 | }, 25 | devMiddleware: { 26 | writeToDisk: true, 27 | }, 28 | }, 29 | plugins: [ 30 | new Webpack.DefinePlugin({ 31 | "process.env.NODE_ENV": JSON.stringify("development"), 32 | }), 33 | new StylelintPlugin({ 34 | files: Path.resolve(__dirname, "../src/**/*.s?(a|c)ss"), 35 | }), 36 | new ESLintPlugin({ 37 | extensions: "js", 38 | emitWarning: true, 39 | files: Path.resolve(__dirname, "../src"), 40 | }), 41 | new MiniCssExtractPlugin({ 42 | filename: "css/[name].css", 43 | chunkFilename: "css/[id].css", 44 | }), 45 | ], 46 | module: { 47 | rules: [ 48 | { 49 | test: /\.html$/i, 50 | loader: "html-loader", 51 | }, 52 | { 53 | test: /\.js$/, 54 | include: Path.resolve(__dirname, "../src"), 55 | loader: "babel-loader", 56 | }, 57 | { 58 | test: /\.s?css$/i, 59 | use: [ 60 | MiniCssExtractPlugin.loader, 61 | { 62 | loader: "css-loader", 63 | options: { 64 | sourceMap: true, 65 | }, 66 | }, 67 | "postcss-loader", 68 | "sass-loader", 69 | ], 70 | }, 71 | ], 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/webpack/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const Webpack = require("webpack"); 2 | const { merge } = require("webpack-merge"); 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 4 | const common = require("./webpack.common.js"); 5 | 6 | module.exports = merge(common, { 7 | mode: "production", 8 | devtool: "source-map", 9 | bail: true, 10 | output: { 11 | filename: "js/[name].[chunkhash:8].js", 12 | chunkFilename: "js/[name].[chunkhash:8].chunk.js", 13 | }, 14 | plugins: [ 15 | new Webpack.DefinePlugin({ 16 | "process.env.NODE_ENV": JSON.stringify("production"), 17 | }), 18 | new MiniCssExtractPlugin({ 19 | filename: "css/[name].[contenthash].css", 20 | chunkFilename: "css/[id].[contenthash].css", 21 | }), 22 | ], 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.js$/, 27 | exclude: /node_modules/, 28 | use: "babel-loader", 29 | }, 30 | { 31 | test: /\.s?css/i, 32 | use: [ 33 | MiniCssExtractPlugin.loader, 34 | "css-loader", 35 | "postcss-loader", 36 | "sass-loader", 37 | ], 38 | }, 39 | ], 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /test/django-init-minimal/frontend/webpack/webpack.config.watch.js: -------------------------------------------------------------------------------- 1 | const Path = require("path"); 2 | const Webpack = require("webpack"); 3 | const { merge } = require("webpack-merge"); 4 | const StylelintPlugin = require("stylelint-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | const ESLintPlugin = require("eslint-webpack-plugin"); 7 | 8 | const common = require("./webpack.common.js"); 9 | 10 | module.exports = merge(common, { 11 | target: "web", 12 | mode: "development", 13 | devtool: "inline-source-map", 14 | output: { 15 | chunkFilename: "js/[name].chunk.js", 16 | }, 17 | plugins: [ 18 | new Webpack.DefinePlugin({ 19 | "process.env.NODE_ENV": JSON.stringify("development"), 20 | }), 21 | new StylelintPlugin({ 22 | files: Path.resolve(__dirname, "../src/**/*.s?(a|c)ss"), 23 | }), 24 | new ESLintPlugin({ 25 | extensions: "js", 26 | emitWarning: true, 27 | files: Path.resolve(__dirname, "../src"), 28 | }), 29 | new MiniCssExtractPlugin({ 30 | filename: "css/[name].css", 31 | chunkFilename: "css/[id].css", 32 | }), 33 | ], 34 | module: { 35 | rules: [ 36 | { 37 | test: /\.html$/i, 38 | loader: "html-loader", 39 | }, 40 | { 41 | test: /\.js$/, 42 | include: Path.resolve(__dirname, "../src"), 43 | loader: "babel-loader", 44 | }, 45 | { 46 | test: /\.s?css$/i, 47 | use: [ 48 | MiniCssExtractPlugin.loader, 49 | { 50 | loader: "css-loader", 51 | options: { 52 | sourceMap: true, 53 | }, 54 | }, 55 | "postcss-loader", 56 | "sass-loader", 57 | ], 58 | }, 59 | ], 60 | }, 61 | }); 62 | -------------------------------------------------------------------------------- /test/django-init-minimal/home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-minimal/home/__init__.py -------------------------------------------------------------------------------- /test/django-init-minimal/home/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin # noqa 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /test/django-init-minimal/home/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class HomeConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'home' 7 | -------------------------------------------------------------------------------- /test/django-init-minimal/home/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-minimal/home/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-minimal/home/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models # noqa 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /test/django-init-minimal/home/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |
5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /test/django-init-minimal/home/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init-minimal/home/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import HomeView 3 | 4 | urlpatterns = [path("", HomeView.as_view(), name="home")] 5 | -------------------------------------------------------------------------------- /test/django-init-minimal/home/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import TemplateView 2 | 3 | 4 | class HomeView(TemplateView): 5 | template_name = "home.html" 6 | -------------------------------------------------------------------------------- /test/django-init-minimal/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | 4 | import os 5 | import sys 6 | 7 | from dotenv import load_dotenv 8 | 9 | 10 | load_dotenv() 11 | 12 | 13 | def main(): 14 | """Run administrative tasks.""" 15 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings.dev") 16 | try: 17 | from django.core.management import execute_from_command_line 18 | except ImportError as exc: 19 | raise ImportError( 20 | "Couldn't import Django. Are you sure it's installed and " 21 | "available on your PYTHONPATH environment variable? Did you " 22 | "forget to activate a virtual environment?" 23 | ) from exc 24 | execute_from_command_line(sys.argv) 25 | 26 | 27 | if __name__ == "__main__": 28 | main() 29 | -------------------------------------------------------------------------------- /test/django-init-minimal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "python-webpack-boilerplate", 3 | "version": "1.0.3", 4 | "description": "Webpack boilerplate for Django & Flask", 5 | "scripts": { 6 | "build": "cross-env NODE_ENV=production webpack --config frontend/webpack/webpack.config.prod.js", 7 | "start": "webpack serve --config frontend/webpack/webpack.config.dev.js", 8 | "watch": "webpack --watch --config frontend/webpack/webpack.config.watch.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/AccordBox/python-webpack-boilerplate" 13 | }, 14 | "keywords": [ 15 | "webpack", 16 | "startkit", 17 | "frontend" 18 | ], 19 | "author": "Michael Yin", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/AccordBox/python-webpack-boilerplate/issues" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.20.5", 26 | "@babel/eslint-parser": "^7.19.1", 27 | "@babel/plugin-transform-class-properties": "^7.18.6", 28 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 29 | "@babel/preset-env": "^7.20.2", 30 | "babel-loader": "^9.1.2", 31 | "clean-webpack-plugin": "^4.0.0", 32 | "copy-webpack-plugin": "^12.0.2", 33 | "cross-env": "^7.0.3", 34 | "css-loader": "^7.1.1", 35 | "eslint": "^8.28.0", 36 | "eslint-webpack-plugin": "^4.1.0", 37 | "mini-css-extract-plugin": "^2.7.1", 38 | "postcss-loader": "^8.0.0", 39 | "postcss-preset-env": "^9.0.0", 40 | "sass": "~1.69.5", 41 | "sass-loader": "^13.3.2", 42 | "style-loader": "^3.3.1", 43 | "stylelint": "^16.4.0", 44 | "stylelint-config-standard-scss": "^13.1.0", 45 | "stylelint-config-standard": "^36.0.0", 46 | "stylelint-webpack-plugin": "^5.0.0", 47 | "webpack": "^5.75.0", 48 | "webpack-assets-manifest": "^5.1.0", 49 | "webpack-cli": "^5.0.0", 50 | "webpack-dev-server": "^5.0.2", 51 | "webpack-merge": "^5.8.0" 52 | }, 53 | "dependencies": { 54 | "core-js": "^3.37.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/django-init-minimal/postcss.config.js: -------------------------------------------------------------------------------- 1 | const postcssPresetEnv = require("postcss-preset-env"); 2 | 3 | module.exports = { 4 | plugins: [postcssPresetEnv()], 5 | }; 6 | -------------------------------------------------------------------------------- /test/django-init-minimal/requirements-test.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-runner 3 | coverage 4 | pytest-mock 5 | pytest-cov 6 | hypothesis 7 | selenium 8 | pytest-django 9 | factory-boy 10 | flake8 11 | tox 12 | -------------------------------------------------------------------------------- /test/django-init-minimal/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==5.1.5 2 | Jinja2==3.1.4 3 | MarkupSafe==2.1.5 4 | PyYAML==6.0.2 5 | Pygments==2.18.0 6 | arrow==1.3.0 7 | asgiref==3.8.1 8 | binaryornot==0.4.4 9 | certifi==2024.8.30 10 | cffi==1.17.0 11 | chardet==5.2.0 12 | charset-normalizer==3.3.2 13 | click==8.1.7 14 | cookiecutter==2.6.0 15 | crispy-bootstrap5==2024.2 16 | cryptography==44.0.1 17 | dj-database-url==2.2.0 18 | django-allauth==64.2.0 19 | django-appconf==1.0.6 20 | django-crispy-forms==2.3 21 | django-cryptography-django5==2.2 22 | django-debug-toolbar==4.4.6 23 | django-extensions==3.2.3 24 | django-recaptcha==4.0.0 25 | django-sql-explorer==5.2 26 | djangorestframework==3.15.2 27 | idna==3.8 28 | markdown-it-py==3.0.0 29 | mdurl==0.1.2 30 | psycopg2-binary==2.9.9 31 | pycparser==2.22 32 | python-dateutil==2.9.0.post0 33 | python-dotenv==1.0.1 34 | python-slugify==8.0.4 35 | python-webpack-boilerplate==1.0.3 36 | requests==2.32.3 37 | rich==13.8.0 38 | six==1.16.0 39 | sqlparse==0.5.1 40 | text-unidecode==1.3 41 | types-python-dateutil==2.9.0.20240821 42 | typing_extensions==4.12.2 43 | urllib3==2.2.2 44 | -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-minimal/siteuser/__init__.py -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.admin import UserAdmin 2 | from django.contrib import admin 3 | 4 | from .models import User 5 | 6 | admin.site.register(User, UserAdmin) 7 | -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SiteuserConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'siteuser' 7 | -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib.auth.forms import UserChangeForm 3 | from crispy_forms.helper import FormHelper 4 | from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit 5 | from .models import User 6 | 7 | 8 | class SiteUserForm(UserChangeForm): 9 | bio = forms.CharField(widget=forms.Textarea(attrs={"id": "editor"}), required=False) 10 | 11 | class Meta(UserChangeForm.Meta): 12 | model = User 13 | fields = ("username", "user_theme_preference", "bio", "rate") 14 | 15 | def __init__(self, *args, **kwargs): 16 | super().__init__(*args, **kwargs) 17 | self.helper = FormHelper() 18 | self.helper.form_method = "post" 19 | self.helper.layout = Layout( 20 | Fieldset( 21 | "Edit Your Profile", 22 | "username", 23 | "user_theme_preference", 24 | "bio", 25 | "rate", 26 | ), 27 | ButtonHolder(Submit("submit", "Save", css_class="btn btn-primary")), 28 | ) 29 | -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-minimal/siteuser/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractUser, Group, Permission 3 | from django.conf import settings 4 | 5 | 6 | class User(AbstractUser): 7 | groups = models.ManyToManyField(Group, related_name="siteuser_set", blank=True) 8 | user_permissions = models.ManyToManyField( 9 | Permission, related_name="siteuser_set", blank=True 10 | ) 11 | 12 | user_theme_preference = models.CharField( 13 | max_length=10, choices=settings.THEMES, default="light" 14 | ) 15 | 16 | bio = models.TextField(blank=True, null=True) 17 | rate = models.FloatField(blank=True, null=True) 18 | -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/templates/user.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 |

User Profile

4 |
5 | Edit 7 |
8 |

Username: {{ user.username }}

9 |

Theme: {{ user.user_theme_preference }}

10 |

Bio: {{ user.bio|default:""|safe }}

11 |

Rate: {{ user.rate|default:"" }}

12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/templates/user_edit.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load crispy_forms_tags %} 3 | {% block content %} 4 |

Edit User

5 | {% crispy form %} 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import UserProfileView, UpdateThemePreferenceView, UserEditView 3 | 4 | urlpatterns = [ 5 | path("profile/", UserProfileView.as_view(), name="user-profile"), 6 | path( 7 | "update_theme_preference/", 8 | UpdateThemePreferenceView.as_view(), 9 | name="update_theme_preference", 10 | ), 11 | path("/edit/", UserEditView.as_view(), name="user-edit"), 12 | ] 13 | -------------------------------------------------------------------------------- /test/django-init-minimal/siteuser/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.contrib.auth.mixins import LoginRequiredMixin 4 | from django.http import JsonResponse 5 | from django.utils.decorators import method_decorator 6 | from django.views import View 7 | from django.views.decorators.csrf import csrf_exempt 8 | from django.views.generic import DetailView 9 | from django.views.generic.edit import UpdateView 10 | from django.urls import reverse_lazy 11 | 12 | from .models import User 13 | from .forms import SiteUserForm 14 | 15 | 16 | class UserProfileView(LoginRequiredMixin, DetailView): 17 | model = User 18 | template_name = "user.html" 19 | 20 | def get_object(self, queryset=None): 21 | return self.request.user 22 | 23 | 24 | @method_decorator(csrf_exempt, name="dispatch") 25 | class UpdateThemePreferenceView(View): 26 | def post(self, request, *args, **kwargs): 27 | try: 28 | data = json.loads(request.body.decode("utf-8")) 29 | new_theme = data.get("theme") 30 | user = request.user 31 | user.user_theme_preference = new_theme 32 | user.save() 33 | response_data = {"theme": new_theme} 34 | return JsonResponse(response_data) 35 | except json.JSONDecodeError as e: 36 | return JsonResponse({"error": e}, status=400) 37 | 38 | def http_method_not_allowed(self, request, *args, **kwargs): 39 | return JsonResponse({"error": "Invalid request method"}, status=405) 40 | 41 | 42 | class UserEditView(LoginRequiredMixin, UpdateView): 43 | model = User 44 | template_name = "user_edit.html" # Create this template in your templates folder 45 | form_class = SiteUserForm 46 | 47 | def get_success_url(self): 48 | # return reverse_lazy("user-profile", kwargs={"pk": self.object.pk}) 49 | return reverse_lazy("user-profile") 50 | -------------------------------------------------------------------------------- /test/django-init-wagtail/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "usage", 7 | "corejs": "3.0.0" 8 | } 9 | ] 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-syntax-dynamic-import", 13 | "@babel/plugin-transform-class-properties" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/django-init-wagtail/.browserslistrc: -------------------------------------------------------------------------------- 1 | [production staging] 2 | >5% 3 | last 2 versions 4 | not ie > 0 5 | not ie_mob > 0 6 | Firefox ESR 7 | 8 | [development] 9 | last 1 chrome version 10 | last 1 firefox version 11 | last 1 edge version 12 | -------------------------------------------------------------------------------- /test/django-init-wagtail/.dockerignore: -------------------------------------------------------------------------------- 1 | # Django project 2 | /media/ 3 | /static/ 4 | *.sqlite3 5 | 6 | # Python and others 7 | __pycache__ 8 | *.pyc 9 | .DS_Store 10 | *.swp 11 | /venv/ 12 | /tmp/ 13 | /.vagrant/ 14 | /Vagrantfile.local 15 | node_modules/ 16 | /npm-debug.log 17 | /.idea/ 18 | .vscode 19 | coverage 20 | .python-version 21 | 22 | # Distribution / packaging 23 | .Python 24 | env/ 25 | build/ 26 | develop-eggs/ 27 | dist/ 28 | downloads/ 29 | eggs/ 30 | .eggs/ 31 | lib/ 32 | lib64/ 33 | parts/ 34 | sdist/ 35 | var/ 36 | wheels/ 37 | *.egg-info/ 38 | .installed.cfg 39 | *.egg 40 | -------------------------------------------------------------------------------- /test/django-init-wagtail/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@babel/eslint-parser", 3 | "extends": [ 4 | "eslint:recommended" 5 | ], 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": 8, 12 | "sourceType": "module", 13 | "requireConfigFile": false 14 | }, 15 | "rules": { 16 | "semi": 2 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/django-init-wagtail/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | dist/ 4 | node_modules/ 5 | _build/ 6 | .elasticbeanstalk/ 7 | db.sqlite3 8 | static/ 9 | backend/inituser 10 | backend/var 11 | .venv/ 12 | -------------------------------------------------------------------------------- /test/django-init-wagtail/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/iron 2 | -------------------------------------------------------------------------------- /test/django-init-wagtail/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard-scss", 3 | "rules": { 4 | "at-rule-no-unknown": null, 5 | "scss/at-rule-no-unknown": true, 6 | "scss/at-import-partial-extension": null 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/django-init-wagtail/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazonlinux:2023 2 | RUN dnf install -y shadow-utils python3.11 python3.11-pip make nodejs20-npm nodejs postgresql15 postgresql15-server 3 | USER postgres 4 | RUN initdb -D /var/lib/pgsql/data 5 | USER root 6 | RUN useradd wagtail 7 | EXPOSE 8000 8 | ENV PYTHONUNBUFFERED=1 PORT=8000 9 | COPY requirements.txt / 10 | RUN python3.11 -m pip install -r /requirements.txt 11 | WORKDIR /app 12 | RUN chown wagtail:wagtail /app 13 | COPY --chown=wagtail:wagtail . . 14 | USER wagtail 15 | RUN npm-20 install; npm-20 run build 16 | RUN python3.11 manage.py collectstatic --noinput --clear 17 | CMD set -xe; pg_ctl -D /var/lib/pgsql/data -l /tmp/logfile start; python3.11 manage.py migrate --noinput; gunicorn backend.wsgi:application 18 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/backend/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin import AdminSite 2 | 3 | 4 | class CustomAdminSite(AdminSite): 5 | site_header = "Project Makefile" 6 | site_title = "Project Makefile" 7 | index_title = "Project Makefile" 8 | 9 | 10 | custom_admin_site = CustomAdminSite(name="custom_admin") 11 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/api.py: -------------------------------------------------------------------------------- 1 | from ninja import NinjaAPI 2 | from rest_framework import viewsets 3 | from siteuser.models import User 4 | from .serializers import UserSerializer 5 | 6 | api = NinjaAPI() 7 | 8 | 9 | @api.get("/hello") 10 | def hello(request): 11 | return "Hello world" 12 | 13 | 14 | class UserViewSet(viewsets.ModelViewSet): 15 | queryset = User.objects.all() 16 | serializer_class = UserSerializer 17 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/apps.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin.apps import AdminConfig 2 | 3 | 4 | class CustomAdminConfig(AdminConfig): 5 | default_site = "backend.admin.CustomAdminSite" 6 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from siteuser.models import User 3 | 4 | 5 | class UserSerializer(serializers.HyperlinkedModelSerializer): 6 | class Meta: 7 | model = User 8 | fields = ["url", "username", "email", "is_staff"] 9 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/backend/settings/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/settings/dev.py: -------------------------------------------------------------------------------- 1 | # project-makefile 2 | from .base import * # noqa 3 | 4 | # SECURITY WARNING: don't run with debug turned on in production! 5 | DEBUG = True 6 | 7 | # SECURITY WARNING: define the correct hosts in production! 8 | ALLOWED_HOSTS = ["*"] 9 | 10 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" 11 | 12 | try: 13 | from .local import * # noqa 14 | except ImportError: 15 | pass 16 | 17 | # LOGGING = { 18 | # "version": 1, 19 | # "disable_existing_loggers": False, 20 | # "formatters": { 21 | # "verbose": { 22 | # "format": "{levelname} {asctime} {module} {message}", 23 | # "style": "{", 24 | # }, 25 | # "simple": { 26 | # "format": "{levelname} {message}", 27 | # "style": "{", 28 | # }, 29 | # }, 30 | # "handlers": { 31 | # "console": { 32 | # "level": "DEBUG", 33 | # "class": "logging.StreamHandler", 34 | # "formatter": "verbose", 35 | # }, 36 | # }, 37 | # "loggers": { 38 | # "django": { 39 | # "handlers": ["console"], 40 | # "level": "DEBUG", 41 | # "propagate": True, 42 | # }, 43 | # }, 44 | # } 45 | 46 | INTERNAL_IPS = [ 47 | "127.0.0.1", 48 | ] 49 | 50 | MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware") # noqa 51 | MIDDLEWARE.append("hijack.middleware.HijackUserMiddleware") # noqa 52 | INSTALLED_APPS.append("django.contrib.admindocs") # noqa 53 | SECRET_KEY = "d9WBgdl4PjGDrvh9xkqgP9j29UQWTqe0qESTJzFcRtB3hiZhlyXlPbelnIVtP+io" 54 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/settings/production.py: -------------------------------------------------------------------------------- 1 | from .base import * # noqa 2 | from backend.utils import get_ec2_metadata 3 | 4 | DEBUG = False 5 | 6 | try: 7 | from .local import * # noqa 8 | except ImportError: 9 | pass 10 | 11 | LOCAL_IPV4 = get_ec2_metadata() 12 | ALLOWED_HOSTS.append(LOCAL_IPV4) # noqa 13 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Page not found{% endblock %} 4 | 5 | {% block body_class %}template-404{% endblock %} 6 | 7 | {% block content %} 8 |

Page not found

9 | 10 |

Sorry, this page could not be found.

11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/templates/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Internal server error 6 | 7 | 8 | 9 |

Internal server error

10 | 11 |

Sorry, there seems to be an error. Please try again soon.

12 | 13 | 14 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/templates/allauth/layouts/base.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static wagtailcore_tags wagtailuserbar %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %} 9 | {% if page.seo_title %}{{ page.seo_title }}{% else %}{{ page.title }}{% endif %} 10 | {% endblock %} 11 | {% block title_suffix %} 12 | {% wagtail_site as current_site %} 13 | {% if current_site and current_site.site_name %}- {{ current_site.site_name }}{% endif %} 14 | {% endblock %} 15 | 16 | {% if page.search_description %} 17 | 18 | {% endif %} 19 | 20 | 21 | {# Force all links in the live preview panel to be opened in a new tab #} 22 | {% if request.in_preview_panel %} 23 | 24 | {% endif %} 25 | 26 | {# Global stylesheets #} 27 | 28 | 29 | {% block extra_css %} 30 | {# Override this in templates to add extra stylesheets #} 31 | {% endblock %} 32 | 33 | 34 | 35 | {% wagtailuserbar %} 36 | 37 | {% block content %}{% endblock %} 38 | 39 | {# Global javascript #} 40 | 41 | 42 | {% block extra_js %} 43 | {# Override this in templates to add extra javascript #} 44 | {% endblock %} 45 | 46 | 47 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/templates/favicon.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/templates/footer.html: -------------------------------------------------------------------------------- 1 |
2 |

© {% now "Y" %} {{ current_site.site_name|default:"Project Makefile" }}

3 |
    4 |
  • 5 | Home 7 |
  • 8 | {% for child in current_site.root_page.get_children %} 9 |
  • 10 | {{ child }} 12 |
  • 13 | {% endfor %} 14 |
15 |
16 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/templates/offcanvas.html: -------------------------------------------------------------------------------- 1 |
5 | 14 |
15 | 37 |
38 |
39 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.urls import include, path 3 | from django.contrib import admin 4 | 5 | from wagtail.admin import urls as wagtailadmin_urls 6 | from wagtail.documents import urls as wagtaildocs_urls 7 | 8 | from search import views as search_views 9 | 10 | urlpatterns = [ 11 | path("django/", admin.site.urls), 12 | path("wagtail/", include(wagtailadmin_urls)), 13 | path("documents/", include(wagtaildocs_urls)), 14 | path("search/", search_views.search, name="search"), 15 | ] 16 | 17 | if settings.DEBUG: 18 | from django.conf.urls.static import static 19 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns 20 | 21 | # Serve static and media files from development server 22 | urlpatterns += staticfiles_urlpatterns() 23 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 24 | if settings.DEBUG: 25 | urlpatterns += [path("__debug__/", include("debug_toolbar.urls"))] 26 | urlpatterns += [path("accounts/", include("allauth.urls"))] 27 | urlpatterns += [path("user/", include("siteuser.urls"))] 28 | urlpatterns += [path("model-form-demo/", include("model_form_demo.urls"))] 29 | urlpatterns += [path("logging-demo/", include("logging_demo.urls"))] 30 | urlpatterns += [path("payments/", include("payments.urls"))] 31 | from rest_framework import routers # noqa 32 | from .api import UserViewSet, api # noqa 33 | 34 | router = routers.DefaultRouter() 35 | router.register(r"users", UserViewSet) 36 | # urlpatterns += [path("api/", include(router.urls))] 37 | urlpatterns += [path("api/", api.urls)] 38 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/utils.py: -------------------------------------------------------------------------------- 1 | from django.urls import URLResolver 2 | import requests 3 | 4 | 5 | def get_ec2_metadata(): 6 | try: 7 | # Step 1: Get the token 8 | token_url = "http://169.254.169.254/latest/api/token" 9 | headers = {"X-aws-ec2-metadata-token-ttl-seconds": "21600"} 10 | response = requests.put(token_url, headers=headers) 11 | response.raise_for_status() # Raise an error for bad responses 12 | 13 | token = response.text 14 | 15 | # Step 2: Use the token to get the instance metadata 16 | metadata_url = "http://169.254.169.254/latest/meta-data/local-ipv4" 17 | headers = {"X-aws-ec2-metadata-token": token} 18 | response = requests.get(metadata_url, headers=headers) 19 | response.raise_for_status() # Raise an error for bad responses 20 | 21 | metadata = response.text 22 | return metadata 23 | except requests.RequestException as e: 24 | print(f"Error retrieving EC2 metadata: {e}") 25 | return None 26 | 27 | 28 | # Function to remove a specific URL pattern based on its route (including catch-all) 29 | def remove_urlpattern(urlpatterns, route_to_remove): 30 | urlpatterns[:] = [ 31 | urlpattern 32 | for urlpattern in urlpatterns 33 | if not ( 34 | isinstance(urlpattern, URLResolver) 35 | and urlpattern.pattern._route == route_to_remove 36 | ) 37 | ] 38 | -------------------------------------------------------------------------------- /test/django-init-wagtail/backend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for backend project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings.dev") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /test/django-init-wagtail/contactpage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/contactpage/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/contactpage/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/contactpage/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ContactpageConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'contactpage' 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/contactpage/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/contactpage/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/contactpage/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from modelcluster.fields import ParentalKey 3 | from wagtail.admin.panels import ( 4 | FieldPanel, FieldRowPanel, 5 | InlinePanel, MultiFieldPanel 6 | ) 7 | from wagtail.fields import RichTextField 8 | from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField 9 | 10 | 11 | class FormField(AbstractFormField): 12 | page = ParentalKey('ContactPage', on_delete=models.CASCADE, related_name='form_fields') 13 | 14 | 15 | class ContactPage(AbstractEmailForm): 16 | intro = RichTextField(blank=True) 17 | thank_you_text = RichTextField(blank=True) 18 | 19 | content_panels = AbstractEmailForm.content_panels + [ 20 | FieldPanel('intro'), 21 | InlinePanel('form_fields', label="Form fields"), 22 | FieldPanel('thank_you_text'), 23 | MultiFieldPanel([ 24 | FieldRowPanel([ 25 | FieldPanel('from_address', classname="col6"), 26 | FieldPanel('to_address', classname="col6"), 27 | ]), 28 | FieldPanel('subject'), 29 | ], "Email"), 30 | ] 31 | 32 | class Meta: 33 | verbose_name = "Contact Page" 34 | -------------------------------------------------------------------------------- /test/django-init-wagtail/contactpage/templates/contactpage/contact_page.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load crispy_forms_tags static wagtailcore_tags %} 3 | {% block content %} 4 |

{{ page.title }}

5 | {{ page.intro|richtext }} 6 |
7 | {% csrf_token %} 8 | {{ form.as_p }} 9 | 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /test/django-init-wagtail/contactpage/templates/contactpage/contact_page_landing.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %}

Thank you!

{% endblock %} 3 | -------------------------------------------------------------------------------- /test/django-init-wagtail/contactpage/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | db: 5 | image: postgres:latest 6 | volumes: 7 | - postgres_data:/var/lib/postgresql/data 8 | environment: 9 | POSTGRES_DB: project 10 | POSTGRES_USER: admin 11 | POSTGRES_PASSWORD: admin 12 | 13 | web: 14 | build: . 15 | command: sh -c "python manage.py migrate && gunicorn project.wsgi:application -b 0.0.0.0:8000" 16 | volumes: 17 | - .:/app 18 | ports: 19 | - "8000:8000" 20 | depends_on: 21 | - db 22 | environment: 23 | DATABASE_URL: postgres://admin:admin@db:5432/project 24 | 25 | volumes: 26 | postgres_data: 27 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-react", 5 | ], 6 | [ 7 | "@babel/preset-env", 8 | { 9 | "useBuiltIns": "usage", 10 | "corejs": "3.0.0" 11 | } 12 | ] 13 | ], 14 | "plugins": [ 15 | "@babel/plugin-syntax-dynamic-import", 16 | "@babel/plugin-transform-class-properties" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended" 10 | ], 11 | "overrides": [ 12 | { 13 | "env": { 14 | "node": true 15 | }, 16 | "files": [ 17 | ".eslintrc.{js,cjs}" 18 | ], 19 | "parserOptions": { 20 | "sourceType": "script" 21 | } 22 | } 23 | ], 24 | "parserOptions": { 25 | "ecmaVersion": "latest", 26 | "sourceType": "module" 27 | }, 28 | "plugins": [ 29 | "react" 30 | ], 31 | "rules": { 32 | "no-unused-vars": "off" 33 | }, 34 | settings: { 35 | react: { 36 | version: 'detect', 37 | }, 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # production 5 | build 6 | 7 | # misc 8 | .DS_Store 9 | 10 | npm-debug.log 11 | yarn-error.log 12 | yarn.lock 13 | .yarnclean 14 | .vscode 15 | .idea 16 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This project was created with [python-webpack-boilerplate](https://github.com/AccordBox/python-webpack-boilerplate) 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm run start` 10 | 11 | `npm run start` will launch a server process, which makes `live reloading` possible. 12 | 13 | If you change JS or SCSS files, the web page would auto refresh after the change. Now the server is working on port 9091 by default, but you can change it in `webpack/webpack.config.dev.js` 14 | 15 | ### `npm run watch` 16 | 17 | run webpack in `watch` mode. 18 | 19 | ### `npm run build` 20 | 21 | [production mode](https://webpack.js.org/guides/production/), Webpack would focus on minified bundles, lighter weight source maps, and optimized assets to improve load time. 22 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/application/README.md: -------------------------------------------------------------------------------- 1 | When Webpacker compiles your JavaScript code, it scans the `src/application` directory for files with the .js extension and automatically includes them as entry points for the Webpack bundling process. 2 | 3 | For the `application/app.js`, you can import it in template like this: 4 | 5 | ``` 6 | {% javascript_pack 'app' attrs='charset="UTF-8"' %} 7 | ``` 8 | 9 | In most cases, you do not need to create another file in this directory. 10 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/application/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import 'bootstrap'; 4 | // import '@fortawesome/fontawesome-free/js/fontawesome'; 5 | // import '@fortawesome/fontawesome-free/js/solid'; 6 | // import '@fortawesome/fontawesome-free/js/regular'; 7 | // import '@fortawesome/fontawesome-free/js/brands'; 8 | import getDataComponents from '../dataComponents'; 9 | import UserContextProvider from '../context'; 10 | import * as components from '../components'; 11 | import "../styles/index.scss"; 12 | import "../styles/theme-blue.scss"; 13 | import "./config"; 14 | 15 | const { ErrorBoundary } = components; 16 | const dataComponents = getDataComponents(components); 17 | const container = document.getElementById('app'); 18 | const root = createRoot(container); 19 | const App = () => ( 20 | 21 | 22 | {dataComponents} 23 | 24 | 25 | ); 26 | root.render(); 27 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/application/config.js: -------------------------------------------------------------------------------- 1 | import '../utils/themeToggler.js'; 2 | // import '../utils/tinymce.js'; 3 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/components/Clock.js: -------------------------------------------------------------------------------- 1 | // Via ChatGPT 2 | import React, { useState, useEffect, useCallback, useRef } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const Clock = ({ color = '#fff' }) => { 6 | const [date, setDate] = useState(new Date()); 7 | const [blink, setBlink] = useState(true); 8 | const timerID = useRef(); 9 | 10 | const tick = useCallback(() => { 11 | setDate(new Date()); 12 | setBlink(prevBlink => !prevBlink); 13 | }, []); 14 | 15 | useEffect(() => { 16 | timerID.current = setInterval(() => tick(), 1000); 17 | 18 | // Return a cleanup function to be run on component unmount 19 | return () => clearInterval(timerID.current); 20 | }, [tick]); 21 | 22 | const formattedDate = date.toLocaleDateString(undefined, { 23 | weekday: 'short', 24 | year: 'numeric', 25 | month: 'short', 26 | day: 'numeric', 27 | }); 28 | 29 | const formattedTime = date.toLocaleTimeString(undefined, { 30 | hour: 'numeric', 31 | minute: 'numeric', 32 | }); 33 | 34 | return ( 35 | <> 36 |
{formattedDate} {formattedTime}
37 | 38 | ); 39 | }; 40 | 41 | Clock.propTypes = { 42 | color: PropTypes.string, 43 | }; 44 | 45 | export default Clock; 46 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/components/ErrorBoundary.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class ErrorBoundary extends Component { 5 | constructor (props) { 6 | super(props); 7 | this.state = { hasError: false }; 8 | } 9 | 10 | static getDerivedStateFromError () { 11 | return { hasError: true }; 12 | } 13 | 14 | componentDidCatch (error, info) { 15 | const { onError } = this.props; 16 | console.error(error); 17 | onError && onError(error, info); 18 | } 19 | 20 | render () { 21 | const { children = null } = this.props; 22 | const { hasError } = this.state; 23 | 24 | return hasError ? null : children; 25 | } 26 | } 27 | 28 | ErrorBoundary.propTypes = { 29 | onError: PropTypes.func, 30 | children: PropTypes.node, 31 | }; 32 | 33 | export default ErrorBoundary; 34 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/components/README.md: -------------------------------------------------------------------------------- 1 | We recommend write Javascript here to make your code reusable. 2 | 3 | And you can check below packages to increase your productivity: 4 | 5 | 1. [Stimulus](https://stimulus.hotwired.dev/) 6 | 2. [Alpine.js](https://alpinejs.dev/) 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as ErrorBoundary } from './ErrorBoundary'; 2 | export { default as UserMenu } from './UserMenu'; 3 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/components/jumbotron.js: -------------------------------------------------------------------------------- 1 | class Jumbotron { 2 | static selector() { 3 | return "[data-jumbotron]"; 4 | } 5 | 6 | constructor(node) { 7 | this.node = node; 8 | console.log(`Jumbotron initialized for node: ${node}`); 9 | // do something here 10 | } 11 | } 12 | 13 | export default Jumbotron; 14 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/context/UserContextProvider.js: -------------------------------------------------------------------------------- 1 | // UserContextProvider.js 2 | import React, { createContext, useContext, useState } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const UserContext = createContext(); 6 | 7 | export const UserContextProvider = ({ children }) => { 8 | const [isAuthenticated, setIsAuthenticated] = useState(false); 9 | 10 | const login = () => { 11 | try { 12 | // Add logic to handle login, set isAuthenticated to true 13 | setIsAuthenticated(true); 14 | } catch (error) { 15 | console.error('Login error:', error); 16 | // Handle error, e.g., show an error message to the user 17 | } 18 | }; 19 | 20 | const logout = () => { 21 | try { 22 | // Add logic to handle logout, set isAuthenticated to false 23 | setIsAuthenticated(false); 24 | } catch (error) { 25 | console.error('Logout error:', error); 26 | // Handle error, e.g., show an error message to the user 27 | } 28 | }; 29 | 30 | return ( 31 | 32 | {children} 33 | 34 | ); 35 | }; 36 | 37 | UserContextProvider.propTypes = { 38 | children: PropTypes.node.isRequired, 39 | }; 40 | 41 | export const useUserContext = () => { 42 | const context = useContext(UserContext); 43 | 44 | if (!context) { 45 | throw new Error('useUserContext must be used within a UserContextProvider'); 46 | } 47 | 48 | return context; 49 | }; 50 | 51 | // Add PropTypes for the return value of useUserContext 52 | useUserContext.propTypes = { 53 | isAuthenticated: PropTypes.bool.isRequired, 54 | login: PropTypes.func.isRequired, 55 | logout: PropTypes.func.isRequired, 56 | }; 57 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/context/index.js: -------------------------------------------------------------------------------- 1 | export { UserContextProvider as default } from './UserContextProvider'; 2 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/dataComponents.js: -------------------------------------------------------------------------------- 1 | // Via pwellever 2 | import React from 'react'; 3 | import { createPortal } from 'react-dom'; 4 | 5 | const parseProps = data => Object.entries(data).reduce((result, [key, value]) => { 6 | if (value.toLowerCase() === 'true') { 7 | value = true; 8 | } else if (value.toLowerCase() === 'false') { 9 | value = false; 10 | } else if (value.toLowerCase() === 'null') { 11 | value = null; 12 | } else if (!isNaN(parseFloat(value)) && isFinite(value)) { 13 | // Parse numeric value 14 | value = parseFloat(value); 15 | } else if ( 16 | (value[0] === '[' && value.slice(-1) === ']') || (value[0] === '{' && value.slice(-1) === '}') 17 | ) { 18 | // Parse JSON strings 19 | value = JSON.parse(value); 20 | } 21 | 22 | result[key] = value; 23 | return result; 24 | }, {}); 25 | 26 | // This method of using portals instead of calling ReactDOM.render on individual components 27 | // ensures that all components are mounted under a single React tree, and are therefore able 28 | // to share context. 29 | 30 | export default function getPageComponents (components) { 31 | const getPortalComponent = domEl => { 32 | // The element's "data-component" attribute is used to determine which component to render. 33 | // All other "data-*" attributes are passed as props. 34 | const { component: componentName, ...rest } = domEl.dataset; 35 | const Component = components[componentName]; 36 | if (!Component) { 37 | console.error(`Component "${componentName}" not found.`); 38 | return null; 39 | } 40 | const props = parseProps(rest); 41 | domEl.innerHTML = ''; 42 | 43 | // eslint-disable-next-line no-unused-vars 44 | const { ErrorBoundary } = components; 45 | return createPortal( 46 | 47 | 48 | , 49 | domEl, 50 | ); 51 | }; 52 | 53 | return Array.from(document.querySelectorAll('[data-component]')).map(getPortalComponent); 54 | } 55 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | // If you comment out code below, bootstrap will use red as primary color 2 | // and btn-primary will become red 3 | 4 | // rimary: red; 5 | 6 | @import "~bootstrap/scss/bootstrap.scss"; 7 | 8 | .jumbotron { 9 | // should be relative path of the entry scss file 10 | background-image: url("../../vendors/images/sample.jpg"); 11 | background-size: cover; 12 | } 13 | 14 | #theme-toggler-authenticated:hover { 15 | cursor: pointer; /* Change cursor to pointer on hover */ 16 | color: #007bff; /* Change color on hover */ 17 | } 18 | 19 | #theme-toggler-anonymous:hover { 20 | cursor: pointer; /* Change cursor to pointer on hover */ 21 | color: #007bff; /* Change color on hover */ 22 | } 23 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/src/styles/theme-blue.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/scss/bootstrap.scss"; 2 | 3 | [data-bs-theme="blue"] { 4 | --bs-body-color: var(--bs-white); 5 | --bs-body-color-rgb: #{to-rgb($white)}; 6 | --bs-body-bg: var(--bs-blue); 7 | --bs-body-bg-rgb: #{to-rgb($blue)}; 8 | --bs-tertiary-bg: #{$blue-600}; 9 | 10 | .dropdown-menu { 11 | --bs-dropdown-bg: #{color-mix($blue-500, $blue-600)}; 12 | --bs-dropdown-link-active-bg: #{$blue-700}; 13 | } 14 | 15 | .btn-secondary { 16 | --bs-btn-bg: #{color-mix(ray-600, lue-400, .5)}; 17 | --bs-btn-border-color: #{rgba($white, .25)}; 18 | --bs-btn-hover-bg: #{color-adjust(color-mix(ray-600, lue-400, .5), 5%)}; 19 | --bs-btn-hover-border-color: #{rgba($white, .25)}; 20 | --bs-btn-active-bg: #{color-adjust(color-mix(ray-600, lue-400, .5), 10%)}; 21 | --bs-btn-active-border-color: #{rgba($white, .5)}; 22 | --bs-btn-focus-border-color: #{rgba($white, .5)}; 23 | 24 | // --bs-btn-focus-box-shadow: 0 0 0 .25rem rgba(255, 255, 255, 20%); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/vendors/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/frontend/vendors/.gitkeep -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/vendors/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/frontend/vendors/images/.gitkeep -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/vendors/images/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/frontend/vendors/images/sample.jpg -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/vendors/images/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/frontend/vendors/images/webpack.png -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/webpack/webpack.common.js: -------------------------------------------------------------------------------- 1 | const glob = require("glob"); 2 | const Path = require("path"); 3 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 4 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 5 | const WebpackAssetsManifest = require("webpack-assets-manifest"); 6 | 7 | const getEntryObject = () => { 8 | const entries = {}; 9 | // for javascript/typescript entry file 10 | glob 11 | .sync(Path.join(__dirname, "../src/application/*.{js,ts}")) 12 | .forEach((path) => { 13 | const name = Path.basename(path); 14 | const extension = Path.extname(path); 15 | const entryName = name.replace(extension, ""); 16 | if (entryName in entries) { 17 | throw new Error(`Entry file conflict: ${entryName}`); 18 | } 19 | entries[entryName] = path; 20 | }); 21 | return entries; 22 | }; 23 | 24 | module.exports = { 25 | entry: getEntryObject(), 26 | output: { 27 | path: Path.join(__dirname, "../build"), 28 | filename: "js/[name].js", 29 | publicPath: "/static/", 30 | assetModuleFilename: "[path][name][ext]", 31 | }, 32 | optimization: { 33 | splitChunks: { 34 | chunks: "all", 35 | }, 36 | 37 | runtimeChunk: "single", 38 | }, 39 | plugins: [ 40 | new CleanWebpackPlugin(), 41 | new CopyWebpackPlugin({ 42 | patterns: [ 43 | { from: Path.resolve(__dirname, "../vendors"), to: "vendors" }, 44 | ], 45 | }), 46 | new WebpackAssetsManifest({ 47 | entrypoints: true, 48 | output: "manifest.json", 49 | writeToDisk: true, 50 | publicPath: true, 51 | }), 52 | ], 53 | resolve: { 54 | alias: { 55 | "~": Path.resolve(__dirname, "../src"), 56 | }, 57 | }, 58 | module: { 59 | rules: [ 60 | { 61 | test: /\.mjs$/, 62 | include: /node_modules/, 63 | type: "javascript/auto", 64 | }, 65 | { 66 | test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, 67 | type: "asset", 68 | }, 69 | ], 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/webpack/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const Path = require("path"); 2 | const Webpack = require("webpack"); 3 | const { merge } = require("webpack-merge"); 4 | const StylelintPlugin = require("stylelint-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | const ESLintPlugin = require("eslint-webpack-plugin"); 7 | 8 | const common = require("./webpack.common.js"); 9 | 10 | module.exports = merge(common, { 11 | target: "web", 12 | mode: "development", 13 | devtool: "inline-source-map", 14 | output: { 15 | chunkFilename: "js/[name].chunk.js", 16 | publicPath: "http://localhost:9091/", 17 | }, 18 | devServer: { 19 | hot: true, 20 | host: "0.0.0.0", 21 | port: 9091, 22 | headers: { 23 | "Access-Control-Allow-Origin": "*", 24 | }, 25 | devMiddleware: { 26 | writeToDisk: true, 27 | }, 28 | }, 29 | plugins: [ 30 | new Webpack.DefinePlugin({ 31 | "process.env.NODE_ENV": JSON.stringify("development"), 32 | }), 33 | new StylelintPlugin({ 34 | files: Path.resolve(__dirname, "../src/**/*.s?(a|c)ss"), 35 | }), 36 | new ESLintPlugin({ 37 | extensions: "js", 38 | emitWarning: true, 39 | files: Path.resolve(__dirname, "../src"), 40 | }), 41 | new MiniCssExtractPlugin({ 42 | filename: "css/[name].css", 43 | chunkFilename: "css/[id].css", 44 | }), 45 | ], 46 | module: { 47 | rules: [ 48 | { 49 | test: /\.html$/i, 50 | loader: "html-loader", 51 | }, 52 | { 53 | test: /\.js$/, 54 | include: Path.resolve(__dirname, "../src"), 55 | loader: "babel-loader", 56 | }, 57 | { 58 | test: /\.s?css$/i, 59 | use: [ 60 | MiniCssExtractPlugin.loader, 61 | { 62 | loader: "css-loader", 63 | options: { 64 | sourceMap: true, 65 | }, 66 | }, 67 | "postcss-loader", 68 | "sass-loader", 69 | ], 70 | }, 71 | ], 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/webpack/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const Webpack = require("webpack"); 2 | const { merge } = require("webpack-merge"); 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 4 | const common = require("./webpack.common.js"); 5 | 6 | module.exports = merge(common, { 7 | mode: "production", 8 | devtool: "source-map", 9 | bail: true, 10 | output: { 11 | filename: "js/[name].[chunkhash:8].js", 12 | chunkFilename: "js/[name].[chunkhash:8].chunk.js", 13 | }, 14 | plugins: [ 15 | new Webpack.DefinePlugin({ 16 | "process.env.NODE_ENV": JSON.stringify("production"), 17 | }), 18 | new MiniCssExtractPlugin({ 19 | filename: "css/[name].[contenthash].css", 20 | chunkFilename: "css/[id].[contenthash].css", 21 | }), 22 | ], 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.js$/, 27 | exclude: /node_modules/, 28 | use: "babel-loader", 29 | }, 30 | { 31 | test: /\.s?css/i, 32 | use: [ 33 | MiniCssExtractPlugin.loader, 34 | "css-loader", 35 | "postcss-loader", 36 | "sass-loader", 37 | ], 38 | }, 39 | ], 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /test/django-init-wagtail/frontend/webpack/webpack.config.watch.js: -------------------------------------------------------------------------------- 1 | const Path = require("path"); 2 | const Webpack = require("webpack"); 3 | const { merge } = require("webpack-merge"); 4 | const StylelintPlugin = require("stylelint-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | const ESLintPlugin = require("eslint-webpack-plugin"); 7 | 8 | const common = require("./webpack.common.js"); 9 | 10 | module.exports = merge(common, { 11 | target: "web", 12 | mode: "development", 13 | devtool: "inline-source-map", 14 | output: { 15 | chunkFilename: "js/[name].chunk.js", 16 | }, 17 | plugins: [ 18 | new Webpack.DefinePlugin({ 19 | "process.env.NODE_ENV": JSON.stringify("development"), 20 | }), 21 | new StylelintPlugin({ 22 | files: Path.resolve(__dirname, "../src/**/*.s?(a|c)ss"), 23 | }), 24 | new ESLintPlugin({ 25 | extensions: "js", 26 | emitWarning: true, 27 | files: Path.resolve(__dirname, "../src"), 28 | }), 29 | new MiniCssExtractPlugin({ 30 | filename: "css/[name].css", 31 | chunkFilename: "css/[id].css", 32 | }), 33 | ], 34 | module: { 35 | rules: [ 36 | { 37 | test: /\.html$/i, 38 | loader: "html-loader", 39 | }, 40 | { 41 | test: /\.js$/, 42 | include: Path.resolve(__dirname, "../src"), 43 | loader: "babel-loader", 44 | }, 45 | { 46 | test: /\.s?css$/i, 47 | use: [ 48 | MiniCssExtractPlugin.loader, 49 | { 50 | loader: "css-loader", 51 | options: { 52 | sourceMap: true, 53 | }, 54 | }, 55 | "postcss-loader", 56 | "sass-loader", 57 | ], 58 | }, 59 | ], 60 | }, 61 | }); 62 | -------------------------------------------------------------------------------- /test/django-init-wagtail/home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/home/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/home/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ("wagtailcore", "0040_page_draft_title"), 8 | ] 9 | 10 | operations = [ 11 | migrations.CreateModel( 12 | name="HomePage", 13 | fields=[ 14 | ( 15 | "page_ptr", 16 | models.OneToOneField( 17 | on_delete=models.CASCADE, 18 | parent_link=True, 19 | auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | to="wagtailcore.Page", 23 | ), 24 | ), 25 | ], 26 | options={ 27 | "abstract": False, 28 | }, 29 | bases=("wagtailcore.page",), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /test/django-init-wagtail/home/migrations/0002_create_homepage.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | 4 | def create_homepage(apps, schema_editor): 5 | # Get models 6 | ContentType = apps.get_model("contenttypes.ContentType") 7 | Page = apps.get_model("wagtailcore.Page") 8 | Site = apps.get_model("wagtailcore.Site") 9 | HomePage = apps.get_model("home.HomePage") 10 | 11 | # Delete the default homepage 12 | # If migration is run multiple times, it may have already been deleted 13 | Page.objects.filter(id=2).delete() 14 | 15 | # Create content type for homepage model 16 | homepage_content_type, __ = ContentType.objects.get_or_create( 17 | model="homepage", app_label="home" 18 | ) 19 | 20 | # Create a new homepage 21 | homepage = HomePage.objects.create( 22 | title="Home", 23 | draft_title="Home", 24 | slug="home", 25 | content_type=homepage_content_type, 26 | path="00010001", 27 | depth=2, 28 | numchild=0, 29 | url_path="/home/", 30 | ) 31 | 32 | # Create a site with the new homepage set as the root 33 | Site.objects.create(hostname="localhost", root_page=homepage, is_default_site=True) 34 | 35 | 36 | def remove_homepage(apps, schema_editor): 37 | # Get models 38 | ContentType = apps.get_model("contenttypes.ContentType") 39 | HomePage = apps.get_model("home.HomePage") 40 | 41 | # Delete the default homepage 42 | # Page and Site objects CASCADE 43 | HomePage.objects.filter(slug="home", depth=2).delete() 44 | 45 | # Delete content type for homepage model 46 | ContentType.objects.filter(model="homepage", app_label="home").delete() 47 | 48 | 49 | class Migration(migrations.Migration): 50 | 51 | run_before = [ 52 | ("wagtailcore", "0053_locale_model"), 53 | ] 54 | 55 | dependencies = [ 56 | ("home", "0001_initial"), 57 | ] 58 | 59 | operations = [ 60 | migrations.RunPython(create_homepage, remove_homepage), 61 | ] 62 | -------------------------------------------------------------------------------- /test/django-init-wagtail/home/migrations/0003_alter_homepage_options_homepage_marketing_blocks.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1.4 on 2024-12-05 00:55 2 | 3 | import wagtail.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('home', '0002_create_homepage'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name='homepage', 16 | options={'verbose_name': 'Home Page'}, 17 | ), 18 | migrations.AddField( 19 | model_name='homepage', 20 | name='marketing_blocks', 21 | field=wagtail.fields.StreamField([('marketing_block', 8)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {'help_text': 'Enter the block title', 'required': False}), 1: ('wagtail.blocks.RichTextBlock', (), {'help_text': 'Enter the block content', 'required': False}), 2: ('wagtail.images.blocks.ImageChooserBlock', (), {'required': False}), 3: ('wagtail.blocks.ListBlock', (2,), {'help_text': 'Select one or two images for column display. Select three or more images for carousel display.'}), 4: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Select one image for background display.', 'required': False}), 5: ('wagtail.blocks.CharBlock', (), {'default': 'vh-100 bg-secondary', 'form_classname': 'full title', 'help_text': 'Enter a CSS class for styling the marketing block', 'required': False}), 6: ('wagtail.blocks.CharBlock', (), {'default': 'img-thumbnail p-5', 'form_classname': 'full title', 'help_text': 'Enter a CSS class for styling the column display image(s)', 'required': False}), 7: ('wagtail.blocks.CharBlock', (), {'default': 'd-flex flex-row', 'form_classname': 'full title', 'help_text': 'Enter a CSS class for styling the layout.', 'required': False}), 8: ('wagtail.blocks.StructBlock', [[('title', 0), ('content', 1), ('images', 3), ('image', 4), ('block_class', 5), ('image_class', 6), ('layout_class', 7)]], {})}, null=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /test/django-init-wagtail/home/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/home/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/home/templates/blocks/carousel_block.html: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /test/django-init-wagtail/home/templates/blocks/marketing_block.html: -------------------------------------------------------------------------------- 1 | {% load wagtailcore_tags %} 2 |
3 | {% if block.value.images.0 %} 4 | {% include 'blocks/carousel_block.html' %} 5 | {% else %} 6 | {{ self.title }} 7 | {{ self.content }} 8 | {% endif %} 9 |
10 | -------------------------------------------------------------------------------- /test/django-init-wagtail/home/templates/home/home_page.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load wagtailcore_tags %} 3 | {% block content %} 4 |
5 | {% for block in page.marketing_blocks %} 6 | {% include_block block %} 7 | {% endfor %} 8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /test/django-init-wagtail/logging_demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/logging_demo/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/logging_demo/admin.py: -------------------------------------------------------------------------------- 1 | # Register your models here. 2 | -------------------------------------------------------------------------------- /test/django-init-wagtail/logging_demo/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class LoggingDemoConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'logging_demo' 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/logging_demo/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/logging_demo/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/logging_demo/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models # noqa 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/logging_demo/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/logging_demo/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import logging_demo 3 | 4 | urlpatterns = [ 5 | path("", logging_demo, name="logging_demo"), 6 | ] 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/logging_demo/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | import logging 3 | 4 | logger = logging.getLogger(__name__) 5 | 6 | 7 | def logging_demo(request): 8 | logger.debug("Hello, world!") 9 | return HttpResponse("Hello, world!") 10 | -------------------------------------------------------------------------------- /test/django-init-wagtail/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | 4 | import os 5 | import sys 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings.dev") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/model_form_demo/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import ModelFormDemo 3 | 4 | 5 | @admin.register(ModelFormDemo) 6 | class ModelFormDemoAdmin(admin.ModelAdmin): 7 | pass 8 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ModelFormDemoConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'model_form_demo' 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from .models import ModelFormDemo 3 | 4 | 5 | class ModelFormDemoForm(forms.ModelForm): 6 | class Meta: 7 | model = ModelFormDemo 8 | fields = ["name", "email", "age", "is_active"] 9 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1.4 on 2024-12-05 00:56 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='ModelFormDemo', 16 | fields=[ 17 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(blank=True, max_length=100, null=True)), 19 | ('email', models.EmailField(blank=True, max_length=254, null=True)), 20 | ('age', models.IntegerField(blank=True, null=True)), 21 | ('is_active', models.BooleanField(default=True)), 22 | ('created_at', models.DateTimeField(auto_now_add=True)), 23 | ], 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/model_form_demo/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.shortcuts import reverse 3 | 4 | 5 | class ModelFormDemo(models.Model): 6 | name = models.CharField(max_length=100, blank=True, null=True) 7 | email = models.EmailField(blank=True, null=True) 8 | age = models.IntegerField(blank=True, null=True) 9 | is_active = models.BooleanField(default=True) 10 | created_at = models.DateTimeField(auto_now_add=True) 11 | 12 | def __str__(self): 13 | return self.name or f"test-model-{self.pk}" 14 | 15 | def get_absolute_url(self): 16 | return reverse("model_form_demo_detail", kwargs={"pk": self.pk}) 17 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/templates/model_form_demo_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 |

Test Model Detail: {{ model_form_demo.name }}

4 |

Name: {{ model_form_demo.name }}

5 |

Email: {{ model_form_demo.email }}

6 |

Age: {{ model_form_demo.age }}

7 |

Active: {{ model_form_demo.is_active }}

8 |

Created At: {{ model_form_demo.created_at }}

9 | Edit Test Model 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/templates/model_form_demo_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 |

4 | {% if form.instance.pk %} 5 | Update Test Model 6 | {% else %} 7 | Create Test Model 8 | {% endif %} 9 |

10 |
11 | {% csrf_token %} 12 | {{ form.as_p }} 13 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/templates/model_form_demo_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 |

Test Models List

4 | 11 | Create New Test Model 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import ( 3 | ModelFormDemoListView, 4 | ModelFormDemoCreateView, 5 | ModelFormDemoUpdateView, 6 | ModelFormDemoDetailView, 7 | ) 8 | 9 | urlpatterns = [ 10 | path("", ModelFormDemoListView.as_view(), name="model_form_demo_list"), 11 | path("create/", ModelFormDemoCreateView.as_view(), name="model_form_demo_create"), 12 | path( 13 | "/update/", 14 | ModelFormDemoUpdateView.as_view(), 15 | name="model_form_demo_update", 16 | ), 17 | path("/", ModelFormDemoDetailView.as_view(), name="model_form_demo_detail"), 18 | ] 19 | -------------------------------------------------------------------------------- /test/django-init-wagtail/model_form_demo/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import ListView, CreateView, UpdateView, DetailView 2 | from .models import ModelFormDemo 3 | from .forms import ModelFormDemoForm 4 | 5 | 6 | class ModelFormDemoListView(ListView): 7 | model = ModelFormDemo 8 | template_name = "model_form_demo_list.html" 9 | context_object_name = "model_form_demos" 10 | 11 | 12 | class ModelFormDemoCreateView(CreateView): 13 | model = ModelFormDemo 14 | form_class = ModelFormDemoForm 15 | template_name = "model_form_demo_form.html" 16 | 17 | def form_valid(self, form): 18 | form.instance.created_by = self.request.user 19 | return super().form_valid(form) 20 | 21 | 22 | class ModelFormDemoUpdateView(UpdateView): 23 | model = ModelFormDemo 24 | form_class = ModelFormDemoForm 25 | template_name = "model_form_demo_form.html" 26 | 27 | 28 | class ModelFormDemoDetailView(DetailView): 29 | model = ModelFormDemo 30 | template_name = "model_form_demo_detail.html" 31 | context_object_name = "model_form_demo" 32 | -------------------------------------------------------------------------------- /test/django-init-wagtail/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "python-webpack-boilerplate", 3 | "version": "1.0.3", 4 | "description": "Webpack boilerplate for Django & Flask", 5 | "scripts": { 6 | "build": "cross-env NODE_ENV=production webpack --config frontend/webpack/webpack.config.prod.js", 7 | "start": "webpack serve --config frontend/webpack/webpack.config.dev.js", 8 | "watch": "webpack --watch --config frontend/webpack/webpack.config.watch.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/AccordBox/python-webpack-boilerplate" 13 | }, 14 | "keywords": [ 15 | "webpack", 16 | "startkit", 17 | "frontend" 18 | ], 19 | "author": "Michael Yin", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/AccordBox/python-webpack-boilerplate/issues" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.20.5", 26 | "@babel/eslint-parser": "^7.19.1", 27 | "@babel/plugin-transform-class-properties": "^7.18.6", 28 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 29 | "@babel/preset-env": "^7.20.2", 30 | "babel-loader": "^9.1.2", 31 | "clean-webpack-plugin": "^4.0.0", 32 | "copy-webpack-plugin": "^12.0.2", 33 | "cross-env": "^7.0.3", 34 | "css-loader": "^7.1.1", 35 | "eslint": "^8.28.0", 36 | "eslint-webpack-plugin": "^4.1.0", 37 | "mini-css-extract-plugin": "^2.7.1", 38 | "postcss-loader": "^8.0.0", 39 | "postcss-preset-env": "^9.0.0", 40 | "sass": "~1.69.5", 41 | "sass-loader": "^13.3.2", 42 | "style-loader": "^3.3.1", 43 | "stylelint": "^16.4.0", 44 | "stylelint-config-standard-scss": "^13.1.0", 45 | "stylelint-config-standard": "^36.0.0", 46 | "stylelint-webpack-plugin": "^5.0.0", 47 | "webpack": "^5.75.0", 48 | "webpack-assets-manifest": "^5.1.0", 49 | "webpack-cli": "^5.0.0", 50 | "webpack-dev-server": "^5.0.2", 51 | "webpack-merge": "^5.8.0" 52 | }, 53 | "dependencies": { 54 | "core-js": "^3.37.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/payments/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Product, Order 3 | 4 | admin.site.register(Product) 5 | admin.site.register(Order) 6 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PaymentsConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'payments' 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | 4 | class PaymentsForm(forms.Form): 5 | stripeToken = forms.CharField(widget=forms.HiddenInput()) 6 | amount = forms.DecimalField( 7 | max_digits=10, decimal_places=2, widget=forms.HiddenInput() 8 | ) 9 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1.4 on 2024-12-05 00:56 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Product', 17 | fields=[ 18 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('name', models.CharField(max_length=100)), 20 | ('description', models.TextField()), 21 | ('price', models.DecimalField(decimal_places=2, max_digits=10)), 22 | ], 23 | ), 24 | migrations.CreateModel( 25 | name='Order', 26 | fields=[ 27 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 28 | ('created_at', models.DateTimeField(auto_now_add=True)), 29 | ('stripe_checkout_session_id', models.CharField(max_length=200)), 30 | ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='payments.product')), 31 | ], 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/migrations/0002_set_stripe_api_keys.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | import os 3 | import secrets 4 | import logging 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | def generate_default_key(): 10 | return "sk_test_" + secrets.token_hex(24) 11 | 12 | 13 | def set_stripe_api_keys(apps, schema_editor): 14 | # Get the Stripe API Key model 15 | APIKey = apps.get_model("djstripe", "APIKey") 16 | 17 | # Fetch the keys from environment variables or generate default keys 18 | test_secret_key = os.environ.get("STRIPE_TEST_SECRET_KEY", generate_default_key()) 19 | live_secret_key = os.environ.get("STRIPE_LIVE_SECRET_KEY", generate_default_key()) 20 | 21 | logger.info("STRIPE_TEST_SECRET_KEY: %s", test_secret_key) 22 | logger.info("STRIPE_LIVE_SECRET_KEY: %s", live_secret_key) 23 | 24 | # Check if the keys are not already in the database 25 | if not APIKey.objects.filter(secret=test_secret_key).exists(): 26 | APIKey.objects.create(secret=test_secret_key, livemode=False) 27 | logger.info("Added test secret key to the database.") 28 | else: 29 | logger.info("Test secret key already exists in the database.") 30 | 31 | if not APIKey.objects.filter(secret=live_secret_key).exists(): 32 | APIKey.objects.create(secret=live_secret_key, livemode=True) 33 | logger.info("Added live secret key to the database.") 34 | else: 35 | logger.info("Live secret key already exists in the database.") 36 | 37 | 38 | class Migration(migrations.Migration): 39 | 40 | dependencies = [ 41 | ("payments", "0001_initial"), 42 | ] 43 | 44 | operations = [ 45 | migrations.RunPython(set_stripe_api_keys), 46 | ] 47 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/migrations/0003_create_initial_products.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | 4 | def create_initial_products(apps, schema_editor): 5 | Product = apps.get_model("payments", "Product") 6 | Product.objects.create(name="T-shirt", description="A cool T-shirt", price=20.00) 7 | Product.objects.create(name="Mug", description="A nice mug", price=10.00) 8 | Product.objects.create(name="Hat", description="A stylish hat", price=15.00) 9 | 10 | 11 | class Migration(migrations.Migration): 12 | dependencies = [ 13 | ( 14 | "payments", 15 | "0002_set_stripe_api_keys", 16 | ), 17 | ] 18 | 19 | operations = [ 20 | migrations.RunPython(create_initial_products), 21 | ] 22 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/payments/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Product(models.Model): 5 | name = models.CharField(max_length=100) 6 | description = models.TextField() 7 | price = models.DecimalField(max_digits=10, decimal_places=2) 8 | 9 | def __str__(self): 10 | return self.name 11 | 12 | 13 | class Order(models.Model): 14 | product = models.ForeignKey(Product, on_delete=models.CASCADE) 15 | created_at = models.DateTimeField(auto_now_add=True) 16 | stripe_checkout_session_id = models.CharField(max_length=200) 17 | 18 | def __str__(self): 19 | return f"Order {self.id} for {self.product.name}" 20 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/templates/payments/cancel.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Cancel{% endblock %} 3 | {% block content %} 4 |

Payment Cancelled

5 |

Your payment was cancelled.

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/templates/payments/checkout.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Checkout{% endblock %} 3 | {% block content %} 4 |

Checkout

5 |
6 | {% csrf_token %} 7 | 8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/templates/payments/product_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}{{ product.name }}{% endblock %} 3 | {% block content %} 4 |

{{ product.name }}

5 |

{{ product.description }}

6 |

Price: }

7 |
8 | {% csrf_token %} 9 | 10 | 11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/templates/payments/product_list.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Products{% endblock %} 3 | {% block content %} 4 |

Products

5 | 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/templates/payments/success.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Success{% endblock %} 3 | {% block content %} 4 |

Payment Successful

5 |

Thank you for your purchase!

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/payments/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import ( 3 | CheckoutView, 4 | SuccessView, 5 | CancelView, 6 | ProductListView, 7 | ProductDetailView, 8 | ) 9 | 10 | urlpatterns = [ 11 | path("", ProductListView.as_view(), name="product_list"), 12 | path("product//", ProductDetailView.as_view(), name="product_detail"), 13 | path("checkout/", CheckoutView.as_view(), name="checkout"), 14 | path("success/", SuccessView.as_view(), name="success"), 15 | path("cancel/", CancelView.as_view(), name="cancel"), 16 | ] 17 | -------------------------------------------------------------------------------- /test/django-init-wagtail/postcss.config.js: -------------------------------------------------------------------------------- 1 | const postcssPresetEnv = require("postcss-preset-env"); 2 | 3 | module.exports = { 4 | plugins: [postcssPresetEnv()], 5 | }; 6 | -------------------------------------------------------------------------------- /test/django-init-wagtail/privacypage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/privacypage/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/privacypage/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/privacypage/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PrivacypageConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'privacypage' 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/privacypage/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1.4 on 2024-12-05 00:56 2 | 3 | import django.db.models.deletion 4 | import wagtailmarkdown.fields 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ('wagtailcore', '0094_alter_page_locale'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='PrivacyPage', 19 | fields=[ 20 | ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), 21 | ('body', wagtailmarkdown.fields.MarkdownField()), 22 | ], 23 | options={ 24 | 'verbose_name': 'Privacy Page', 25 | }, 26 | bases=('wagtailcore.page',), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /test/django-init-wagtail/privacypage/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/privacypage/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/privacypage/models.py: -------------------------------------------------------------------------------- 1 | from wagtail.models import Page 2 | from wagtail.admin.panels import FieldPanel 3 | from wagtailmarkdown.fields import MarkdownField 4 | 5 | 6 | class PrivacyPage(Page): 7 | """ 8 | A Wagtail Page model for the Privacy Policy page. 9 | """ 10 | 11 | template = "privacy_page.html" 12 | 13 | body = MarkdownField() 14 | 15 | content_panels = Page.content_panels + [ 16 | FieldPanel("body", classname="full"), 17 | ] 18 | 19 | class Meta: 20 | verbose_name = "Privacy Page" 21 | -------------------------------------------------------------------------------- /test/django-init-wagtail/privacypage/templates/privacy_page.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load wagtailmarkdown %} 3 | {% block content %}
{{ page.body|markdown }}
{% endblock %} 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/privacypage/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/privacypage/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/requirements-test.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-runner 3 | coverage 4 | pytest-mock 5 | pytest-cov 6 | hypothesis 7 | selenium 8 | pytest-django 9 | factory-boy 10 | flake8 11 | tox 12 | -------------------------------------------------------------------------------- /test/django-init-wagtail/search/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/search/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/search/templates/search/search.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static wagtailcore_tags %} 3 | {% block body_class %}template-searchresults{% endblock %} 4 | {% block title %}Search{% endblock %} 5 | {% block content %} 6 |

Search

7 |
8 | 11 | 12 |
13 | {% if search_results %} 14 |
    15 | {% for result in search_results %} 16 |
  • 17 |

    18 | {{ result }} 19 |

    20 | {% if result.search_description %}{{ result.search_description }}{% endif %} 21 |
  • 22 | {% endfor %} 23 |
24 | {% if search_results.has_previous %} 25 | Previous 26 | {% endif %} 27 | {% if search_results.has_next %} 28 | Next 29 | {% endif %} 30 | {% elif search_query %} 31 | No results found 32 | {% else %} 33 | No results found. Try a test query? 34 | {% endif %} 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /test/django-init-wagtail/search/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import search 3 | 4 | urlpatterns = [path("", search, name="search")] 5 | -------------------------------------------------------------------------------- /test/django-init-wagtail/search/views.py: -------------------------------------------------------------------------------- 1 | from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator 2 | from django.template.response import TemplateResponse 3 | 4 | from wagtail.models import Page 5 | 6 | # To enable logging of search queries for use with the "Promoted search results" module 7 | # 8 | # uncomment the following line and the lines indicated in the search function 9 | # (after adding wagtail.contrib.search_promotions to INSTALLED_APPS): 10 | 11 | # from wagtail.contrib.search_promotions.models import Query 12 | 13 | 14 | def search(request): 15 | search_query = request.GET.get("query", None) 16 | page = request.GET.get("page", 1) 17 | 18 | # Search 19 | if search_query: 20 | search_results = Page.objects.live().search(search_query) 21 | 22 | # To log this query for use with the "Promoted search results" module: 23 | 24 | # query = Query.get(search_query) 25 | # query.add_hit() 26 | 27 | else: 28 | search_results = Page.objects.none() 29 | 30 | # Pagination 31 | paginator = Paginator(search_results, 10) 32 | try: 33 | search_results = paginator.page(page) 34 | except PageNotAnInteger: 35 | search_results = paginator.page(1) 36 | except EmptyPage: 37 | search_results = paginator.page(paginator.num_pages) 38 | 39 | return TemplateResponse( 40 | request, 41 | "search/search.html", 42 | { 43 | "search_query": search_query, 44 | "search_results": search_results, 45 | }, 46 | ) 47 | -------------------------------------------------------------------------------- /test/django-init-wagtail/sitepage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/sitepage/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/sitepage/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/sitepage/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SitepageConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'sitepage' 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/sitepage/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1.4 on 2024-12-05 00:56 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('wagtailcore', '0094_alter_page_locale'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='SitePage', 18 | fields=[ 19 | ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), 20 | ], 21 | options={ 22 | 'verbose_name': 'Site Page', 23 | }, 24 | bases=('wagtailcore.page',), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /test/django-init-wagtail/sitepage/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/sitepage/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/sitepage/models.py: -------------------------------------------------------------------------------- 1 | from wagtail.models import Page 2 | 3 | 4 | class SitePage(Page): 5 | template = "sitepage/site_page.html" 6 | 7 | class Meta: 8 | verbose_name = "Site Page" 9 | -------------------------------------------------------------------------------- /test/django-init-wagtail/sitepage/templates/sitepage/site_page.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 |

{{ page.title }}

4 | {% endblock %} 5 | -------------------------------------------------------------------------------- /test/django-init-wagtail/sitepage/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/sitepage/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/siteuser/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.admin import UserAdmin 2 | from django.contrib import admin 3 | 4 | from .models import User 5 | 6 | admin.site.register(User, UserAdmin) 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SiteuserConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'siteuser' 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib.auth.forms import UserChangeForm 3 | from crispy_forms.helper import FormHelper 4 | from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit 5 | from .models import User 6 | 7 | 8 | class SiteUserForm(UserChangeForm): 9 | bio = forms.CharField(widget=forms.Textarea(attrs={"id": "editor"}), required=False) 10 | 11 | class Meta(UserChangeForm.Meta): 12 | model = User 13 | fields = ("username", "user_theme_preference", "bio", "rate") 14 | 15 | def __init__(self, *args, **kwargs): 16 | super().__init__(*args, **kwargs) 17 | self.helper = FormHelper() 18 | self.helper.form_method = "post" 19 | self.helper.layout = Layout( 20 | Fieldset( 21 | "Edit Your Profile", 22 | "username", 23 | "user_theme_preference", 24 | "bio", 25 | "rate", 26 | ), 27 | ButtonHolder(Submit("submit", "Save", css_class="btn btn-primary")), 28 | ) 29 | -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/siteuser/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractUser, Group, Permission 3 | from django.conf import settings 4 | 5 | 6 | class User(AbstractUser): 7 | groups = models.ManyToManyField(Group, related_name="siteuser_set", blank=True) 8 | user_permissions = models.ManyToManyField( 9 | Permission, related_name="siteuser_set", blank=True 10 | ) 11 | 12 | user_theme_preference = models.CharField( 13 | max_length=10, choices=settings.THEMES, default="light" 14 | ) 15 | 16 | bio = models.TextField(blank=True, null=True) 17 | rate = models.FloatField(blank=True, null=True) 18 | -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/templates/user.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 |

User Profile

4 |
5 | Edit 7 |
8 |

Username: {{ user.username }}

9 |

Theme: {{ user.user_theme_preference }}

10 |

Bio: {{ user.bio|default:""|safe }}

11 |

Rate: {{ user.rate|default:"" }}

12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/templates/user_edit.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load crispy_forms_tags %} 3 | {% block content %} 4 |

Edit User

5 | {% crispy form %} 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import UserProfileView, UpdateThemePreferenceView, UserEditView 3 | 4 | urlpatterns = [ 5 | path("profile/", UserProfileView.as_view(), name="user-profile"), 6 | path( 7 | "update_theme_preference/", 8 | UpdateThemePreferenceView.as_view(), 9 | name="update_theme_preference", 10 | ), 11 | path("/edit/", UserEditView.as_view(), name="user-edit"), 12 | ] 13 | -------------------------------------------------------------------------------- /test/django-init-wagtail/siteuser/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.contrib.auth.mixins import LoginRequiredMixin 4 | from django.http import JsonResponse 5 | from django.utils.decorators import method_decorator 6 | from django.views import View 7 | from django.views.decorators.csrf import csrf_exempt 8 | from django.views.generic import DetailView 9 | from django.views.generic.edit import UpdateView 10 | from django.urls import reverse_lazy 11 | 12 | from .models import User 13 | from .forms import SiteUserForm 14 | 15 | 16 | class UserProfileView(LoginRequiredMixin, DetailView): 17 | model = User 18 | template_name = "user.html" 19 | 20 | def get_object(self, queryset=None): 21 | return self.request.user 22 | 23 | 24 | @method_decorator(csrf_exempt, name="dispatch") 25 | class UpdateThemePreferenceView(View): 26 | def post(self, request, *args, **kwargs): 27 | try: 28 | data = json.loads(request.body.decode("utf-8")) 29 | new_theme = data.get("theme") 30 | user = request.user 31 | user.user_theme_preference = new_theme 32 | user.save() 33 | response_data = {"theme": new_theme} 34 | return JsonResponse(response_data) 35 | except json.JSONDecodeError as e: 36 | return JsonResponse({"error": e}, status=400) 37 | 38 | def http_method_not_allowed(self, request, *args, **kwargs): 39 | return JsonResponse({"error": "Invalid request method"}, status=405) 40 | 41 | 42 | class UserEditView(LoginRequiredMixin, UpdateView): 43 | model = User 44 | template_name = "user_edit.html" # Create this template in your templates folder 45 | form_class = SiteUserForm 46 | 47 | def get_success_url(self): 48 | # return reverse_lazy("user-profile", kwargs={"pk": self.object.pk}) 49 | return reverse_lazy("user-profile") 50 | -------------------------------------------------------------------------------- /test/django-init-wagtail/unit_test_demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/unit_test_demo/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/unit_test_demo/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /test/django-init-wagtail/unit_test_demo/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UnitTestDemoConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'unit_test_demo' 7 | -------------------------------------------------------------------------------- /test/django-init-wagtail/unit_test_demo/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from .models import UnitTestDemoModel 3 | 4 | class UnitTestDemoForm(forms.ModelForm): 5 | class Meta: 6 | model = UnitTestDemoModel 7 | fields = ["field1", "field2"] 8 | -------------------------------------------------------------------------------- /test/django-init-wagtail/unit_test_demo/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.1.4 on 2024-12-05 00:56 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='UnitTestDemoModel', 16 | fields=[ 17 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('field1', models.CharField(max_length=100)), 19 | ('field2', models.CharField(max_length=100)), 20 | ], 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /test/django-init-wagtail/unit_test_demo/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init-wagtail/unit_test_demo/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init-wagtail/unit_test_demo/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class UnitTestDemoModel(models.Model): 4 | field1 = models.CharField(max_length=100) 5 | field2 = models.CharField(max_length=100) 6 | 7 | def __str__(self): 8 | return "Expected String Representation" 9 | -------------------------------------------------------------------------------- /test/django-init-wagtail/unit_test_demo/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /test/django-init/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "usage", 7 | "corejs": "3.0.0" 8 | } 9 | ] 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-syntax-dynamic-import", 13 | "@babel/plugin-transform-class-properties" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/django-init/.browserslistrc: -------------------------------------------------------------------------------- 1 | [production staging] 2 | >5% 3 | last 2 versions 4 | not ie > 0 5 | not ie_mob > 0 6 | Firefox ESR 7 | 8 | [development] 9 | last 1 chrome version 10 | last 1 firefox version 11 | last 1 edge version 12 | -------------------------------------------------------------------------------- /test/django-init/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@babel/eslint-parser", 3 | "extends": [ 4 | "eslint:recommended" 5 | ], 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": 8, 12 | "sourceType": "module", 13 | "requireConfigFile": false 14 | }, 15 | "rules": { 16 | "semi": 2 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/django-init/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | dist/ 4 | node_modules/ 5 | _build/ 6 | .elasticbeanstalk/ 7 | db.sqlite3 8 | static/ 9 | backend/inituser 10 | backend/var 11 | -------------------------------------------------------------------------------- /test/django-init/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/iron 2 | -------------------------------------------------------------------------------- /test/django-init/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard-scss", 3 | "rules": { 4 | "at-rule-no-unknown": null, 5 | "scss/at-rule-no-unknown": true, 6 | "scss/at-import-partial-extension": null 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/django-init/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazonlinux:2023 2 | RUN dnf install -y shadow-utils python3.11 python3.11-pip make nodejs20-npm nodejs postgresql15 postgresql15-server 3 | USER postgres 4 | RUN initdb -D /var/lib/pgsql/data 5 | USER root 6 | RUN useradd wagtail 7 | EXPOSE 8000 8 | ENV PYTHONUNBUFFERED=1 PORT=8000 9 | COPY requirements.txt / 10 | RUN python3.11 -m pip install -r /requirements.txt 11 | WORKDIR /app 12 | RUN chown wagtail:wagtail /app 13 | COPY --chown=wagtail:wagtail . . 14 | USER wagtail 15 | RUN npm-20 install; npm-20 run build 16 | RUN python3.11 manage.py collectstatic --noinput --clear 17 | CMD set -xe; pg_ctl -D /var/lib/pgsql/data -l /tmp/logfile start; python3.11 manage.py migrate --noinput; gunicorn backend.wsgi:application 18 | -------------------------------------------------------------------------------- /test/django-init/backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/backend/__init__.py -------------------------------------------------------------------------------- /test/django-init/backend/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin import AdminSite 2 | 3 | 4 | class CustomAdminSite(AdminSite): 5 | site_header = "Project Makefile" 6 | site_title = "Project Makefile" 7 | index_title = "Project Makefile" 8 | 9 | 10 | custom_admin_site = CustomAdminSite(name="custom_admin") 11 | -------------------------------------------------------------------------------- /test/django-init/backend/api.py: -------------------------------------------------------------------------------- 1 | from ninja import NinjaAPI 2 | from rest_framework import viewsets 3 | from siteuser.models import User 4 | from .serializers import UserSerializer 5 | 6 | api = NinjaAPI() 7 | 8 | 9 | @api.get("/hello") 10 | def hello(request): 11 | return "Hello world" 12 | 13 | 14 | class UserViewSet(viewsets.ModelViewSet): 15 | queryset = User.objects.all() 16 | serializer_class = UserSerializer 17 | -------------------------------------------------------------------------------- /test/django-init/backend/apps.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin.apps import AdminConfig 2 | 3 | 4 | class CustomAdminConfig(AdminConfig): 5 | default_site = "backend.admin.CustomAdminSite" 6 | -------------------------------------------------------------------------------- /test/django-init/backend/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for backend project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /test/django-init/backend/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from siteuser.models import User 3 | 4 | 5 | class UserSerializer(serializers.HyperlinkedModelSerializer): 6 | class Meta: 7 | model = User 8 | fields = ["url", "username", "email", "is_staff"] 9 | -------------------------------------------------------------------------------- /test/django-init/backend/settings/dev.py: -------------------------------------------------------------------------------- 1 | # project-makefile 2 | from .base import * # noqa 3 | 4 | # SECURITY WARNING: don't run with debug turned on in production! 5 | DEBUG = True 6 | 7 | # SECURITY WARNING: define the correct hosts in production! 8 | ALLOWED_HOSTS = ["*"] 9 | 10 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" 11 | 12 | try: 13 | from .local import * # noqa 14 | except ImportError: 15 | pass 16 | 17 | LOGGING = { 18 | "version": 1, 19 | "disable_existing_loggers": False, 20 | "formatters": { 21 | "verbose": { 22 | "format": "{levelname} {asctime} {module} {message}", 23 | "style": "{", 24 | }, 25 | "simple": { 26 | "format": "{levelname} {message}", 27 | "style": "{", 28 | }, 29 | }, 30 | "handlers": { 31 | "console": { 32 | "level": "DEBUG", 33 | "class": "logging.StreamHandler", 34 | "formatter": "verbose", 35 | }, 36 | }, 37 | "loggers": { 38 | "django": { 39 | "handlers": ["console"], 40 | "level": "DEBUG", 41 | "propagate": True, 42 | }, 43 | }, 44 | } 45 | 46 | INTERNAL_IPS = [ 47 | "127.0.0.1", 48 | ] 49 | 50 | MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware") # noqa 51 | MIDDLEWARE.append("hijack.middleware.HijackUserMiddleware") # noqa 52 | INSTALLED_APPS.append("django.contrib.admindocs") # noqa 53 | SECRET_KEY = "OYKI8mHGZtI9pmFhHnN0NEJotnW7XoUySs3hGP2fGRgNaro3ZhPjNwfBgFaI/3Z3" 54 | -------------------------------------------------------------------------------- /test/django-init/backend/settings/production.py: -------------------------------------------------------------------------------- 1 | from .base import * # noqa 2 | from backend.utils import get_ec2_metadata 3 | 4 | DEBUG = False 5 | 6 | try: 7 | from .local import * # noqa 8 | except ImportError: 9 | pass 10 | 11 | LOCAL_IPV4 = get_ec2_metadata() 12 | ALLOWED_HOSTS.append(LOCAL_IPV4) # noqa 13 | -------------------------------------------------------------------------------- /test/django-init/backend/templates/allauth/layouts/base.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | -------------------------------------------------------------------------------- /test/django-init/backend/templates/favicon.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | -------------------------------------------------------------------------------- /test/django-init/backend/templates/footer.html: -------------------------------------------------------------------------------- 1 |
2 |

© {% now "Y" %} {{ current_site.site_name|default:"Project Makefile" }}

3 |
    4 |
  • 5 | Home 7 |
  • 8 | {% for child in current_site.root_page.get_children %} 9 |
  • 10 | {{ child }} 12 |
  • 13 | {% endfor %} 14 |
15 |
16 | -------------------------------------------------------------------------------- /test/django-init/backend/templates/offcanvas.html: -------------------------------------------------------------------------------- 1 |
5 | 14 |
15 | 37 |
38 |
39 | -------------------------------------------------------------------------------- /test/django-init/backend/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | from django.conf import settings 4 | 5 | urlpatterns = [ 6 | path("django/", admin.site.urls), 7 | ] 8 | if settings.DEBUG: 9 | urlpatterns += [path("__debug__/", include("debug_toolbar.urls"))] 10 | urlpatterns += [path("accounts/", include("allauth.urls"))] 11 | urlpatterns += [path("user/", include("siteuser.urls"))] 12 | urlpatterns += [path("", include("home.urls"))] 13 | from rest_framework import routers # noqa 14 | from .api import UserViewSet, api # noqa 15 | 16 | router = routers.DefaultRouter() 17 | router.register(r"users", UserViewSet) 18 | # urlpatterns += [path("api/", include(router.urls))] 19 | urlpatterns += [path("api/", api.urls)] 20 | -------------------------------------------------------------------------------- /test/django-init/backend/utils.py: -------------------------------------------------------------------------------- 1 | from django.urls import URLResolver 2 | import requests 3 | 4 | 5 | def get_ec2_metadata(): 6 | try: 7 | # Step 1: Get the token 8 | token_url = "http://169.254.169.254/latest/api/token" 9 | headers = {"X-aws-ec2-metadata-token-ttl-seconds": "21600"} 10 | response = requests.put(token_url, headers=headers) 11 | response.raise_for_status() # Raise an error for bad responses 12 | 13 | token = response.text 14 | 15 | # Step 2: Use the token to get the instance metadata 16 | metadata_url = "http://169.254.169.254/latest/meta-data/local-ipv4" 17 | headers = {"X-aws-ec2-metadata-token": token} 18 | response = requests.get(metadata_url, headers=headers) 19 | response.raise_for_status() # Raise an error for bad responses 20 | 21 | metadata = response.text 22 | return metadata 23 | except requests.RequestException as e: 24 | print(f"Error retrieving EC2 metadata: {e}") 25 | return None 26 | 27 | 28 | # Function to remove a specific URL pattern based on its route (including catch-all) 29 | def remove_urlpattern(urlpatterns, route_to_remove): 30 | urlpatterns[:] = [ 31 | urlpattern 32 | for urlpattern in urlpatterns 33 | if not ( 34 | isinstance(urlpattern, URLResolver) 35 | and urlpattern.pattern._route == route_to_remove 36 | ) 37 | ] 38 | -------------------------------------------------------------------------------- /test/django-init/backend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for backend project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /test/django-init/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | db: 5 | image: postgres:latest 6 | volumes: 7 | - postgres_data:/var/lib/postgresql/data 8 | environment: 9 | POSTGRES_DB: project 10 | POSTGRES_USER: admin 11 | POSTGRES_PASSWORD: admin 12 | 13 | web: 14 | build: . 15 | command: sh -c "python manage.py migrate && gunicorn project.wsgi:application -b 0.0.0.0:8000" 16 | volumes: 17 | - .:/app 18 | ports: 19 | - "8000:8000" 20 | depends_on: 21 | - db 22 | environment: 23 | DATABASE_URL: postgres://admin:admin@db:5432/project 24 | 25 | volumes: 26 | postgres_data: 27 | -------------------------------------------------------------------------------- /test/django-init/frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-react", 5 | ], 6 | [ 7 | "@babel/preset-env", 8 | { 9 | "useBuiltIns": "usage", 10 | "corejs": "3.0.0" 11 | } 12 | ] 13 | ], 14 | "plugins": [ 15 | "@babel/plugin-syntax-dynamic-import", 16 | "@babel/plugin-transform-class-properties" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/django-init/frontend/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended" 10 | ], 11 | "overrides": [ 12 | { 13 | "env": { 14 | "node": true 15 | }, 16 | "files": [ 17 | ".eslintrc.{js,cjs}" 18 | ], 19 | "parserOptions": { 20 | "sourceType": "script" 21 | } 22 | } 23 | ], 24 | "parserOptions": { 25 | "ecmaVersion": "latest", 26 | "sourceType": "module" 27 | }, 28 | "plugins": [ 29 | "react" 30 | ], 31 | "rules": { 32 | "no-unused-vars": "off" 33 | }, 34 | settings: { 35 | react: { 36 | version: 'detect', 37 | }, 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /test/django-init/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # production 5 | build 6 | 7 | # misc 8 | .DS_Store 9 | 10 | npm-debug.log 11 | yarn-error.log 12 | yarn.lock 13 | .yarnclean 14 | .vscode 15 | .idea 16 | -------------------------------------------------------------------------------- /test/django-init/frontend/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This project was created with [python-webpack-boilerplate](https://github.com/AccordBox/python-webpack-boilerplate) 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm run start` 10 | 11 | `npm run start` will launch a server process, which makes `live reloading` possible. 12 | 13 | If you change JS or SCSS files, the web page would auto refresh after the change. Now the server is working on port 9091 by default, but you can change it in `webpack/webpack.config.dev.js` 14 | 15 | ### `npm run watch` 16 | 17 | run webpack in `watch` mode. 18 | 19 | ### `npm run build` 20 | 21 | [production mode](https://webpack.js.org/guides/production/), Webpack would focus on minified bundles, lighter weight source maps, and optimized assets to improve load time. 22 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/application/README.md: -------------------------------------------------------------------------------- 1 | When Webpacker compiles your JavaScript code, it scans the `src/application` directory for files with the .js extension and automatically includes them as entry points for the Webpack bundling process. 2 | 3 | For the `application/app.js`, you can import it in template like this: 4 | 5 | ``` 6 | {% javascript_pack 'app' attrs='charset="UTF-8"' %} 7 | ``` 8 | 9 | In most cases, you do not need to create another file in this directory. 10 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/application/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import 'bootstrap'; 4 | // import '@fortawesome/fontawesome-free/js/fontawesome'; 5 | // import '@fortawesome/fontawesome-free/js/solid'; 6 | // import '@fortawesome/fontawesome-free/js/regular'; 7 | // import '@fortawesome/fontawesome-free/js/brands'; 8 | import getDataComponents from '../dataComponents'; 9 | import UserContextProvider from '../context'; 10 | import * as components from '../components'; 11 | import "../styles/index.scss"; 12 | import "../styles/theme-blue.scss"; 13 | import "./config"; 14 | 15 | const { ErrorBoundary } = components; 16 | const dataComponents = getDataComponents(components); 17 | const container = document.getElementById('app'); 18 | const root = createRoot(container); 19 | const App = () => ( 20 | 21 | 22 | {dataComponents} 23 | 24 | 25 | ); 26 | root.render(); 27 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/application/config.js: -------------------------------------------------------------------------------- 1 | import '../utils/themeToggler.js'; 2 | // import '../utils/tinymce.js'; 3 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/components/Clock.js: -------------------------------------------------------------------------------- 1 | // Via ChatGPT 2 | import React, { useState, useEffect, useCallback, useRef } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const Clock = ({ color = '#fff' }) => { 6 | const [date, setDate] = useState(new Date()); 7 | const [blink, setBlink] = useState(true); 8 | const timerID = useRef(); 9 | 10 | const tick = useCallback(() => { 11 | setDate(new Date()); 12 | setBlink(prevBlink => !prevBlink); 13 | }, []); 14 | 15 | useEffect(() => { 16 | timerID.current = setInterval(() => tick(), 1000); 17 | 18 | // Return a cleanup function to be run on component unmount 19 | return () => clearInterval(timerID.current); 20 | }, [tick]); 21 | 22 | const formattedDate = date.toLocaleDateString(undefined, { 23 | weekday: 'short', 24 | year: 'numeric', 25 | month: 'short', 26 | day: 'numeric', 27 | }); 28 | 29 | const formattedTime = date.toLocaleTimeString(undefined, { 30 | hour: 'numeric', 31 | minute: 'numeric', 32 | }); 33 | 34 | return ( 35 | <> 36 |
{formattedDate} {formattedTime}
37 | 38 | ); 39 | }; 40 | 41 | Clock.propTypes = { 42 | color: PropTypes.string, 43 | }; 44 | 45 | export default Clock; 46 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/components/ErrorBoundary.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class ErrorBoundary extends Component { 5 | constructor (props) { 6 | super(props); 7 | this.state = { hasError: false }; 8 | } 9 | 10 | static getDerivedStateFromError () { 11 | return { hasError: true }; 12 | } 13 | 14 | componentDidCatch (error, info) { 15 | const { onError } = this.props; 16 | console.error(error); 17 | onError && onError(error, info); 18 | } 19 | 20 | render () { 21 | const { children = null } = this.props; 22 | const { hasError } = this.state; 23 | 24 | return hasError ? null : children; 25 | } 26 | } 27 | 28 | ErrorBoundary.propTypes = { 29 | onError: PropTypes.func, 30 | children: PropTypes.node, 31 | }; 32 | 33 | export default ErrorBoundary; 34 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/components/README.md: -------------------------------------------------------------------------------- 1 | We recommend write Javascript here to make your code reusable. 2 | 3 | And you can check below packages to increase your productivity: 4 | 5 | 1. [Stimulus](https://stimulus.hotwired.dev/) 6 | 2. [Alpine.js](https://alpinejs.dev/) 7 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as ErrorBoundary } from './ErrorBoundary'; 2 | export { default as UserMenu } from './UserMenu'; 3 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/components/jumbotron.js: -------------------------------------------------------------------------------- 1 | class Jumbotron { 2 | static selector() { 3 | return "[data-jumbotron]"; 4 | } 5 | 6 | constructor(node) { 7 | this.node = node; 8 | console.log(`Jumbotron initialized for node: ${node}`); 9 | // do something here 10 | } 11 | } 12 | 13 | export default Jumbotron; 14 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/context/UserContextProvider.js: -------------------------------------------------------------------------------- 1 | // UserContextProvider.js 2 | import React, { createContext, useContext, useState } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const UserContext = createContext(); 6 | 7 | export const UserContextProvider = ({ children }) => { 8 | const [isAuthenticated, setIsAuthenticated] = useState(false); 9 | 10 | const login = () => { 11 | try { 12 | // Add logic to handle login, set isAuthenticated to true 13 | setIsAuthenticated(true); 14 | } catch (error) { 15 | console.error('Login error:', error); 16 | // Handle error, e.g., show an error message to the user 17 | } 18 | }; 19 | 20 | const logout = () => { 21 | try { 22 | // Add logic to handle logout, set isAuthenticated to false 23 | setIsAuthenticated(false); 24 | } catch (error) { 25 | console.error('Logout error:', error); 26 | // Handle error, e.g., show an error message to the user 27 | } 28 | }; 29 | 30 | return ( 31 | 32 | {children} 33 | 34 | ); 35 | }; 36 | 37 | UserContextProvider.propTypes = { 38 | children: PropTypes.node.isRequired, 39 | }; 40 | 41 | export const useUserContext = () => { 42 | const context = useContext(UserContext); 43 | 44 | if (!context) { 45 | throw new Error('useUserContext must be used within a UserContextProvider'); 46 | } 47 | 48 | return context; 49 | }; 50 | 51 | // Add PropTypes for the return value of useUserContext 52 | useUserContext.propTypes = { 53 | isAuthenticated: PropTypes.bool.isRequired, 54 | login: PropTypes.func.isRequired, 55 | logout: PropTypes.func.isRequired, 56 | }; 57 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/context/index.js: -------------------------------------------------------------------------------- 1 | export { UserContextProvider as default } from './UserContextProvider'; 2 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/dataComponents.js: -------------------------------------------------------------------------------- 1 | // Via pwellever 2 | import React from 'react'; 3 | import { createPortal } from 'react-dom'; 4 | 5 | const parseProps = data => Object.entries(data).reduce((result, [key, value]) => { 6 | if (value.toLowerCase() === 'true') { 7 | value = true; 8 | } else if (value.toLowerCase() === 'false') { 9 | value = false; 10 | } else if (value.toLowerCase() === 'null') { 11 | value = null; 12 | } else if (!isNaN(parseFloat(value)) && isFinite(value)) { 13 | // Parse numeric value 14 | value = parseFloat(value); 15 | } else if ( 16 | (value[0] === '[' && value.slice(-1) === ']') || (value[0] === '{' && value.slice(-1) === '}') 17 | ) { 18 | // Parse JSON strings 19 | value = JSON.parse(value); 20 | } 21 | 22 | result[key] = value; 23 | return result; 24 | }, {}); 25 | 26 | // This method of using portals instead of calling ReactDOM.render on individual components 27 | // ensures that all components are mounted under a single React tree, and are therefore able 28 | // to share context. 29 | 30 | export default function getPageComponents (components) { 31 | const getPortalComponent = domEl => { 32 | // The element's "data-component" attribute is used to determine which component to render. 33 | // All other "data-*" attributes are passed as props. 34 | const { component: componentName, ...rest } = domEl.dataset; 35 | const Component = components[componentName]; 36 | if (!Component) { 37 | console.error(`Component "${componentName}" not found.`); 38 | return null; 39 | } 40 | const props = parseProps(rest); 41 | domEl.innerHTML = ''; 42 | 43 | // eslint-disable-next-line no-unused-vars 44 | const { ErrorBoundary } = components; 45 | return createPortal( 46 | 47 | 48 | , 49 | domEl, 50 | ); 51 | }; 52 | 53 | return Array.from(document.querySelectorAll('[data-component]')).map(getPortalComponent); 54 | } 55 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | // If you comment out code below, bootstrap will use red as primary color 2 | // and btn-primary will become red 3 | 4 | // rimary: red; 5 | 6 | @import "~bootstrap/scss/bootstrap.scss"; 7 | 8 | .jumbotron { 9 | // should be relative path of the entry scss file 10 | background-image: url("../../vendors/images/sample.jpg"); 11 | background-size: cover; 12 | } 13 | 14 | #theme-toggler-authenticated:hover { 15 | cursor: pointer; /* Change cursor to pointer on hover */ 16 | color: #007bff; /* Change color on hover */ 17 | } 18 | 19 | #theme-toggler-anonymous:hover { 20 | cursor: pointer; /* Change cursor to pointer on hover */ 21 | color: #007bff; /* Change color on hover */ 22 | } 23 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/styles/theme-blue.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/scss/bootstrap.scss"; 2 | 3 | [data-bs-theme="blue"] { 4 | --bs-body-color: var(--bs-white); 5 | --bs-body-color-rgb: #{to-rgb($white)}; 6 | --bs-body-bg: var(--bs-blue); 7 | --bs-body-bg-rgb: #{to-rgb($blue)}; 8 | --bs-tertiary-bg: #{$blue-600}; 9 | 10 | .dropdown-menu { 11 | --bs-dropdown-bg: #{color-mix($blue-500, $blue-600)}; 12 | --bs-dropdown-link-active-bg: #{$blue-700}; 13 | } 14 | 15 | .btn-secondary { 16 | --bs-btn-bg: #{color-mix(ray-600, lue-400, .5)}; 17 | --bs-btn-border-color: #{rgba($white, .25)}; 18 | --bs-btn-hover-bg: #{color-adjust(color-mix(ray-600, lue-400, .5), 5%)}; 19 | --bs-btn-hover-border-color: #{rgba($white, .25)}; 20 | --bs-btn-active-bg: #{color-adjust(color-mix(ray-600, lue-400, .5), 10%)}; 21 | --bs-btn-active-border-color: #{rgba($white, .5)}; 22 | --bs-btn-focus-border-color: #{rgba($white, .5)}; 23 | 24 | // --bs-btn-focus-box-shadow: 0 0 0 .25rem rgba(255, 255, 255, 20%); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/django-init/frontend/src/utils/themeToggler.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function () { 2 | const rootElement = document.documentElement; 3 | const anonThemeToggle = document.getElementById('theme-toggler-anonymous'); 4 | const authThemeToggle = document.getElementById('theme-toggler-authenticated'); 5 | if (authThemeToggle) { 6 | localStorage.removeItem('data-bs-theme'); 7 | } 8 | const anonSavedTheme = localStorage.getItem('data-bs-theme'); 9 | if (anonSavedTheme) { 10 | rootElement.setAttribute('data-bs-theme', anonSavedTheme); 11 | } 12 | if (anonThemeToggle) { 13 | anonThemeToggle.addEventListener('click', function () { 14 | const currentTheme = rootElement.getAttribute('data-bs-theme') || 'light'; 15 | const newTheme = currentTheme === 'light' ? 'dark' : 'light'; 16 | rootElement.setAttribute('data-bs-theme', newTheme); 17 | localStorage.setItem('data-bs-theme', newTheme); 18 | }); 19 | } 20 | if (authThemeToggle) { 21 | const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value; 22 | authThemeToggle.addEventListener('click', function () { 23 | const currentTheme = rootElement.getAttribute('data-bs-theme') || 'light'; 24 | const newTheme = currentTheme === 'light' ? 'dark' : 'light'; 25 | fetch('/user/update_theme_preference/', { 26 | method: 'POST', 27 | headers: { 28 | 'Content-Type': 'application/json', 29 | 'X-CSRFToken': csrfToken, // Include the CSRF token in the headers 30 | }, 31 | body: JSON.stringify({ theme: newTheme }), 32 | }) 33 | .then(response => response.json()) 34 | .then(data => { 35 | rootElement.setAttribute('data-bs-theme', newTheme); 36 | }) 37 | .catch(error => { 38 | console.error('Error updating theme preference:', error); 39 | }); 40 | }); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /test/django-init/frontend/vendors/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/frontend/vendors/.gitkeep -------------------------------------------------------------------------------- /test/django-init/frontend/vendors/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/frontend/vendors/images/.gitkeep -------------------------------------------------------------------------------- /test/django-init/frontend/vendors/images/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/frontend/vendors/images/sample.jpg -------------------------------------------------------------------------------- /test/django-init/frontend/vendors/images/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/frontend/vendors/images/webpack.png -------------------------------------------------------------------------------- /test/django-init/frontend/webpack/webpack.common.js: -------------------------------------------------------------------------------- 1 | const glob = require("glob"); 2 | const Path = require("path"); 3 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 4 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 5 | const WebpackAssetsManifest = require("webpack-assets-manifest"); 6 | 7 | const getEntryObject = () => { 8 | const entries = {}; 9 | // for javascript/typescript entry file 10 | glob 11 | .sync(Path.join(__dirname, "../src/application/*.{js,ts}")) 12 | .forEach((path) => { 13 | const name = Path.basename(path); 14 | const extension = Path.extname(path); 15 | const entryName = name.replace(extension, ""); 16 | if (entryName in entries) { 17 | throw new Error(`Entry file conflict: ${entryName}`); 18 | } 19 | entries[entryName] = path; 20 | }); 21 | return entries; 22 | }; 23 | 24 | module.exports = { 25 | entry: getEntryObject(), 26 | output: { 27 | path: Path.join(__dirname, "../build"), 28 | filename: "js/[name].js", 29 | publicPath: "/static/", 30 | assetModuleFilename: "[path][name][ext]", 31 | }, 32 | optimization: { 33 | splitChunks: { 34 | chunks: "all", 35 | }, 36 | 37 | runtimeChunk: "single", 38 | }, 39 | plugins: [ 40 | new CleanWebpackPlugin(), 41 | new CopyWebpackPlugin({ 42 | patterns: [ 43 | { from: Path.resolve(__dirname, "../vendors"), to: "vendors" }, 44 | ], 45 | }), 46 | new WebpackAssetsManifest({ 47 | entrypoints: true, 48 | output: "manifest.json", 49 | writeToDisk: true, 50 | publicPath: true, 51 | }), 52 | ], 53 | resolve: { 54 | alias: { 55 | "~": Path.resolve(__dirname, "../src"), 56 | }, 57 | }, 58 | module: { 59 | rules: [ 60 | { 61 | test: /\.mjs$/, 62 | include: /node_modules/, 63 | type: "javascript/auto", 64 | }, 65 | { 66 | test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, 67 | type: "asset", 68 | }, 69 | ], 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /test/django-init/frontend/webpack/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const Path = require("path"); 2 | const Webpack = require("webpack"); 3 | const { merge } = require("webpack-merge"); 4 | const StylelintPlugin = require("stylelint-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | const ESLintPlugin = require("eslint-webpack-plugin"); 7 | 8 | const common = require("./webpack.common.js"); 9 | 10 | module.exports = merge(common, { 11 | target: "web", 12 | mode: "development", 13 | devtool: "inline-source-map", 14 | output: { 15 | chunkFilename: "js/[name].chunk.js", 16 | publicPath: "http://localhost:9091/", 17 | }, 18 | devServer: { 19 | hot: true, 20 | host: "0.0.0.0", 21 | port: 9091, 22 | headers: { 23 | "Access-Control-Allow-Origin": "*", 24 | }, 25 | devMiddleware: { 26 | writeToDisk: true, 27 | }, 28 | }, 29 | plugins: [ 30 | new Webpack.DefinePlugin({ 31 | "process.env.NODE_ENV": JSON.stringify("development"), 32 | }), 33 | new StylelintPlugin({ 34 | files: Path.resolve(__dirname, "../src/**/*.s?(a|c)ss"), 35 | }), 36 | new ESLintPlugin({ 37 | extensions: "js", 38 | emitWarning: true, 39 | files: Path.resolve(__dirname, "../src"), 40 | }), 41 | new MiniCssExtractPlugin({ 42 | filename: "css/[name].css", 43 | chunkFilename: "css/[id].css", 44 | }), 45 | ], 46 | module: { 47 | rules: [ 48 | { 49 | test: /\.html$/i, 50 | loader: "html-loader", 51 | }, 52 | { 53 | test: /\.js$/, 54 | include: Path.resolve(__dirname, "../src"), 55 | loader: "babel-loader", 56 | }, 57 | { 58 | test: /\.s?css$/i, 59 | use: [ 60 | MiniCssExtractPlugin.loader, 61 | { 62 | loader: "css-loader", 63 | options: { 64 | sourceMap: true, 65 | }, 66 | }, 67 | "postcss-loader", 68 | "sass-loader", 69 | ], 70 | }, 71 | ], 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /test/django-init/frontend/webpack/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const Webpack = require("webpack"); 2 | const { merge } = require("webpack-merge"); 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 4 | const common = require("./webpack.common.js"); 5 | 6 | module.exports = merge(common, { 7 | mode: "production", 8 | devtool: "source-map", 9 | bail: true, 10 | output: { 11 | filename: "js/[name].[chunkhash:8].js", 12 | chunkFilename: "js/[name].[chunkhash:8].chunk.js", 13 | }, 14 | plugins: [ 15 | new Webpack.DefinePlugin({ 16 | "process.env.NODE_ENV": JSON.stringify("production"), 17 | }), 18 | new MiniCssExtractPlugin({ 19 | filename: "css/[name].[contenthash].css", 20 | chunkFilename: "css/[id].[contenthash].css", 21 | }), 22 | ], 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.js$/, 27 | exclude: /node_modules/, 28 | use: "babel-loader", 29 | }, 30 | { 31 | test: /\.s?css/i, 32 | use: [ 33 | MiniCssExtractPlugin.loader, 34 | "css-loader", 35 | "postcss-loader", 36 | "sass-loader", 37 | ], 38 | }, 39 | ], 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /test/django-init/frontend/webpack/webpack.config.watch.js: -------------------------------------------------------------------------------- 1 | const Path = require("path"); 2 | const Webpack = require("webpack"); 3 | const { merge } = require("webpack-merge"); 4 | const StylelintPlugin = require("stylelint-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | const ESLintPlugin = require("eslint-webpack-plugin"); 7 | 8 | const common = require("./webpack.common.js"); 9 | 10 | module.exports = merge(common, { 11 | target: "web", 12 | mode: "development", 13 | devtool: "inline-source-map", 14 | output: { 15 | chunkFilename: "js/[name].chunk.js", 16 | }, 17 | plugins: [ 18 | new Webpack.DefinePlugin({ 19 | "process.env.NODE_ENV": JSON.stringify("development"), 20 | }), 21 | new StylelintPlugin({ 22 | files: Path.resolve(__dirname, "../src/**/*.s?(a|c)ss"), 23 | }), 24 | new ESLintPlugin({ 25 | extensions: "js", 26 | emitWarning: true, 27 | files: Path.resolve(__dirname, "../src"), 28 | }), 29 | new MiniCssExtractPlugin({ 30 | filename: "css/[name].css", 31 | chunkFilename: "css/[id].css", 32 | }), 33 | ], 34 | module: { 35 | rules: [ 36 | { 37 | test: /\.html$/i, 38 | loader: "html-loader", 39 | }, 40 | { 41 | test: /\.js$/, 42 | include: Path.resolve(__dirname, "../src"), 43 | loader: "babel-loader", 44 | }, 45 | { 46 | test: /\.s?css$/i, 47 | use: [ 48 | MiniCssExtractPlugin.loader, 49 | { 50 | loader: "css-loader", 51 | options: { 52 | sourceMap: true, 53 | }, 54 | }, 55 | "postcss-loader", 56 | "sass-loader", 57 | ], 58 | }, 59 | ], 60 | }, 61 | }); 62 | -------------------------------------------------------------------------------- /test/django-init/home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/home/__init__.py -------------------------------------------------------------------------------- /test/django-init/home/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin # noqa 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /test/django-init/home/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class HomeConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "home" 7 | -------------------------------------------------------------------------------- /test/django-init/home/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/home/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init/home/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models # noqa 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /test/django-init/home/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |
5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /test/django-init/home/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init/home/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import HomeView 3 | 4 | urlpatterns = [path("", HomeView.as_view(), name="home")] 5 | -------------------------------------------------------------------------------- /test/django-init/home/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import TemplateView 2 | 3 | 4 | class HomeView(TemplateView): 5 | template_name = "home.html" 6 | -------------------------------------------------------------------------------- /test/django-init/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | 4 | import os 5 | import sys 6 | 7 | 8 | def main(): 9 | """Run administrative tasks.""" 10 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings.dev") 11 | try: 12 | from django.core.management import execute_from_command_line 13 | except ImportError as exc: 14 | raise ImportError( 15 | "Couldn't import Django. Are you sure it's installed and " 16 | "available on your PYTHONPATH environment variable? Did you " 17 | "forget to activate a virtual environment?" 18 | ) from exc 19 | execute_from_command_line(sys.argv) 20 | 21 | 22 | if __name__ == "__main__": 23 | main() 24 | -------------------------------------------------------------------------------- /test/django-init/postcss.config.js: -------------------------------------------------------------------------------- 1 | const postcssPresetEnv = require("postcss-preset-env"); 2 | 3 | module.exports = { 4 | plugins: [postcssPresetEnv()], 5 | }; 6 | -------------------------------------------------------------------------------- /test/django-init/requirements-test.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-runner 3 | coverage 4 | pytest-mock 5 | pytest-cov 6 | hypothesis 7 | selenium 8 | pytest-django 9 | factory-boy 10 | flake8 11 | tox 12 | -------------------------------------------------------------------------------- /test/django-init/search/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/search/__init__.py -------------------------------------------------------------------------------- /test/django-init/search/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /test/django-init/search/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SearchConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "search" 7 | -------------------------------------------------------------------------------- /test/django-init/search/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | 4 | class SearchForm(forms.Form): 5 | query = forms.CharField(max_length=100, required=True, label="Search") 6 | 7 | -------------------------------------------------------------------------------- /test/django-init/search/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/search/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init/search/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /test/django-init/search/templates/search.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 |

Search Results

4 |
5 | {{ form.as_p }} 6 | 7 |
8 |
    9 | {% for result in results %} 10 |
  • {{ result }}
  • 11 | {% endfor %} 12 |
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /test/django-init/search/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init/search/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import SearchView 3 | 4 | urlpatterns = [ 5 | path("search/", SearchView.as_view(), name="search"), 6 | ] 7 | -------------------------------------------------------------------------------- /test/django-init/search/utils.py: -------------------------------------------------------------------------------- 1 | from django.apps import apps 2 | from django.conf import settings 3 | 4 | def get_search_models(): 5 | models = [] 6 | for model_path in settings.SEARCH_MODELS: 7 | app_label, model_name = model_path.split(".") 8 | model = apps.get_model(app_label, model_name) 9 | models.append(model) 10 | return models 11 | -------------------------------------------------------------------------------- /test/django-init/search/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import ListView 2 | from django.db import models 3 | from django.db.models import Q 4 | from .forms import SearchForm 5 | from .utils import get_search_models 6 | 7 | 8 | class SearchView(ListView): 9 | template_name = "your_app/search_results.html" 10 | context_object_name = "results" 11 | paginate_by = 10 12 | 13 | def get_queryset(self): 14 | form = SearchForm(self.request.GET) 15 | query = None 16 | results = [] 17 | 18 | if form.is_valid(): 19 | query = form.cleaned_data["query"] 20 | search_models = get_search_models() 21 | 22 | for model in search_models: 23 | fields = [f.name for f in model._meta.fields if isinstance(f, (models.CharField, models.TextField))] 24 | queries = [Q(**{f"{field}__icontains": query}) for field in fields] 25 | model_results = model.objects.filter(queries.pop()) 26 | 27 | for item in queries: 28 | model_results = model_results.filter(item) 29 | 30 | results.extend(model_results) 31 | 32 | return results 33 | 34 | def get_context_data(self, **kwargs): 35 | context = super().get_context_data(**kwargs) 36 | context["form"] = SearchForm(self.request.GET) 37 | context["query"] = self.request.GET.get("query", "") 38 | return context 39 | -------------------------------------------------------------------------------- /test/django-init/siteuser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/siteuser/__init__.py -------------------------------------------------------------------------------- /test/django-init/siteuser/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.admin import UserAdmin 2 | from django.contrib import admin 3 | 4 | from .models import User 5 | 6 | admin.site.register(User, UserAdmin) 7 | -------------------------------------------------------------------------------- /test/django-init/siteuser/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SiteuserConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "siteuser" 7 | -------------------------------------------------------------------------------- /test/django-init/siteuser/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib.auth.forms import UserChangeForm 3 | from crispy_forms.helper import FormHelper 4 | from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit 5 | from .models import User 6 | 7 | 8 | class SiteUserForm(UserChangeForm): 9 | bio = forms.CharField(widget=forms.Textarea(attrs={"id": "editor"}), required=False) 10 | 11 | class Meta(UserChangeForm.Meta): 12 | model = User 13 | fields = ("username", "user_theme_preference", "bio", "rate") 14 | 15 | def __init__(self, *args, **kwargs): 16 | super().__init__(*args, **kwargs) 17 | self.helper = FormHelper() 18 | self.helper.form_method = "post" 19 | self.helper.layout = Layout( 20 | Fieldset( 21 | "Edit Your Profile", 22 | "username", 23 | "user_theme_preference", 24 | "bio", 25 | "rate", 26 | ), 27 | ButtonHolder(Submit("submit", "Save", css_class="btn btn-primary")), 28 | ) 29 | -------------------------------------------------------------------------------- /test/django-init/siteuser/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aclark4life/project-makefile/848a5497171dd3c0885398763d6df175dd3b31b3/test/django-init/siteuser/migrations/__init__.py -------------------------------------------------------------------------------- /test/django-init/siteuser/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractUser, Group, Permission 3 | from django.conf import settings 4 | 5 | 6 | class User(AbstractUser): 7 | groups = models.ManyToManyField(Group, related_name="siteuser_set", blank=True) 8 | user_permissions = models.ManyToManyField( 9 | Permission, related_name="siteuser_set", blank=True 10 | ) 11 | 12 | user_theme_preference = models.CharField( 13 | max_length=10, choices=settings.THEMES, default="light" 14 | ) 15 | 16 | bio = models.TextField(blank=True, null=True) 17 | rate = models.FloatField(blank=True, null=True) 18 | -------------------------------------------------------------------------------- /test/django-init/siteuser/templates/user.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 |

User Profile

4 |
5 | Edit 7 |
8 |

Username: {{ user.username }}

9 |

Theme: {{ user.user_theme_preference }}

10 |

Bio: {{ user.bio|default:""|safe }}

11 |

Rate: {{ user.rate|default:"" }}

12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /test/django-init/siteuser/templates/user_edit.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load crispy_forms_tags %} 3 | {% block content %} 4 |

Edit User

5 | {% crispy form %} 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /test/django-init/siteuser/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /test/django-init/siteuser/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import UserProfileView, UpdateThemePreferenceView, UserEditView 3 | 4 | urlpatterns = [ 5 | path("profile/", UserProfileView.as_view(), name="user-profile"), 6 | path( 7 | "update_theme_preference/", 8 | UpdateThemePreferenceView.as_view(), 9 | name="update_theme_preference", 10 | ), 11 | path("/edit/", UserEditView.as_view(), name="user-edit"), 12 | ] 13 | -------------------------------------------------------------------------------- /test/django-init/siteuser/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.contrib.auth.mixins import LoginRequiredMixin 4 | from django.http import JsonResponse 5 | from django.utils.decorators import method_decorator 6 | from django.views import View 7 | from django.views.decorators.csrf import csrf_exempt 8 | from django.views.generic import DetailView 9 | from django.views.generic.edit import UpdateView 10 | from django.urls import reverse_lazy 11 | 12 | from .models import User 13 | from .forms import SiteUserForm 14 | 15 | 16 | class UserProfileView(LoginRequiredMixin, DetailView): 17 | model = User 18 | template_name = "user.html" 19 | 20 | def get_object(self, queryset=None): 21 | return self.request.user 22 | 23 | 24 | @method_decorator(csrf_exempt, name="dispatch") 25 | class UpdateThemePreferenceView(View): 26 | def post(self, request, *args, **kwargs): 27 | try: 28 | data = json.loads(request.body.decode("utf-8")) 29 | new_theme = data.get("theme") 30 | user = request.user 31 | user.user_theme_preference = new_theme 32 | user.save() 33 | response_data = {"theme": new_theme} 34 | return JsonResponse(response_data) 35 | except json.JSONDecodeError as e: 36 | return JsonResponse({"error": e}, status=400) 37 | 38 | def http_method_not_allowed(self, request, *args, **kwargs): 39 | return JsonResponse({"error": "Invalid request method"}, status=405) 40 | 41 | 42 | class UserEditView(LoginRequiredMixin, UpdateView): 43 | model = User 44 | template_name = "user_edit.html" # Create this template in your templates folder 45 | form_class = SiteUserForm 46 | 47 | def get_success_url(self): 48 | # return reverse_lazy("user-profile", kwargs={"pk": self.object.pk}) 49 | return reverse_lazy("user-profile") 50 | --------------------------------------------------------------------------------