Compare commits
12 Commits
6-simple-s
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
24cdebe960 | ||
|
1edd0892af | ||
|
d56b5725a0 | ||
|
3b05d0983a | ||
|
9d4ce6c413 | ||
|
d0d25ef499 | ||
|
832e39fcd7 | ||
|
1dd1188bdc | ||
|
cd2e4fca26 | ||
|
9930bec65a | ||
6ba83ac2d3 | |||
|
ebb48c1a42 |
1
.python-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.9.0
|
20
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Python: Django",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/api/manage.py",
|
||||||
|
"args": [
|
||||||
|
"runserver",
|
||||||
|
"0.0.0.0:9000"
|
||||||
|
],
|
||||||
|
"django": true,
|
||||||
|
"justMyCode": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
.vscode/settings.json
vendored
@ -16,7 +16,8 @@
|
|||||||
"titleBar.activeBackground": "#f9e64f",
|
"titleBar.activeBackground": "#f9e64f",
|
||||||
"titleBar.activeForeground": "#15202b",
|
"titleBar.activeForeground": "#15202b",
|
||||||
"titleBar.inactiveBackground": "#f9e64f99",
|
"titleBar.inactiveBackground": "#f9e64f99",
|
||||||
"titleBar.inactiveForeground": "#15202b99"
|
"titleBar.inactiveForeground": "#15202b99",
|
||||||
|
"commandCenter.border": "#15202b99"
|
||||||
},
|
},
|
||||||
"peacock.color": "#f9e64f"
|
"peacock.color": "#f9e64f"
|
||||||
}
|
}
|
26
README.md
@ -5,14 +5,36 @@ The AHosking.com API
|
|||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
1. `sudo apt install libpq-dev python-dev python3-psycopg2`
|
1. `sudo apt install libpq-dev python-dev python3-psycopg2`
|
||||||
1. `pip install virtualenv`
|
1. `pip install virtualenv`
|
||||||
1. `python3 -m virtualenv .env`
|
1. `python3 -m virtualenv .env`
|
||||||
1. Activate environment
|
1. Activate environment
|
||||||
`.\.env\Scripts\activate`
|
`.\.env\Scripts\activate`
|
||||||
1.
|
1. `export DEBUG=false`
|
||||||
|
1. `docker run --name api-postgres -e POSTGRES_PASSWORD=mysecretpassword -e POSTGRES_USER=api -e POSTGRES_DB=api -p 5432:5432 -d postgres`
|
||||||
|
1. ```
|
||||||
|
export DATABASE_HOST=localhost
|
||||||
|
export DATABASE_PORT=5432
|
||||||
|
export DATABASE_USER=api
|
||||||
|
export DATABASE_PASSWORD=mysecretpassword
|
||||||
|
export DATABASE_NAME=api
|
||||||
|
```
|
||||||
|
1. `python3 manage.py makemigrations`
|
||||||
|
1. `python3 manage.py migrate`
|
||||||
|
1. `python3 manage.py createsuperuser`
|
||||||
|
1. `python manage.py runserver 0.0.0.0:9000`
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
`pip3 install --upgrade --force-reinstall -r requirements.txt` will re-install requirements and upgrade based on requirements.txt
|
||||||
|
|
||||||
### Third-party
|
### Third-party
|
||||||
|
|
||||||
* https://pypi.org/project/django-3-jet/
|
- https://pypi.org/project/django-4-jet/
|
||||||
|
|
||||||
|
## Themes and Templates
|
||||||
|
|
||||||
|
- https://startbootstrap.com/theme/landing-page
|
||||||
|
- https://startbootstrap.com/previews/new-age
|
||||||
|
- https://startbootstrap.com/theme/new-age
|
||||||
|
0
api/accounts/__init__.py
Normal file
3
api/accounts/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
api/accounts/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AccountsConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'accounts'
|
0
api/accounts/migrations/__init__.py
Normal file
3
api/accounts/models.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
3
api/accounts/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
3
api/accounts/views.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
@ -1,5 +0,0 @@
|
|||||||
# This will make sure the app is always imported when
|
|
||||||
# Django starts so that shared_task will use this app.
|
|
||||||
from .celery import app as celery_app
|
|
||||||
|
|
||||||
__all__ = ('celery_app',)
|
|
@ -1,23 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from celery import Celery
|
|
||||||
|
|
||||||
# Set the default Django settings module for the 'celery' program.
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings')
|
|
||||||
|
|
||||||
app = Celery('api')
|
|
||||||
|
|
||||||
|
|
||||||
# Using a string here means the worker doesn't have to serialize
|
|
||||||
# the configuration object to child processes.
|
|
||||||
# - namespace='CELERY' means all celery-related configuration keys
|
|
||||||
# should have a `CELERY_` prefix.
|
|
||||||
app.config_from_object('django.conf:settings', namespace='CELERY')
|
|
||||||
|
|
||||||
# Load task modules from all registered Django apps.
|
|
||||||
app.autodiscover_tasks()
|
|
||||||
|
|
||||||
|
|
||||||
@app.task(bind=True)
|
|
||||||
def debug_task(self):
|
|
||||||
print(f'Request: {self.request!r}')
|
|
@ -2,7 +2,10 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from jet.dashboard import modules
|
from jet.dashboard import modules
|
||||||
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
|
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
|
||||||
|
|
||||||
|
|
||||||
class CustomIndexDashboard(Dashboard):
|
class CustomIndexDashboard(Dashboard):
|
||||||
|
# app_label = "api"
|
||||||
|
|
||||||
columns = 3
|
columns = 3
|
||||||
|
|
||||||
def init_with_context(self, context):
|
def init_with_context(self, context):
|
||||||
@ -25,19 +28,7 @@ class CustomIndexDashboard(Dashboard):
|
|||||||
'url': 'irc://irc.freenode.net/django',
|
'url': 'irc://irc.freenode.net/django',
|
||||||
'external': True,
|
'external': True,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'title': _('COME ON'),
|
|
||||||
'url': 'irc://irc.freenode.net/django',
|
|
||||||
'external': True,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
column=0,
|
column=0,
|
||||||
order=0
|
order=0
|
||||||
))
|
))
|
||||||
|
|
||||||
self.children.append(modules.ModelList(
|
|
||||||
_('Models'),
|
|
||||||
exclude=('auth.*',),
|
|
||||||
column=0,
|
|
||||||
order=0
|
|
||||||
))
|
|
@ -12,6 +12,8 @@ https://docs.djangoproject.com/en/3.2/ref/settings/
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import environ
|
import environ
|
||||||
|
import os
|
||||||
|
import environ
|
||||||
|
|
||||||
env = environ.Env()
|
env = environ.Env()
|
||||||
environ.Env.read_env()
|
environ.Env.read_env()
|
||||||
@ -24,20 +26,22 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|||||||
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
SECRET_KEY = env("SECRET_KEY", default='django-insecure-7uajhmbt^@)mklk1ur=slkmn3*+9_cnfhww6wi8jg*h@qqd%6u')
|
SECRET_KEY = env(
|
||||||
|
"SECRET_KEY", default='django-insecure-7uajhmbt^@)mklk1ur=slkmn3*+9_cnfhww6wi8jg*h@qqd%6u')
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = env("DEBUG")
|
DEBUG = env("DEBUG")
|
||||||
|
|
||||||
ALLOWED_HOSTS = ["127.0.0.1", "192.168.1.187", "192.168.1.23"]
|
ALLOWED_HOSTS = ["localhost", "127.0.0.1", "192.168.1.187",
|
||||||
|
"192.168.1.23", "192.168.1.125", "0.0.0.0"]
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'jet.dashboard',
|
'bills',
|
||||||
'jet',
|
# 'bills.apps.BillsConfig',
|
||||||
'bills.apps.BillsConfig',
|
'admin_soft.apps.AdminSoftDashboardConfig',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
@ -47,6 +51,7 @@ INSTALLED_APPS = [
|
|||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
'django_filters',
|
'django_filters',
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@ -64,7 +69,7 @@ ROOT_URLCONF = 'api.urls'
|
|||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [],
|
'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'api/templates')],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
@ -129,7 +134,7 @@ USE_TZ = True
|
|||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/3.2/howto/static-files/
|
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
@ -171,6 +176,12 @@ JET_THEMES = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
JET_INDEX_DASHBOARD = 'jet.dashboard.dashboard.DefaultIndexDashboard'
|
# JET_INDEX_DASHBOARD = 'jet.dashboard.dashboard.DefaultIndexDashboard'
|
||||||
JET_APP_INDEX_DASHBOARD = 'bills.dashboard.CustomIndexDashboard'
|
# JET_APP_INDEX_DASHBOARD = 'dashboard.CustomIndexDashboard'
|
||||||
# JET_INDEX_DASHBOARD = 'bills.dashboard.CustomIndexDashboard'
|
JET_INDEX_DASHBOARD = 'dashboard.CustomIndexDashboard'
|
||||||
|
|
||||||
|
|
||||||
|
STATICFILES_DIRS = [
|
||||||
|
BASE_DIR / "api/static",
|
||||||
|
("assets", BASE_DIR / "api/static/assets"),
|
||||||
|
]
|
||||||
|
BIN
api/api/static/assets/favicon.ico
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
api/api/static/assets/img/bg-masthead.jpg
Normal file
After Width: | Height: | Size: 939 KiB |
BIN
api/api/static/assets/img/bg-showcase-1.jpg
Normal file
After Width: | Height: | Size: 298 KiB |
BIN
api/api/static/assets/img/bg-showcase-2.jpg
Normal file
After Width: | Height: | Size: 193 KiB |
BIN
api/api/static/assets/img/bg-showcase-3.jpg
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
api/api/static/assets/img/testimonials-1.jpg
Normal file
After Width: | Height: | Size: 133 KiB |
BIN
api/api/static/assets/img/testimonials-2.jpg
Normal file
After Width: | Height: | Size: 189 KiB |
BIN
api/api/static/assets/img/testimonials-3.jpg
Normal file
After Width: | Height: | Size: 244 KiB |
4124
api/api/static/css/bootstrap-grid.css
vendored
Normal file
1
api/api/static/css/bootstrap-grid.css.map
Normal file
7
api/api/static/css/bootstrap-grid.min.css
vendored
Normal file
1
api/api/static/css/bootstrap-grid.min.css.map
Normal file
4123
api/api/static/css/bootstrap-grid.rtl.css
vendored
Normal file
1
api/api/static/css/bootstrap-grid.rtl.css.map
Normal file
7
api/api/static/css/bootstrap-grid.rtl.min.css
vendored
Normal file
1
api/api/static/css/bootstrap-grid.rtl.min.css.map
Normal file
488
api/api/static/css/bootstrap-reboot.css
vendored
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.2.3 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2022 The Bootstrap Authors
|
||||||
|
* Copyright 2011-2022 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
*/
|
||||||
|
:root {
|
||||||
|
--bs-blue: #0d6efd;
|
||||||
|
--bs-indigo: #6610f2;
|
||||||
|
--bs-purple: #6f42c1;
|
||||||
|
--bs-pink: #d63384;
|
||||||
|
--bs-red: #dc3545;
|
||||||
|
--bs-orange: #fd7e14;
|
||||||
|
--bs-yellow: #ffc107;
|
||||||
|
--bs-green: #198754;
|
||||||
|
--bs-teal: #20c997;
|
||||||
|
--bs-cyan: #0dcaf0;
|
||||||
|
--bs-black: #000;
|
||||||
|
--bs-white: #fff;
|
||||||
|
--bs-gray: #6c757d;
|
||||||
|
--bs-gray-dark: #343a40;
|
||||||
|
--bs-gray-100: #f8f9fa;
|
||||||
|
--bs-gray-200: #e9ecef;
|
||||||
|
--bs-gray-300: #dee2e6;
|
||||||
|
--bs-gray-400: #ced4da;
|
||||||
|
--bs-gray-500: #adb5bd;
|
||||||
|
--bs-gray-600: #6c757d;
|
||||||
|
--bs-gray-700: #495057;
|
||||||
|
--bs-gray-800: #343a40;
|
||||||
|
--bs-gray-900: #212529;
|
||||||
|
--bs-primary: #0d6efd;
|
||||||
|
--bs-secondary: #6c757d;
|
||||||
|
--bs-success: #198754;
|
||||||
|
--bs-info: #0dcaf0;
|
||||||
|
--bs-warning: #ffc107;
|
||||||
|
--bs-danger: #dc3545;
|
||||||
|
--bs-light: #f8f9fa;
|
||||||
|
--bs-dark: #212529;
|
||||||
|
--bs-primary-rgb: 13, 110, 253;
|
||||||
|
--bs-secondary-rgb: 108, 117, 125;
|
||||||
|
--bs-success-rgb: 25, 135, 84;
|
||||||
|
--bs-info-rgb: 13, 202, 240;
|
||||||
|
--bs-warning-rgb: 255, 193, 7;
|
||||||
|
--bs-danger-rgb: 220, 53, 69;
|
||||||
|
--bs-light-rgb: 248, 249, 250;
|
||||||
|
--bs-dark-rgb: 33, 37, 41;
|
||||||
|
--bs-white-rgb: 255, 255, 255;
|
||||||
|
--bs-black-rgb: 0, 0, 0;
|
||||||
|
--bs-body-color-rgb: 33, 37, 41;
|
||||||
|
--bs-body-bg-rgb: 255, 255, 255;
|
||||||
|
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||||
|
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||||
|
--bs-body-font-size: 1rem;
|
||||||
|
--bs-body-font-weight: 400;
|
||||||
|
--bs-body-line-height: 1.5;
|
||||||
|
--bs-body-color: #212529;
|
||||||
|
--bs-body-bg: #fff;
|
||||||
|
--bs-border-width: 1px;
|
||||||
|
--bs-border-style: solid;
|
||||||
|
--bs-border-color: #dee2e6;
|
||||||
|
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-border-radius: 0.375rem;
|
||||||
|
--bs-border-radius-sm: 0.25rem;
|
||||||
|
--bs-border-radius-lg: 0.5rem;
|
||||||
|
--bs-border-radius-xl: 1rem;
|
||||||
|
--bs-border-radius-2xl: 2rem;
|
||||||
|
--bs-border-radius-pill: 50rem;
|
||||||
|
--bs-link-color: #0d6efd;
|
||||||
|
--bs-link-hover-color: #0a58ca;
|
||||||
|
--bs-code-color: #d63384;
|
||||||
|
--bs-highlight-bg: #fff3cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
:root {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--bs-body-font-family);
|
||||||
|
font-size: var(--bs-body-font-size);
|
||||||
|
font-weight: var(--bs-body-font-weight);
|
||||||
|
line-height: var(--bs-body-line-height);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
text-align: var(--bs-body-text-align);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: inherit;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6, h5, h4, h3, h2, h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h3 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: help;
|
||||||
|
-webkit-text-decoration-skip-ink: none;
|
||||||
|
text-decoration-skip-ink: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
address {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
dl {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ul ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
padding: 0.1875em;
|
||||||
|
background-color: var(--bs-highlight-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.75em;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--bs-link-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: var(--bs-link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: var(--bs-font-monospace);
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-code-color);
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
a > code {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 0.1875rem 0.375rem;
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-body-bg);
|
||||||
|
background-color: var(--bs-body-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
kbd kbd {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
caption-side: bottom;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
color: #6c757d;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus:not(:focus-visible) {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
optgroup,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
select:disabled {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
button:not(:disabled),
|
||||||
|
[type=button]:not(:disabled),
|
||||||
|
[type=reset]:not(:disabled),
|
||||||
|
[type=submit]:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
legend {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
legend + * {
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper,
|
||||||
|
::-webkit-datetime-edit-text,
|
||||||
|
::-webkit-datetime-edit-minute,
|
||||||
|
::-webkit-datetime-edit-hour-field,
|
||||||
|
::-webkit-datetime-edit-day-field,
|
||||||
|
::-webkit-datetime-edit-month-field,
|
||||||
|
::-webkit-datetime-edit-year-field {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-inner-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=search] {
|
||||||
|
outline-offset: -2px;
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rtl:raw:
|
||||||
|
[type="tel"],
|
||||||
|
[type="url"],
|
||||||
|
[type="email"],
|
||||||
|
[type="number"] {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
::file-selector-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
1
api/api/static/css/bootstrap-reboot.css.map
Normal file
7
api/api/static/css/bootstrap-reboot.min.css
vendored
Normal file
1
api/api/static/css/bootstrap-reboot.min.css.map
Normal file
485
api/api/static/css/bootstrap-reboot.rtl.css
vendored
Normal file
@ -0,0 +1,485 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.2.3 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2022 The Bootstrap Authors
|
||||||
|
* Copyright 2011-2022 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
*/
|
||||||
|
:root {
|
||||||
|
--bs-blue: #0d6efd;
|
||||||
|
--bs-indigo: #6610f2;
|
||||||
|
--bs-purple: #6f42c1;
|
||||||
|
--bs-pink: #d63384;
|
||||||
|
--bs-red: #dc3545;
|
||||||
|
--bs-orange: #fd7e14;
|
||||||
|
--bs-yellow: #ffc107;
|
||||||
|
--bs-green: #198754;
|
||||||
|
--bs-teal: #20c997;
|
||||||
|
--bs-cyan: #0dcaf0;
|
||||||
|
--bs-black: #000;
|
||||||
|
--bs-white: #fff;
|
||||||
|
--bs-gray: #6c757d;
|
||||||
|
--bs-gray-dark: #343a40;
|
||||||
|
--bs-gray-100: #f8f9fa;
|
||||||
|
--bs-gray-200: #e9ecef;
|
||||||
|
--bs-gray-300: #dee2e6;
|
||||||
|
--bs-gray-400: #ced4da;
|
||||||
|
--bs-gray-500: #adb5bd;
|
||||||
|
--bs-gray-600: #6c757d;
|
||||||
|
--bs-gray-700: #495057;
|
||||||
|
--bs-gray-800: #343a40;
|
||||||
|
--bs-gray-900: #212529;
|
||||||
|
--bs-primary: #0d6efd;
|
||||||
|
--bs-secondary: #6c757d;
|
||||||
|
--bs-success: #198754;
|
||||||
|
--bs-info: #0dcaf0;
|
||||||
|
--bs-warning: #ffc107;
|
||||||
|
--bs-danger: #dc3545;
|
||||||
|
--bs-light: #f8f9fa;
|
||||||
|
--bs-dark: #212529;
|
||||||
|
--bs-primary-rgb: 13, 110, 253;
|
||||||
|
--bs-secondary-rgb: 108, 117, 125;
|
||||||
|
--bs-success-rgb: 25, 135, 84;
|
||||||
|
--bs-info-rgb: 13, 202, 240;
|
||||||
|
--bs-warning-rgb: 255, 193, 7;
|
||||||
|
--bs-danger-rgb: 220, 53, 69;
|
||||||
|
--bs-light-rgb: 248, 249, 250;
|
||||||
|
--bs-dark-rgb: 33, 37, 41;
|
||||||
|
--bs-white-rgb: 255, 255, 255;
|
||||||
|
--bs-black-rgb: 0, 0, 0;
|
||||||
|
--bs-body-color-rgb: 33, 37, 41;
|
||||||
|
--bs-body-bg-rgb: 255, 255, 255;
|
||||||
|
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||||
|
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||||
|
--bs-body-font-size: 1rem;
|
||||||
|
--bs-body-font-weight: 400;
|
||||||
|
--bs-body-line-height: 1.5;
|
||||||
|
--bs-body-color: #212529;
|
||||||
|
--bs-body-bg: #fff;
|
||||||
|
--bs-border-width: 1px;
|
||||||
|
--bs-border-style: solid;
|
||||||
|
--bs-border-color: #dee2e6;
|
||||||
|
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-border-radius: 0.375rem;
|
||||||
|
--bs-border-radius-sm: 0.25rem;
|
||||||
|
--bs-border-radius-lg: 0.5rem;
|
||||||
|
--bs-border-radius-xl: 1rem;
|
||||||
|
--bs-border-radius-2xl: 2rem;
|
||||||
|
--bs-border-radius-pill: 50rem;
|
||||||
|
--bs-link-color: #0d6efd;
|
||||||
|
--bs-link-hover-color: #0a58ca;
|
||||||
|
--bs-code-color: #d63384;
|
||||||
|
--bs-highlight-bg: #fff3cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
:root {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--bs-body-font-family);
|
||||||
|
font-size: var(--bs-body-font-size);
|
||||||
|
font-weight: var(--bs-body-font-weight);
|
||||||
|
line-height: var(--bs-body-line-height);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
text-align: var(--bs-body-text-align);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: inherit;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6, h5, h4, h3, h2, h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h3 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: help;
|
||||||
|
-webkit-text-decoration-skip-ink: none;
|
||||||
|
text-decoration-skip-ink: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
address {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-right: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
dl {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ul ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
padding: 0.1875em;
|
||||||
|
background-color: var(--bs-highlight-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.75em;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--bs-link-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: var(--bs-link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: var(--bs-font-monospace);
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-code-color);
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
a > code {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 0.1875rem 0.375rem;
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-body-bg);
|
||||||
|
background-color: var(--bs-body-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
kbd kbd {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
caption-side: bottom;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
color: #6c757d;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus:not(:focus-visible) {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
optgroup,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
select:disabled {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
button:not(:disabled),
|
||||||
|
[type=button]:not(:disabled),
|
||||||
|
[type=reset]:not(:disabled),
|
||||||
|
[type=submit]:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
float: right;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
legend {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
legend + * {
|
||||||
|
clear: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper,
|
||||||
|
::-webkit-datetime-edit-text,
|
||||||
|
::-webkit-datetime-edit-minute,
|
||||||
|
::-webkit-datetime-edit-hour-field,
|
||||||
|
::-webkit-datetime-edit-day-field,
|
||||||
|
::-webkit-datetime-edit-month-field,
|
||||||
|
::-webkit-datetime-edit-year-field {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-inner-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=search] {
|
||||||
|
outline-offset: -2px;
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="tel"],
|
||||||
|
[type="url"],
|
||||||
|
[type="email"],
|
||||||
|
[type="number"] {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
::file-selector-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
1
api/api/static/css/bootstrap-reboot.rtl.css.map
Normal file
7
api/api/static/css/bootstrap-reboot.rtl.min.css
vendored
Normal file
1
api/api/static/css/bootstrap-reboot.rtl.min.css.map
Normal file
4266
api/api/static/css/bootstrap-utilities.css
vendored
Normal file
1
api/api/static/css/bootstrap-utilities.css.map
Normal file
7
api/api/static/css/bootstrap-utilities.min.css
vendored
Normal file
1
api/api/static/css/bootstrap-utilities.min.css.map
Normal file
4257
api/api/static/css/bootstrap-utilities.rtl.css
vendored
Normal file
1
api/api/static/css/bootstrap-utilities.rtl.css.map
Normal file
7
api/api/static/css/bootstrap-utilities.rtl.min.css
vendored
Normal file
1
api/api/static/css/bootstrap-utilities.rtl.min.css.map
Normal file
10878
api/api/static/css/bootstrap.css
vendored
Normal file
1
api/api/static/css/bootstrap.css.map
Normal file
7
api/api/static/css/bootstrap.min.css
vendored
Normal file
1
api/api/static/css/bootstrap.min.css.map
Normal file
10842
api/api/static/css/bootstrap.rtl.css
vendored
Normal file
1
api/api/static/css/bootstrap.rtl.css.map
Normal file
7
api/api/static/css/bootstrap.rtl.min.css
vendored
Normal file
1
api/api/static/css/bootstrap.rtl.min.css.map
Normal file
10919
api/api/static/css/styles.css
Normal file
7075
api/api/static/js/bootstrap.bundle.js
vendored
Normal file
1
api/api/static/js/bootstrap.bundle.js.map
Normal file
7
api/api/static/js/bootstrap.bundle.min.js
vendored
Normal file
1
api/api/static/js/bootstrap.bundle.min.js.map
Normal file
5202
api/api/static/js/bootstrap.esm.js
vendored
Normal file
1
api/api/static/js/bootstrap.esm.js.map
Normal file
7
api/api/static/js/bootstrap.esm.min.js
vendored
Normal file
1
api/api/static/js/bootstrap.esm.min.js.map
Normal file
5249
api/api/static/js/bootstrap.js
vendored
Normal file
1
api/api/static/js/bootstrap.js.map
Normal file
7
api/api/static/js/bootstrap.min.js
vendored
Normal file
1
api/api/static/js/bootstrap.min.js.map
Normal file
7
api/api/static/js/scripts.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*!
|
||||||
|
* Start Bootstrap - Landing Page v6.0.6 (https://startbootstrap.com/theme/landing-page)
|
||||||
|
* Copyright 2013-2023 Start Bootstrap
|
||||||
|
* Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-landing-page/blob/master/LICENSE)
|
||||||
|
*/
|
||||||
|
// This file is intentionally blank
|
||||||
|
// Use this file to add JavaScript to your project
|
14
api/api/templates/bills/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<b>Bills!</b>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{% url 'bills:create' %}">Create Bill</a></li>
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
|
{% if bills_list %}
|
||||||
|
<ul>
|
||||||
|
{% for bill in bills_list %}
|
||||||
|
<li><a href="{% url 'bills:detail' bill.id %}"> {{ bill.name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p>No bills are available.</p>
|
||||||
|
{% endif %}
|
100
api/api/templates/index.html
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Bootstrap demo</title>
|
||||||
|
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="p-3 text-bg-dark">
|
||||||
|
<div class="container">
|
||||||
|
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
|
||||||
|
<a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
|
||||||
|
<svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap"><use xlink:href="#bootstrap"></use></svg>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
|
||||||
|
<li><a href="#" class="nav-link px-2 text-secondary">Home</a></li>
|
||||||
|
<li><a href="#" class="nav-link px-2 text-white">Features</a></li>
|
||||||
|
<li><a href="#" class="nav-link px-2 text-white">Pricing</a></li>
|
||||||
|
<li><a href="#" class="nav-link px-2 text-white">FAQs</a></li>
|
||||||
|
<li><a href="#" class="nav-link px-2 text-white">About</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search">
|
||||||
|
<input type="search" class="form-control form-control-dark text-bg-dark" placeholder="Search..." aria-label="Search">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="text-end">
|
||||||
|
<button type="button" class="btn btn-outline-light me-2">Login</button>
|
||||||
|
<button type="button" class="btn btn-warning">Sign-up</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<script src="{% static 'js/bootstrap.bundle.min.js' %}" crossorigin="anonymous"></script>
|
||||||
|
<!-- end header -->
|
||||||
|
<div class="col-lg-8 mx-auto p-4 py-md-5">
|
||||||
|
<!-- main -->
|
||||||
|
<main>
|
||||||
|
<h1>Bills</h1>
|
||||||
|
<p class="fs-5 col-md-8">Quickly and easily start adding your bills and service costs to get a better understanding of your operational expenses.</p>
|
||||||
|
|
||||||
|
<div class="mb-5">
|
||||||
|
<a href="#" class="btn btn-primary btn-lg px-4">Sign-up!</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="col-3 col-md-2 mb-5">
|
||||||
|
|
||||||
|
<div class="row g-5">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h2>Starter projects</h2>
|
||||||
|
<p>Ready to beyond the starter template? Check out these open source projects that you can quickly duplicate to a new GitHub repository.</p>
|
||||||
|
<ul class="icon-list ps-0">
|
||||||
|
<li class="d-flex align-items-start mb-1"><a href="https://github.com/twbs/bootstrap-npm-starter" rel="noopener" target="_blank">Bootstrap npm starter</a></li>
|
||||||
|
<li class="text-muted d-flex align-items-start mb-1">Bootstrap Parcel starter (coming soon!)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h2>Guides</h2>
|
||||||
|
<p>Read more detailed instructions and documentation on using or contributing to Bootstrap.</p>
|
||||||
|
<ul class="icon-list ps-0">
|
||||||
|
<li class="d-flex align-items-start mb-1"><a href="../getting-started/introduction/">Bootstrap quick start guide</a></li>
|
||||||
|
<li class="d-flex align-items-start mb-1"><a href="../getting-started/webpack/">Bootstrap Webpack guide</a></li>
|
||||||
|
<li class="d-flex align-items-start mb-1"><a href="../getting-started/parcel/">Bootstrap Parcel guide</a></li>
|
||||||
|
<li class="d-flex align-items-start mb-1"><a href="../getting-started/vite/">Bootstrap Vite guide</a></li>
|
||||||
|
<li class="d-flex align-items-start mb-1"><a href="../getting-started/contribute/">Contributing to Bootstrap</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<!-- end main -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- footer -->
|
||||||
|
<div class="container">
|
||||||
|
<footer class="d-flex flex-wrap justify-content-between align-items-center py-3 my-4 border-top">
|
||||||
|
<p class="col-md-4 mb-0 text-muted">© 2022 Automated Bytes Inc.</p>
|
||||||
|
|
||||||
|
<a href="/" class="col-md-4 d-flex align-items-center justify-content-center mb-3 mb-md-0 me-md-auto link-dark text-decoration-none">
|
||||||
|
<svg class="bi me-2" width="40" height="32"><use xlink:href="#bootstrap"></use></svg>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ul class="nav col-md-4 justify-content-end">
|
||||||
|
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">Home</a></li>
|
||||||
|
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">Features</a></li>
|
||||||
|
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">Pricing</a></li>
|
||||||
|
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">FAQs</a></li>
|
||||||
|
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">About</a></li>
|
||||||
|
</ul>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -13,7 +13,7 @@ Including another URLconf
|
|||||||
1. Import the include() function: from django.urls import include, path
|
1. Import the include() function: from django.urls import include, path
|
||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
"""
|
"""
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
@ -24,10 +24,6 @@ urlpatterns = [
|
|||||||
# url(r'^$', view=TemplateView.as_view(template_name='bills/home.html')),
|
# url(r'^$', view=TemplateView.as_view(template_name='bills/home.html')),
|
||||||
# url(r'^$', view=TemplateView.as_view(template_name='main_api/home.html')),
|
# url(r'^$', view=TemplateView.as_view(template_name='main_api/home.html')),
|
||||||
path('', views.index, name='index'),
|
path('', views.index, name='index'),
|
||||||
# url(r'^jet/', include('jet.urls', 'jet')), # Django JET URLS
|
|
||||||
# url(r'^jet/dashboard/', include('jet.dashboard.urls', 'jet-dashboard')),
|
|
||||||
path('jet/', include('jet.urls', 'jet')),
|
|
||||||
path('jet/dashboard', include('jet.dashboard.urls', 'jet-dashboard')),
|
|
||||||
path('bills/', include('bills.urls')),
|
path('bills/', include('bills.urls')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
]
|
]
|
||||||
|
@ -1,4 +1,16 @@
|
|||||||
|
from django.contrib.auth import logout
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
from django.template import loader
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
return HttpResponse("Hi there! This is the API index")
|
# return HttpResponse("Hi there! This is the API index")
|
||||||
|
template = loader.get_template('index.html')
|
||||||
|
context = {}
|
||||||
|
return HttpResponse(template.render(context, request))
|
||||||
|
|
||||||
|
|
||||||
|
def logout(request):
|
||||||
|
logout(request)
|
||||||
|
redirect(index)
|
||||||
|
@ -1,32 +1,21 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
import pendulum
|
|
||||||
|
|
||||||
from .models import Bill
|
from .models import Bill, Organization
|
||||||
from.tasks import get_overdue_bills, get_upcoming_bills
|
|
||||||
|
|
||||||
@admin.action(description='Duplicate Bill')
|
|
||||||
def duplicate(modeladmin, request, queryset):
|
|
||||||
for object in queryset:
|
|
||||||
object.id = None
|
|
||||||
# object.name = object.name+'-duplicate' ## This was temporary while I figured out how to massage the date
|
|
||||||
# print(object.due)
|
|
||||||
new_date = (pendulum.parse(str(object.due), exact=True)).add(months=1)
|
|
||||||
# print(new_date)
|
|
||||||
object.due = new_date
|
|
||||||
object.save()
|
|
||||||
|
|
||||||
@admin.action(description='Duplicate Older Bill')
|
|
||||||
def duplicate_old(modeladmin, request, queryset):
|
|
||||||
for object in queryset:
|
|
||||||
object.id = None
|
|
||||||
new_date = (pendulum.parse(str(object.due), exact=True)).subtract(months=1)
|
|
||||||
object.due = new_date
|
|
||||||
object.save()
|
|
||||||
|
|
||||||
@admin.register(Bill)
|
@admin.register(Bill)
|
||||||
class BillAdmin(admin.ModelAdmin):
|
class BillAdmin(admin.ModelAdmin):
|
||||||
list_display = ['name', 'due', 'amount']
|
list_display = ['name', 'due', 'amount']
|
||||||
list_filter = ('name', 'type', 'is_paid', 'is_overdue', 'is_missed')
|
list_filter = ('name', 'type', 'is_paid', 'is_overdue', 'is_missed')
|
||||||
search_fields = ['name', 'type', 'amount'
|
search_fields = ['name', 'type', 'amount']
|
||||||
]
|
|
||||||
actions = [duplicate, duplicate_old, get_overdue_bills, get_upcoming_bills]
|
|
||||||
|
@admin.register(Organization)
|
||||||
|
class Organization(admin.ModelAdmin):
|
||||||
|
list_display = ['name']
|
||||||
|
search_fields = ['name']
|
||||||
|
list_filter = ('name',)
|
||||||
|
ordering = ['name']
|
||||||
|
# prepopulated_fields = {'slug': ('name',)}
|
||||||
|
# raw_id_fields = ('members',)
|
||||||
|
# readonly_fields = ('members',)
|
||||||
|
24
api/bills/migrations/0005_organization.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 4.1.7 on 2023-04-08 15:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bills', '0004_auto_20211203_1549'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Organization',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=64, verbose_name='Name')),
|
||||||
|
('address', models.CharField(max_length=64, verbose_name='Address')),
|
||||||
|
('phone', models.CharField(max_length=64, verbose_name='Phone')),
|
||||||
|
('email', models.CharField(max_length=64, verbose_name='Email')),
|
||||||
|
('website', models.CharField(max_length=64, verbose_name='Website')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
19
api/bills/migrations/0006_bill_organization.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.1.7 on 2023-04-08 15:51
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bills', '0005_organization'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='bill',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='bills.organization', verbose_name='Organization'),
|
||||||
|
),
|
||||||
|
]
|
@ -1,6 +1,8 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
|
|
||||||
class Bill(models.Model):
|
class Bill(models.Model):
|
||||||
name = models.CharField(max_length=64, verbose_name='Name')
|
name = models.CharField(max_length=64, verbose_name='Name')
|
||||||
type = models.CharField(max_length=64, verbose_name="Type")
|
type = models.CharField(max_length=64, verbose_name="Type")
|
||||||
@ -9,10 +11,26 @@ class Bill(models.Model):
|
|||||||
is_paid = models.BooleanField(default=False, verbose_name="Paid")
|
is_paid = models.BooleanField(default=False, verbose_name="Paid")
|
||||||
paid_date = models.DateField('Paid Date', null=True, blank=True)
|
paid_date = models.DateField('Paid Date', null=True, blank=True)
|
||||||
is_overdue = models.BooleanField(default=False, verbose_name="Overdue")
|
is_overdue = models.BooleanField(default=False, verbose_name="Overdue")
|
||||||
is_missed = models.BooleanField(default=False, verbose_name='Missed Payment')
|
is_missed = models.BooleanField(
|
||||||
|
default=False, verbose_name='Missed Payment')
|
||||||
|
organization = models.ForeignKey(
|
||||||
|
'Organization', on_delete=models.CASCADE, verbose_name='Organization', null=True, blank=True)
|
||||||
|
|
||||||
def overdue(self):
|
def overdue(self):
|
||||||
return self.is_overdue
|
return self.is_overdue
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ("%s - %s" % (self.name, self.due))
|
return ("%s - %s" % (self.name, self.due))
|
||||||
|
|
||||||
|
# Create organization model
|
||||||
|
|
||||||
|
|
||||||
|
class Organization(models.Model):
|
||||||
|
name = models.CharField(max_length=64, verbose_name='Name')
|
||||||
|
address = models.CharField(max_length=64, verbose_name='Address')
|
||||||
|
phone = models.CharField(max_length=64, verbose_name='Phone')
|
||||||
|
email = models.CharField(max_length=64, verbose_name='Email')
|
||||||
|
website = models.CharField(max_length=64, verbose_name='Website')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return ("%s - %s" % (self.name, self.phone))
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
### Celery Tasks!
|
|
||||||
|
|
||||||
from django.core.checks import messages
|
|
||||||
import pendulum
|
|
||||||
from .models import Bill
|
|
||||||
|
|
||||||
from celery import shared_task
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
def get_overdue_bills(modeladmin, request, queryset):
|
|
||||||
|
|
||||||
try:
|
|
||||||
bills_list = []
|
|
||||||
bill_request = Bill.objects.filter(is_overdue=True)
|
|
||||||
print(bill_request)
|
|
||||||
for bill in bill_request:
|
|
||||||
# print(bill.id)
|
|
||||||
bills_list.append(bill.id)
|
|
||||||
except Bill.DoesNotExist:
|
|
||||||
bills_list = "There are no bills that are overdue!"
|
|
||||||
print("function complete")
|
|
||||||
print(bills_list)
|
|
||||||
return('This is a test')
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
def get_upcoming_bills(modeladmin, request, queryset):
|
|
||||||
today = pendulum.today().add(days=7)
|
|
||||||
print(today)
|
|
||||||
try:
|
|
||||||
bill_request = Bill.objects.filter(is_paid=False)
|
|
||||||
print(bill_request)
|
|
||||||
except Bill.DoesNotExist:
|
|
||||||
message = 'There are no bill coming due soon that are unpaid.'
|
|
||||||
print(message)
|
|
||||||
return(message)
|
|
10
api/bills/templates/bills/create.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<form action="{% url 'bills:create' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<fieldset>
|
||||||
|
<legend><h1>Create a New Bill</h1></legend>
|
||||||
|
<label for="name">Bill Name:</label>
|
||||||
|
<input type="text" name="name" id="name" value="">
|
||||||
|
<br>
|
||||||
|
</fieldset>
|
||||||
|
<input type="submit" value="Create Bill">
|
||||||
|
</form>
|
17
api/bills/templates/bills/detail.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<h1> {{bill.name }}</h1>
|
||||||
|
|
||||||
|
{{ bill.due}}
|
||||||
|
<br>
|
||||||
|
{{ bill.type }}
|
||||||
|
<br>
|
||||||
|
{{ bill.amount}}
|
||||||
|
<br>
|
||||||
|
{{ bill.is_paid}}
|
||||||
|
<br>
|
||||||
|
{{ bill.paid_date}}
|
||||||
|
<br>
|
||||||
|
{{ bill.is_overdue}}
|
||||||
|
<br>
|
||||||
|
{{ bill.is_missed}}
|
||||||
|
<br>
|
||||||
|
{{ bill.organization.name}}
|
14
api/bills/templates/bills/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<b>Bills!</b>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{% url 'bills:create' %}">Create Bill</a></li>
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
|
{% if bills_list %}
|
||||||
|
<ul>
|
||||||
|
{% for bill in bills_list %}
|
||||||
|
<li><a href="{% url 'bills:detail' bill.id %}"> {{ bill.name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p>No bills are available.</p>
|
||||||
|
{% endif %}
|
@ -1,3 +1,9 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
from .models import Bill, Organization
|
||||||
|
|
||||||
|
|
||||||
|
class BillModelTests(TestCase):
|
||||||
|
def test_has_due_date_no_amount(self):
|
||||||
|
pass
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'bills'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
path('', views.index, name='index'),
|
path('', views.index, name='index'),
|
||||||
path('admin/', admin.site.urls),
|
path('<int:bill_id>/', views.detail, name='detail'),
|
||||||
|
path('create', views.create, name='create')
|
||||||
|
# path('admin/', admin.site.urls),
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,32 @@
|
|||||||
from django.shortcuts import render
|
# from django.shortcuts import render
|
||||||
from django.http import HttpResponse
|
from django.contrib.auth import authenticate, login
|
||||||
|
from django.http import HttpResponse, Http404
|
||||||
|
from django.template import loader
|
||||||
|
from django.shortcuts import render, get_object_or_404, get_list_or_404, redirect
|
||||||
|
|
||||||
|
from .models import Bill
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
return HttpResponse("Let there be bills!")
|
if request.user.is_authenticated:
|
||||||
# Create your views here.
|
bills_list = Bill.objects.order_by('-due')
|
||||||
|
# bills_list = get_list_or_404(Bill)
|
||||||
|
template = loader.get_template('bills/index.html')
|
||||||
|
context = {'bills_list': bills_list, }
|
||||||
|
print(bills_list)
|
||||||
|
return HttpResponse(template.render(context, request))
|
||||||
|
else:
|
||||||
|
# return redirect(api.index)
|
||||||
|
return render(request, "index.html")
|
||||||
|
# return render(request, 'bills/index.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
def detail(request, bill_id):
|
||||||
|
bill = get_object_or_404(Bill, pk=bill_id)
|
||||||
|
return render(request, 'bills/detail.html', {'bill': bill})
|
||||||
|
|
||||||
|
# view to retriev a users bills
|
||||||
|
|
||||||
|
|
||||||
|
def create(request):
|
||||||
|
return render(request, 'bills/create.html')
|
||||||
|