Merge branch 'master' into prod

This commit is contained in:
Aaron Gutierrez
2019-01-27 18:53:43 +00:00
32 changed files with 675 additions and 493 deletions

View File

@@ -1,5 +1,6 @@
from django.contrib import admin from django.contrib import admin
from models import Committee
from committee.models import Committee
# Register your models here. # Register your models here.
admin.site.register(Committee) admin.site.register(Committee)

View File

@@ -5,7 +5,7 @@ from django.db import models
class Committee(models.Model): class Committee(models.Model):
name = models.CharField(max_length=100, unique=True) name = models.CharField(max_length=100, unique=True)
chair = models.ForeignKey(User, null=True) chair = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
def __str__(self): def __str__(self):
return self.name return self.name

View File

@@ -1,10 +1,11 @@
from django.conf.urls import url from django.urls import path
from . import views
from committee import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.edit, name='edit'), path('', views.edit, name='edit'),
url(r'^update/(?P<committee>\d+)/$', views.update, name='update'), path('update/<int:committee>/', views.update, name='update'),
url(r'^fincom/$', views.add_to_fincom, name='add_to_fincom'), path('fincom/', views.add_to_fincom, name='add_to_fincom'),
url(r'^fincom/delete$', views.remove_fincom, name='remove_fincom'), path('fincom/delete', views.remove_fincom, name='remove_fincom'),
url(r'^new$', views.new_committee, name='new_committee'), path('new', views.new_committee, name='new_committee'),
] ]

View File

@@ -2,7 +2,8 @@ from django.shortcuts import HttpResponse, HttpResponseRedirect
from django.template import loader from django.template import loader
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from models import Committee
from committee.models import Committee
@login_required @login_required
def edit(request): def edit(request):
@@ -68,4 +69,4 @@ def remove_fincom(request):
user.groups.remove(Group.objects.filter(name='Fincom')[0]) user.groups.remove(Group.objects.filter(name='Fincom')[0])
user.save() user.save()
return HttpResponseRedirect('/committees') return HttpResponseRedirect('/committees')

View File

@@ -28,7 +28,7 @@ DEBUG = False
ALLOWED_HOSTS = [ ALLOWED_HOSTS = [
'localhost', 'localhost',
'dtd-fincom.herokuapp.com', 'fincom.frat.tech',
'fincom.delt.space', 'fincom.delt.space',
] ]
@@ -98,10 +98,10 @@ WSGI_APPLICATION = 'fincom.wsgi.application'
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql', 'ENGINE': 'django.db.backends.postgresql',
'NAME': 'dbu9ujb6cnhc3n', 'NAME': 'fincom',
'USER': 'dexgqmkgoabqyd', 'USER': 'fincom',
'PASSWORD': os.environ['DBPASSWD'], 'PASSWORD': 'fincom',
'HOST': 'ec2-50-19-89-124.compute-1.amazonaws.com', 'HOST': '127.0.0.1',
'PORT': '5432', 'PORT': '5432',
} }
} }
@@ -110,8 +110,8 @@ DATABASES = {
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_ACCESS_KEY_ID=os.environ['AWS_ACCESS_KEY_ID'] AWS_ACCESS_KEY_ID=os.environ['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY=os.environ['AWS_SECRET_ACCESS_KEY'] AWS_SECRET_ACCESS_KEY=os.environ['AWS_SECRET_ACCESS_KEY']
AWS_AUTO_CREATE_BUCKET=True
AWS_STORAGE_BUCKET_NAME='fincom' AWS_STORAGE_BUCKET_NAME='fincom'
AWS_DEFAULT_ACL=None
AWS_S3_FILE_OVERWRITE=False AWS_S3_FILE_OVERWRITE=False
@@ -139,7 +139,7 @@ AUTH_PASSWORD_VALIDATORS = [
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = os.environ['SOCIAL_AUTH_GOOGLE_OAUTH2_KEY'] SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = os.environ['SOCIAL_AUTH_GOOGLE_OAUTH2_KEY']
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = os.environ['SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET'] SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = os.environ['SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET']
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = ['andrew.cmu.edu', 'alumni.cmu.edu'] SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = ['andrew.cmu.edu', 'alumni.cmu.edu']
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'hd': 'andrew.cmu.edu' } SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'hd': '*' }
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/items/' SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/items/'
LOGIN_URL = '/login/google-oauth2/' LOGIN_URL = '/login/google-oauth2/'

View File

@@ -13,16 +13,16 @@ Including another URLconf
1. Import the include() function: from django.conf.urls import url, include 1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
""" """
from django.conf.urls import include, url from django.urls import include, path
from django.contrib import admin from django.contrib import admin
from . import views
from fincom import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.index, name='index'), path('', views.index, name='index'),
url(r'^logout$', views.user_logout, name='logout'), path('logout', views.user_logout, name='logout'),
url(r'^items/', include('items.urls')), path('items/', include('items.urls')),
url(r'^committees/', include('committee.urls')), path('committees/', include('committee.urls')),
url('.well-known/acme-challenge/tVQ35BIrm7ybiASBxRMHG2CAO44x-I2BVMqcrv_yJ2k', views.encrypt, name='encrypt'), path('', include('social_django.urls', namespace='social')),
url('', include('social_django.urls', namespace='social')), path('admin/', admin.site.urls),
url(r'^admin/', admin.site.urls),
] ]

View File

