From 2804a0fee57060afdcfea505f5c7059f425558dd Mon Sep 17 00:00:00 2001 From: Pat Pannuto Date: Wed, 1 Jun 2016 19:35:20 -0400 Subject: [PATCH] Rewrite requests Rewrite the request infrastructure to facilitate more interation and to intrinsically enforce some of our policies (must sell at known store, must provide URL if you can, etc). This also moves requests to logged-in users only, which should help following through on confusing requests. --- chezbetty/__init__.py | 16 ++- chezbetty/datalayer.py | 4 +- chezbetty/jinja2_filters.py | 4 +- .../migrations/migration_1.18.1-1.19.0.sql | 28 ++++ chezbetty/models/request.py | 48 +++++-- chezbetty/models/request_post.py | 33 +++++ chezbetty/models/user.py | 9 ++ chezbetty/models/vendor.py | 7 +- chezbetty/static/css/chezbetty-common.css | 16 +++ chezbetty/static/js/chezbetty-admin-onload.js | 4 +- .../static/js/chezbetty-common-onload.js | 111 +++++++++++++-- chezbetty/templates/admin/base.jinja2 | 1 + .../templates/admin/macro_buttons.jinja2 | 23 +-- chezbetty/templates/admin/pool.jinja2 | 2 +- chezbetty/templates/admin/requests.jinja2 | 66 ++++++++- chezbetty/templates/macro_buttons.jinja2 | 25 +++- chezbetty/templates/public/exception.jinja2 | 6 + chezbetty/templates/public/index.jinja2 | 6 +- .../templates/public/item_request.jinja2 | 43 ------ chezbetty/templates/user/base.jinja2 | 1 + chezbetty/templates/user/item_request.jinja2 | 131 ++++++++++++++++++ chezbetty/templates/user/sidebar.jinja2 | 1 + chezbetty/views.py | 11 +- chezbetty/views_admin.py | 59 +++++++- chezbetty/views_public.py | 25 +--- chezbetty/views_user.py | 80 ++++++++++- 26 files changed, 610 insertions(+), 150 deletions(-) create mode 100644 chezbetty/migrations/migration_1.18.1-1.19.0.sql create mode 100644 chezbetty/models/request_post.py delete mode 100644 chezbetty/templates/public/item_request.jinja2 create mode 100644 chezbetty/templates/user/item_request.jinja2 diff --git a/chezbetty/__init__.py b/chezbetty/__init__.py index f977a5e..8817db9 100644 --- a/chezbetty/__init__.py +++ b/chezbetty/__init__.py @@ -101,12 +101,10 @@ def debug(request): config.add_route('shame', '/shame') config.add_route('shame_csv', '/shame.csv') - config.add_route('items', '/items') - config.add_route('item_request', '/item/request') - config.add_route('item_request_new', '/item/request/new') + config.add_route('items', '/items') - config.add_route('paydebt', '/paydebt/{uniqname}') - config.add_route('paydebt_submit', '/paydebt/{uniqname}/submit') + config.add_route('paydebt', '/paydebt/{uniqname}') + config.add_route('paydebt_submit', '/paydebt/{uniqname}/submit') # TERMINAL VIEWS @@ -139,6 +137,10 @@ def debug(request): config.add_route('user_deposit_cc_custom', '/user/deposit_cc/custom') config.add_route('user_deposit_cc_submit', '/user/deposit_cc/submit') + config.add_route('user_item_request', '/user/item/request') + config.add_route('user_item_request_new', '/user/item/request/new') + config.add_route('user_item_request_post_new', '/user/item/request/{id}/post/new') + config.add_route('user_pools', '/user/pools') config.add_route('user_pools_new_submit', '/user/pools/new/submit') config.add_route('user_pool', '/user/pool/{pool_id}') @@ -153,7 +155,8 @@ def debug(request): config.add_route('admin_index', '/admin') config.add_route('admin_index_dashboard', '/admin/dashboard') - config.add_route('admin_ajax_bool', '/admin/ajax/bool/{object}/{id}/{field}/{state}') + config.add_route('admin_ajax_bool', '/admin/ajax/bool/{object}/{id}/{field}/{value}') + config.add_route('admin_ajax_text', '/admin/ajax/text/{object}/{id}/{field}') config.add_route('admin_ajax_new', '/admin/ajax/new/{object}/{arg}') config.add_route('admin_ajax_connection', '/admin/ajax/connection/{object1}/{object2}/{arg1}/{arg2}') @@ -235,6 +238,7 @@ def debug(request): config.add_route('admin_password_edit_submit', '/admin/password/edit/submit') config.add_route('admin_requests', '/admin/requests') + config.add_route('admin_item_request_post_new', '/admin/item/request/{id}/post/new') config.add_route('admin_announcements_edit', '/admin/announcements/edit') config.add_route('admin_announcements_edit_submit', '/admin/announcements/edit/submit') diff --git a/chezbetty/datalayer.py b/chezbetty/datalayer.py index 7d694ca..9cc50a0 100644 --- a/chezbetty/datalayer.py +++ b/chezbetty/datalayer.py @@ -156,8 +156,8 @@ def delete_box(box): # Call this to make a new item request -def new_request(user, request_text): - r = request.Request(user, request_text) +def new_request(user, request_text, vendor, vendor_url=None): + r = request.Request(user, request_text, vendor, vendor_url) DBSession.add(r) DBSession.flush() return r diff --git a/chezbetty/jinja2_filters.py b/chezbetty/jinja2_filters.py index b9a4c6c..1bd2472 100644 --- a/chezbetty/jinja2_filters.py +++ b/chezbetty/jinja2_filters.py @@ -55,7 +55,9 @@ def shorten(s, l): return s[0:l-1] + '…' def make_link(obj, str_len=0): - if type(obj) is box.Box: + if obj is None: + return '' + elif type(obj) is box.Box: return '{}'.format(obj.id, shorten(obj.name, str_len)) elif type(obj) is item.Item: return '{}'.format(obj.id, shorten(obj.name, str_len)) diff --git a/chezbetty/migrations/migration_1.18.1-1.19.0.sql b/chezbetty/migrations/migration_1.18.1-1.19.0.sql new file mode 100644 index 0000000..78e75d8 --- /dev/null +++ b/chezbetty/migrations/migration_1.18.1-1.19.0.sql @@ -0,0 +1,28 @@ +/* WARN: This migration drops all old requests since the storage format changed significantly */ +COPY requests TO '/tmp/cb_old_requests.csv' DELIMITER ',' CSV HEADER; + +CREATE TABLE requests_pre_v1_19 AS SELECT * FROM requests; + +DELETE FROM requests; +ALTER TABLE requests ADD COLUMN vendor_id INTEGER NOT NULL; +ALTER TABLE requests ADD CONSTRAINT vendor_id FOREIGN KEY(vendor_id) REFERENCES vendors(id) MATCH FULL; +ALTER TABLE requests ADD COLUMN vendor_url TEXT; + +ALTER TABLE vendors ADD COLUMN product_urls BOOLEAN; +UPDATE vendors SET product_urls=True WHERE name='Amazon'; +UPDATE vendors SET product_urls=True WHERE name='TalDepot'; + +CREATE TABLE request_posts ( + id SERIAL, + "timestamp" timestamp without time zone NOT NULL, + request_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + post TEXT, + staff_post BOOLEAN NOT NULL DEFAULT FALSE, + deleted BOOLEAN NOT NULL DEFAULT FALSE +); +ALTER TABLE request_posts ADD CONSTRAINT request_id FOREIGN KEY(request_id) REFERENCES requests(id) MATCH FULL; +ALTER TABLE request_posts ADD CONSTRAINT user_id FOREIGN KEY(user_id) REFERENCES users(id) MATCH FULL; + +/* ALTER TABLE requests ADD COLUMN response TEXT; */ + diff --git a/chezbetty/models/request.py b/chezbetty/models/request.py index 3bf181b..fd66540 100644 --- a/chezbetty/models/request.py +++ b/chezbetty/models/request.py @@ -1,21 +1,47 @@ from .model import * +from . import vendor +from . import request_post from sqlalchemy_utils import ArrowType class Request(Base): __tablename__ = 'requests' - id = Column(Integer, primary_key=True, nullable=False) - timestamp = Column(ArrowType, nullable=False, default=datetime.datetime.utcnow) - user_id = Column(Integer, ForeignKey("users.id"), nullable=True) # user that made the request - request = Column(Text) - enabled = Column(Boolean, default=True, nullable=False) - deleted = Column(Boolean, default=False, nullable=False) + id = Column(Integer, primary_key=True, nullable=False) + timestamp = Column(ArrowType, nullable=False, default=datetime.datetime.utcnow) + user_id = Column(Integer, ForeignKey("users.id"), nullable=False) + request = Column(Text) + vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=False) + vendor_url = Column(Text) + enabled = Column(Boolean, default=True, nullable=False) + deleted = Column(Boolean, default=False, nullable=False) - def __init__(self, user, request): - if user: - self.user_id = user.id + vendor = relationship( + vendor.Vendor, + primaryjoin="and_(Request.vendor_id==Vendor.id, Request.deleted==False)", + backref="requests", + ) + + posts = relationship( + request_post.RequestPost, + primaryjoin="and_(RequestPost.request_id==Request.id, RequestPost.deleted==False)", + backref="request", + ) + deleted_posts = relationship( + request_post.RequestPost, + primaryjoin="and_(RequestPost.request_id==Request.id, RequestPost.deleted==True)", + ) + all_posts = relationship( + request_post.RequestPost, + primaryjoin="RequestPost.request_id==Request.id", + ) + + def __init__(self, user, request, vendor, vendor_url=None): + self.user_id = user.id self.request = request + self.vendor_id = vendor.id + if vendor_url: + self.vendor_url = vendor_url @classmethod def from_id(cls, id): @@ -23,7 +49,9 @@ def from_id(cls, id): @classmethod def all(cls): - return DBSession.query(cls).filter(cls.deleted==False).all() + return DBSession.query(cls).filter(cls.deleted==False)\ + .order_by(desc(cls.timestamp))\ + .all() @classmethod def count(cls): diff --git a/chezbetty/models/request_post.py b/chezbetty/models/request_post.py new file mode 100644 index 0000000..654f6dd --- /dev/null +++ b/chezbetty/models/request_post.py @@ -0,0 +1,33 @@ +from .model import * + +from sqlalchemy_utils import ArrowType + +class RequestPost(Base): + __tablename__ = 'request_posts' + + id = Column(Integer, primary_key=True, nullable=False) + timestamp = Column(ArrowType, nullable=False, default=datetime.datetime.utcnow) + request_id = Column(Integer, ForeignKey("requests.id"), nullable=False) + user_id = Column(Integer, ForeignKey("users.id"), nullable=False) + post = Column(Text) + # Allow admin users to post as users or admins by tracking the view that the post is posted from + staff_post = Column(Boolean, default=False, nullable=False) + deleted = Column(Boolean, default=False, nullable=False) + + def __init__(self, request, user, post, staff_post=False, deleted=False): + self.request_id = request.id + self.user_id = user.id + self.post = post + self.staff_post = staff_post + self.deleted = deleted + + @classmethod + def from_id(cls, id): + return DBSession.query(cls).filter(cls.id == id).one() + + @classmethod + def all(cls): + return DBSession.query(cls).filter(cls.deleted==False)\ + .order_by(desc(cls.timestamp))\ + .all() + diff --git a/chezbetty/models/user.py b/chezbetty/models/user.py index cf7e965..d17bb64 100644 --- a/chezbetty/models/user.py +++ b/chezbetty/models/user.py @@ -5,6 +5,8 @@ from .model import * from . import account from . import event +from . import request +from . import request_post from chezbetty import utility import ldap3 @@ -120,6 +122,13 @@ class User(account.Account): administrative_events = relationship(event.Event, foreign_keys=[event.Event.user_id], backref="admin") events_deleted = relationship(event.Event, foreign_keys=[event.Event.deleted_user_id], backref="deleted_user") + requests = relationship(request.Request, foreign_keys=[request.Request.user_id], backref="user") + request_posts = relationship( + request_post.RequestPost, + foreign_keys=[request_post.RequestPost.user_id], + backref="user", + ) + __ldap = LDAPLookup() def __init__(self, uniqname, umid, name): diff --git a/chezbetty/models/vendor.py b/chezbetty/models/vendor.py index 36c1e5f..b410a65 100644 --- a/chezbetty/models/vendor.py +++ b/chezbetty/models/vendor.py @@ -3,10 +3,11 @@ class Vendor(Base): __tablename__ = 'vendors' - id = Column(Integer, primary_key=True, nullable=False) - name = Column(String(255), nullable=False) + id = Column(Integer, primary_key=True, nullable=False) + name = Column(String(255), nullable=False) - enabled = Column(Boolean, default=True, nullable=False) + enabled = Column(Boolean, default=True, nullable=False) + product_urls = Column(Boolean) def __init__(self, name, enabled=True): self.name = name diff --git a/chezbetty/static/css/chezbetty-common.css b/chezbetty/static/css/chezbetty-common.css index dc24e1c..fc7543a 100644 --- a/chezbetty/static/css/chezbetty-common.css +++ b/chezbetty/static/css/chezbetty-common.css @@ -21,6 +21,22 @@ padding: 2px 5px 2px 5px; } +.deleted { + background-color: #f0f0f0; + text-decoration: line-through; + display: none; +} + +/* Highlight a form element that's required that was not filled out */ +.form-required-missing { + background-color: #ff8080; + padding: 2px 5px 2px 5px; +} +.form-required-message { + color: red; + display: none; +} + /* Support for auto-sizing text */ .fitin { overflow: hidden; diff --git a/chezbetty/static/js/chezbetty-admin-onload.js b/chezbetty/static/js/chezbetty-admin-onload.js index d9283cd..966cd03 100644 --- a/chezbetty/static/js/chezbetty-admin-onload.js +++ b/chezbetty/static/js/chezbetty-admin-onload.js @@ -2,8 +2,8 @@ // Make the Demo Mode checkbox in the sidebar a pretty on/off slider $(".admin-switch").bootstrapSwitch(); -function ajax_bool (js_obj, object, field, id, status) { - var url = "/admin/ajax/bool/"+object+"/"+id+"/"+field+"/"+status; +function ajax_bool (js_obj, object, field, id, state) { + var url = "/admin/ajax/bool/"+object+"/"+id+"/"+field+"/"+state; $.ajax({ url: url, context: js_obj, diff --git a/chezbetty/static/js/chezbetty-common-onload.js b/chezbetty/static/js/chezbetty-common-onload.js index 2f45ad3..c6dd153 100644 --- a/chezbetty/static/js/chezbetty-common-onload.js +++ b/chezbetty/static/js/chezbetty-common-onload.js @@ -1,5 +1,16 @@ // single use button +$(".button-showhide").on('click', function () { + cls = $(this).attr('data-class'); + $('.'+cls).toggle(); + var alt_text = $(this).attr('data-alt-text'); + if ( alt_text != undefined ) { + var text = $(this).text(); + $(this).text(alt_text); + $(this).attr('data-alt-text', text); + } +}); + function button_singleuse_success (data) { if (data["status"] == "success") { $(this).hide(); @@ -25,8 +36,11 @@ $(".btn-ajax_singleuse").on('click', function () { function button_save_success (data) { if (data["status"] == "success") { - var input = $('#' + $(this).attr("id").slice(0,-4) + '-input'); - $(this).hide(); + var input = $(this); + var save = $('#' + $(this).attr("id") + '-btn-save' ); + var revert = $('#' + $(this).attr("id") + '-btn-revert'); + save.hide(); + revert.hide(); input.attr('data-initial', data["value"]); alert_success(data["msg"]); } else { @@ -35,32 +49,46 @@ function button_save_success (data) { } function button_save_fail (data) { - alert_error("Button save failed."); + alert_error("Error saving changes."); } -$(".btn-ajax_savefield").on('click', function () { - var url = $(this).attr("data-url"); - var id = $(this).attr("id").slice(0,-4); - var input = $('#' + $(this).attr("id").slice(0,-4) + '-input'); +function ajax_button_textlike (js_obj, object, field, id, value) { + var url = "/admin/ajax/text/"+object+"/"+id+"/"+field; $.ajax({ url: url, method: 'POST', data: { - 'pool' : id, - 'name' : input.val(), + 'value' : value, }, - context: $(this), + context: js_obj, success: button_save_success, error: button_save_fail }); +}; + +$(".ajax-textlike-btn-save").on('click', function () { + var fields = $(this).attr("id").split("-"); + var input = $('#' + $(this).attr("id").slice(0,-9)); + var value = input.val(); + ajax_button_textlike(input, fields[2], fields[3], fields[4], value); }); -$(".input-ajax_savefield").on('input', function () { - var btn = $('#' + $(this).attr("id").slice(0,-6) + '-btn'); +$(".ajax-textlike-btn-revert").on('click', function () { + var textarea_id = $(this).attr("id").slice(0,-11); + var textarea = $('#'+textarea_id); + textarea.val(textarea.attr('data-initial')); + textarea.trigger('input'); +}); + +$(".ajax-textlike").on('input', function () { + var save_btn = $('#' + $(this).attr("id") + '-btn-save'); + var revert_btn = $('#' + $(this).attr("id") + '-btn-revert'); if ($(this).attr('data-initial') != $(this).val()) { - btn.show(); + save_btn.show(); + revert_btn.show(); } else { - btn.hide(); + save_btn.hide(); + revert_btn.hide(); } }); @@ -114,3 +142,58 @@ $(".fitin").each(function () { } }); + +// Generic JS to disable a controlled element if this input is empty +$('.disable-controlled-when-empty').on('change input', function disable_controlled_on_change() { + var controlled = $('#' + $(this).attr('data-controlled')); + var contents = $.trim($(this).val()); + if ( contents == '' ) { + controlled.prop('disabled', true); + } else { + controlled.prop('disabled', false); + } +}); + + +// Generic JS to verify that all form fields have been filled out +$('.form-with-requirements').submit(function check_submit(evt) { + evt.preventDefault(); + + var missing_requirements = []; + $('.form-required').each(function check_submit_each(index) { + $('.form-required-message').hide(); + $(this).removeClass('form-required-missing'); + if ( $(this).is("input") ) { + if ( $(this).val() == '' ) { + missing_requirements.push($(this)); + } + } else if ( $(this).is("select") ) { + if ( $(this).val() == null ) { + missing_requirements.push($(this)); + } + } + }); + + if (missing_requirements.length != 0) { + $('.form-required-message').show(); + $.each(missing_requirements, function report_missing_requirement(index) { + $(this).addClass('form-required-missing'); + }); + return false; + } + + $(this).unbind('submit').trigger('submit'); +}); + + +// Item request page hook to selectively validate URL field +$('#request-vendor').change(function() { + var selected = $(this).find(":selected"); + if ( selected.attr('data-product-urls') == 'True' ) { + $('#request-vendor-url').addClass('form-required'); + } else { + $('#request-vendor-url').removeClass('form-required'); + $('#request-vendor-url').removeClass('form-required-missing'); + } +}); + diff --git a/chezbetty/templates/admin/base.jinja2 b/chezbetty/templates/admin/base.jinja2 index c64e3e1..171f6fc 100644 --- a/chezbetty/templates/admin/base.jinja2 +++ b/chezbetty/templates/admin/base.jinja2 @@ -14,6 +14,7 @@ + diff --git a/chezbetty/templates/admin/macro_buttons.jinja2 b/chezbetty/templates/admin/macro_buttons.jinja2 index f1266ab..b8718de 100644 --- a/chezbetty/templates/admin/macro_buttons.jinja2 +++ b/chezbetty/templates/admin/macro_buttons.jinja2 @@ -6,25 +6,6 @@ {% endmacro %} -{% macro onoff_switch(object, field, id, status) %} - -{% endmacro %} - -{% macro delete(object, field, id) %} - -{% endmacro %} - -{# Button that can only be clicked once. Will create an AJAX request to the - URL specified. Javascript will handle the response. If success, the button - will be removed and banner displayed. If failure, button will stay and error - will be displayed. - #} -{% macro ajax_singleuse_button(button_text, ajax_url, id) %} - -{% endmacro %} - -{% macro ajax_saveable_input(initial_value, ajax_url, id) %} - - -{% endmacro %} +{% set role = 'admin' %} +{% extends "../macro_buttons.jinja2" %} diff --git a/chezbetty/templates/admin/pool.jinja2 b/chezbetty/templates/admin/pool.jinja2 index 94e6592..928df5b 100644 --- a/chezbetty/templates/admin/pool.jinja2 +++ b/chezbetty/templates/admin/pool.jinja2 @@ -18,7 +18,7 @@
Name
-
{{ button.ajax_saveable_input(pool.name, "/admin/pool/"~pool.id~"/name", pool.id)}}
+
{{ button.ajax_saveable_input(pool, "pool", "name")}}
Owner
{{ pool_owner|make_link|safe }}
Balance
{{ pool.balance|format_currency|safe }}
Credit Limit
{{ pool.credit_limit|format_currency|safe }}
diff --git a/chezbetty/templates/admin/requests.jinja2 b/chezbetty/templates/admin/requests.jinja2 index 3756637..601c834 100644 --- a/chezbetty/templates/admin/requests.jinja2 +++ b/chezbetty/templates/admin/requests.jinja2 @@ -13,19 +13,75 @@ - Date - Request Delete + Date / User + Request / Response - {% for request in requests|reverse %} + {% for request in requests %} - {{ request.timestamp|pretty_date|safe }} - {{ request.request }} {{ button.delete("request", "deleted", request.id) }} + + {{ request.timestamp|pretty_date|safe }}
{{ request.user|make_link|safe }} + {% if request.deleted_posts %} +
+ {{ button.showhide("request-" + request.id|string + "-deleted", "Show deleted comments", "Hide deleted comments") }} + {% endif %} + + +
+
+
+
Item
+
{{ request.request }}
+
Store
+
{{ request.vendor.name }}
+ {% if request.vendor_url %} +
URL
+
{{ request.vendor_url }}
+ {% endif %} +
+
+
+ {% for post in request.all_posts %} +
+
+
+
+ {{ post.timestamp|human_date|safe}} +
+ {% if post.staff_post %} + Staff Post ({{ post.user|make_link|safe }}) + {% else %} + {{ post.user|make_link|safe }} + {% endif %} +
+
{{ post.post }}
+
+
+
+ {% if not post.deleted %} + {{ button.delete("request_post", "deleted", post.id) }} + {% endif %} +
+
+ {% endfor %} +
+
+
+
+ +
+
+ +
+
+
+
+ {% endfor %} diff --git a/chezbetty/templates/macro_buttons.jinja2 b/chezbetty/templates/macro_buttons.jinja2 index 52dfc86..45d7678 100644 --- a/chezbetty/templates/macro_buttons.jinja2 +++ b/chezbetty/templates/macro_buttons.jinja2 @@ -1,9 +1,17 @@ +{% if role is not defined %} +{% set role = 'user' %} +{% endif %} + +{% macro showhide(class, text="Show/Hide", alt_text=none) %} + +{% endmacro %} + {% macro onoff_switch(object, field, id, status) %} - + {% endmacro %} {% macro delete(object, field, id) %} - + {% endmacro %} {# Button that can only be clicked once. Will create an AJAX request to the @@ -14,3 +22,16 @@ {% macro ajax_singleuse_button(button_text, ajax_url, id) %} {% endmacro %} + +{% macro ajax_saveable_input(object, type, field) %} + + + +{% endmacro %} + +{% macro ajax_saveable_textarea(object, type, field) %} + + + +{% endmacro %} + diff --git a/chezbetty/templates/public/exception.jinja2 b/chezbetty/templates/public/exception.jinja2 index ab546fc..359d22d 100644 --- a/chezbetty/templates/public/exception.jinja2 +++ b/chezbetty/templates/public/exception.jinja2 @@ -14,6 +14,12 @@ well. Sorry for the trouble. Touch here to return to the home page. +{% if request.debug %} +Reload {{ request.GET['excepting_path'] }} +{% if excepting_path in request.GET %} +Reload {{ request.GET['excepting_path'] }} +{% endif %} +{% endif %} {% endblock %} diff --git a/chezbetty/templates/public/index.jinja2 b/chezbetty/templates/public/index.jinja2 index 9b32c40..4a41662 100644 --- a/chezbetty/templates/public/index.jinja2 +++ b/chezbetty/templates/public/index.jinja2 @@ -32,7 +32,11 @@ body {
Navigation
{{ _('Item List') }} - {{ _('Request an Item') }} + {% if request.user %} {# logged in? #} + {{ _('Request an Item') }} + {% else %} + {{ _('Request an Item') }} + {% endif %} {{ _('About') }} {{ _('Wall of Shame') }}
diff --git a/chezbetty/templates/public/item_request.jinja2 b/chezbetty/templates/public/item_request.jinja2 deleted file mode 100644 index 6a05064..0000000 --- a/chezbetty/templates/public/item_request.jinja2 +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "base.jinja2" %} -{% block title %}{{ _('Item Request') }}{% endblock %} -{% block header %}{{ _('Request a New Item at Chez Betty') }}{% endblock %} - - - -{% block content %} - -

{{ _('Expanding the store.') }}

- -
-
-

{{ _('New Item Request') }}

-
-
- -

- {{ _('Enter an item you would like to see Betty sell. Please include ' - 'where you purchased the item from before so we have a chance at finding it. ' - 'We normally shop at Costco, Meijer, and Kroger, so if those stores ' - 'do not carry it, it is unlikely that Betty will either. ' - 'If you do not include where you know we can buy the item we will just delete ' - 'the request.') }} -

-

- {{ _('Also, depending on your motivation, you could be the supplier of a new item. ' - 'For instance, if you are grocery shopping you can pick up a few extra of ' - 'what you want, add it the Betty system, and see if it sells well. ' - 'The process is pretty simple (particularly for small numbers of items) ' - 'and we can reimburse you by just writing a check. Get in touch with ' - 'us if you are interested.') }} -

- - -
- -
- -
-
-
- -{% endblock %} diff --git a/chezbetty/templates/user/base.jinja2 b/chezbetty/templates/user/base.jinja2 index 1ad8e0a..8999505 100644 --- a/chezbetty/templates/user/base.jinja2 +++ b/chezbetty/templates/user/base.jinja2 @@ -14,6 +14,7 @@ + diff --git a/chezbetty/templates/user/item_request.jinja2 b/chezbetty/templates/user/item_request.jinja2 new file mode 100644 index 0000000..55f8de2 --- /dev/null +++ b/chezbetty/templates/user/item_request.jinja2 @@ -0,0 +1,131 @@ +{% extends "base.jinja2" %} +{% set active_page = 'item_request' %} +{% block title %}{{ _('Item Request') }}{% endblock %} +{% block header %}{{ _('Request a New Item at Chez Betty') }}{% endblock %} + + + +{% block content %} + +

{{ _('Expanding the store.') }}

+ +
+
+

{{ _('New Item Request') }}

+
+
+ +

+ {{ _('Depending on your motivation, you could be the supplier of a new item. ' + 'For instance, if you are grocery shopping you can pick up a few extra of ' + 'what you want, add it the Betty system, and see if it sells well. ' + 'The process is pretty simple (particularly for small numbers of items) ' + 'and we can reimburse you by just writing a check.') }} + {{ _('Get in touch with us if you are interested.') }} +

+ + +
+
+
+
{{ _('Item') }}
+
+
{{ _('Store') }}
+
+ +
+
+
{{ _('Betty only shops at a few regular stores. If these stores do not sell your item, Betty will not be able to carry it regularly, sorry.') }}
+
{{ _('Product URL') }}
+
+
+ +

* Please fill out all required fields

+
+
+
+ +
+
+

{{ _('Open Item Requests') }}

+
+
+ + + + + + + + + + {% for req in requests %} + + + + + {% endfor %} + +
WhenRequest
{{ req.timestamp|human_date|safe }} +
+
+
+
Item
+
{{ req.request }}
+
Store
+
{{ req.vendor.name }}
+ {% if req.vendor_url %} +
URL
+
{{ req.vendor_url }}
+ {% endif %} +
+
+
+ {% if req.posts %} +
+ {% endif %} + {% for post in req.posts %} +
+
+
+
+ {{ post.timestamp|human_date|safe}} +
+ {% if post.staff_post %} + Betty Staff + {% else %} + {{ post.user.name }} + {% endif %} +
+
{{ post.post }}
+
+
+
+ {% if request.user == post.user %} + {{ button.delete("request_post", "deleted", post.id) }} + {% endif %} +
+
+ {% endfor %} +
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+ +{% endblock %} diff --git a/chezbetty/templates/user/sidebar.jinja2 b/chezbetty/templates/user/sidebar.jinja2 index 4a3355f..5370e71 100644 --- a/chezbetty/templates/user/sidebar.jinja2 +++ b/chezbetty/templates/user/sidebar.jinja2 @@ -1,6 +1,7 @@