Compare commits

...

12 Commits

Author SHA1 Message Date
ahosking
24cdebe960 Add templates! 2023-05-06 11:23:21 -04:00
ahosking
1edd0892af Add Static Assets 2023-05-06 11:22:42 -04:00
ahosking
d56b5725a0 Start Accounts application 2023-05-06 11:22:11 -04:00
ahosking
3b05d0983a Add Debugger config for VS code 2023-04-17 09:35:22 -04:00
ahosking
9d4ce6c413 Switch to Soft-UI admin from Jet
This also adds a bunch of templates and URLS
Start to add routes for user-facing bills pages

Add an Index page that may start to display some great work
2023-04-17 09:35:06 -04:00
ahosking
d0d25ef499 Add Theme Updates 2023-04-17 09:32:36 -04:00
ahosking
832e39fcd7 Chore: Update URLS
This is required to run under python3.11 and Django4.1
2023-03-25 11:52:25 -04:00
ahosking
1dd1188bdc Chore: Add localhost to allowed hosts 2023-03-25 11:51:58 -04:00
ahosking
cd2e4fca26 Chore: Add Developer Documentation
Add Instructions for getting started from scratch
Add command to create/run a database for dev/testing
Specify Python version for pyenv
Update requirements for nev versions of packages
2023-03-25 11:51:36 -04:00
ahosking
9930bec65a Add Operating system requirements 2023-01-19 15:23:12 -05:00
6ba83ac2d3 Add better docs and allowed hosts 2022-05-31 23:07:03 -04:00
Alexander Hosking
ebb48c1a42 Adding functions for bills 2021-12-25 22:04:59 -05:00
82 changed files with 68392 additions and 32 deletions

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.9.0

20
.vscode/launch.json vendored Normal file
View 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
}
]
}

View File

@ -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"
} }

View File

@ -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
View File

3
api/accounts/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
api/accounts/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'

View File

3
api/accounts/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
api/accounts/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
api/accounts/views.py Normal file
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

@ -4,6 +4,8 @@ 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):

View File

@ -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/'
@ -140,9 +145,9 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
JET_THEMES = [ JET_THEMES = [
{ {
'theme': 'default', # theme folder name 'theme': 'default', # theme folder name
'color': '#47bac1', # color of the theme's button in user menu 'color': '#47bac1', # color of the theme's button in user menu
'title': 'Default' # theme title 'title': 'Default' # theme title
}, },
{ {
'theme': 'green', 'theme': 'green',
@ -172,5 +177,11 @@ JET_THEMES = [
] ]
# JET_INDEX_DASHBOARD = 'jet.dashboard.dashboard.DefaultIndexDashboard' # JET_INDEX_DASHBOARD = 'jet.dashboard.dashboard.DefaultIndexDashboard'
JET_APP_INDEX_DASHBOARD = 'dashboard.CustomIndexDashboard' # JET_APP_INDEX_DASHBOARD = 'dashboard.CustomIndexDashboard'
# JET_INDEX_DASHBOARD = 'dashboard.CustomIndexDashboard' JET_INDEX_DASHBOARD = 'dashboard.CustomIndexDashboard'
STATICFILES_DIRS = [
BASE_DIR / "api/static",
("assets", BASE_DIR / "api/static/assets"),
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

4124
api/api/static/css/bootstrap-grid.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4123
api/api/static/css/bootstrap-grid.rtl.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

488
api/api/static/css/bootstrap-reboot.css vendored Normal file
View 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 */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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 */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4266
api/api/static/css/bootstrap-utilities.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

10878
api/api/static/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

7
api/api/static/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

10842
api/api/static/css/bootstrap.rtl.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

10919
api/api/static/css/styles.css Normal file

File diff suppressed because it is too large Load Diff

7075
api/api/static/js/bootstrap.bundle.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

5202
api/api/static/js/bootstrap.esm.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

5249
api/api/static/js/bootstrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

7
api/api/static/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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

View 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 %}

View 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>

View File

@ -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),
] ]

View File

@ -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)

View File

@ -1,9 +1,21 @@
from django.contrib import admin from django.contrib import admin
from .models import Bill from .models import Bill, Organization
@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']
@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',)

View 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')),
],
),
]

View 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'),
),
]

View File

@ -1,18 +1,36 @@
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")
due = models.DateField(verbose_name="Due Date") due = models.DateField(verbose_name="Due Date")
amount = models.FloatField(default='00.00') amount = models.FloatField(default='00.00')
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))

View 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>

View 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}}

View 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 %}

View File

@ -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

View File

@ -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),
] ]

View File

@ -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')

Binary file not shown.