@@ -3,15 +3,12 @@ from django.contrib.auth import logout
from django.template import loader from django.template import loader
def index(request): def index(request):
if request.user.is_authenticated(): if request.user.is_authenticated:
return HttpResponseRedirect('/items') return HttpResponseRedirect('/items')
template = loader.get_template('fincom/index.html') template = loader.get_template('fincom/index.html')
return HttpResponse(template.render({}, request)) return HttpResponse(template.render({}, request))
def encrypt(request):
return HttpResponse('tVQ35BIrm7ybiASBxRMHG2CAO44x-I2BVMqcrv_yJ2k.g4Ct3egntTJZl1LOzJH8v9Ri24BQ7blYjcbzPucJVE4', request)
def user_logout(request): def user_logout(request):
logout(request) logout(request)

View File

@@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from models import Item from items.models import Item
# Register your models here. # Register your models here.
admin.site.register(Item) admin.site.register(Item)

View File

@@ -1,15 +1,62 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from __future__ import with_statement
from datetime import datetime, date
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.files.base import ContentFile
from django.core.mail import send_mail from django.core.mail import send_mail
from django.template import loader from django.template import loader
from io import BytesIO
from PIL import Image
from storages.backends.s3boto3 import S3Boto3Storage from storages.backends.s3boto3 import S3Boto3Storage
from stdimage.models import StdImageField from stdimage.models import StdImageField
from datetime import datetime, date from stdimage.utils import render_variations
from committee.models import Committee from committee.models import Committee
from django.db import models from django.db import models
#EXIF key index for the orientation value
ORIENTATION_KEY = 274
ROTATE_VALUES = {
3: Image.ROTATE_180,
6: Image.ROTATE_270,
8: Image.ROTATE_90,
}
# helper to fix "soft" rotated images
def resizeAndRotate(file_name, variations, storage):
rotated = False
with storage.open(file_name) as f:
try:
image = Image.open(f)
except:
return False
else:
with image:
try:
file_format = image.format
exif = image._getexif()
if exif and ORIENTATION_KEY in exif:
orientation = exif[ORIENTATION_KEY]
if orientation in ROTATE_VALUES:
image = image.transpose(ROTATE_VALUES[orientation])
rotated = True
if rotated:
with BytesIO() as file_buffer:
image.save(file_buffer, file_format)
f = ContentFile(file_buffer.getvalue())
storage.delete(file_name)
storage.save(file_name, f)
except:
return True
render_variations(file_name, variations, storage=storage)
return False
class Item(models.Model): class Item(models.Model):
S3 = S3Boto3Storage() S3 = S3Boto3Storage()
@@ -25,21 +72,24 @@ class Item(models.Model):
(REJECTED, 'Rejected'), (REJECTED, 'Rejected'),
) )
desc = models.CharField(max_length=200) desc = models.CharField(max_length=200)
event = models.CharField(max_length=200) event = models.CharField(max_length=200)
committee = models.ForeignKey(Committee) committee = models.ForeignKey(Committee, on_delete=models.DO_NOTHING)
details = models.TextField() details = models.TextField()
cost = models.DecimalField(max_digits=7, decimal_places=2) cost = models.DecimalField(max_digits=7, decimal_places=2)
date_purchased = models.DateField('date purchased') date_purchased = models.DateField('date purchased')
image = StdImageField(upload_to='images/%Y/%m/%d', image = StdImageField(upload_to='images/%Y/%m/%d',
render_variations=resizeAndRotate,
variations={'thumbnail': (600, 600)} variations={'thumbnail': (600, 600)}
) )
created_by = models.ForeignKey(User, related_name='created_by') created_by = models.ForeignKey(User, related_name='created_by', on_delete=models.DO_NOTHING)
approved_by = models.ManyToManyField(User, blank=True, related_name='approved_by') approved_by = models.ManyToManyField(User, blank=True, related_name='approved_by')
date_filed = models.DateTimeField('date filed') date_filed = models.DateTimeField('date filed')
status = models.CharField(max_length=2, choices=STATUS) status = models.CharField(max_length=2, choices=STATUS)
def approved(self): def approved(self):
return self.status == Item.PREAPPROVED return self.status == Item.PREAPPROVED

View File

@@ -1,12 +1,12 @@
from django.conf.urls import url from django.urls import path
from . import views from items import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.list, name='list'), path(r'', views.list, name='list'),
url(r'^(?P<item_id>\d+)/$', views.details, name='details'), path('<int:item_id>/', views.details, name='details'),
url(r'^(?P<item_id>\d+)/approve$', views.approve, name='approve'), path('<int:item_id>/approve', views.approve, name='approve'),
url(r'^(?P<item_id>\d+)/reject$', views.reject, name='reject'), path('<int:item_id>/reject', views.reject, name='reject'),
url(r'^(?P<item_id>\d+)/edit$', views.edit, name='edit'), path('<int:item_id>/edit', views.edit, name='edit'),
url(r'^(?P<item_id>\d+)/delete$', views.delete, name='delete'), path('<int:item_id>/delete', views.delete, name='delete'),
url(r'^new$', views.new_form, name='new_form'), path('new', views.new_form, name='new_form'),
] ]

View File

