Django + Next + Postgres + Docker + Nginx
first commit
30 files changed, +1611 -0
+2-0.gitignore
| @@ -0,0 +1,2 @@ | ||
| 1 | +venv | |
| 2 | +postgres |
+46-0Dockerfile
| @@ -0,0 +1,46 @@ | ||
| 1 | +FROM alpine:3.17 | |
| 2 | + | |
| 3 | +# install node, python, postgres, and nginx | |
| 4 | +RUN apk add --no-cache postgresql | |
| 5 | +RUN apk add --no-cache nodejs | |
| 6 | +RUN apk add --no-cache python3 | |
| 7 | +RUN apk add --no-cache nginx | |
| 8 | +RUN apk add --no-cache py3-pip | |
| 9 | +RUN apk add --no-cache npm | |
| 10 | + | |
| 11 | +# configure postgres | |
| 12 | +RUN mkdir -p /run/postgresql | |
| 13 | +RUN chown -R postgres:postgres /run/postgresql | |
| 14 | + | |
| 15 | +# configure nginx | |
| 16 | +RUN mkdir -p /run/nginx | |
| 17 | +RUN chown -R nginx:nginx /run/nginx | |
| 18 | + | |
| 19 | +# install node dependencies | |
| 20 | +COPY ./frontend/package.json /frontend/package.json | |
| 21 | +WORKDIR /frontend | |
| 22 | +RUN npm install | |
| 23 | + | |
| 24 | +# install python dependencies | |
| 25 | +COPY ./backend/requirements.txt /backend/requirements.txt | |
| 26 | +WORKDIR /backend | |
| 27 | +RUN pip install -r requirements.txt | |
| 28 | + | |
| 29 | +# configure node | |
| 30 | +COPY ./frontend /frontend | |
| 31 | +WORKDIR /frontend | |
| 32 | +RUN npm run build | |
| 33 | + | |
| 34 | +# configure django | |
| 35 | +ENV IS_DOCKER=1 | |
| 36 | +ENV PYTHONDONTWRITEBYTECODE=1 | |
| 37 | +ENV PYTHONUNBUFFERED=1 | |
| 38 | +COPY ./backend /backend | |
| 39 | + | |
| 40 | +# copy files | |
| 41 | +COPY ./backend /backend | |
| 42 | + | |
| 43 | +WORKDIR / | |
| 44 | +COPY ./docker-entrypoint.sh / | |
| 45 | + | |
| 46 | +CMD ["sh", "docker-entrypoint.sh"] |
+160-0backend/.gitignore
| @@ -0,0 +1,160 @@ | ||
| 1 | +# Byte-compiled / optimized / DLL files | |
| 2 | +__pycache__/ | |
| 3 | +*.py[cod] | |
| 4 | +*$py.class | |
| 5 | + | |
| 6 | +# C extensions | |
| 7 | +*.so | |
| 8 | + | |
| 9 | +# Distribution / packaging | |
| 10 | +.Python | |
| 11 | +build/ | |
| 12 | +develop-eggs/ | |
| 13 | +dist/ | |
| 14 | +downloads/ | |
| 15 | +eggs/ | |
| 16 | +.eggs/ | |
| 17 | +lib/ | |
| 18 | +lib64/ | |
| 19 | +parts/ | |
| 20 | +sdist/ | |
| 21 | +var/ | |
| 22 | +wheels/ | |
| 23 | +share/python-wheels/ | |
| 24 | +*.egg-info/ | |
| 25 | +.installed.cfg | |
| 26 | +*.egg | |
| 27 | +MANIFEST | |
| 28 | + | |
| 29 | +# PyInstaller | |
| 30 | +# Usually these files are written by a python script from a template | |
| 31 | +# before PyInstaller builds the exe, so as to inject date/other infos into it. | |
| 32 | +*.manifest | |
| 33 | +*.spec | |
| 34 | + | |
| 35 | +# Installer logs | |
| 36 | +pip-log.txt | |
| 37 | +pip-delete-this-directory.txt | |
| 38 | + | |
| 39 | +# Unit test / coverage reports | |
| 40 | +htmlcov/ | |
| 41 | +.tox/ | |
| 42 | +.nox/ | |
| 43 | +.coverage | |
| 44 | +.coverage.* | |
| 45 | +.cache | |
| 46 | +nosetests.xml | |
| 47 | +coverage.xml | |
| 48 | +*.cover | |
| 49 | +*.py,cover | |
| 50 | +.hypothesis/ | |
| 51 | +.pytest_cache/ | |
| 52 | +cover/ | |
| 53 | + | |
| 54 | +# Translations | |
| 55 | +*.mo | |
| 56 | +*.pot | |
| 57 | + | |
| 58 | +# Django stuff: | |
| 59 | +*.log | |
| 60 | +local_settings.py | |
| 61 | +db.sqlite3 | |
| 62 | +db.sqlite3-journal | |
| 63 | + | |
| 64 | +# Flask stuff: | |
| 65 | +instance/ | |
| 66 | +.webassets-cache | |
| 67 | + | |
| 68 | +# Scrapy stuff: | |
| 69 | +.scrapy | |
| 70 | + | |
| 71 | +# Sphinx documentation | |
| 72 | +docs/_build/ | |
| 73 | + | |
| 74 | +# PyBuilder | |
| 75 | +.pybuilder/ | |
| 76 | +target/ | |
| 77 | + | |
| 78 | +# Jupyter Notebook | |
| 79 | +.ipynb_checkpoints | |
| 80 | + | |
| 81 | +# IPython | |
| 82 | +profile_default/ | |
| 83 | +ipython_config.py | |
| 84 | + | |
| 85 | +# pyenv | |
| 86 | +# For a library or package, you might want to ignore these files since the code is | |
| 87 | +# intended to run in multiple environments; otherwise, check them in: | |
| 88 | +# .python-version | |
| 89 | + | |
| 90 | +# pipenv | |
| 91 | +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | |
| 92 | +# However, in case of collaboration, if having platform-specific dependencies or dependencies | |
| 93 | +# having no cross-platform support, pipenv may install dependencies that don't work, or not | |
| 94 | +# install all needed dependencies. | |
| 95 | +#Pipfile.lock | |
| 96 | + | |
| 97 | +# poetry | |
| 98 | +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. | |
| 99 | +# This is especially recommended for binary packages to ensure reproducibility, and is more | |
| 100 | +# commonly ignored for libraries. | |
| 101 | +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control | |
| 102 | +#poetry.lock | |
| 103 | + | |
| 104 | +# pdm | |
| 105 | +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. | |
| 106 | +#pdm.lock | |
| 107 | +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it | |
| 108 | +# in version control. | |
| 109 | +# https://pdm.fming.dev/#use-with-ide | |
| 110 | +.pdm.toml | |
| 111 | + | |
| 112 | +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm | |
| 113 | +__pypackages__/ | |
| 114 | + | |
| 115 | +# Celery stuff | |
| 116 | +celerybeat-schedule | |
| 117 | +celerybeat.pid | |
| 118 | + | |
| 119 | +# SageMath parsed files | |
| 120 | +*.sage.py | |
| 121 | + | |
| 122 | +# Environments | |
| 123 | +.env | |
| 124 | +.venv | |
| 125 | +env/ | |
| 126 | +venv/ | |
| 127 | +ENV/ | |
| 128 | +env.bak/ | |
| 129 | +venv.bak/ | |
| 130 | + | |
| 131 | +# Spyder project settings | |
| 132 | +.spyderproject | |
| 133 | +.spyproject | |
| 134 | + | |
| 135 | +# Rope project settings | |
| 136 | +.ropeproject | |
| 137 | + | |
| 138 | +# mkdocs documentation | |
| 139 | +/site | |
| 140 | + | |
| 141 | +# mypy | |
| 142 | +.mypy_cache/ | |
| 143 | +.dmypy.json | |
| 144 | +dmypy.json | |
| 145 | + | |
| 146 | +# Pyre type checker | |
| 147 | +.pyre/ | |
| 148 | + | |
| 149 | +# pytype static type analyzer | |
| 150 | +.pytype/ | |
| 151 | + | |
| 152 | +# Cython debug symbols | |
| 153 | +cython_debug/ | |
| 154 | + | |
| 155 | +# PyCharm | |
| 156 | +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can | |
| 157 | +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore | |
| 158 | +# and can be added to the global gitignore or merged into this file. For a more nuclear | |
| 159 | +# option (not recommended) you can uncomment the following to ignore the entire idea folder. | |
| 160 | +#.idea/ |
+0-0backend/backend/__init__.py
No content changes (mode or rename only).
+16-0backend/backend/asgi.py
| @@ -0,0 +1,16 @@ | ||
| 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/4.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() |
+0-0backend/backend/management/commands/__init__.py
No content changes (mode or rename only).
+16-0backend/backend/management/commands/createsecret.py
| @@ -0,0 +1,16 @@ | ||
| 1 | +from django.core.management.base import BaseCommand, CommandError | |
| 2 | + | |
| 3 | +class Command(BaseCommand): | |
| 4 | + help = 'Create a new secret key for the .env file.' | |
| 5 | + | |
| 6 | + def handle(self, *args, **options): | |
| 7 | + # Generate a django secret key | |
| 8 | + # https://stackoverflow.com/questions/4664720/how-to-generate-a-random-secret-key-in-django | |
| 9 | + from django.core.management.utils import get_random_secret_key | |
| 10 | + new_secret_key = get_random_secret_key() | |
| 11 | + # Update the .env file | |
| 12 | + from backend.settings import BASE_DIR | |
| 13 | + env_file = BASE_DIR / '.env' | |
| 14 | + with open(env_file, 'w') as f: | |
| 15 | + f.write(f'SECRET_KEY="{new_secret_key}"') | |
| 16 | + self.stdout.write(self.style.SUCCESS('Succesfully updated new key into .env file.')) |
+136-0backend/backend/settings.py
| @@ -0,0 +1,136 @@ | ||
| 1 | +""" | |
| 2 | +Django settings for backend project. | |
| 3 | + | |
| 4 | +Generated by 'django-admin startproject' using Django 4.1.5. | |
| 5 | + | |
| 6 | +For more information on this file, see | |
| 7 | +https://docs.djangoproject.com/en/4.1/topics/settings/ | |
| 8 | + | |
| 9 | +For the full list of settings and their values, see | |
| 10 | +https://docs.djangoproject.com/en/4.1/ref/settings/ | |
| 11 | +""" | |
| 12 | + | |
| 13 | +from pathlib import Path | |
| 14 | +import dotenv | |
| 15 | +import os | |
| 16 | +dotenv.load_dotenv() | |
| 17 | + | |
| 18 | +# Build paths inside the project like this: BASE_DIR / 'subdir'. | |
| 19 | +BASE_DIR = Path(__file__).resolve().parent.parent | |
| 20 | + | |
| 21 | + | |
| 22 | +# Quick-start development settings - unsuitable for production | |
| 23 | +# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ | |
| 24 | + | |
| 25 | +# SECURITY WARNING: keep the secret key used in production secret! | |
| 26 | +SECRET_KEY = os.environ.get('SECRET_KEY') | |
| 27 | + | |
| 28 | +# SECURITY WARNING: don't run with debug turned on in production! | |
| 29 | +DEBUG = True | |
| 30 | + | |
| 31 | +ALLOWED_HOSTS = ['*'] | |
| 32 | + | |
| 33 | + | |
| 34 | +# Application definition | |
| 35 | + | |
| 36 | +INSTALLED_APPS = [ | |
| 37 | + 'django.contrib.admin', | |
| 38 | + 'django.contrib.auth', | |
| 39 | + 'django.contrib.contenttypes', | |
| 40 | + 'django.contrib.sessions', | |
| 41 | + 'django.contrib.messages', | |
| 42 | + 'django.contrib.staticfiles', | |
| 43 | + 'backend', | |
| 44 | +] | |
| 45 | + | |
| 46 | +MIDDLEWARE = [ | |
| 47 | + 'django.middleware.security.SecurityMiddleware', | |
| 48 | + 'django.contrib.sessions.middleware.SessionMiddleware', | |
| 49 | + 'django.middleware.common.CommonMiddleware', | |
| 50 | + 'django.middleware.csrf.CsrfViewMiddleware', | |
| 51 | + 'django.contrib.auth.middleware.AuthenticationMiddleware', | |
| 52 | + 'django.contrib.messages.middleware.MessageMiddleware', | |
| 53 | + 'django.middleware.clickjacking.XFrameOptionsMiddleware', | |
| 54 | +] | |
| 55 | + | |
| 56 | +ROOT_URLCONF = 'backend.urls' | |
| 57 | + | |
| 58 | +TEMPLATES = [ | |
| 59 | + { | |
| 60 | + 'BACKEND': 'django.template.backends.django.DjangoTemplates', | |
| 61 | + 'DIRS': [], | |
| 62 | + 'APP_DIRS': True, | |
| 63 | + 'OPTIONS': { | |
| 64 | + 'context_processors': [ | |
| 65 | + 'django.template.context_processors.debug', | |
| 66 | + 'django.template.context_processors.request', | |
| 67 | + 'django.contrib.auth.context_processors.auth', | |
| 68 | + 'django.contrib.messages.context_processors.messages', | |
| 69 | + ], | |
| 70 | + }, | |
| 71 | + }, | |
| 72 | +] | |
| 73 | + | |
| 74 | +WSGI_APPLICATION = 'backend.wsgi.application' | |
| 75 | + | |
| 76 | + | |
| 77 | +# Database | |
| 78 | +# https://docs.djangoproject.com/en/4.1/ref/settings/#databases | |
| 79 | +import os | |
| 80 | +if os.environ.get('IS_DOCKER'): | |
| 81 | + DATABASES = { | |
| 82 | + 'default': { | |
| 83 | + 'ENGINE': 'django.db.backends.postgresql', | |
| 84 | + 'NAME': 'postgres', | |
| 85 | + 'USER': 'postgres' | |
| 86 | + } | |
| 87 | + } | |
| 88 | +else: | |
| 89 | + DATABASES = { | |
| 90 | + 'default': { | |
| 91 | + 'ENGINE': 'django.db.backends.sqlite3', | |
| 92 | + 'NAME': BASE_DIR / 'db.sqlite3', | |
| 93 | + } | |
| 94 | + } | |
| 95 | + | |
| 96 | + | |
| 97 | +# Password validation | |
| 98 | +# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators | |
| 99 | + | |
| 100 | +AUTH_PASSWORD_VALIDATORS = [ | |
| 101 | + { | |
| 102 | + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', | |
| 103 | + }, | |
| 104 | + { | |
| 105 | + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', | |
| 106 | + }, | |
| 107 | + { | |
| 108 | + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', | |
| 109 | + }, | |
| 110 | + { | |
| 111 | + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', | |
| 112 | + }, | |
| 113 | +] | |
| 114 | + | |
| 115 | + | |
| 116 | +# Internationalization | |
| 117 | +# https://docs.djangoproject.com/en/4.1/topics/i18n/ | |
| 118 | + | |
| 119 | +LANGUAGE_CODE = 'en-us' | |
| 120 | + | |
| 121 | +TIME_ZONE = 'UTC' | |
| 122 | + | |
| 123 | +USE_I18N = True | |
| 124 | + | |
| 125 | +USE_TZ = True | |
| 126 | + | |
| 127 | + | |
| 128 | +# Static files (CSS, JavaScript, Images) | |
| 129 | +# https://docs.djangoproject.com/en/4.1/howto/static-files/ | |
| 130 | + | |
| 131 | +STATIC_URL = 'static/' | |
| 132 | + | |
| 133 | +# Default primary key field type | |
| 134 | +# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field | |
| 135 | + | |
| 136 | +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' |
+21-0backend/backend/urls.py
| @@ -0,0 +1,21 @@ | ||
| 1 | +"""backend URL Configuration | |
| 2 | + | |
| 3 | +The `urlpatterns` list routes URLs to views. For more information please see: | |
| 4 | + https://docs.djangoproject.com/en/4.1/topics/http/urls/ | |
| 5 | +Examples: | |
| 6 | +Function views | |
| 7 | + 1. Add an import: from my_app import views | |
| 8 | + 2. Add a URL to urlpatterns: path('', views.home, name='home') | |
| 9 | +Class-based views | |
| 10 | + 1. Add an import: from other_app.views import Home | |
| 11 | + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') | |
| 12 | +Including another URLconf | |
| 13 | + 1. Import the include() function: from django.urls import include, path | |
| 14 | + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) | |
| 15 | +""" | |
| 16 | +from django.contrib import admin | |
| 17 | +from django.urls import path | |
| 18 | + | |
| 19 | +urlpatterns = [ | |
| 20 | + path('admin/', admin.site.urls), | |
| 21 | +] |
+16-0backend/backend/wsgi.py
| @@ -0,0 +1,16 @@ | ||
| 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/4.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() |
+22-0backend/manage.py
| @@ -0,0 +1,22 @@ | ||
| 1 | +#!/usr/bin/env python | |
| 2 | +"""Django's command-line utility for administrative tasks.""" | |
| 3 | +import os | |
| 4 | +import sys | |
| 5 | + | |
| 6 | + | |
| 7 | +def main(): | |
| 8 | + """Run administrative tasks.""" | |
| 9 | + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') | |
| 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() |
+11-0backend/requirements.txt
| @@ -0,0 +1,11 @@ | ||
| 1 | +asgiref==3.6.0 | |
| 2 | +click==8.1.3 | |
| 3 | +colorama==0.4.6 | |
| 4 | +Django==4.1.5 | |
| 5 | +djangorestframework==3.14.0 | |
| 6 | +h11==0.14.0 | |
| 7 | +psycopg2-binary==2.9.5 | |
| 8 | +pytz==2022.7 | |
| 9 | +sqlparse==0.4.3 | |
| 10 | +tzdata==2022.7 | |
| 11 | +uvicorn==0.20.0 |
+10-0docker-compose.yml
| @@ -0,0 +1,10 @@ | ||
| 1 | +services: | |
| 2 | + nppn: | |
| 3 | + build: . | |
| 4 | + container_name: nppn | |
| 5 | + volumes: | |
| 6 | + - ./postgres:/var/lib/postgresql/data | |
| 7 | + ports: | |
| 8 | + - "80:80" | |
| 9 | + - "3000:3000" | |
| 10 | + - "8000:8000" |
+16-0docker-entrypoint.sh
| @@ -0,0 +1,16 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | + | |
| 3 | +# postgres stuff | |
| 4 | +chown postgres:postgres /var/lib/postgresql/data | |
| 5 | +if [ ! "$(ls -A /var/lib/postgresql/data)" ]; then | |
| 6 | + su postgres -c "initdb -D /var/lib/postgresql/data" | |
| 7 | +fi | |
| 8 | +sed -i 's/\/run\/postgresql/\/tmp/' /var/lib/postgresql/data/postgresql.conf | |
| 9 | +su postgres -c 'pg_ctl start -D /var/lib/postgresql/data' | |
| 10 | + | |
| 11 | +nginx | |
| 12 | +tail -f /var/lib/nginx/logs/access.log & | |
| 13 | + | |
| 14 | +cd frontend && npm start & | |
| 15 | +cd backend && python manage.py migrate | |
| 16 | +cd /backend && uvicorn backend.asgi:application --host 0.0.0.0 |
+36-0frontend/.gitignore
| @@ -0,0 +1,36 @@ | ||
| 1 | +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | |
| 2 | + | |
| 3 | +# dependencies | |
| 4 | +/node_modules | |
| 5 | +/.pnp | |
| 6 | +.pnp.js | |
| 7 | + | |
| 8 | +# testing | |
| 9 | +/coverage | |
| 10 | + | |
| 11 | +# next.js | |
| 12 | +/.next/ | |
| 13 | +/out/ | |
| 14 | + | |
| 15 | +# production | |
| 16 | +/build | |
| 17 | + | |
| 18 | +# misc | |
| 19 | +.DS_Store | |
| 20 | +*.pem | |
| 21 | + | |
| 22 | +# debug | |
| 23 | +npm-debug.log* | |
| 24 | +yarn-debug.log* | |
| 25 | +yarn-error.log* | |
| 26 | +.pnpm-debug.log* | |
| 27 | + | |
| 28 | +# local env files | |
| 29 | +.env*.local | |
| 30 | + | |
| 31 | +# vercel | |
| 32 | +.vercel | |
| 33 | + | |
| 34 | +# typescript | |
| 35 | +*.tsbuildinfo | |
| 36 | +next-env.d.ts |
+36-0frontend/README.md
| @@ -0,0 +1,36 @@ | ||
| 1 | +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). | |
| 2 | + | |
| 3 | +## Getting Started | |
| 4 | + | |
| 5 | +First, run the development server: | |
| 6 | + | |
| 7 | +```bash | |
| 8 | +npm run dev | |
| 9 | +# or | |
| 10 | +yarn dev | |
| 11 | +``` | |
| 12 | + | |
| 13 | +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. | |
| 14 | + | |
| 15 | +You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. | |
| 16 | + | |
| 17 | +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. | |
| 18 | + | |
| 19 | +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. | |
| 20 | + | |
| 21 | +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. | |
| 22 | + | |
| 23 | +## Learn More | |
| 24 | + | |
| 25 | +To learn more about Next.js, take a look at the following resources: | |
| 26 | + | |
| 27 | +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. | |
| 28 | +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. | |
| 29 | + | |
| 30 | +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! | |
| 31 | + | |
| 32 | +## Deploy on Vercel | |
| 33 | + | |
| 34 | +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. | |
| 35 | + | |
| 36 | +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. |
+6-0frontend/next.config.js
| @@ -0,0 +1,6 @@ | ||
| 1 | +/** @type {import('next').NextConfig} */ | |
| 2 | +const nextConfig = { | |
| 3 | + reactStrictMode: true, | |
| 4 | +} | |
| 5 | + | |
| 6 | +module.exports = nextConfig |
+477-0frontend/package-lock.json
| @@ -0,0 +1,477 @@ | ||
| 1 | +{ | |
| 2 | + "name": "frontend", | |
| 3 | + "version": "0.1.0", | |
| 4 | + "lockfileVersion": 3, | |
| 5 | + "requires": true, | |
| 6 | + "packages": { | |
| 7 | + "": { | |
| 8 | + "name": "frontend", | |
| 9 | + "version": "0.1.0", | |
| 10 | + "dependencies": { | |
| 11 | + "@next/font": "13.1.1", | |
| 12 | + "@types/node": "18.11.18", | |
| 13 | + "@types/react": "18.0.26", | |
| 14 | + "@types/react-dom": "18.0.10", | |
| 15 | + "next": "13.1.1", | |
| 16 | + "react": "18.2.0", | |
| 17 | + "react-dom": "18.2.0", | |
| 18 | + "typescript": "4.9.4" | |
| 19 | + } | |
| 20 | + }, | |
| 21 | + "node_modules/@next/env": { | |
| 22 | + "version": "13.1.1", | |
| 23 | + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.1.tgz", | |
| 24 | + "integrity": "sha512-vFMyXtPjSAiOXOywMojxfKIqE3VWN5RCAx+tT3AS3pcKjMLFTCJFUWsKv8hC+87Z1F4W3r68qTwDFZIFmd5Xkw==" | |
| 25 | + }, | |
| 26 | + "node_modules/@next/font": { | |
| 27 | + "version": "13.1.1", | |
| 28 | + "resolved": "https://registry.npmjs.org/@next/font/-/font-13.1.1.tgz", | |
| 29 | + "integrity": "sha512-amygRorS05hYK1/XQRZo5qBl7l2fpHnezeKU/cNveWU5QJg+sg8gMGkUXHtvesNKpiKIJshBRH1TzvO+2sKpvQ==" | |
| 30 | + }, | |
| 31 | + "node_modules/@next/swc-android-arm-eabi": { | |
| 32 | + "version": "13.1.1", | |
| 33 | + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.1.tgz", | |
| 34 | + "integrity": "sha512-qnFCx1kT3JTWhWve4VkeWuZiyjG0b5T6J2iWuin74lORCupdrNukxkq9Pm+Z7PsatxuwVJMhjUoYz7H4cWzx2A==", | |
| 35 | + "cpu": [ | |
| 36 | + "arm" | |
| 37 | + ], | |
| 38 | + "optional": true, | |
| 39 | + "os": [ | |
| 40 | + "android" | |
| 41 | + ], | |
| 42 | + "engines": { | |
| 43 | + "node": ">= 10" | |
| 44 | + } | |
| 45 | + }, | |
| 46 | + "node_modules/@next/swc-android-arm64": { | |
| 47 | + "version": "13.1.1", | |
| 48 | + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.1.tgz", | |
| 49 | + "integrity": "sha512-eCiZhTzjySubNqUnNkQCjU3Fh+ep3C6b5DCM5FKzsTH/3Gr/4Y7EiaPZKILbvnXmhWtKPIdcY6Zjx51t4VeTfA==", | |
| 50 | + "cpu": [ | |
| 51 | + "arm64" | |
| 52 | + ], | |
| 53 | + "optional": true, | |
| 54 | + "os": [ | |
| 55 | + "android" | |
| 56 | + ], | |
| 57 | + "engines": { | |
| 58 | + "node": ">= 10" | |
| 59 | + } | |
| 60 | + }, | |
| 61 | + "node_modules/@next/swc-darwin-arm64": { | |
| 62 | + "version": "13.1.1", | |
| 63 | + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.1.tgz", | |
| 64 | + "integrity": "sha512-9zRJSSIwER5tu9ADDkPw5rIZ+Np44HTXpYMr0rkM656IvssowPxmhK0rTreC1gpUCYwFsRbxarUJnJsTWiutPg==", | |
| 65 | + "cpu": [ | |
| 66 | + "arm64" | |
| 67 | + ], | |
| 68 | + "optional": true, | |
| 69 | + "os": [ | |
| 70 | + "darwin" | |
| 71 | + ], | |
| 72 | + "engines": { | |
| 73 | + "node": ">= 10" | |
| 74 | + } | |
| 75 | + }, | |
| 76 | + "node_modules/@next/swc-darwin-x64": { | |
| 77 | + "version": "13.1.1", | |
| 78 | + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.1.tgz", | |
| 79 | + "integrity": "sha512-qWr9qEn5nrnlhB0rtjSdR00RRZEtxg4EGvicIipqZWEyayPxhUu6NwKiG8wZiYZCLfJ5KWr66PGSNeDMGlNaiA==", | |
| 80 | + "cpu": [ | |
| 81 | + "x64" | |
| 82 | + ], | |
| 83 | + "optional": true, | |
| 84 | + "os": [ | |
| 85 | + "darwin" | |
| 86 | + ], | |
| 87 | + "engines": { | |
| 88 | + "node": ">= 10" | |
| 89 | + } | |
| 90 | + }, | |
| 91 | + "node_modules/@next/swc-freebsd-x64": { | |
| 92 | + "version": "13.1.1", | |
| 93 | + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.1.tgz", | |
| 94 | + "integrity": "sha512-UwP4w/NcQ7V/VJEj3tGVszgb4pyUCt3lzJfUhjDMUmQbzG9LDvgiZgAGMYH6L21MoyAATJQPDGiAMWAPKsmumA==", | |
| 95 | + "cpu": [ | |
| 96 | + "x64" | |
| 97 | + ], | |
| 98 | + "optional": true, | |
| 99 | + "os": [ | |
| 100 | + "freebsd" | |
| 101 | + ], | |
| 102 | + "engines": { | |
| 103 | + "node": ">= 10" | |
| 104 | + } | |
| 105 | + }, | |
| 106 | + "node_modules/@next/swc-linux-arm-gnueabihf": { | |
| 107 | + "version": "13.1.1", | |
| 108 | + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.1.tgz", | |
| 109 | + "integrity": "sha512-CnsxmKHco9sosBs1XcvCXP845Db+Wx1G0qouV5+Gr+HT/ZlDYEWKoHVDgnJXLVEQzq4FmHddBNGbXvgqM1Gfkg==", | |
| 110 | + "cpu": [ | |
| 111 | + "arm" | |
| 112 | + ], | |
| 113 | + "optional": true, | |
| 114 | + "os": [ | |
| 115 | + "linux" | |
| 116 | + ], | |
| 117 | + "engines": { | |
| 118 | + "node": ">= 10" | |
| 119 | + } | |
| 120 | + }, | |
| 121 | + "node_modules/@next/swc-linux-arm64-gnu": { | |
| 122 | + "version": "13.1.1", | |
| 123 | + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.1.tgz", | |
| 124 | + "integrity": "sha512-JfDq1eri5Dif+VDpTkONRd083780nsMCOKoFG87wA0sa4xL8LGcXIBAkUGIC1uVy9SMsr2scA9CySLD/i+Oqiw==", | |
| 125 | + "cpu": [ | |
| 126 | + "arm64" | |
| 127 | + ], | |
| 128 | + "optional": true, | |
| 129 | + "os": [ | |
| 130 | + "linux" | |
| 131 | + ], | |
| 132 | + "engines": { | |
| 133 | + "node": ">= 10" | |
| 134 | + } | |
| 135 | + }, | |
| 136 | + "node_modules/@next/swc-linux-arm64-musl": { | |
| 137 | + "version": "13.1.1", | |
| 138 | + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.1.tgz", | |
| 139 | + "integrity": "sha512-GA67ZbDq2AW0CY07zzGt07M5b5Yaq5qUpFIoW3UFfjOPgb0Sqf3DAW7GtFMK1sF4ROHsRDMGQ9rnT0VM2dVfKA==", | |
| 140 | + "cpu": [ | |
| 141 | + "arm64" | |
| 142 | + ], | |
| 143 | + "optional": true, | |
| 144 | + "os": [ | |
| 145 | + "linux" | |
| 146 | + ], | |
| 147 | + "engines": { | |
| 148 | + "node": ">= 10" | |
| 149 | + } | |
| 150 | + }, | |
| 151 | + "node_modules/@next/swc-linux-x64-gnu": { | |
| 152 | + "version": "13.1.1", | |
| 153 | + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.1.tgz", | |
| 154 | + "integrity": "sha512-nnjuBrbzvqaOJaV+XgT8/+lmXrSCOt1YYZn/irbDb2fR2QprL6Q7WJNgwsZNxiLSfLdv+2RJGGegBx9sLBEzGA==", | |
| 155 | + "cpu": [ | |
| 156 | + "x64" | |
| 157 | + ], | |
| 158 | + "optional": true, | |
| 159 | + "os": [ | |
| 160 | + "linux" | |
| 161 | + ], | |
| 162 | + "engines": { | |
| 163 | + "node": ">= 10" | |
| 164 | + } | |
| 165 | + }, | |
| 166 | + "node_modules/@next/swc-linux-x64-musl": { | |
| 167 | + "version": "13.1.1", | |
| 168 | + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.1.tgz", | |
| 169 | + "integrity": "sha512-CM9xnAQNIZ8zf/igbIT/i3xWbQZYaF397H+JroF5VMOCUleElaMdQLL5riJml8wUfPoN3dtfn2s4peSr3azz/g==", | |
| 170 | + "cpu": [ | |
| 171 | + "x64" | |
| 172 | + ], | |
| 173 | + "optional": true, | |
| 174 | + "os": [ | |
| 175 | + "linux" | |
| 176 | + ], | |
| 177 | + "engines": { | |
| 178 | + "node": ">= 10" | |
| 179 | + } | |
| 180 | + }, | |
| 181 | + "node_modules/@next/swc-win32-arm64-msvc": { | |
| 182 | + "version": "13.1.1", | |
| 183 | + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.1.tgz", | |
| 184 | + "integrity": "sha512-pzUHOGrbgfGgPlOMx9xk3QdPJoRPU+om84hqVoe6u+E0RdwOG0Ho/2UxCgDqmvpUrMab1Deltlt6RqcXFpnigQ==", | |
| 185 | + "cpu": [ | |
| 186 | + "arm64" | |
| 187 | + ], | |
| 188 | + "optional": true, | |
| 189 | + "os": [ | |
| 190 | + "win32" | |
| 191 | + ], | |
| 192 | + "engines": { | |
| 193 | + "node": ">= 10" | |
| 194 | + } | |
| 195 | + }, | |
| 196 | + "node_modules/@next/swc-win32-ia32-msvc": { | |
| 197 | + "version": "13.1.1", | |
| 198 | + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.1.tgz", | |
| 199 | + "integrity": "sha512-WeX8kVS46aobM9a7Xr/kEPcrTyiwJqQv/tbw6nhJ4fH9xNZ+cEcyPoQkwPo570dCOLz3Zo9S2q0E6lJ/EAUOBg==", | |
| 200 | + "cpu": [ | |
| 201 | + "ia32" | |
| 202 | + ], | |
| 203 | + "optional": true, | |
| 204 | + "os": [ | |
| 205 | + "win32" | |
| 206 | + ], | |
| 207 | + "engines": { | |
| 208 | + "node": ">= 10" | |
| 209 | + } | |
| 210 | + }, | |
| 211 | + "node_modules/@next/swc-win32-x64-msvc": { | |
| 212 | + "version": "13.1.1", | |
| 213 | + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.1.tgz", | |
| 214 | + "integrity": "sha512-mVF0/3/5QAc5EGVnb8ll31nNvf3BWpPY4pBb84tk+BfQglWLqc5AC9q1Ht/YMWiEgs8ALNKEQ3GQnbY0bJF2Gg==", | |
| 215 | + "cpu": [ | |
| 216 | + "x64" | |
| 217 | + ], | |
| 218 | + "optional": true, | |
| 219 | + "os": [ | |
| 220 | + "win32" | |
| 221 | + ], | |
| 222 | + "engines": { | |
| 223 | + "node": ">= 10" | |
| 224 | + } | |
| 225 | + }, | |
| 226 | + "node_modules/@swc/helpers": { | |
| 227 | + "version": "0.4.14", | |
| 228 | + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", | |
| 229 | + "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", | |
| 230 | + "dependencies": { | |
| 231 | + "tslib": "^2.4.0" | |
| 232 | + } | |
| 233 | + }, | |
| 234 | + "node_modules/@types/node": { | |
| 235 | + "version": "18.11.18", | |
| 236 | + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", | |
| 237 | + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" | |
| 238 | + }, | |
| 239 | + "node_modules/@types/prop-types": { | |
| 240 | + "version": "15.7.5", | |
| 241 | + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", | |
| 242 | + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" | |
| 243 | + }, | |
| 244 | + "node_modules/@types/react": { | |
| 245 | + "version": "18.0.26", | |
| 246 | + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", | |
| 247 | + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", | |
| 248 | + "dependencies": { | |
| 249 | + "@types/prop-types": "*", | |
| 250 | + "@types/scheduler": "*", | |
| 251 | + "csstype": "^3.0.2" | |
| 252 | + } | |
| 253 | + }, | |
| 254 | + "node_modules/@types/react-dom": { | |
| 255 | + "version": "18.0.10", | |
| 256 | + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz", | |
| 257 | + "integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==", | |
| 258 | + "dependencies": { | |
| 259 | + "@types/react": "*" | |
| 260 | + } | |
| 261 | + }, | |
| 262 | + "node_modules/@types/scheduler": { | |
| 263 | + "version": "0.16.2", | |
| 264 | + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", | |
| 265 | + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" | |
| 266 | + }, | |
| 267 | + "node_modules/caniuse-lite": { | |
| 268 | + "version": "1.0.30001442", | |
| 269 | + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz", | |
| 270 | + "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==", | |
| 271 | + "funding": [ | |
| 272 | + { | |
| 273 | + "type": "opencollective", | |
| 274 | + "url": "https://opencollective.com/browserslist" | |
| 275 | + }, | |
| 276 | + { | |
| 277 | + "type": "tidelift", | |
| 278 | + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" | |
| 279 | + } | |
| 280 | + ] | |
| 281 | + }, | |
| 282 | + "node_modules/client-only": { | |
| 283 | + "version": "0.0.1", | |
| 284 | + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", | |
| 285 | + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" | |
| 286 | + }, | |
| 287 | + "node_modules/csstype": { | |
| 288 | + "version": "3.1.1", | |
| 289 | + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", | |
| 290 | + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" | |
| 291 | + }, | |
| 292 | + "node_modules/js-tokens": { | |
| 293 | + "version": "4.0.0", | |
| 294 | + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", | |
| 295 | + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" | |
| 296 | + }, | |
| 297 | + "node_modules/loose-envify": { | |
| 298 | + "version": "1.4.0", | |
| 299 | + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", | |
| 300 | + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", | |
| 301 | + "dependencies": { | |
| 302 | + "js-tokens": "^3.0.0 || ^4.0.0" | |
| 303 | + }, | |
| 304 | + "bin": { | |
| 305 | + "loose-envify": "cli.js" | |
| 306 | + } | |
| 307 | + }, | |
| 308 | + "node_modules/nanoid": { | |
| 309 | + "version": "3.3.4", | |
| 310 | + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", | |
| 311 | + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", | |
| 312 | + "bin": { | |
| 313 | + "nanoid": "bin/nanoid.cjs" | |
| 314 | + }, | |
| 315 | + "engines": { | |
| 316 | + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" | |
| 317 | + } | |
| 318 | + }, | |
| 319 | + "node_modules/next": { | |
| 320 | + "version": "13.1.1", | |
| 321 | + "resolved": "https://registry.npmjs.org/next/-/next-13.1.1.tgz", | |
| 322 | + "integrity": "sha512-R5eBAaIa3X7LJeYvv1bMdGnAVF4fVToEjim7MkflceFPuANY3YyvFxXee/A+acrSYwYPvOvf7f6v/BM/48ea5w==", | |
| 323 | + "dependencies": { | |
| 324 | + "@next/env": "13.1.1", | |
| 325 | + "@swc/helpers": "0.4.14", | |
| 326 | + "caniuse-lite": "^1.0.30001406", | |
| 327 | + "postcss": "8.4.14", | |
| 328 | + "styled-jsx": "5.1.1" | |
| 329 | + }, | |
| 330 | + "bin": { | |
| 331 | + "next": "dist/bin/next" | |
| 332 | + }, | |
| 333 | + "engines": { | |
| 334 | + "node": ">=14.6.0" | |
| 335 | + }, | |
| 336 | + "optionalDependencies": { | |
| 337 | + "@next/swc-android-arm-eabi": "13.1.1", | |
| 338 | + "@next/swc-android-arm64": "13.1.1", | |
| 339 | + "@next/swc-darwin-arm64": "13.1.1", | |
| 340 | + "@next/swc-darwin-x64": "13.1.1", | |
| 341 | + "@next/swc-freebsd-x64": "13.1.1", | |
| 342 | + "@next/swc-linux-arm-gnueabihf": "13.1.1", | |
| 343 | + "@next/swc-linux-arm64-gnu": "13.1.1", | |
| 344 | + "@next/swc-linux-arm64-musl": "13.1.1", | |
| 345 | + "@next/swc-linux-x64-gnu": "13.1.1", | |
| 346 | + "@next/swc-linux-x64-musl": "13.1.1", | |
| 347 | + "@next/swc-win32-arm64-msvc": "13.1.1", | |
| 348 | + "@next/swc-win32-ia32-msvc": "13.1.1", | |
| 349 | + "@next/swc-win32-x64-msvc": "13.1.1" | |
| 350 | + }, | |
| 351 | + "peerDependencies": { | |
| 352 | + "fibers": ">= 3.1.0", | |
| 353 | + "node-sass": "^6.0.0 || ^7.0.0", | |
| 354 | + "react": "^18.2.0", | |
| 355 | + "react-dom": "^18.2.0", | |
| 356 | + "sass": "^1.3.0" | |
| 357 | + }, | |
| 358 | + "peerDependenciesMeta": { | |
| 359 | + "fibers": { | |
| 360 | + "optional": true | |
| 361 | + }, | |
| 362 | + "node-sass": { | |
| 363 | + "optional": true | |
| 364 | + }, | |
| 365 | + "sass": { | |
| 366 | + "optional": true | |
| 367 | + } | |
| 368 | + } | |
| 369 | + }, | |
| 370 | + "node_modules/picocolors": { | |
| 371 | + "version": "1.0.0", | |
| 372 | + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", | |
| 373 | + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" | |
| 374 | + }, | |
| 375 | + "node_modules/postcss": { | |
| 376 | + "version": "8.4.14", | |
| 377 | + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", | |
| 378 | + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", | |
| 379 | + "funding": [ | |
| 380 | + { | |
| 381 | + "type": "opencollective", | |
| 382 | + "url": "https://opencollective.com/postcss/" | |
| 383 | + }, | |
| 384 | + { | |
| 385 | + "type": "tidelift", | |
| 386 | + "url": "https://tidelift.com/funding/github/npm/postcss" | |
| 387 | + } | |
| 388 | + ], | |
| 389 | + "dependencies": { | |
| 390 | + "nanoid": "^3.3.4", | |
| 391 | + "picocolors": "^1.0.0", | |
| 392 | + "source-map-js": "^1.0.2" | |
| 393 | + }, | |
| 394 | + "engines": { | |
| 395 | + "node": "^10 || ^12 || >=14" | |
| 396 | + } | |
| 397 | + }, | |
| 398 | + "node_modules/react": { | |
| 399 | + "version": "18.2.0", | |
| 400 | + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", | |
| 401 | + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", | |
| 402 | + "dependencies": { | |
| 403 | + "loose-envify": "^1.1.0" | |
| 404 | + }, | |
| 405 | + "engines": { | |
| 406 | + "node": ">=0.10.0" | |
| 407 | + } | |
| 408 | + }, | |
| 409 | + "node_modules/react-dom": { | |
| 410 | + "version": "18.2.0", | |
| 411 | + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", | |
| 412 | + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", | |
| 413 | + "dependencies": { | |
| 414 | + "loose-envify": "^1.1.0", | |
| 415 | + "scheduler": "^0.23.0" | |
| 416 | + }, | |
| 417 | + "peerDependencies": { | |
| 418 | + "react": "^18.2.0" | |
| 419 | + } | |
| 420 | + }, | |
| 421 | + "node_modules/scheduler": { | |
| 422 | + "version": "0.23.0", | |
| 423 | + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", | |
| 424 | + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", | |
| 425 | + "dependencies": { | |
| 426 | + "loose-envify": "^1.1.0" | |
| 427 | + } | |
| 428 | + }, | |
| 429 | + "node_modules/source-map-js": { | |
| 430 | + "version": "1.0.2", | |
| 431 | + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", | |
| 432 | + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", | |
| 433 | + "engines": { | |
| 434 | + "node": ">=0.10.0" | |
| 435 | + } | |
| 436 | + }, | |
| 437 | + "node_modules/styled-jsx": { | |
| 438 | + "version": "5.1.1", | |
| 439 | + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", | |
| 440 | + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", | |
| 441 | + "dependencies": { | |
| 442 | + "client-only": "0.0.1" | |
| 443 | + }, | |
| 444 | + "engines": { | |
| 445 | + "node": ">= 12.0.0" | |
| 446 | + }, | |
| 447 | + "peerDependencies": { | |
| 448 | + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" | |
| 449 | + }, | |
| 450 | + "peerDependenciesMeta": { | |
| 451 | + "@babel/core": { | |
| 452 | + "optional": true | |
| 453 | + }, | |
| 454 | + "babel-plugin-macros": { | |
| 455 | + "optional": true | |
| 456 | + } | |
| 457 | + } | |
| 458 | + }, | |
| 459 | + "node_modules/tslib": { | |
| 460 | + "version": "2.4.1", | |
| 461 | + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", | |
| 462 | + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" | |
| 463 | + }, | |
| 464 | + "node_modules/typescript": { | |
| 465 | + "version": "4.9.4", | |
| 466 | + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", | |
| 467 | + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", | |
| 468 | + "bin": { | |
| 469 | + "tsc": "bin/tsc", | |
| 470 | + "tsserver": "bin/tsserver" | |
| 471 | + }, | |
| 472 | + "engines": { | |
| 473 | + "node": ">=4.2.0" | |
| 474 | + } | |
| 475 | + } | |
| 476 | + } | |
| 477 | +} |
+21-0frontend/package.json
| @@ -0,0 +1,21 @@ | ||
| 1 | +{ | |
| 2 | + "name": "frontend", | |
| 3 | + "version": "0.1.0", | |
| 4 | + "private": true, | |
| 5 | + "scripts": { | |
| 6 | + "dev": "next dev", | |
| 7 | + "build": "next build", | |
| 8 | + "start": "next start", | |
| 9 | + "lint": "next lint" | |
| 10 | + }, | |
| 11 | + "dependencies": { | |
| 12 | + "@next/font": "13.1.1", | |
| 13 | + "@types/node": "18.11.18", | |
| 14 | + "@types/react": "18.0.26", | |
| 15 | + "@types/react-dom": "18.0.10", | |
| 16 | + "next": "13.1.1", | |
| 17 | + "react": "18.2.0", | |
| 18 | + "react-dom": "18.2.0", | |
| 19 | + "typescript": "4.9.4" | |
| 20 | + } | |
| 21 | +} |
+6-0frontend/pages/_app.tsx
| @@ -0,0 +1,6 @@ | ||
| 1 | +import '../styles/globals.css' | |
| 2 | +import type { AppProps } from 'next/app' | |
| 3 | + | |
| 4 | +export default function App({ Component, pageProps }: AppProps) { | |
| 5 | + return <Component {...pageProps} /> | |
| 6 | +} |
+13-0frontend/pages/_document.tsx
| @@ -0,0 +1,13 @@ | ||
| 1 | +import { Html, Head, Main, NextScript } from 'next/document' | |
| 2 | + | |
| 3 | +export default function Document() { | |
| 4 | + return ( | |
| 5 | + <Html lang="en"> | |
| 6 | + <Head /> | |
| 7 | + <body> | |
| 8 | + <Main /> | |
| 9 | + <NextScript /> | |
| 10 | + </body> | |
| 11 | + </Html> | |
| 12 | + ) | |
| 13 | +} |
+13-0frontend/pages/api/hello.ts
| @@ -0,0 +1,13 @@ | ||
| 1 | +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction | |
| 2 | +import type { NextApiRequest, NextApiResponse } from 'next' | |
| 3 | + | |
| 4 | +type Data = { | |
| 5 | + name: string | |
| 6 | +} | |
| 7 | + | |
| 8 | +export default function handler( | |
| 9 | + req: NextApiRequest, | |
| 10 | + res: NextApiResponse<Data> | |
| 11 | +) { | |
| 12 | + res.status(200).json({ name: 'John Doe' }) | |
| 13 | +} |
+123-0frontend/pages/index.tsx
| @@ -0,0 +1,123 @@ | ||
| 1 | +import Head from 'next/head' | |
| 2 | +import Image from 'next/image' | |
| 3 | +import { Inter } from '@next/font/google' | |
| 4 | +import styles from '../styles/Home.module.css' | |
| 5 | + | |
| 6 | +const inter = Inter({ subsets: ['latin'] }) | |
| 7 | + | |
| 8 | +export default function Home() { | |
| 9 | + return ( | |
| 10 | + <> | |
| 11 | + <Head> | |
| 12 | + <title>Create Next App</title> | |
| 13 | + <meta name="description" content="Generated by create next app" /> | |
| 14 | + <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| 15 | + <link rel="icon" href="/favicon.ico" /> | |
| 16 | + </Head> | |
| 17 | + <main className={styles.main}> | |
| 18 | + <div className={styles.description}> | |
| 19 | + <p> | |
| 20 | + Get started by editing | |
| 21 | + <code className={styles.code}>pages/index.tsx</code> | |
| 22 | + </p> | |
| 23 | + <div> | |
| 24 | + <a | |
| 25 | + href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" | |
| 26 | + target="_blank" | |
| 27 | + rel="noopener noreferrer" | |
| 28 | + > | |
| 29 | + By{' '} | |
| 30 | + <Image | |
| 31 | + src="/vercel.svg" | |
| 32 | + alt="Vercel Logo" | |
| 33 | + className={styles.vercelLogo} | |
| 34 | + width={100} | |
| 35 | + height={24} | |
| 36 | + priority | |
| 37 | + /> | |
| 38 | + </a> | |
| 39 | + </div> | |
| 40 | + </div> | |
| 41 | + | |
| 42 | + <div className={styles.center}> | |
| 43 | + <Image | |
| 44 | + className={styles.logo} | |
| 45 | + src="/next.svg" | |
| 46 | + alt="Next.js Logo" | |
| 47 | + width={180} | |
| 48 | + height={37} | |
| 49 | + priority | |
| 50 | + /> | |
| 51 | + <div className={styles.thirteen}> | |
| 52 | + <Image | |
| 53 | + src="/thirteen.svg" | |
| 54 | + alt="13" | |
| 55 | + width={40} | |
| 56 | + height={31} | |
| 57 | + priority | |
| 58 | + /> | |
| 59 | + </div> | |
| 60 | + </div> | |
| 61 | + | |
| 62 | + <div className={styles.grid}> | |
| 63 | + <a | |
| 64 | + href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" | |
| 65 | + className={styles.card} | |
| 66 | + target="_blank" | |
| 67 | + rel="noopener noreferrer" | |
| 68 | + > | |
| 69 | + <h2 className={inter.className}> | |
| 70 | + Docs <span>-></span> | |
| 71 | + </h2> | |
| 72 | + <p className={inter.className}> | |
| 73 | + Find in-depth information about Next.js features and API. | |
| 74 | + </p> | |
| 75 | + </a> | |
| 76 | + | |
| 77 | + <a | |
| 78 | + href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" | |
| 79 | + className={styles.card} | |
| 80 | + target="_blank" | |
| 81 | + rel="noopener noreferrer" | |
| 82 | + > | |
| 83 | + <h2 className={inter.className}> | |
| 84 | + Learn <span>-></span> | |
| 85 | + </h2> | |
| 86 | + <p className={inter.className}> | |
| 87 | + Learn about Next.js in an interactive course with quizzes! | |
| 88 | + </p> | |
| 89 | + </a> | |
| 90 | + | |
| 91 | + <a | |
| 92 | + href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" | |
| 93 | + className={styles.card} | |
| 94 | + target="_blank" | |
| 95 | + rel="noopener noreferrer" | |
| 96 | + > | |
| 97 | + <h2 className={inter.className}> | |
| 98 | + Templates <span>-></span> | |
| 99 | + </h2> | |
| 100 | + <p className={inter.className}> | |
| 101 | + Discover and deploy boilerplate example Next.js projects. | |
| 102 | + </p> | |
| 103 | + </a> | |
| 104 | + | |
| 105 | + <a | |
| 106 | + href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" | |
| 107 | + className={styles.card} | |
| 108 | + target="_blank" | |
| 109 | + rel="noopener noreferrer" | |
| 110 | + > | |
| 111 | + <h2 className={inter.className}> | |
| 112 | + Deploy <span>-></span> | |
| 113 | + </h2> | |
| 114 | + <p className={inter.className}> | |
| 115 | + Instantly deploy your Next.js site to a shareable URL | |
| 116 | + with Vercel. | |
| 117 | + </p> | |
| 118 | + </a> | |
| 119 | + </div> | |
| 120 | + </main> | |
| 121 | + </> | |
| 122 | + ) | |
| 123 | +} |
+0-0frontend/public/favicon.ico
Binary file changed.
+1-0frontend/public/next.svg
| @@ -0,0 +1 @@ | ||
| 1 | +<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg> |
+1-0frontend/public/thirteen.svg
| @@ -0,0 +1 @@ | ||
| 1 | +<svg xmlns="http://www.w3.org/2000/svg" width="40" height="31" fill="none"><g opacity=".9"><path fill="url(#a)" d="M13 .4v29.3H7V6.3h-.2L0 10.5V5L7.2.4H13Z"/><path fill="url(#b)" d="M28.8 30.1c-2.2 0-4-.3-5.7-1-1.7-.8-3-1.8-4-3.1a7.7 7.7 0 0 1-1.4-4.6h6.2c0 .8.3 1.4.7 2 .4.5 1 .9 1.7 1.2.7.3 1.6.4 2.5.4 1 0 1.7-.2 2.5-.5.7-.3 1.3-.8 1.7-1.4.4-.6.6-1.2.6-2s-.2-1.5-.7-2.1c-.4-.6-1-1-1.8-1.4-.8-.4-1.8-.5-2.9-.5h-2.7v-4.6h2.7a6 6 0 0 0 2.5-.5 4 4 0 0 0 1.7-1.3c.4-.6.6-1.3.6-2a3.5 3.5 0 0 0-2-3.3 5.6 5.6 0 0 0-4.5 0 4 4 0 0 0-1.7 1.2c-.4.6-.6 1.2-.6 2h-6c0-1.7.6-3.2 1.5-4.5 1-1.3 2.2-2.3 3.8-3C25 .4 26.8 0 28.8 0s3.8.4 5.3 1.1c1.5.7 2.7 1.7 3.6 3a7.2 7.2 0 0 1 1.2 4.2c0 1.6-.5 3-1.5 4a7 7 0 0 1-4 2.2v.2c2.2.3 3.8 1 5 2.2a6.4 6.4 0 0 1 1.6 4.6c0 1.7-.5 3.1-1.4 4.4a9.7 9.7 0 0 1-4 3.1c-1.7.8-3.7 1.1-5.8 1.1Z"/></g><defs><linearGradient id="a" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient><linearGradient id="b" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient></defs></svg> |
+1-0frontend/public/vercel.svg
| @@ -0,0 +1 @@ | ||
| 1 | +<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg> |
+278-0frontend/styles/Home.module.css
| @@ -0,0 +1,278 @@ | ||
| 1 | +.main { | |
| 2 | + display: flex; | |
| 3 | + flex-direction: column; | |
| 4 | + justify-content: space-between; | |
| 5 | + align-items: center; | |
| 6 | + padding: 6rem; | |
| 7 | + min-height: 100vh; | |
| 8 | +} | |
| 9 | + | |
| 10 | +.description { | |
| 11 | + display: inherit; | |
| 12 | + justify-content: inherit; | |
| 13 | + align-items: inherit; | |
| 14 | + font-size: 0.85rem; | |
| 15 | + max-width: var(--max-width); | |
| 16 | + width: 100%; | |
| 17 | + z-index: 2; | |
| 18 | + font-family: var(--font-mono); | |
| 19 | +} | |
| 20 | + | |
| 21 | +.description a { | |
| 22 | + display: flex; | |
| 23 | + justify-content: center; | |
| 24 | + align-items: center; | |
| 25 | + gap: 0.5rem; | |
| 26 | +} | |
| 27 | + | |
| 28 | +.description p { | |
| 29 | + position: relative; | |
| 30 | + margin: 0; | |
| 31 | + padding: 1rem; | |
| 32 | + background-color: rgba(var(--callout-rgb), 0.5); | |
| 33 | + border: 1px solid rgba(var(--callout-border-rgb), 0.3); | |
| 34 | + border-radius: var(--border-radius); | |
| 35 | +} | |
| 36 | + | |
| 37 | +.code { | |
| 38 | + font-weight: 700; | |
| 39 | + font-family: var(--font-mono); | |
| 40 | +} | |
| 41 | + | |
| 42 | +.grid { | |
| 43 | + display: grid; | |
| 44 | + grid-template-columns: repeat(4, minmax(25%, auto)); | |
| 45 | + width: var(--max-width); | |
| 46 | + max-width: 100%; | |
| 47 | +} | |
| 48 | + | |
| 49 | +.card { | |
| 50 | + padding: 1rem 1.2rem; | |
| 51 | + border-radius: var(--border-radius); | |
| 52 | + background: rgba(var(--card-rgb), 0); | |
| 53 | + border: 1px solid rgba(var(--card-border-rgb), 0); | |
| 54 | + transition: background 200ms, border 200ms; | |
| 55 | +} | |
| 56 | + | |
| 57 | +.card span { | |
| 58 | + display: inline-block; | |
| 59 | + transition: transform 200ms; | |
| 60 | +} | |
| 61 | + | |
| 62 | +.card h2 { | |
| 63 | + font-weight: 600; | |
| 64 | + margin-bottom: 0.7rem; | |
| 65 | +} | |
| 66 | + | |
| 67 | +.card p { | |
| 68 | + margin: 0; | |
| 69 | + opacity: 0.6; | |
| 70 | + font-size: 0.9rem; | |
| 71 | + line-height: 1.5; | |
| 72 | + max-width: 30ch; | |
| 73 | +} | |
| 74 | + | |
| 75 | +.center { | |
| 76 | + display: flex; | |
| 77 | + justify-content: center; | |
| 78 | + align-items: center; | |
| 79 | + position: relative; | |
| 80 | + padding: 4rem 0; | |
| 81 | +} | |
| 82 | + | |
| 83 | +.center::before { | |
| 84 | + background: var(--secondary-glow); | |
| 85 | + border-radius: 50%; | |
| 86 | + width: 480px; | |
| 87 | + height: 360px; | |
| 88 | + margin-left: -400px; | |
| 89 | +} | |
| 90 | + | |
| 91 | +.center::after { | |
| 92 | + background: var(--primary-glow); | |
| 93 | + width: 240px; | |
| 94 | + height: 180px; | |
| 95 | + z-index: -1; | |
| 96 | +} | |
| 97 | + | |
| 98 | +.center::before, | |
| 99 | +.center::after { | |
| 100 | + content: ''; | |
| 101 | + left: 50%; | |
| 102 | + position: absolute; | |
| 103 | + filter: blur(45px); | |
| 104 | + transform: translateZ(0); | |
| 105 | +} | |
| 106 | + | |
| 107 | +.logo, | |
| 108 | +.thirteen { | |
| 109 | + position: relative; | |
| 110 | +} | |
| 111 | + | |
| 112 | +.thirteen { | |
| 113 | + display: flex; | |
| 114 | + justify-content: center; | |
| 115 | + align-items: center; | |
| 116 | + width: 75px; | |
| 117 | + height: 75px; | |
| 118 | + padding: 25px 10px; | |
| 119 | + margin-left: 16px; | |
| 120 | + transform: translateZ(0); | |
| 121 | + border-radius: var(--border-radius); | |
| 122 | + overflow: hidden; | |
| 123 | + box-shadow: 0px 2px 8px -1px #0000001a; | |
| 124 | +} | |
| 125 | + | |
| 126 | +.thirteen::before, | |
| 127 | +.thirteen::after { | |
| 128 | + content: ''; | |
| 129 | + position: absolute; | |
| 130 | + z-index: -1; | |
| 131 | +} | |
| 132 | + | |
| 133 | +/* Conic Gradient Animation */ | |
| 134 | +.thirteen::before { | |
| 135 | + animation: 6s rotate linear infinite; | |
| 136 | + width: 200%; | |
| 137 | + height: 200%; | |
| 138 | + background: var(--tile-border); | |
| 139 | +} | |
| 140 | + | |
| 141 | +/* Inner Square */ | |
| 142 | +.thirteen::after { | |
| 143 | + inset: 0; | |
| 144 | + padding: 1px; | |
| 145 | + border-radius: var(--border-radius); | |
| 146 | + background: linear-gradient( | |
| 147 | + to bottom right, | |
| 148 | + rgba(var(--tile-start-rgb), 1), | |
| 149 | + rgba(var(--tile-end-rgb), 1) | |
| 150 | + ); | |
| 151 | + background-clip: content-box; | |
| 152 | +} | |
| 153 | + | |
| 154 | +/* Enable hover only on non-touch devices */ | |
| 155 | +@media (hover: hover) and (pointer: fine) { | |
| 156 | + .card:hover { | |
| 157 | + background: rgba(var(--card-rgb), 0.1); | |
| 158 | + border: 1px solid rgba(var(--card-border-rgb), 0.15); | |
| 159 | + } | |
| 160 | + | |
| 161 | + .card:hover span { | |
| 162 | + transform: translateX(4px); | |
| 163 | + } | |
| 164 | +} | |
| 165 | + | |
| 166 | +@media (prefers-reduced-motion) { | |
| 167 | + .thirteen::before { | |
| 168 | + animation: none; | |
| 169 | + } | |
| 170 | + | |
| 171 | + .card:hover span { | |
| 172 | + transform: none; | |
| 173 | + } | |
| 174 | +} | |
| 175 | + | |
| 176 | +/* Mobile */ | |
| 177 | +@media (max-width: 700px) { | |
| 178 | + .content { | |
| 179 | + padding: 4rem; | |
| 180 | + } | |
| 181 | + | |
| 182 | + .grid { | |
| 183 | + grid-template-columns: 1fr; | |
| 184 | + margin-bottom: 120px; | |
| 185 | + max-width: 320px; | |
| 186 | + text-align: center; | |
| 187 | + } | |
| 188 | + | |
| 189 | + .card { | |
| 190 | + padding: 1rem 2.5rem; | |
| 191 | + } | |
| 192 | + | |
| 193 | + .card h2 { | |
| 194 | + margin-bottom: 0.5rem; | |
| 195 | + } | |
| 196 | + | |
| 197 | + .center { | |
| 198 | + padding: 8rem 0 6rem; | |
| 199 | + } | |
| 200 | + | |
| 201 | + .center::before { | |
| 202 | + transform: none; | |
| 203 | + height: 300px; | |
| 204 | + } | |
| 205 | + | |
| 206 | + .description { | |
| 207 | + font-size: 0.8rem; | |
| 208 | + } | |
| 209 | + | |
| 210 | + .description a { | |
| 211 | + padding: 1rem; | |
| 212 | + } | |
| 213 | + | |
| 214 | + .description p, | |
| 215 | + .description div { | |
| 216 | + display: flex; | |
| 217 | + justify-content: center; | |
| 218 | + position: fixed; | |
| 219 | + width: 100%; | |
| 220 | + } | |
| 221 | + | |
| 222 | + .description p { | |
| 223 | + align-items: center; | |
| 224 | + inset: 0 0 auto; | |
| 225 | + padding: 2rem 1rem 1.4rem; | |
| 226 | + border-radius: 0; | |
| 227 | + border: none; | |
| 228 | + border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); | |
| 229 | + background: linear-gradient( | |
| 230 | + to bottom, | |
| 231 | + rgba(var(--background-start-rgb), 1), | |
| 232 | + rgba(var(--callout-rgb), 0.5) | |
| 233 | + ); | |
| 234 | + background-clip: padding-box; | |
| 235 | + backdrop-filter: blur(24px); | |
| 236 | + } | |
| 237 | + | |
| 238 | + .description div { | |
| 239 | + align-items: flex-end; | |
| 240 | + pointer-events: none; | |
| 241 | + inset: auto 0 0; | |
| 242 | + padding: 2rem; | |
| 243 | + height: 200px; | |
| 244 | + background: linear-gradient( | |
| 245 | + to bottom, | |
| 246 | + transparent 0%, | |
| 247 | + rgb(var(--background-end-rgb)) 40% | |
| 248 | + ); | |
| 249 | + z-index: 1; | |
| 250 | + } | |
| 251 | +} | |
| 252 | + | |
| 253 | +/* Tablet and Smaller Desktop */ | |
| 254 | +@media (min-width: 701px) and (max-width: 1120px) { | |
| 255 | + .grid { | |
| 256 | + grid-template-columns: repeat(2, 50%); | |
| 257 | + } | |
| 258 | +} | |
| 259 | + | |
| 260 | +@media (prefers-color-scheme: dark) { | |
| 261 | + .vercelLogo { | |
| 262 | + filter: invert(1); | |
| 263 | + } | |
| 264 | + | |
| 265 | + .logo, | |
| 266 | + .thirteen img { | |
| 267 | + filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); | |
| 268 | + } | |
| 269 | +} | |
| 270 | + | |
| 271 | +@keyframes rotate { | |
| 272 | + from { | |
| 273 | + transform: rotate(360deg); | |
| 274 | + } | |
| 275 | + to { | |
| 276 | + transform: rotate(0deg); | |
| 277 | + } | |
| 278 | +} |
+107-0frontend/styles/globals.css
| @@ -0,0 +1,107 @@ | ||
| 1 | +:root { | |
| 2 | + --max-width: 1100px; | |
| 3 | + --border-radius: 12px; | |
| 4 | + --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', | |
| 5 | + 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', | |
| 6 | + 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; | |
| 7 | + | |
| 8 | + --foreground-rgb: 0, 0, 0; | |
| 9 | + --background-start-rgb: 214, 219, 220; | |
| 10 | + --background-end-rgb: 255, 255, 255; | |
| 11 | + | |
| 12 | + --primary-glow: conic-gradient( | |
| 13 | + from 180deg at 50% 50%, | |
| 14 | + #16abff33 0deg, | |
| 15 | + #0885ff33 55deg, | |
| 16 | + #54d6ff33 120deg, | |
| 17 | + #0071ff33 160deg, | |
| 18 | + transparent 360deg | |
| 19 | + ); | |
| 20 | + --secondary-glow: radial-gradient( | |
| 21 | + rgba(255, 255, 255, 1), | |
| 22 | + rgba(255, 255, 255, 0) | |
| 23 | + ); | |
| 24 | + | |
| 25 | + --tile-start-rgb: 239, 245, 249; | |
| 26 | + --tile-end-rgb: 228, 232, 233; | |
| 27 | + --tile-border: conic-gradient( | |
| 28 | + #00000080, | |
| 29 | + #00000040, | |
| 30 | + #00000030, | |
| 31 | + #00000020, | |
| 32 | + #00000010, | |
| 33 | + #00000010, | |
| 34 | + #00000080 | |
| 35 | + ); | |
| 36 | + | |
| 37 | + --callout-rgb: 238, 240, 241; | |
| 38 | + --callout-border-rgb: 172, 175, 176; | |
| 39 | + --card-rgb: 180, 185, 188; | |
| 40 | + --card-border-rgb: 131, 134, 135; | |
| 41 | +} | |
| 42 | + | |
| 43 | +@media (prefers-color-scheme: dark) { | |
| 44 | + :root { | |
| 45 | + --foreground-rgb: 255, 255, 255; | |
| 46 | + --background-start-rgb: 0, 0, 0; | |
| 47 | + --background-end-rgb: 0, 0, 0; | |
| 48 | + | |
| 49 | + --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); | |
| 50 | + --secondary-glow: linear-gradient( | |
| 51 | + to bottom right, | |
| 52 | + rgba(1, 65, 255, 0), | |
| 53 | + rgba(1, 65, 255, 0), | |
| 54 | + rgba(1, 65, 255, 0.3) | |
| 55 | + ); | |
| 56 | + | |
| 57 | + --tile-start-rgb: 2, 13, 46; | |
| 58 | + --tile-end-rgb: 2, 5, 19; | |
| 59 | + --tile-border: conic-gradient( | |
| 60 | + #ffffff80, | |
| 61 | + #ffffff40, | |
| 62 | + #ffffff30, | |
| 63 | + #ffffff20, | |
| 64 | + #ffffff10, | |
| 65 | + #ffffff10, | |
| 66 | + #ffffff80 | |
| 67 | + ); | |
| 68 | + | |
| 69 | + --callout-rgb: 20, 20, 20; | |
| 70 | + --callout-border-rgb: 108, 108, 108; | |
| 71 | + --card-rgb: 100, 100, 100; | |
| 72 | + --card-border-rgb: 200, 200, 200; | |
| 73 | + } | |
| 74 | +} | |
| 75 | + | |
| 76 | +* { | |
| 77 | + box-sizing: border-box; | |
| 78 | + padding: 0; | |
| 79 | + margin: 0; | |
| 80 | +} | |
| 81 | + | |
| 82 | +html, | |
| 83 | +body { | |
| 84 | + max-width: 100vw; | |
| 85 | + overflow-x: hidden; | |
| 86 | +} | |
| 87 | + | |
| 88 | +body { | |
| 89 | + color: rgb(var(--foreground-rgb)); | |
| 90 | + background: linear-gradient( | |
| 91 | + to bottom, | |
| 92 | + transparent, | |
| 93 | + rgb(var(--background-end-rgb)) | |
| 94 | + ) | |
| 95 | + rgb(var(--background-start-rgb)); | |
| 96 | +} | |
| 97 | + | |
| 98 | +a { | |
| 99 | + color: inherit; | |
| 100 | + text-decoration: none; | |
| 101 | +} | |
| 102 | + | |
| 103 | +@media (prefers-color-scheme: dark) { | |
| 104 | + html { | |
| 105 | + color-scheme: dark; | |
| 106 | + } | |
| 107 | +} |
+20-0frontend/tsconfig.json
| @@ -0,0 +1,20 @@ | ||
| 1 | +{ | |
| 2 | + "compilerOptions": { | |
| 3 | + "target": "es5", | |
| 4 | + "lib": ["dom", "dom.iterable", "esnext"], | |
| 5 | + "allowJs": true, | |
| 6 | + "skipLibCheck": true, | |
| 7 | + "strict": true, | |
| 8 | + "forceConsistentCasingInFileNames": true, | |
| 9 | + "noEmit": true, | |
| 10 | + "esModuleInterop": true, | |
| 11 | + "module": "esnext", | |
| 12 | + "moduleResolution": "node", | |
| 13 | + "resolveJsonModule": true, | |
| 14 | + "isolatedModules": true, | |
| 15 | + "jsx": "preserve", | |
| 16 | + "incremental": true | |
| 17 | + }, | |
| 18 | + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], | |
| 19 | + "exclude": ["node_modules"] | |
| 20 | +} |