commiting
32
Makefile
@ -1,33 +1,17 @@
|
||||
IP_ADDR = allpawcare.com
|
||||
DOMAIN = allpawcare.com
|
||||
USER = awkawb
|
||||
IMG_ORIGINAL = "img_original"
|
||||
IMG_PUBLIC = "all_paw_care/static/img"
|
||||
LOCAL_ARCHIVE = $(shell pwd)/public.tar
|
||||
PUBLIC_DIR = "$(shell pwd)/public"
|
||||
REMOTE_DIR = /home/awkawb
|
||||
BOOTSTRAP_MODULE = "bootstrap-custom/boostrap/scss"
|
||||
CUSTOM_SCSS = "bootstrap-custom/custom.scss"
|
||||
PUBLIC_CSS = "all_paw_care/static/css/custom.css"
|
||||
|
||||
|
||||
.PHONY: dev-server
|
||||
dev-server:
|
||||
flask run --no-reload
|
||||
|
||||
.PHONY: build-custom-bootstrap
|
||||
build-custom-bootstrap:
|
||||
sass -I $(BOOTSTRAP_MODULE) $(CUSTOM_SCSS) $(PUBLIC_CSS)
|
||||
|
||||
.PHONY: process-images
|
||||
process-images:
|
||||
./process-images.sh
|
||||
|
||||
.PHONY: push-archive
|
||||
push-archive: archive
|
||||
scp $(LOCAL_ARCHIVE) root@$(DOMAIN):/srv/www && rm $(LOCAL_ARCHIVE)
|
||||
|
||||
.PHONY: archive
|
||||
archive:
|
||||
tar -cf $(LOCAL_ARCHIVE) $(PUBLIC_DIR)
|
||||
|
||||
.PHONY: ssh
|
||||
ssh:
|
||||
ssh $(USER)@$(DOMAIN)
|
||||
|
||||
.PHONY: ssh-root
|
||||
ssh-root:
|
||||
ssh root@$(DOMAIN)
|
||||
|
||||
|
||||
11
TODO.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Services/<service>
|
||||
|
||||
Route to service documentation page.
|
||||
|
||||
# Hand written typography
|
||||
|
||||
# Hand drawn doodles
|
||||
|
||||
# Banner for accepting/not accepting clients
|
||||
|
||||
Let users of the site know if I'm seeking new clients.
|
||||
BIN
all_paw_care.db
Normal file
5
all_paw_care.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
username TEXT NOT NULL
|
||||
);
|
||||
|
||||
@ -1,61 +1,28 @@
|
||||
title = 'Frequently asked questions'
|
||||
import os
|
||||
from markdown import markdown
|
||||
import frontmatter
|
||||
|
||||
content = {
|
||||
'How does this process work?': {
|
||||
'tag': 'basics-faq',
|
||||
'answer': '''First go to the services page. On the bottom
|
||||
of the page, you'll find a "Request meet & greet"
|
||||
button. After filling out the form, I'll get an email
|
||||
with the form information. I will contact you about
|
||||
the request within 24 hours.'''
|
||||
},
|
||||
'What types of payments do you accept?': {
|
||||
'tag': 'accepted-payments-faq',
|
||||
'answer': '''A broad range of payment types are accepted '
|
||||
to make the process easy.''',
|
||||
'include': [
|
||||
'Cash',
|
||||
'Credit/Debit cards',
|
||||
'ACH (direct bank transfers)',
|
||||
'Paypal',
|
||||
'Apple Pay',
|
||||
'Google Pay',
|
||||
'Zelle'
|
||||
]
|
||||
},
|
||||
'When is payment for a service due?': {
|
||||
'tag': 'payment-terms-faq',
|
||||
'answer': 'Payments are due 7 days after invoicing. '
|
||||
+ 'Invoices are usually sent after completed services.'
|
||||
},
|
||||
'What kind of animals do you caretake?': {
|
||||
'tag': 'animal-types-faq',
|
||||
'answer': '''I have experience careing for many types of animals.
|
||||
I provide care for reptiles, mammals, etc.'''
|
||||
},
|
||||
'Can you care for special needs pets?': {
|
||||
'tag': 'special-needs-faq',
|
||||
'answer': '''I have experience care for special needs, including older
|
||||
animals'''
|
||||
},
|
||||
'Can you stay at my house?': {
|
||||
'tag': 'house-sitting-faq',
|
||||
'answer': 'I offer house sitting services. This includes in home stays.'
|
||||
},
|
||||
'Can you take care of my plants?': {
|
||||
'tag': 'plant-faq',
|
||||
'answer': '''I love plants and have a garden of my own. I'll gladly
|
||||
remove the mess for you.'''
|
||||
},
|
||||
'My yard is a mess, can you clean up the dog poop?': {
|
||||
'tag': 'poop-scoop-faq',
|
||||
'answer': 'I do offer animal poop clean up add-on yard'
|
||||
}
|
||||
}
|
||||
markdown_dir = '{}/markdown'.format(
|
||||
os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
def get_title():
|
||||
return title
|
||||
def __get_content_dict():
|
||||
faqs = dict()
|
||||
for f in os.listdir(markdown_dir):
|
||||
f_abspath = '{}/{}'.format(markdown_dir, f)
|
||||
matter = frontmatter.load(f_abspath)
|
||||
faq = {matter.metadata['question']: {
|
||||
'tag': matter.metadata['tag'],
|
||||
'answer': markdown(matter.content)}}
|
||||
|
||||
faqs.update(faq)
|
||||
|
||||
return faqs
|
||||
|
||||
content = __get_content_dict()
|
||||
def get_content():
|
||||
return content
|
||||
|
||||
title = 'Frequently asked questions'
|
||||
def get_title():
|
||||
return title
|
||||
|
||||
|
||||
23
all_paw_care/content/pages/faqs/faq.py
Normal file
@ -0,0 +1,23 @@
|
||||
from markdown import markdown
|
||||
import frontmatter
|
||||
|
||||
import os
|
||||
|
||||
class FAQ(object):
|
||||
def __init__(self, markdown_file: str):
|
||||
with frontmatter.load(markdown_file) as matter:
|
||||
metadata = matter.metadata
|
||||
self.answer = markdown(matter.content)
|
||||
|
||||
self.question = metadata['question']
|
||||
self.tag = metadata['tag']
|
||||
|
||||
def get_question(self):
|
||||
return self.question
|
||||
|
||||
def get_answer_html(self):
|
||||
return self.answer
|
||||
|
||||
def get_tag(self):
|
||||
return self.tag
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
---
|
||||
tag: accepted-payments-faq
|
||||
question: What types of payments do you accept?
|
||||
---
|
||||
|
||||
A broad range of payment types are accepted to make the process
|
||||
easy. Below is a list of what is accepted:
|
||||
|
||||
- cash
|
||||
- credit/debit
|
||||
- ACH (bank transfers)
|
||||
- Paypal
|
||||
- Apple Pay
|
||||
- Zelle
|
||||
|
||||
11
all_paw_care/content/pages/faqs/markdown/animal-types-faq.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
tag: animal-types-faq
|
||||
question: What kind of animals do you caretake?
|
||||
---
|
||||
|
||||
I have experience caring for many types of animals. Below is a
|
||||
non-exhaustive list of animals I have experience with:
|
||||
|
||||
- reptiles (e.g. snakes, lizards, and spiders)
|
||||
- mammals (e.g. cats, dogs, and guinea pigs)
|
||||
|
||||
10
all_paw_care/content/pages/faqs/markdown/basics-faq.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
tag: basics-faq
|
||||
question: How does this process work?
|
||||
---
|
||||
|
||||
First go to the [services](/pages/services#request-meet-greet) page. On the bottom
|
||||
of the page, you'll find a "Request meet and & greet" button. After
|
||||
filling out the form, I'll get an email with the form information.
|
||||
I will contact you about the request within 24 hours.
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
---
|
||||
tag: house-sitting-faq
|
||||
question: Can you watch my pets while I'm away?
|
||||
---
|
||||
|
||||
I offer house sitting services. The service includes overnight
|
||||
in-home stays with your pet.
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
---
|
||||
tag: payment-terms-faq
|
||||
question: When is payment for a service due?
|
||||
---
|
||||
|
||||
Payments are due 7 days after invoicing. Invoices are typically sent
|
||||
after services are completed.
|
||||
|
||||
8
all_paw_care/content/pages/faqs/markdown/plant-faq.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
tag: plant-faq
|
||||
question: I have plants, can you care for them while I'm away?
|
||||
---
|
||||
|
||||
I'll gladly make sure your plants stay healthy while your gone. I love
|
||||
plants and have a collection of my own.
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
---
|
||||
tag: poop-scoop-faq
|
||||
question: My yard is a mess, can you remove the dog poop?
|
||||
---
|
||||
|
||||
I do offer dog poop clean-up as an add-on services. I can provide
|
||||
this service along with a scheduled primary service.
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
---
|
||||
tag: special-needs-faq
|
||||
question: Can you care for special needs pets?
|
||||
---
|
||||
|
||||
I have experience caring for animals with special needs, including
|
||||
older pets.
|
||||
|
||||
@ -1,51 +1,31 @@
|
||||
from flask import url_for
|
||||
from markdown import markdown
|
||||
import frontmatter
|
||||
|
||||
import os
|
||||
|
||||
markdown_dir = '{}/markdown'.format(
|
||||
os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
title = 'About me'
|
||||
|
||||
content = {
|
||||
'section 1': {
|
||||
'images': ['img/about_me/about_me_1_sm.jpg',
|
||||
'img/about_me/about_me_1_md.jpg',
|
||||
'img/about_me/about_me_1_lg.jpg',
|
||||
'img/about_me/about_me_1_xl.jpg',
|
||||
'img/about_me/about_me_1_xxl.jpg'],
|
||||
|
||||
'content': '''Before I provided animal caretaking services,
|
||||
I focused my time on computers. I attended AACC
|
||||
after high school, computer network management and
|
||||
computer science are the two areas I studied.
|
||||
I designed and coded this website!'''
|
||||
},
|
||||
'section 2': {
|
||||
'images': ['img/about_me/about_me_2_sm.jpg',
|
||||
'img/about_me/about_me_2_md.jpg',
|
||||
'img/about_me/about_me_2_lg.jpg',
|
||||
'img/about_me/about_me_2_xl.jpg',
|
||||
'img/about_me/about_me_2_xxl.jpg'],
|
||||
'content': '''For the past 5 years, my interest in plants and
|
||||
horticulture have expanded exponentially. As a kid,
|
||||
my mother always had plants around our home. I
|
||||
have many plants of my own, most of which I've
|
||||
grown from cuttings I take around town. My favorite
|
||||
plant type is succulents (e.g jade plants and aloes)
|
||||
because they're so easy to grow.'''
|
||||
},
|
||||
'section 3': {
|
||||
'images': ['img/about_me/about_me_3_sm.jpg',
|
||||
'img/about_me/about_me_3_md.jpg',
|
||||
'img/about_me/about_me_3_lg.jpg',
|
||||
'img/about_me/about_me_3_xl.jpg',
|
||||
'img/about_me/about_me_3_xxl.jpg'],
|
||||
'content': '''My passion for animals and plants are what I
|
||||
spend most my time on currently. I love providing
|
||||
quality animal care.'''
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_title():
|
||||
return title
|
||||
|
||||
def get_content():
|
||||
return content
|
||||
def __get_content_dict():
|
||||
sections = dict()
|
||||
for file in os.listdir(markdown_dir):
|
||||
images = list()
|
||||
matter = frontmatter.load('{}/{}'.format(markdown_dir, file))
|
||||
for img in matter.metadata['images']:
|
||||
images.append('{}/{}'.format(
|
||||
matter.metadata['image-dir'], img))
|
||||
|
||||
sections[file.removesuffix('.md')] = {'images':
|
||||
images, 'content': markdown(matter.content)}
|
||||
|
||||
return sections
|
||||
|
||||
content = __get_content_dict()
|
||||
def get_content():
|
||||
return content.values()
|
||||
|
||||
|
||||
16
all_paw_care/content/pages/home/markdown/section1.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
image-dir: img/about_me
|
||||
images:
|
||||
- about_me_1_sm.jpg
|
||||
- about_me_1_md.jpg
|
||||
- about_me_1_lg.jpg
|
||||
- about_me_1_xl.jpg
|
||||
- about_me_1_xxl.jpg
|
||||
---
|
||||
|
||||
Before I provided animal caretaking services, I focused on my interest
|
||||
in computer technologies. I studied computer networking and computer
|
||||
science at [AACC](https://www.aacc.edu). The knowledge I gained from my
|
||||
studies was harnessed writing the code that powers this website and
|
||||
configuring the servers that the code runs on.
|
||||
|
||||
18
all_paw_care/content/pages/home/markdown/section2.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
image-dir: img/about_me
|
||||
images:
|
||||
- about_me_2_sm.jpg
|
||||
- about_me_2_md.jpg
|
||||
- about_me_2_lg.jpg
|
||||
- about_me_2_xl.jpg
|
||||
- about_me_2_xxl.jpg
|
||||
---
|
||||
|
||||
Plants and horticulture are a passion of mine, too. My interest has
|
||||
actively grown since starting caretaking services. Caring for
|
||||
animals in the Baltimore area, I have the opportunity of observing
|
||||
a broad range of vegetation. I've collected cuttings for propagation
|
||||
and many survive and thrive enough to make it to my garden or potted
|
||||
house plants. Succulent type plants are a favorite of mine and I have
|
||||
a ever growing collection.
|
||||
|
||||
17
all_paw_care/content/pages/home/markdown/section3.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
image-dir: img/about_me
|
||||
images:
|
||||
- about_me_3_sm.jpg
|
||||
- about_me_3_md.jpg
|
||||
- about_me_3_lg.jpg
|
||||
- about_me_3_xl.jpg
|
||||
- about_me_3_xxl.jpg
|
||||
---
|
||||
|
||||
I've been lucky enough to have the opportunity to grow my animal
|
||||
caretaking services to my primary source of income. I started
|
||||
listing availability on the [Rover](https://www.rover.com/) platform
|
||||
in 2018. After gaining loyal clients and reputation, moving my services
|
||||
to being independent was a logical move. I really love providing great
|
||||
and affordable caretaking services.
|
||||
|
||||
@ -1,52 +1,25 @@
|
||||
title = 'Services & Pricing'
|
||||
|
||||
content = {
|
||||
'Primary services': [
|
||||
{
|
||||
'title': 'Walking',
|
||||
'images': [
|
||||
'img/services/dog_walking_card_sm.png',
|
||||
'img/services/dog_walking_card_md.png',
|
||||
'img/services/dog_walking_card_lg.png',
|
||||
'img/services/dog_walking_card_xl.png',
|
||||
'img/services/dog_walking_card_xxl.png'],
|
||||
'price': '$20/visit',
|
||||
'included': [
|
||||
'30 minute walk.',
|
||||
'Bags for poop clean-up.']
|
||||
},
|
||||
{
|
||||
'title': 'Drop-in',
|
||||
'images': [
|
||||
'img/services/drop_in_card_sm.png',
|
||||
'img/services/drop_in_card_md.png',
|
||||
'img/services/drop_in_card_lg.png',
|
||||
'img/services/drop_in_card_xl.png',
|
||||
'img/services/drop_in_card_xxl.png'],
|
||||
'price': '$20/visit',
|
||||
'included': [
|
||||
'30 minute in home visit.',
|
||||
'Bags for poop clean-up.']
|
||||
},
|
||||
{
|
||||
'title': 'House sitting',
|
||||
'images': [
|
||||
'img/services/house_sitting_card_sm.png',
|
||||
'img/services/house_sitting_card_md.png',
|
||||
'img/services/house_sitting_card_lg.png',
|
||||
'img/services/house_sitting_card_xl.png',
|
||||
'img/services/house_sitting_card_xxl.png'],
|
||||
'price': '$38/visit',
|
||||
'included': [
|
||||
'Animal care.',
|
||||
'In-home overnight stays.']
|
||||
}
|
||||
]
|
||||
}
|
||||
from all_paw_care.content.pages.services.cards import primary_services
|
||||
|
||||
#def __get_content_dict():
|
||||
# content = {'primary-services': dict(), 'addon-services': dict()}
|
||||
# for file in os.listdir(markdown_dir):
|
||||
# images = list()
|
||||
# matter = frontmatter.load('{}/{}'.format(markdown_dir, file))
|
||||
# if matter.metadata['type'] == 'primary':
|
||||
# for img in matter.metadata['images']:
|
||||
# images.append('{}/{}'.format(
|
||||
# matter.metadata['image-dir'], img))
|
||||
#
|
||||
# content['primary-services'].update()
|
||||
#
|
||||
# return content
|
||||
#
|
||||
#content = __get_content_dict()
|
||||
#
|
||||
__title = 'Services & Pricing'
|
||||
def get_title():
|
||||
return title
|
||||
return __title
|
||||
|
||||
def get_content():
|
||||
return content
|
||||
def get_primary_service_cards():
|
||||
return primary_services
|
||||
|
||||
|
||||
6
all_paw_care/content/pages/services/cards/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
from all_paw_care.content.pages.services.cards.drop_ins import drop_ins
|
||||
from all_paw_care.content.pages.services.cards.house_sitting import house_sitting
|
||||
from all_paw_care.content.pages.services.cards.walking import walking
|
||||
|
||||
primary_services = [drop_ins, house_sitting, walking]
|
||||
|
||||
37
all_paw_care/content/pages/services/cards/card.py
Normal file
@ -0,0 +1,37 @@
|
||||
from markdown import markdown
|
||||
import frontmatter
|
||||
|
||||
import os
|
||||
|
||||
class Card(object):
|
||||
def __init__(self, card_file: str):
|
||||
metadata = frontmatter.load(card_file).metadata
|
||||
self.service_type = metadata['type']
|
||||
self.title = metadata['title']
|
||||
self.images = self._get_image_list(
|
||||
metadata['image-dir'], metadata['images'])
|
||||
self.price = metadata['price']
|
||||
self.included = metadata['included']
|
||||
|
||||
def _get_image_list(self, base_dir: str, images: list):
|
||||
image_list = list()
|
||||
for img in images:
|
||||
image_list.append(os.path.join(base_dir, img))
|
||||
|
||||
return image_list
|
||||
|
||||
def get_title(self):
|
||||
return self.title
|
||||
|
||||
def get_price(self):
|
||||
return self.price
|
||||
|
||||
def get_included(self):
|
||||
return self.included
|
||||
|
||||
def get_images(self):
|
||||
return self.images
|
||||
|
||||
def get_default_image(self):
|
||||
return self.images[1]
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
from all_paw_care.content.pages.services.cards.card import Card
|
||||
|
||||
import os
|
||||
|
||||
__markdown_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'drop-ins.md')
|
||||
|
||||
drop_ins = Card(__markdown_file)
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
---
|
||||
type: primary
|
||||
title: Drop-ins
|
||||
image-dir: img/services
|
||||
images:
|
||||
- drop_in_card_sm.png
|
||||
- drop_in_card_md.png
|
||||
- drop_in_card_lg.png
|
||||
- drop_in_card_xl.png
|
||||
- drop_in_card_xxl.png
|
||||
price: $20/visit
|
||||
included:
|
||||
- 30 minute at home visit
|
||||
- Bags for poop clean-up
|
||||
---
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
from all_paw_care.content.pages.services.cards.card import Card
|
||||
|
||||
import os
|
||||
|
||||
__markdown_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'house-sitting.md')
|
||||
|
||||
house_sitting = Card(__markdown_file)
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
---
|
||||
type: primary
|
||||
title: House sitting
|
||||
image-dir: img/services
|
||||
images:
|
||||
- house_sitting_card_sm.png
|
||||
- house_sitting_card_md.png
|
||||
- house_sitting_card_lg.png
|
||||
- house_sitting_card_xl.png
|
||||
- house_sitting_card_xxl.png
|
||||
price: $38/night
|
||||
included:
|
||||
- Animal care
|
||||
- At home overnight stays
|
||||
- Bags for poop clean-up
|
||||
---
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
from all_paw_care.content.pages.services.cards.card import Card
|
||||
|
||||
from markdown import markdown
|
||||
import frontmatter
|
||||
|
||||
import os
|
||||
|
||||
__markdown_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'walking.md')
|
||||
|
||||
walking = Card(__markdown_file)
|
||||
|
||||
16
all_paw_care/content/pages/services/cards/walking/walking.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
type: primary
|
||||
title: Walking
|
||||
image-dir: img/services
|
||||
images:
|
||||
- dog_walking_card_sm.png
|
||||
- dog_walking_card_md.png
|
||||
- dog_walking_card_lg.png
|
||||
- dog_walking_card_xl.png
|
||||
- dog_walking_card_xxl.png
|
||||
price: $20/visit
|
||||
included:
|
||||
- 30 minute walk
|
||||
- Bags for poop clean-up
|
||||
---
|
||||
|
||||
7
all_paw_care/content/pages/services/services.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Services & Pricing
|
||||
---
|
||||
|
||||
Below are animal caretaking services I provide. New clients should
|
||||
fill out the [meet & greet request](/forms/meet_and_gree) form
|
||||
|
||||
57
all_paw_care/forms/meetgreetnotification.py
Normal file
@ -0,0 +1,57 @@
|
||||
from config import Config
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
# TODO replace self.smtp_url and self.email_user with Config values
|
||||
|
||||
class MeetGreetNotification(object):
|
||||
def __init__(self, config: str):
|
||||
self.smtp_url =
|
||||
self.email_user =
|
||||
|
||||
def __get_smtp_client(self):
|
||||
from smtplib import SMTP_SSL
|
||||
|
||||
smtp_client = SMTP_SSL(host=self.smtp_url)
|
||||
smtp_client.login(self.email_user,self.c['fastmail-smtp']['pass'])
|
||||
|
||||
return smtp_client
|
||||
|
||||
def send(self, name: str, address: str, greet_time: datetime, notes: str = None):
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
|
||||
subject = "Meet & greet request: {}".format(name)
|
||||
email = MIMEMultipart()
|
||||
email['Subject'] = subject
|
||||
email['From'] = self.email_user
|
||||
email['To'] = self.email_user
|
||||
|
||||
content = '''
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<td>{}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Address</th>
|
||||
<td>{}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Datetime</th>
|
||||
<td>{}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Notes</th>
|
||||
<td>{}</td>
|
||||
</tr>
|
||||
</table>
|
||||
'''.format(name,address,greet_time,notes)
|
||||
|
||||
email_content = MIMEMultipart('alternative')
|
||||
email_content.attach(MIMEText(content, 'html'))
|
||||
email.attach(email_content)
|
||||
|
||||
with self.__get_smtp_client() as c:
|
||||
c.sendmail(self.email_user,self.email_user,email.as_string())
|
||||
@ -1,25 +1,10 @@
|
||||
# Python Invoice Ninja library
|
||||
#from all_paw_care.invoice_ninja import swagger_client
|
||||
#from all_paw_care.swagger_client.rest import ApiException
|
||||
|
||||
# Invoice Ninja library dependencies
|
||||
#from __future__ import print_function
|
||||
#import time
|
||||
#from pprint import pprint
|
||||
|
||||
BASE_URL='https://invoice.allpawcare.com/api/v1'
|
||||
|
||||
API_TOKEN='r4AKEz9xRgk2SA7IotAvLrj64f7s0BczLDVjVwmjiPWeyG0fpu2eyib4VKI23QNO'
|
||||
|
||||
INVOICE_NINJA_USERNAME = 'billing@allpawcare.com'
|
||||
INVOICE_NINJA_PASSWORD = """sw'1eMqN6#9fO!3"RY$L"""
|
||||
|
||||
INVOICE_NINJA_LOGIN_DICT = {
|
||||
'email': INVOICE_NINJA_USERNAME,
|
||||
'pasword': INVOICE_NINJA_PASSWORD
|
||||
}
|
||||
|
||||
#invoice_ninja = swagger_client()
|
||||
|
||||
|
||||
'email': INVOICE_NINJA_USERNAME,
|
||||
'pasword': INVOICE_NINJA_PASSWORD
|
||||
}
|
||||
|
||||
|
||||
94
all_paw_care/invoice_ninja/clients/__init__.py
Normal file
@ -0,0 +1,94 @@
|
||||
from all_paw_care.invoice_ninja import API_TOKEN
|
||||
from all_paw_care.invoice_ninja import BASE_URL
|
||||
from all_paw_care.invoice_ninja.types.client import Client
|
||||
|
||||
import requests
|
||||
|
||||
class Clients(object):
|
||||
CLIENTS_URL = '{}/clients'.format(BASE_URL)
|
||||
HEADERS = {'X-API-TOKEN': API_TOKEN}
|
||||
|
||||
def __build_sort_params(self, sort: dict):
|
||||
sort_params = {'sort': str()}
|
||||
is_first_entry = True
|
||||
for option in sort.keys():
|
||||
if is_first_entry:
|
||||
sort_params['sort'] += '{}|{}'.format(option, sort[option])
|
||||
is_first_entry = False
|
||||
|
||||
else:
|
||||
sort_params['sort'] += ' {}|{}'.format(option, sort[option])
|
||||
|
||||
return sort_params
|
||||
|
||||
def __client_from_dict(self, client: dict):
|
||||
return Client(client_id=client['id'],
|
||||
name=client['name'],
|
||||
address=client['address1'],
|
||||
city=client['city'],
|
||||
state=client['state'],
|
||||
postal_code=client['postal_code'],
|
||||
phone=client['phone'],
|
||||
email=client['contacts'][0]['email'],
|
||||
pets=client['custom_value1'])
|
||||
|
||||
def __clients_from_response(self, response: requests.Response):
|
||||
clients = list()
|
||||
for client_dict in response.json()['data']:
|
||||
clients.append(self.__client_from_dict(client_dict))
|
||||
|
||||
return clients
|
||||
|
||||
def list_client(self, client_id: str = None):
|
||||
"""
|
||||
Get client based on client id.
|
||||
"""
|
||||
|
||||
if client_id:
|
||||
request_url = '{}/{}'.format(self.CLIENTS_URL, client_id)
|
||||
response = requests.get(url=request_url,
|
||||
headers=self.HEADERS)
|
||||
|
||||
if response.ok:
|
||||
return self.__client_from_dict(response.json()['data'])
|
||||
|
||||
return None
|
||||
|
||||
def list_clients(self, include: str = 'activities',
|
||||
sort: dict = dict(), status: str = 'active',
|
||||
name: str = None):
|
||||
"""
|
||||
Finds Invoice Ninja clients.
|
||||
"""
|
||||
|
||||
request_params = dict()
|
||||
|
||||
# Add sort parameters to request
|
||||
if len(sort) > 0:
|
||||
request_params.update(self.__build_sort_params(sort))
|
||||
|
||||
# Add include parameters to request
|
||||
request_params.update({'include': include})
|
||||
|
||||
# Add status parameters to request
|
||||
request_params.update({'status': status})
|
||||
|
||||
# Add name parameters to request
|
||||
if name:
|
||||
request_params.update({'name': name})
|
||||
|
||||
# Check is request should be sent with parameters
|
||||
if len(request_params) > 0:
|
||||
response = requests.get(url=self.CLIENTS_URL,
|
||||
params=request_params,
|
||||
headers=self.HEADERS)
|
||||
else:
|
||||
response = requests.get(url=self.CLIENTS_URL,
|
||||
headers=self.HEADERS)
|
||||
|
||||
if response.ok:
|
||||
return self.__clients_from_response(response)
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
class Sort(object):
|
||||
def __init__(self, id: str = None,
|
||||
name: str = None,
|
||||
balance: str = None):
|
||||
self.sort_params = {'sort': str()}
|
||||
is_first_entry = True
|
||||
if id:
|
||||
if is_first_entry:
|
||||
self.sort_params['sort'] += '{}|{}'.format(option, sort[option])
|
||||
is_first_entry = False
|
||||
|
||||
else:
|
||||
self.sort_params['sort'] += ' {}|{}'.format(option, sort[option])
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
from all_paw_care.invoice_ninja import API_TOKEN
|
||||
from all_paw_care.invoice_ninja import BASE_URL
|
||||
from all_paw_care.invoice_ninja.mappings.client import Client
|
||||
from all_paw_care.invoice_ninja.types.product import Product
|
||||
|
||||
import requests
|
||||
|
||||
class Clients(object):
|
||||
class Products(object):
|
||||
CLIENTS_URL = '{}/clients'.format(BASE_URL)
|
||||
HEADERS = {'X-API-TOKEN': API_TOKEN}
|
||||
|
||||
9617
all_paw_care/static/css/custom.css
Normal file
1
all_paw_care/static/css/custom.css.map
Normal file
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 275 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 348 KiB After Width: | Height: | Size: 132 KiB |
BIN
all_paw_care/static/img/about_me_1_lg.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
all_paw_care/static/img/about_me_1_md.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
all_paw_care/static/img/about_me_1_sm.jpg
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
all_paw_care/static/img/about_me_1_xl.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
all_paw_care/static/img/about_me_1_xxl.jpg
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
all_paw_care/static/img/about_me_2_lg.jpg
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
all_paw_care/static/img/about_me_2_md.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
all_paw_care/static/img/about_me_2_sm.jpg
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
all_paw_care/static/img/about_me_2_xl.jpg
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
all_paw_care/static/img/about_me_2_xxl.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
all_paw_care/static/img/about_me_3_lg.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
all_paw_care/static/img/about_me_3_md.jpg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
all_paw_care/static/img/about_me_3_sm.jpg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
all_paw_care/static/img/about_me_3_xl.jpg
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
all_paw_care/static/img/about_me_3_xxl.jpg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
all_paw_care/static/img/logo_instagram_lg.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
all_paw_care/static/img/logo_instagram_md.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
all_paw_care/static/img/logo_instagram_sm.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
all_paw_care/static/img/logo_instagram_xl.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
all_paw_care/static/img/logo_instagram_xxl.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
all_paw_care/static/img/services_dog_walking_card_lg.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
all_paw_care/static/img/services_dog_walking_card_md.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
all_paw_care/static/img/services_dog_walking_card_sm.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
all_paw_care/static/img/services_dog_walking_card_xl.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
all_paw_care/static/img/services_dog_walking_card_xxl.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
all_paw_care/static/img/services_drop_in_card_lg.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
all_paw_care/static/img/services_drop_in_card_md.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
all_paw_care/static/img/services_drop_in_card_sm.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
all_paw_care/static/img/services_drop_in_card_xl.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
all_paw_care/static/img/services_drop_in_card_xxl.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
all_paw_care/static/img/services_house_sitting_card_lg.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
all_paw_care/static/img/services_house_sitting_card_md.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
all_paw_care/static/img/services_house_sitting_card_sm.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
all_paw_care/static/img/services_house_sitting_card_xl.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
all_paw_care/static/img/services_house_sitting_card_xxl.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
49
all_paw_care/templates/contact-me.html
Normal file
@ -0,0 +1,49 @@
|
||||
{% extends "jinja/types/page.html" %}
|
||||
|
||||
{% set title %}Contact me{% endset %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<h4>If you have any questions, the
|
||||
<a href="../faq.html">FAQ</a>
|
||||
page may have an answer. Contacting me directly by
|
||||
<a href="mailto: bryant.caregiving@fastmail.com">email</a> or
|
||||
<a href="tel:+14102799125">text</a> message is welcome.
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-6">
|
||||
<h3 class="text-center text-primary">
|
||||
Contact me directly!
|
||||
</h3>
|
||||
|
||||
<a href="tel::+14102799125" class="mr-8">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" fill="currentColor" class="bi bi-telephone-fill" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M1.885.511a1.745 1.745 0 0 1 2.61.163L6.29 2.98c.329.423.445.974.315 1.494l-.547 2.19a.678.678 0 0 0 .178.643l2.457 2.457a.678.678 0 0 0 .644.178l2.189-.547a1.745 1.745 0 0 1 1.494.315l2.306 1.794c.829.645.905 1.87.163 2.611l-1.034 1.034c-.74.74-1.846 1.065-2.877.702a18.634 18.634 0 0 1-7.01-4.42 18.634 18.634 0 0 1-4.42-7.009c-.362-1.03-.037-2.137.703-2.877L1.885.511z"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a href="mailto: bryant.caregiving@fastmail.com">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" fill="currentColor" class="bi bi-envelope-fill" viewBox="0 0 16 16">
|
||||
<path d="M.05 3.555A2 2 0 0 1 2 2h12a2 2 0 0 1 1.95 1.555L8 8.414.05 3.555ZM0 4.697v7.104l5.803-3.558L0 4.697ZM6.761 8.83l-6.57 4.027A2 2 0 0 0 2 14h12a2 2 0 0 0 1.808-1.144l-6.57-4.027L8 9.586l-1.239-.757Zm3.436-.586L16 11.801V4.697l-5.803 3.546Z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<h3 class="text-center text-primary">
|
||||
Follow me!
|
||||
</h3>
|
||||
|
||||
<a href="https://www.instagram.com/awkawb" target="_blank">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" fill="currentColor" class="bi bi-instagram" viewBox="0 0 16 16">
|
||||
<path d="M8 0C5.829 0 5.556.01 4.703.048 3.85.088 3.269.222 2.76.42a3.917 3.917 0 0 0-1.417.923A3.927 3.927 0 0 0 .42 2.76C.222 3.268.087 3.85.048 4.7.01 5.555 0 5.827 0 8.001c0 2.172.01 2.444.048 3.297.04.852.174 1.433.372 1.942.205.526.478.972.923 1.417.444.445.89.719 1.416.923.51.198 1.09.333 1.942.372C5.555 15.99 5.827 16 8 16s2.444-.01 3.298-.048c.851-.04 1.434-.174 1.943-.372a3.916 3.916 0 0 0 1.416-.923c.445-.445.718-.891.923-1.417.197-.509.332-1.09.372-1.942C15.99 10.445 16 10.173 16 8s-.01-2.445-.048-3.299c-.04-.851-.175-1.433-.372-1.941a3.926 3.926 0 0 0-.923-1.417A3.911 3.911 0 0 0 13.24.42c-.51-.198-1.092-.333-1.943-.372C10.443.01 10.172 0 7.998 0h.003zm-.717 1.442h.718c2.136 0 2.389.007 3.232.046.78.035 1.204.166 1.486.275.373.145.64.319.92.599.28.28.453.546.598.92.11.281.24.705.275 1.485.039.843.047 1.096.047 3.231s-.008 2.389-.047 3.232c-.035.78-.166 1.203-.275 1.485a2.47 2.47 0 0 1-.599.919c-.28.28-.546.453-.92.598-.28.11-.704.24-1.485.276-.843.038-1.096.047-3.232.047s-2.39-.009-3.233-.047c-.78-.036-1.203-.166-1.485-.276a2.478 2.478 0 0 1-.92-.598 2.48 2.48 0 0 1-.6-.92c-.109-.281-.24-.705-.275-1.485-.038-.843-.046-1.096-.046-3.233 0-2.136.008-2.388.046-3.231.036-.78.166-1.204.276-1.486.145-.373.319-.64.599-.92.28-.28.546-.453.92-.598.282-.11.705-.24 1.485-.276.738-.034 1.024-.044 2.515-.045v.002zm4.988 1.328a.96.96 0 1 0 0 1.92.96.96 0 0 0 0-1.92zm-4.27 1.122a4.109 4.109 0 1 0 0 8.217 4.109 4.109 0 0 0 0-8.217zm0 1.441a2.667 2.667 0 1 1 0 5.334 2.667 2.667 0 0 1 0-5.334z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
39
all_paw_care/templates/faq.html
Normal file
@ -0,0 +1,39 @@
|
||||
{% extends "jinja/types/page.html" %}
|
||||
|
||||
{% set title %}FAQ{% endset %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row text-center">
|
||||
<h2 class="text-primary">Do you accept more than one animal?</h2>
|
||||
</div>
|
||||
|
||||
<div class="row text-center pb-3">
|
||||
<h5 class="text-secondary">
|
||||
I do but all services are priced for one animal. For more than
|
||||
one pet I charge $8 per additional animal for walking and house
|
||||
sitting services.
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div class="row text-center">
|
||||
<h2 class="text-primary">What types of payments do you accept?</h2>
|
||||
</div>
|
||||
|
||||
<div class="row text-center pb-3">
|
||||
<h5 class="text-secondary">
|
||||
I accept all major credit cards, bank transfers, and Paypal. Invoices are sent after service is rendered.
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div class="row text-center">
|
||||
<h2 class="text-primary">Can you clean up my yard, my dog has made it a mess?</h2>
|
||||
</div>
|
||||
|
||||
<div class="row text-center">
|
||||
<h5 class="text-secondary">
|
||||
I offer poop clean up as an additional add-on service. If you
|
||||
decide to schedule a walk or house sitting, I will gladly
|
||||
remove the poop.
|
||||
</h5>
|
||||
</div>
|
||||
{% endblock %}
|
||||
49
all_paw_care/templates/index.html
Normal file
@ -0,0 +1,49 @@
|
||||
{% extends "jinja/types/page.html" %}
|
||||
|
||||
{% set title %}About me{% endset %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row pb-3">
|
||||
<div class="col-8 text-center">
|
||||
Welcome to All Paw Care!
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<img class="img-fluid"
|
||||
src="/static/img/about_me_1_md.jpg"
|
||||
srcset="/static/img/about_me_1_sm.jpg 540w, /static/img/about_me_1_md.jpg 720w, /static/img/about_me_1_lg.jpg 960w"
|
||||
sizes="50vw">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pb-3">
|
||||
<div class="col-4">
|
||||
<img class="img-fluid" src="/static/img/about_me_2_lg.jpg"
|
||||
srcset="/static/img/about_me_2_sm.jpg 540w, /static/img/about_me_2_md.jpg 720w, /static/img/about_me_2_lg.jpg 960w"
|
||||
sizes="50vw">
|
||||
</div>
|
||||
|
||||
<div class="col-8 text-center">
|
||||
<p>
|
||||
I've been providing professional animal caretaking
|
||||
services in the Baltimore area for over 4 years. I
|
||||
provide services for paws of all shapes and sizes.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-8 text-center">
|
||||
<p>
|
||||
I specialize in dog and cat care. Animals add so much
|
||||
to our lives so caring for them is just as important.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<img class="img-fluid" src="/static/img/about_me_3_lg.jpg"
|
||||
srcset="/static/img/about_me_3_sm.jpg 540w, /static/img/about_me_3_md.jpg 720w, /static/img/about_me_3_lg.jpg 960w"
|
||||
sizes="50vw">
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -1,10 +1,14 @@
|
||||
<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">
|
||||
© {{ current_year }} Andrew Bryant
|
||||
</p>
|
||||
<footer class="footer mt-5 bg-tertiary">
|
||||
<div class="container">
|
||||
<nav class="navbar navbar-expand-sm text-light">
|
||||
<div class="container">
|
||||
<p class="col-md-4 mb-0 text-muted">
|
||||
© {{ current_year }} Andrew Bryant
|
||||
</p>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
{% include "jinja/menu.html" %}
|
||||
</nav>
|
||||
{% include "jinja/menu.html" %}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
@ -1,7 +1,17 @@
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-md sticky-top navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
{% include "jinja/menu.html" %}
|
||||
<nav class="navbar navbar-expand-sm sticky-top bg-light" data-bs-theme="light">
|
||||
<div class="container">
|
||||
<button class="navbar-toggler" type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#menu-bar-items"
|
||||
aria-controls="menu-bar-items"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle menu bar">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="menu-bar-items">
|
||||
{% include "jinja/menu.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
19
all_paw_care/templates/jinja/menu (copy).html
Normal file
@ -0,0 +1,19 @@
|
||||
{% for page in pages %}
|
||||
<li>
|
||||
{% if title == page %}
|
||||
<a href="{{ pages[page] }}" class="nav-link text-secondary">
|
||||
<svg class="bi d-block mx-auto mb-1" width="24" height="24">
|
||||
<use xlink:href="#home"></use>
|
||||
</svg>
|
||||
{{ page }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ pages[page] }}" class="nav-link text-white">
|
||||
<svg class="bi d-block mx-auto mb-1" width="24" height="24">
|
||||
<use xlink:href="#home"></use>
|
||||
</svg>
|
||||
{{ page }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
@ -1,30 +1,30 @@
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('pages.home') }}" class="nav-link text-secondary">
|
||||
<a href="{{ url_for('pages.home') }}" class="nav-link link-white">
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('pages.faq') }}" class="nav-link text-secondary">
|
||||
<a href="{{ url_for('pages.faq') }}" class="nav-link link-white">
|
||||
FAQ
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('pages.services') }}" class="nav-link text-secondary">
|
||||
<a href="{{ url_for('pages.services') }}" class="nav-link link-white">
|
||||
Services
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('users.user_login') }}" class="nav-link text-secondary">
|
||||
<a href="{{ url_for('users.user_login') }}" class="nav-link link-white">
|
||||
Login
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ url_for('users.user_create') }}" class="nav-link text-secondary">
|
||||
<a href="{{ url_for('users.user_create') }}" class="nav-link link-white">
|
||||
Create account
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@ -6,8 +6,7 @@
|
||||
<title>
|
||||
{% block title %}{% endblock %}
|
||||
</title>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
|
||||
<link href="{{ url_for('static', filename="css/custom.css") }}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
{% include "jinja/header.html" %}
|
||||
|
||||
@ -4,15 +4,12 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ title }}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
|
||||
|
||||
<link href="{{ url_for('static', filename="css/custom.css") }}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-white">
|
||||
{% include "jinja/header.html" %}
|
||||
|
||||
<main>
|
||||
<hr class="invisible pb-3">
|
||||
|
||||
<div class="container">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
96
all_paw_care/templates/meet-and-greet.html
Normal file
@ -0,0 +1,96 @@
|
||||
{% extends "jinja/types/form.html" %}
|
||||
|
||||
{% set title %}Request meet and greet{% endset %}
|
||||
|
||||
{% block content %}
|
||||
<form method="POST">
|
||||
<h1 class="text-success text-center my-4">Meet & greet</h1>
|
||||
|
||||
<div class="d-flex flex-row justify-content-center px-4">
|
||||
<div class="input-group mx-2">
|
||||
<span class="input-group-text">Pet name(s)</span>
|
||||
<input class="form-control" type="text"
|
||||
placeholder="Tabby, Curly" name="client-pets" id="client-pets" required>
|
||||
</div>
|
||||
|
||||
<div class="input-group mx-2">
|
||||
<span class="input-group-text">Name</span>
|
||||
<input class="form-control" type="text"
|
||||
placeholder="Kasey" name="client-name" id="client-name" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-row justify-content-center input-group m-2">
|
||||
<span class="input-group-text">Visit type</span>
|
||||
|
||||
<input class="btn-check" type="radio"
|
||||
name="visit-type" id="visit-type-walk" required>
|
||||
<label class="btn btn-primary" for="visit-type-walk">
|
||||
Walk
|
||||
</label>
|
||||
|
||||
<input class="btn-check" type="radio"
|
||||
name="visit-type" id="visit-type-sitting">
|
||||
<label class="btn btn-primary" for="visit-type-sitting">
|
||||
House sitting
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-row justify-content-center m-2">
|
||||
<div class="col-4 pe-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Date</span>
|
||||
<input type="date" id="date"
|
||||
name="meet-greet-date" class="form-control"
|
||||
placeholder="mm/dd/yyyy" value="" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-4 ps-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="time">Time</span>
|
||||
<input type="time" id="meet-greet-time"
|
||||
name="meet-greet-time" class="form-control"
|
||||
min="09:00" max="16:00" value="09:00" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-row justify-content-center mx-4 mb-2">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Contact</span>
|
||||
|
||||
<input class="btn-check" type="radio"
|
||||
name="contact-type" id="email" required>
|
||||
<label class="btn btn-primary" for="email">
|
||||
Email
|
||||
</label>
|
||||
|
||||
<input class="btn-check" type="radio"
|
||||
name="contact-type" id="phone">
|
||||
<label class="btn btn-primary" for="phone">
|
||||
Phone
|
||||
</label>
|
||||
|
||||
<input type="text" class="form-control" name="contact"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-row justify-content-center mx-4 mb-2">
|
||||
<span class="input-group-text">Additional notes</span>
|
||||
<textarea class="form-control" rows="5" for="notes"
|
||||
id="notes" name="notes">
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<div class="row m-2">
|
||||
<button class="btn btn-primary" type="submit"
|
||||
action="{{ url_for('forms.meet_and_greet') }}" method="post">
|
||||
Book it
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||