@@ -4,8 +4,9 @@ from django.utils import timezone
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Q from django.db.models import Q
from models import Item
from committee.models import Committee from committee.models import Committee
from items.models import Item
def isAuthorised(request, item): def isAuthorised(request, item):
return (request.user == item.committee.chair return (request.user == item.committee.chair

View File

@@ -1,29 +1,36 @@
appdirs==1.4.0 appdirs==1.4.0
boto3==1.4.4 boto3==1.9.86
botocore==1.5.7 botocore==1.12.86
Django==1.10.5 certifi==2018.11.29
chardet==3.0.4
defusedxml==0.5.0
Django==2.1.5
django-cleanup==0.4.2 django-cleanup==0.4.2
django-stdimage==2.4.1 django-stdimage==2.4.1
django-storages==1.5.2 django-storages==1.5.2
docutils==0.13.1 docutils==0.14
futures==3.0.5 futures==3.0.5
gunicorn==19.6.0 idna==2.8
jmespath==0.9.1 jmespath==0.9.3
oauthlib==2.0.1 oauthlib==3.0.1
olefile==0.44 olefile==0.44
packaging==16.8 packaging==16.8
Pillow==4.0.0 Pillow==4.0.0
pkg-resources==0.0.0
progressbar2==3.12.0 progressbar2==3.12.0
psycopg2-binary==2.7.5 psycopg2-binary==2.7.5
PyJWT==1.4.2 PyJWT==1.7.1
pyparsing==2.1.10 pyparsing==2.1.10
python-dateutil==2.6.0 python-dateutil==2.7.5
python-openid==2.2.5 python-openid==2.2.5
python-utils==2.0.1 python-utils==2.0.1
requests==2.13.0 python3-openid==3.1.0
requests-oauthlib==0.7.0 pytz==2018.9
s3transfer==0.1.10 requests==2.21.0
six==1.10.0 requests-oauthlib==1.2.0
social-auth-app-django==1.0.1 s3transfer==0.1.13
social-auth-core==1.1.0 six==1.12.0
social-auth-app-django==3.1.0
social-auth-core==3.0.0
urllib3==1.24.1
whitenoise==3.3.0 whitenoise==3.3.0

4
sass/Makefile Normal file
View File

@@ -0,0 +1,4 @@
SRC=$(wildcard *.scss)
all: $(SRC)
sass include.scss ../static/css/site.css

View File

@@ -13,28 +13,30 @@
img { img {
border: 1px solid $midgray; border: 1px solid $midgray;
max-width: 100%; width: 100%;
} }
} }
.actions { .actions {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: $pad-m 0; margin: $pad-m 0;
a { a {
flex: 0 0 auto;
margin-bottom: $pad-m;
margin-right: $pad-m;
text-decoration: none; text-decoration: none;
font-size: 12px;
font-weight: normal;
} }
} }
.details { .details {
color: $text; border: 1px solid $midgray;
border-radius: $pad-s;
font-size: 12px; font-size: 12px;
font-weight: normal;
margin: $pad-m 0; margin: $pad-m 0;
padding: $pad-l; padding: $pad-l;
border-radius: $pad-s;
border: 1px solid $midgray;
span { span {
color: $midgray; color: $midgray;

105
sass/globals.scss Normal file
View File

@@ -0,0 +1,105 @@
$purple: #563d7c;
$gold: #fdd017;
$lightgray: #f8f8f8;
$midgray: #969499;
$darkgray: #4b4a4d;
$text: #252526;
$shadowgray: rgba(0, 0, 0, .8);
$lightshadowgray: rgba(0, 0, 0, 0.2);
// size constants
$pad-s: 2px;
$pad-m: 8px;
$pad-l: 16px;
$pad-xl: 36px;
html,
body {
background-color: $lightgray;
color: $text;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Helvetica,Arial,sans-serif;
height: 100vh;
margin: 0;
padding: 0;
width: 100vw;
}
body {
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.nav {
align-items: center;
box-shadow: 0px 3px 12px $shadowgray;
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: space-between;
flex: 0 0 auto;
height: 50px;
padding: $pad-l;
background-color: $purple;
color: $gold;
z-index: 10;
h3 {
font-weight: normal;
font-size: 16px;
margin: 0;
}
a {
color: $gold;
text-decoration: none;
font-size: 16px;
margin-left: $pad-m;
&:hover {
color: darken($gold, 20%);
text-decoration: initial;
}
}
}
.content {
align-items: center;
display: flex;
flex: 1 1 auto;
flex-direction: column;
position: relative;
overflow-y: auto;
min-height: 1;
}
.container {
background-color: #fff;
box-shadow: 0px 3px 12px $lightshadowgray;
display: flex;
flex: 0 0 auto;
flex-direction: column;
min-height: 1;
}
.btn {
background-color: #fff;
border-radius: $pad-s;
border: 1px solid $midgray;
box-sizing: border-box;
color: $purple;
cursor: pointer;
display: inline-block;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Helvetica,Arial,sans-serif;
font-size: 12px;
font-weight: normal;
padding: $pad-m $pad-l;
text-decoration: none;
vertical-align: middle;
white-space: nowrap;
&:hover {
background-color: lighten($purple, 35%);
}
}

View File

@@ -1,15 +1,17 @@
section { section {
color: $midgray; color: $midgray;
flex: 0 0 auto;
font-weight: lighter; font-weight: lighter;
font-size: 16px; font-size: 16px;
margin-top: 2*$pad-xl; margin-top: $pad-xl;
margin-bottom: -2*$pad-xl + $pad-l; text-align: center;
max-width: 960px;
margin-right: auto;
margin-left: auto;
} }
.items { .items {
display: flex;
flex-direction: column;
flex: 1 0 auto;
a.page { a.page {
display: inline-block; display: inline-block;
margin: $pad-m; margin: $pad-m;
@@ -30,13 +32,33 @@ section {
} }
} }
.pagination {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: $pad-l;
padding: 0 $pad-xl;
width: 100%;
}
.item { .item {
display: flex;
flex-direction: column;
padding: $pad-m $pad-xl; padding: $pad-m $pad-xl;
margin: 0px; margin: 0px;
border-bottom: 1px solid $midgray; border-bottom: 1px solid $midgray;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;
.committeeAndCost {
align-items: center;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.committee { .committee {
color: $midgray; color: $midgray;
font-size: 12px; font-size: 12px;
@@ -49,10 +71,11 @@ section {
} }
.details-row { .details-row {
margin: $pad-m 0; color: $text;
font-size: 16px; font-size: 16px;
font-weight: lighter; font-weight: lighter;
color: $text; line-height: 24px;
margin: $pad-m 0;
padding: 0px; padding: 0px;
b { b {
@@ -103,17 +126,11 @@ section {
} }
} }
@media (min-width: 800px) { .emptyItems {
.item .cost { margin: $pad-xl;
float: right;
}
}
.empty {
margin: $pad-l;
color: $midgray; color: $midgray;
font-weight: lighter; font-size: 12px;
font-size: 16px; font-style: italic;
text-align: center; text-align: center;
} }
@@ -141,4 +158,9 @@ $button-size: 24px;
height: 36; height: 36;
font-size: 28; font-size: 28;
} }
&:hover {
background-color: darken($purple, 10%);
box-shadow: 0 0 12px rgba(0,0,0,.16),0 12px 12px rgba(0,0,0,.32);
}
} }

View File

@@ -14,17 +14,18 @@ body.login {
h3 { h3 {
font-size: 24px; font-size: 24px;
margin-bottom: $pad-m; margin-bottom: 0;
} }
h2 { h2 {
font-size: 28px; font-size: 28px;
margin-top: -$pad-l; margin: 0 auto;
} }
.btn { .btn {
background-color: $gold; background-color: $gold;
color: $purple; color: $purple;
font-size: 16px;
&:hover { &:hover {
background-color: darken($gold, 15%); background-color: darken($gold, 15%);

54
sass/new.scss Normal file
View File

@@ -0,0 +1,54 @@
h1 {
font-size: 18px;
font-weight: lighter;
color: $purple;
margin: $pad-l;
}
hr {
margin: $pad-l;
color: $midgray;
}
.inputRow {
box-sizing: border-box;
margin-bottom: $pad-l;
padding: 0 $pad-xl;
label {
color: $midgray;
font-weight: bold;
font-size: 12px;
margin-left: $pad-s;
}
input,
select,
textarea {
box-sizing: border-box;
border: 1px solid $midgray;
font-size: 16px;
line-height: 24px;
padding: $pad-s;
width: 100%;
}
textarea {
resize: vertical;
}
}
.editActions {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0 $pad-xl;
width: 100%;
}
.notes {
color: $midgray;
font-size: 12px;
margin: $pad-l $pad-xl;
}

View File

@@ -1,178 +1,254 @@
html,
body { body {
color: #252526;
font-family: sans-serif;
background-color: #f8f8f8; background-color: #f8f8f8;
padding-bottom: 0px; color: #252526;
margin: 0px 16px; } font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Helvetica, Arial, sans-serif;
height: 100vh;
margin: 0;
padding: 0;
width: 100vw;
}
body {
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.nav { .nav {
position: fixed; align-items: center;
top: 0; box-shadow: 0px 3px 12px rgba(0, 0, 0, 0.8);
left: 0;
width: 100%;
height: 50px;
box-sizing: border-box; box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: space-between;
flex: 0 0 auto;
height: 50px;
padding: 16px; padding: 16px;
background-color: #563d7c; background-color: #563d7c;
color: #fdd017; color: #fdd017;
box-shadow: 0px 3px 12px rgba(0, 0, 0, 0.8); z-index: 10;
z-index: 10; } }
.nav h3 { .nav h3 {
font-weight: lighter; font-weight: normal;
font-size: 16px; font-size: 16px;
margin: 0; } margin: 0;
.nav a { }
float: right; .nav a {
color: #fdd017; color: #fdd017;
text-decoration: none; text-decoration: none;
font-size: 16px; font-size: 16px;
margin-left: 8px; } margin-left: 8px;
}
.nav a:hover {
color: #ad8b01;
text-decoration: initial;
}
.pull-right { .content {
float: right; align-items: center;
margin: 16px; } display: flex;
flex: 1 1 auto;
flex-direction: column;
position: relative;
overflow-y: auto;
min-height: 1;
}
.container { .container {
margin-bottom: 36; } background-color: #fff;
.container > div { box-shadow: 0px 3px 12px rgba(0, 0, 0, 0.2);
margin-left: auto; display: flex;
margin-right: auto; flex: 0 0 auto;
margin-top: 64px; flex-direction: column;
max-width: 960; min-height: 1;
background-color: #fff; }
border: solid 1px #969499;
box-shadow: 0px 3px 9px rgba(51, 51, 51, 0.8); }
.btn { .btn {
background-color: #fff;
border-radius: 2px; border-radius: 2px;
border: 1px solid #969499; border: 1px solid #969499;
box-sizing: border-box;
color: #563d7c; color: #563d7c;
cursor: pointer;
display: inline-block; display: inline-block;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 12px;
font-weight: normal;
padding: 8px 16px; padding: 8px 16px;
text-decoration: none; text-decoration: none;
vertical-align: middle; vertical-align: middle;
white-space: nowrap; } white-space: nowrap;
.btn:hover { }
background-color: #b19dcf; } .btn:hover {
background-color: #b19dcf;
}
.approve { .approve {
padding: 36; } padding: 36px;
.approve .status { }
font-size: 16px; .approve .status {
color: #969499; font-size: 16px;
font-weight: lighter; } color: #969499;
.approve .status span { font-weight: lighter;
color: #252526; } }
.approve img { .approve .status span {
border: 1px solid #969499; color: #252526;
max-width: 100%; } }
.approve img {
border: 1px solid #969499;
width: 100%;
}
.actions { .actions {
margin: 8px 0; } display: flex;
.actions a { flex-direction: row;
text-decoration: none; flex-wrap: wrap;
font-size: 12px; margin: 8px 0;
font-weight: normal; } }
.actions a {
flex: 0 0 auto;
margin-bottom: 8px;
margin-right: 8px;
text-decoration: none;
}
.details { .details {
color: #252526; border: 1px solid #969499;
border-radius: 2px;
font-size: 12px; font-size: 12px;
font-weight: normal;
margin: 8px 0; margin: 8px 0;
padding: 16px; padding: 16px;
border-radius: 2px; }
border: 1px solid #969499; } .details span {
.details span { color: #969499;
color: #969499; } }
form.committee { form.committee {
border-bottom: 1px solid #969499; border-bottom: 1px solid #969499;
padding: 16px; padding: 16px;
margin-bottom: 0; } margin-bottom: 0;
}
section { section {
color: #969499; color: #969499;
flex: 0 0 auto;
font-weight: lighter; font-weight: lighter;
font-size: 16px; font-size: 16px;
margin-top: 72; margin-top: 36px;
margin-bottom: -56px; text-align: center;
max-width: 960px; }
margin-right: auto;
margin-left: auto; }
.items {
display: flex;
flex-direction: column;
flex: 1 0 auto;
}
.items a.page { .items a.page {
display: inline-block; display: inline-block;
margin: 8px; margin: 8px;
text-decoration: none; text-decoration: none;
color: #563d7c; color: #563d7c;
font-size: 12px; font-size: 12px;
float: right; } float: right;
}
.items a.page:hover { .items a.page:hover {
text-decoration: underline; } text-decoration: underline;
}
.items a.page.empty { .items a.page.empty {
color: #969499; color: #969499;
text-decoration: none; text-decoration: none;
cursor: default; } cursor: default;
}
.pagination {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 16px;
padding: 0 36px;
width: 100%;
}
.item { .item {
padding: 8px 36; display: flex;
flex-direction: column;
padding: 8px 36px;
margin: 0px; margin: 0px;
border-bottom: 1px solid #969499; border-bottom: 1px solid #969499;
box-sizing: border-box; box-sizing: border-box;
width: 100%; } width: 100%;
.item .committee { }
color: #969499; .item .committeeAndCost {
font-size: 12px; align-items: center;
font-weight: bold; display: flex;
margin: 0px; } flex-direction: row;
.item a { justify-content: space-between;
text-decoration: none; } }
.item .details-row { .item .committee {
margin: 8px 0;
font-size: 16px;
font-weight: lighter;
color: #252526;
padding: 0px; }
.item .details-row b {
color: #4b4a4d; }
.item .details-row em {
color: #969499;
margin: 0 2px; }
.item .status {
color: #969499;
font-size: 12px;
margin: 0px; }
.item .cost {
display: inline-block;
text-align: center;
width: 120px;
height: 24px;
font-size: 12px;
color: #969499;
border-radius: 2px;
border: 1px solid #969499;
box-sizing: border-box;
padding: 4px;
margin: 4px 0; }
.item .approved {
background-color: #ffd9d9; }
.item .processed {
background-color: #d9ffd9; }
.item .rejected {
background-color: #969499;
color: #f8f8f8; }
.item .newItem {
background-color: #fff7d9; }
@media (min-width: 800px) {
.item .cost {
float: right; } }
.empty {
margin: 16px;
color: #969499; color: #969499;
font-weight: lighter; font-size: 12px;
font-weight: bold;
margin: 0px;
}
.item a {
text-decoration: none;
}
.item .details-row {
color: #252526;
font-size: 16px; font-size: 16px;
text-align: center; } font-weight: lighter;
line-height: 24px;
margin: 8px 0;
padding: 0px;
}
.item .details-row b {
color: #4b4a4d;
}
.item .details-row em {
color: #969499;
margin: 0 2px;
}
.item .status {
color: #969499;
font-size: 12px;
margin: 0px;
}
.item .cost {
display: inline-block;
text-align: center;
width: 120px;
height: 24px;
font-size: 12px;
color: #969499;
border-radius: 2px;
border: 1px solid #969499;
box-sizing: border-box;
padding: 4px;
margin: 4px 0;
}
.item .approved {
background-color: #ffd9d9;
}
.item .processed {
background-color: #d9ffd9;
}
.item .rejected {
background-color: #969499;
color: #f8f8f8;
}
.item .newItem {
background-color: #fff7d9;
}
.emptyItems {
margin: 36px;
color: #969499;
font-size: 12px;
font-style: italic;
text-align: center;
}
.btn-floating { .btn-floating {
position: fixed; position: fixed;
@@ -186,78 +262,98 @@ section {
z-index: 1000; z-index: 1000;
border-radius: 50%; border-radius: 50%;
text-align: center; text-align: center;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.16), 0 6px 12px rgba(0, 0, 0, 0.32); } box-shadow: 0 0 6px rgba(0, 0, 0, 0.16), 0 6px 12px rgba(0, 0, 0, 0.32);
.btn-floating span { }
display: block; .btn-floating span {
width: 36; display: block;
height: 36; width: 36;
font-size: 28; } height: 36;
font-size: 28;
}
.btn-floating:hover {
background-color: #3e2c5a;
box-shadow: 0 0 12px rgba(0, 0, 0, 0.16), 0 12px 12px rgba(0, 0, 0, 0.32);
}
body.login { body.login {
background-color: #563d7c; background-color: #563d7c;
color: #fdd017; } color: #fdd017;
}
.login { .login {
text-align: center; text-align: center;
color: #fdd017; } color: #fdd017;
.login div { }
margin-top: 30%; } .login div {
.login h3 { margin-top: 30%;
font-size: 24px; }
margin-bottom: 8px; } .login h3 {
.login h2 { font-size: 24px;
font-size: 28px; margin-bottom: 0;
margin-top: -16px; } }
.login .btn { .login h2 {
background-color: #fdd017; font-size: 28px;
color: #563d7c; } margin: 0 auto;
.login .btn:hover { }
background-color: #c69f02; } .login .btn {
background-color: #fdd017;
color: #563d7c;
font-size: 16px;
}
.login .btn:hover {
background-color: #c69f02;
}
h1 { h1 {
font-size: 18px; font-size: 18px;
font-weight: lighter; font-weight: lighter;
color: #563d7c; color: #563d7c;
margin: 16px; } margin: 16px;
}
hr { hr {
margin: 16px;
color: #969499; }
form div {
margin: 16px; }
form div * {
display: inline-block; }
form div label {
width: 180px;
color: #252526;
vertical-align: top; }
form div input, form div select, form div textarea {
width: 180px; }
form div.clear {
margin: -16px 0;
width: 180px; }
form div.clear:after {
content: "";
display: table;
clear: both; }
.rcol {
margin: 16px; margin: 16px;
color: #969499; color: #969499;
font-weight: lighter; } }
@media (min-width: 430px) { .inputRow {
form div { box-sizing: border-box;
width: 376px; } margin-bottom: 16px;
form div label { padding: 0 36px;
text-align: right; } }
form div input, form div select, form div textarea { .inputRow label {
width: 180px; color: #969499;
float: right; } } font-weight: bold;
@media (min-width: 800px) { font-size: 12px;
.rcol { margin-left: 2px;
float: right; }
width: 40%; } } .inputRow input,
.inputRow select,
.inputRow textarea {
box-sizing: border-box;
border: 1px solid #969499;
font-size: 16px;
line-height: 24px;
padding: 2px;
width: 100%;
}
.inputRow textarea {
resize: vertical;
}
.editActions {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0 36px;
width: 100%;
}
.notes {
color: #969499;
font-size: 12px;
margin: 16px 36px;
}
/*# sourceMappingURL=site.css.map */ /*# sourceMappingURL=site.css.map */

View File

@@ -1,4 +0,0 @@
SRC=$(wildcard *.scss)
all: $(SRC)
sass include.scss ../css/site.css

View File

@@ -1,86 +0,0 @@
$purple: #563d7c;
$gold: #fdd017;
$lightgray: #f8f8f8;
$midgray: #969499;
$darkgray: #4b4a4d;
$text: #252526;
$shadowgray: rgba(0, 0, 0, .8);
// size constants
$pad-s: 2px;
$pad-m: 8px;
$pad-l: 16px;
$pad-xl: 36;
body {
color: $text;
font-family: sans-serif;
background-color: $lightgray;
padding-bottom: 0px;
margin: 0px $pad-l;
}
.nav {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 50px;;
box-sizing: border-box;
padding: 16px;
background-color: $purple;
color: $gold;
box-shadow: 0px 3px 12px $shadowgray;
z-index: 10;
h3 {
font-weight: lighter;
font-size: 16px;
margin: 0;
}
a {
float: right;
color: $gold;
text-decoration: none;
font-size: 16px;
margin-left: $pad-m;
}
}
.pull-right {
float: right;
margin: $pad-l;
}
.container {
margin-bottom: $pad-xl;
& > div {
margin-left: auto;
margin-right: auto;
margin-top: 64px;
max-width: 960;
background-color: #fff;
border: solid 1px $midgray;
box-shadow: 0px 3px 9px lighten($shadowgray, 20%);
}
}
.btn {
border-radius: $pad-s;
border: 1px solid $midgray;
color: $purple;
display: inline-block;
padding: $pad-m $pad-l;
text-decoration: none;
vertical-align: middle;
white-space: nowrap;
&:hover {
background-color: lighten($purple, 35%);
}
}

View File

@@ -1,74 +0,0 @@
h1 {
font-size: 18px;
font-weight: lighter;
color: $purple;
margin: $pad-l;
}
hr {
margin: $pad-l;
color: $midgray;
}
form {
div {
margin: $pad-l;
* {
display: inline-block;
}
label {
width: 180px;
color: $text;
vertical-align: top;
}
input, select, textarea {
width: 180px;
}
&.clear {
margin: -$pad-l 0;
width: 180px;
}
&.clear:after {
content: "";
display: table;
clear: both;
}
}
}
.rcol {
margin: $pad-l;
color: $midgray;
font-weight: lighter;
}
@media (min-width: 430px) {
form {
div {
width: 376px;
label {
text-align: right;
}
input, select, textarea {
width: 180px;
float: right;
}
}
}
}
@media (min-width: 800px) {
.rcol {
float: right;
width: 40%;
}
}

View File

@@ -9,15 +9,19 @@
</head> </head>
<body> <body>
<div class="nav"> <div class="nav">
<a href="/logout">logout</a>
{% if fincom %}
<a href="/committees">manage</a>
{% endif %}
<h3>The Aaron Gutierrez, '17, Fincom Web App</h3> <h3>The Aaron Gutierrez, '17, Fincom Web App</h3>
<div>
{% if fincom %}
<a href="/committees">manage</a>
{% endif %}
<a href="/logout">logout</a>
</div>
</div> </div>
<div class="container"> <div class="content">
{% block main %} <div class="container">
{% endblock %} {% block main %}
{% endblock %}
</div>
</div> </div>
{% block bottom %} {% block bottom %}
{% endblock %} {% endblock %}

View File

@@ -11,13 +11,13 @@
<div> <div>
<h3>Delta Beta Chapter</h3> <h3>Delta Beta Chapter</h3>
<h2>Fincom Webapp</h2> <h2>Fincom Webapp</h2>
{% if error %} <a class="btn btn-info"
<p>{{ error }}</p>
{% endif %}
<a class="btn btn-info btn-lg"
href="/login/google-oauth2/?next={{ next }}"> href="/login/google-oauth2/?next={{ next }}">
Login with your AndrewID Login with your AndrewID
</a> </a>
{% if error %}
<p>{{ error }}</p>
{% endif %}
</div> </div>
</body> </body>
</html> </html>

View File

@@ -8,7 +8,7 @@
<div class="approve"> <div class="approve">
{% if I.details %} {% if I.details %}
<div class="details"> <div class="details">
<span>Details:</span><br> <span>Details</span><br>
{{ I.details }}</div> {{ I.details }}</div>
{% endif %} {% endif %}
<div class="status">Status: <span>{{ I.statusText }}</span></div> <div class="status">Status: <span>{{ I.statusText }}</span></div>
@@ -23,7 +23,9 @@
<a href="/items" class="btn">Back</a> <a href="/items" class="btn">Back</a>
</div> </div>
<a href="{{ I.image.url }}"><img src="{{ I.image.thumbnail.url }}"></a> <a href="{{ I.image.url }}">
<img src="{{ I.image.thumbnail.url }}" alt="Click to view">
</a>
</div> </div>
</div> </div>

View File

@@ -5,25 +5,24 @@
{% block main %} {% block main %}
<div> <div>
<a href="/items" class="btn pull-right">Back</a>
<h1>Edit Reimbursements</h1> <h1>Edit Reimbursements</h1>
<hr> <hr>
<form action="/items/{{ I.pk }}/edit" method="post"> <form action="/items/{{ I.pk }}/edit" method="post">
{% csrf_token %} {% csrf_token %}
<div> <div class="inputRow">
<label for="desc">Item</label> <label for="desc">Item</label>
<input id="desc" type="text" name="desc" placeholder="What you bought" <input id="desc" type="text" name="desc" placeholder="What you bought"
value="{{ I.desc }}"> value="{{ I.desc }}">
</div> </div>
<div> <div class="inputRow">
<label for="event">Event</label> <label for="event">Event</label>
<input id="event" type="text" name="event" placeholder="When we need it" <input id="event" type="text" name="event" placeholder="When we need it"
value="{{ I.event }}"> value="{{ I.event }}">
</div> </div>
<div> <div class="inputRow">
<label for="committee">Committee or Budget</label> <label for="committee">Committee or Budget</label>
<select name="committee" id="committee"> <select name="committee" id="committee">
{% for C in committees %} {% for C in committees %}
@@ -36,29 +35,26 @@
</select> </select>
</div> </div>
<div> <div class="inputRow">
<label for="cost">Cost</label> <label for="cost">Cost</label>
<input id="cost" type="text" name="cost" placeholder="15.00" <input id="cost" type="text" name="cost" placeholder="15.00"
value="{{ I.cost }}"> value="{{ I.cost }}">
</div> </div>
<div> <div class="inputRow">
<label for="date">Date Purchased</label> <label for="date">Date Purchased</label>
<input id="date" type="date" name="date" placeholder="mm/dd/yyyy" <input id="date" type="date" name="date" placeholder="mm/dd/yyyy"
value="{{ I.date_purchased }}"> value="{{ I.date_purchased }}">
</div> </div>
<div class="clear"> <div class="inputRow">
<div>
<label for="details">Details</label> <label for="details">Details</label>
<textarea id="details" name="details" rows="4" class="clear">{{ I.details }}</textarea> <textarea id="details" name="details" rows="4">{{ I.details }}</textarea>
</div>
</div> </div>
<div class="clear"> <div class="editActions">
<div> <a href="/items" class="btn">Back</a>
<input type="submit"> <input type="submit" class="btn">
</div>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -30,7 +30,7 @@ Please review and then approve or reject the request from {{ submitter }} for {{
</a> </a>
<div style="color: #969499; font-size: 12px; margin: 0px;"> <div style="color: #969499; font-size: 12px; margin: 0px;">
{% if I.approved or I.processed %} {% if I.approved or I.processed %}
Approved By: Approved By:
{% for u in I.approved_by.all %} {% for u in I.approved_by.all %}
{{ u.first_name }} {{ u.last_name }} {{ u.first_name }} {{ u.last_name }}
{% endfor %} {% endfor %}

View File

@@ -1,18 +1,20 @@
<div class="item"> <div class="item">
<div class="committee"> <div class="committeeAndCost">
{{ I.comName }} <div class="committee">
</div> {{ I.comName }}
{% if I.approved %}
<div class="cost approved">
{% elif I.processed %}
<div class="cost processed">
{% elif I.rejected %}
<div class="cost rejected">
{% else %}
<div class="cost newItem">
{% endif %}
${{ I.cost|floatformat:"2" }}
</div> </div>
{% if I.approved %}
<div class="cost approved">
{% elif I.processed %}
<div class="cost processed">
{% elif I.rejected %}
<div class="cost rejected">
{% else %}
<div class="cost newItem">
{% endif %}
${{ I.cost|floatformat:"2" }}
</div>
</div>
<a href="/items/{{ I.pk }}"> <a href="/items/{{ I.pk }}">
<div class="details-row"> <div class="details-row">
<b>{{ I.event }}:</b> <b>{{ I.event }}:</b>

View File

@@ -5,27 +5,23 @@
{% block main %} {% block main %}
<div> <div>
<h1>Add New Reimbursements</h1> <h1>New Reimbursement</h1>
<hr> <hr>
<div class="rcol">
Make sure all information is accurate, you submit an itemized receipt, and
you select the correct committee.
</div>
<form action="/items/new" method="post" enctype="multipart/form-data"> <form action="/items/new" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div> <div class="inputRow">
<label for="desc">Item</label> <label for="desc">Item</label>
<input id="desc" type="text" name="desc" placeholder="What you bought"> <input id="desc" type="text" name="desc" placeholder="What you bought">
</div> </div>
<div> <div class="inputRow">
<label for="event">Event</label> <label for="event">Event</label>
<input id="event" type="text" name="event" placeholder="When we need it"> <input id="event" type="text" name="event" placeholder="When we need it">
</div> </div>
<div> <div class="inputRow">
<label for="committee">Committee or Budget</label> <label for="committee">Committee or Budget</label>
<select name="committee" id="committee"> <select name="committee" id="committee">
<option disabled selected>Select a committee</option> <option disabled selected>Select a committee</option>
@@ -35,32 +31,34 @@
</select> </select>
</div> </div>
<div> <div class="inputRow">
<label for="cost">Cost</label> <label for="cost">Cost</label>
<input id="cost" type="text" name="cost" placeholder="15.00"> <input id="cost" type="text" name="cost" placeholder="15.00">
</div> </div>
<div> <div class="inputRow">
<label for="date">Date Purchased</label> <label for="date">Date Purchased</label>
<input id="date" type="date" name="date" placeholder="mm/dd/yyyy"> <input id="date" type="date" name="date" placeholder="mm/dd/yyyy">
</div> </div>
<div class="clear"> <div class="inputRow">
<div>
<label for="details">Details</label> <label for="details">Details</label>
<textarea id="details" name="details" rows="4" class="clear"></textarea> <textarea id="details" name="details" rows="4"></textarea>
</div>
</div> </div>
<div> <div class="inputRow">
<label for="image">Receipt</label> <label for="image">Receipt</label>
<input type="file" name="image" id="image" accept="image/*"> <input type="file" name="image" id="image" accept="image/*">
</div> </div>
<div class="clear"> <div class="notes">
<div> Make sure all information is accurate, you submit an itemized receipt, and
<input type="submit"> you select the correct committee.
</div> </div>
<div class="editActions">
<a href="/items" class="btn">Back</a>
<input type="submit" class="btn">
</div> </div>
</form> </form>
</div> </div>

View File

@@ -3,15 +3,9 @@
{% for I in items %} {% for I in items %}
{% include "./item.html" %} {% include "./item.html" %}
{% empty %} {% empty %}
<div class="empty">No Reimbursements</div> <div class="emptyItems">No Reimbursements</div>
{% endfor %} {% endfor %}
{% if items.has_next %} <div class="pagination">
<a class="page" href="?{{ pagination_key }}={{ items.next_page_number }}">
Older &rarr;
</a>
{% else %}
<a class="page empty">Older &rarr;</a>
{% endif %}
{% if items.has_previous %} {% if items.has_previous %}
<a class="page" href="?{{ pagination_key }}={{ items.previous_page_number }}"> <a class="page" href="?{{ pagination_key }}={{ items.previous_page_number }}">
&larr; Newer &larr; Newer
@@ -19,4 +13,12 @@
{% else %} {% else %}
<a class="page empty">&larr; Newer</a> <a class="page empty">&larr; Newer</a>
{% endif %} {% endif %}
{% if items.has_next %}
<a class="page" href="?{{ pagination_key }}={{ items.next_page_number }}">
Older &rarr;
</a>
{% else %}
<a class="page empty">Older &rarr;</a>
{% endif %}
</div>
</div> </div>