Upload
commit
295f9e9700
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Use the official Python image from the Docker Hub
|
||||||
|
FROM python:3.8-slim
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy the current directory contents into the container
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
# Ensure instance directory exists and is writable
|
||||||
|
RUN mkdir -p /app/instance && chmod -R 777 /app/instance
|
||||||
|
|
||||||
|
# Install any needed packages specified in requirements.txt
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Make port 5000 available to the world outside this container
|
||||||
|
EXPOSE 5005
|
||||||
|
|
||||||
|
# Run app.py when the container launches
|
||||||
|
CMD ["python", "app.py"]
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,44 @@
|
||||||
|
from flask import Flask
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from flask_wtf import CSRFProtect
|
||||||
|
from models import db, Company
|
||||||
|
import os
|
||||||
|
from routes import register_routes
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Construct the full path for the SQLite database
|
||||||
|
database_path = os.path.join(app.root_path, 'instance', 'daily_overview.sqlite')
|
||||||
|
|
||||||
|
# Ensure the instance directory exists
|
||||||
|
instance_path = os.path.join(app.root_path, 'instance')
|
||||||
|
if not os.path.exists(instance_path):
|
||||||
|
os.makedirs(instance_path)
|
||||||
|
|
||||||
|
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{database_path}'
|
||||||
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||||
|
app.config['SECRET_KEY'] = 'your_secret_key'
|
||||||
|
|
||||||
|
db.init_app(app)
|
||||||
|
csrf = CSRFProtect(app)
|
||||||
|
|
||||||
|
print(f"Instance path: {instance_path}")
|
||||||
|
print(f"Database path: {database_path}")
|
||||||
|
print(f"Database URI: {app.config['SQLALCHEMY_DATABASE_URI']}")
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
try:
|
||||||
|
db.create_all()
|
||||||
|
if Company.query.count() == 0:
|
||||||
|
company1 = Company(name="Hartford Funding")
|
||||||
|
company2 = Company(name="Alpha Tech Lending")
|
||||||
|
db.session.add(company1)
|
||||||
|
db.session.add(company2)
|
||||||
|
db.session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error creating database: {e}")
|
||||||
|
|
||||||
|
register_routes(app)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', debug=True, port=5005)
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "5005:5005"
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
environment:
|
||||||
|
FLASK_ENV: development
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import SelectField, StringField, TextAreaField, SubmitField
|
||||||
|
from wtforms.validators import DataRequired
|
||||||
|
from models import Company
|
||||||
|
|
||||||
|
class TicketForm(FlaskForm):
|
||||||
|
company = SelectField('Company', coerce=int, validators=[DataRequired()])
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
subject = StringField('Subject', validators=[DataRequired()])
|
||||||
|
ticket_number = StringField('Ticket Number', validators=[DataRequired()])
|
||||||
|
status = SelectField('Status', choices=[('Open', 'Open'), ('Closed', 'Closed')], validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
class NoteForm(FlaskForm):
|
||||||
|
note = TextAreaField('Note', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
class CompanyForm(FlaskForm):
|
||||||
|
name = StringField('Company Name', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Add Company')
|
||||||
|
|
||||||
|
class OnsiteForm(FlaskForm):
|
||||||
|
company = SelectField('Company', coerce=int, validators=[DataRequired()])
|
||||||
|
day_of_week = SelectField('Day of the Week', choices=[
|
||||||
|
('Monday', 'Monday'),
|
||||||
|
('Tuesday', 'Tuesday'),
|
||||||
|
('Wednesday', 'Wednesday'),
|
||||||
|
('Thursday', 'Thursday'),
|
||||||
|
('Friday', 'Friday')
|
||||||
|
], validators=[DataRequired()])
|
||||||
|
note = TextAreaField('Note', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(OnsiteForm, self).__init__(*args, **kwargs)
|
||||||
|
self.company.choices = [(company.id, company.name) for company in Company.query.all()]
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,35 @@
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
class Company(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
name = db.Column(db.String(50), nullable=False, unique=True)
|
||||||
|
tickets = db.relationship('Ticket', backref='company', lazy=True)
|
||||||
|
onsites = db.relationship('Onsite', backref='company', lazy=True)
|
||||||
|
|
||||||
|
class Ticket(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
company_id = db.Column(db.Integer, db.ForeignKey('company.id'), nullable=False)
|
||||||
|
name = db.Column(db.String(100), nullable=False)
|
||||||
|
subject = db.Column(db.String(100), nullable=False)
|
||||||
|
ticket_number = db.Column(db.String(100), nullable=False)
|
||||||
|
status = db.Column(db.String(10), nullable=False)
|
||||||
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||||
|
closed_at = db.Column(db.DateTime, nullable=True)
|
||||||
|
archived = db.Column(db.Boolean, default=False, nullable=False)
|
||||||
|
|
||||||
|
class Note(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
ticket_id = db.Column(db.Integer, db.ForeignKey('ticket.id'), nullable=False)
|
||||||
|
note = db.Column(db.Text, nullable=False)
|
||||||
|
archive = db.Column(db.Boolean, default=False, nullable=False)
|
||||||
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||||
|
ticket = db.relationship('Ticket', backref=db.backref('notes', lazy=True))
|
||||||
|
|
||||||
|
class Onsite(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
company_id = db.Column(db.Integer, db.ForeignKey('company.id'), nullable=False)
|
||||||
|
day_of_week = db.Column(db.String(10), nullable=False)
|
||||||
|
note = db.Column(db.Text, nullable=False)
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
Flask>=1.0
|
||||||
|
Flask-SQLAlchemy
|
||||||
|
Flask-WTF
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
from flask import render_template, redirect, url_for, request, jsonify
|
||||||
|
from models import db, Ticket, Note, Company, Onsite
|
||||||
|
from forms import TicketForm, NoteForm, CompanyForm, OnsiteForm
|
||||||
|
from datetime import datetime, date
|
||||||
|
|
||||||
|
def register_routes(app):
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
today = date.today()
|
||||||
|
open_tickets = Ticket.query.filter(Ticket.status == 'Open', Ticket.archived == False).all()
|
||||||
|
closed_tickets = Ticket.query.filter(Ticket.status == 'Closed', Ticket.archived == False).all()
|
||||||
|
onsites = {day: Onsite.query.filter_by(day_of_week=day).all() for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']}
|
||||||
|
return render_template('index.html', open_tickets=open_tickets, closed_tickets=closed_tickets, onsites=onsites)
|
||||||
|
|
||||||
|
@app.route('/overview')
|
||||||
|
def overview():
|
||||||
|
today = date.today()
|
||||||
|
open_tickets = Ticket.query.filter(Ticket.status == 'Open', Ticket.archived == False).all()
|
||||||
|
closed_tickets = Ticket.query.filter(Ticket.status == 'Closed', Ticket.archived == False).all()
|
||||||
|
onsites = {day: Onsite.query.filter_by(day_of_week=day).all() for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']}
|
||||||
|
return render_template('overview.html', open_tickets=open_tickets, closed_tickets=closed_tickets, onsites=onsites)
|
||||||
|
|
||||||
|
@app.route('/add_ticket', methods=['GET', 'POST'])
|
||||||
|
def add_ticket():
|
||||||
|
form = TicketForm()
|
||||||
|
form.company.choices = [(company.id, company.name) for company in Company.query.all()]
|
||||||
|
if form.validate_on_submit():
|
||||||
|
new_ticket = Ticket(
|
||||||
|
company_id=form.company.data,
|
||||||
|
name=form.name.data,
|
||||||
|
subject=form.subject.data,
|
||||||
|
ticket_number=form.ticket_number.data,
|
||||||
|
status=form.status.data,
|
||||||
|
created_at=datetime.utcnow()
|
||||||
|
)
|
||||||
|
db.session.add(new_ticket)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
return render_template('add_ticket.html', form=form)
|
||||||
|
|
||||||
|
@app.route('/add_note/<int:ticket_id>', methods=['GET', 'POST'])
|
||||||
|
def add_note(ticket_id):
|
||||||
|
form = NoteForm()
|
||||||
|
ticket = Ticket.query.get(ticket_id)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
new_note = Note(ticket_id=ticket_id, note=form.note.data, created_at=datetime.utcnow())
|
||||||
|
db.session.add(new_note)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
return render_template('add_note.html', form=form, ticket_id=ticket_id, ticket=ticket)
|
||||||
|
|
||||||
|
@app.route('/close_ticket/<int:ticket_id>', methods=['GET'])
|
||||||
|
def close_ticket(ticket_id):
|
||||||
|
ticket = Ticket.query.get(ticket_id)
|
||||||
|
if ticket:
|
||||||
|
ticket.status = 'Closed'
|
||||||
|
ticket.closed_at = datetime.utcnow()
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
@app.route('/archive_ticket/<int:ticket_id>', methods=['GET'])
|
||||||
|
def archive_ticket(ticket_id):
|
||||||
|
ticket = Ticket.query.get(ticket_id)
|
||||||
|
if ticket:
|
||||||
|
ticket.archived = True
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
@app.route('/archive_note/<int:note_id>', methods=['GET'])
|
||||||
|
def archive_note(note_id):
|
||||||
|
note = Note.query.get(note_id)
|
||||||
|
if note:
|
||||||
|
note.archive = True
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
@app.route('/delete_ticket/<int:ticket_id>', methods=['GET'])
|
||||||
|
def delete_ticket(ticket_id):
|
||||||
|
ticket = Ticket.query.get(ticket_id)
|
||||||
|
if ticket:
|
||||||
|
db.session.delete(ticket)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('all_tickets'))
|
||||||
|
|
||||||
|
@app.route('/all_tickets')
|
||||||
|
def all_tickets():
|
||||||
|
closed_tickets = Ticket.query.filter_by(status='Closed').all()
|
||||||
|
open_tickets = Ticket.query.filter_by(status='Open').all()
|
||||||
|
return render_template('all_tickets.html', closed_tickets=closed_tickets, open_tickets=open_tickets)
|
||||||
|
|
||||||
|
@app.route('/companies', methods=['GET', 'POST'])
|
||||||
|
def companies():
|
||||||
|
form = CompanyForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
new_company = Company(name=form.name.data)
|
||||||
|
db.session.add(new_company)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('companies'))
|
||||||
|
all_companies = Company.query.all()
|
||||||
|
return render_template('companies.html', form=form, companies=all_companies)
|
||||||
|
|
||||||
|
@app.route('/add_onsite', methods=['GET', 'POST'])
|
||||||
|
def add_onsite():
|
||||||
|
form = OnsiteForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
new_onsite = Onsite(
|
||||||
|
company_id=form.company.data,
|
||||||
|
day_of_week=form.day_of_week.data,
|
||||||
|
note=form.note.data
|
||||||
|
)
|
||||||
|
db.session.add(new_onsite)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
return render_template('add_onsite.html', form=form)
|
||||||
|
|
||||||
|
@app.route('/delete_onsite/<int:onsite_id>', methods=['GET'])
|
||||||
|
def delete_onsite(onsite_id):
|
||||||
|
onsite = Onsite.query.get(onsite_id)
|
||||||
|
if onsite:
|
||||||
|
db.session.delete(onsite)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
@app.route('/update_ticket/<int:ticket_id>', methods=['POST'])
|
||||||
|
def update_ticket(ticket_id):
|
||||||
|
data = request.get_json()
|
||||||
|
ticket = Ticket.query.get(ticket_id)
|
||||||
|
if ticket:
|
||||||
|
if data['field'] == 'company_id':
|
||||||
|
company = Company.query.filter_by(name=data['value']).first()
|
||||||
|
if company:
|
||||||
|
ticket.company_id = company.id
|
||||||
|
elif data['field'] == 'name':
|
||||||
|
ticket.name = data['value']
|
||||||
|
elif data['field'] == 'subject':
|
||||||
|
ticket.subject = data['value']
|
||||||
|
elif data['field'] == 'ticket_number':
|
||||||
|
ticket.ticket_number = data['value']
|
||||||
|
db.session.commit()
|
||||||
|
return jsonify(success=True)
|
||||||
|
return jsonify(success=False), 400
|
||||||
|
|
||||||
|
@app.route('/reopen_ticket/<int:ticket_id>', methods=['GET'])
|
||||||
|
def reopen_ticket(ticket_id):
|
||||||
|
ticket = Ticket.query.get(ticket_id)
|
||||||
|
if ticket:
|
||||||
|
ticket.status = 'Open'
|
||||||
|
ticket.archived = False
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
header, nav, main {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li {
|
||||||
|
display: inline;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Add Note{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Add Note to {{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }}</h1>
|
||||||
|
<form method="POST" action="">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
{{ form.note.label }} {{ form.note() }}<br>
|
||||||
|
{{ form.submit() }}
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Add Onsite{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Add Onsite</h2>
|
||||||
|
<form method="POST" action="">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<p>
|
||||||
|
{{ form.company.label }}<br>
|
||||||
|
{{ form.company }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ form.day_of_week.label }}<br>
|
||||||
|
{{ form.day_of_week }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ form.note.label }}<br>
|
||||||
|
{{ form.note }}
|
||||||
|
</p>
|
||||||
|
<p>{{ form.submit }}</p>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Add Ticket{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Add Ticket</h2>
|
||||||
|
<form method="POST" action="">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<p>
|
||||||
|
{{ form.company.label }}<br>
|
||||||
|
{{ form.company }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ form.name.label }}<br>
|
||||||
|
{{ form.name }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ form.subject.label }}<br>
|
||||||
|
{{ form.subject }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ form.ticket_number.label }}<br>
|
||||||
|
{{ form.ticket_number }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ form.status.label }}<br>
|
||||||
|
{{ form.status }}
|
||||||
|
</p>
|
||||||
|
<p>{{ form.submit }}</p>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}All Tickets{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Closed Tickets</h2>
|
||||||
|
<ul>
|
||||||
|
{% for ticket in closed_tickets %}
|
||||||
|
<li>
|
||||||
|
<span contenteditable="true" onblur="updateField({{ ticket.id }}, 'company_id', this)">
|
||||||
|
{{ ticket.company.name }}
|
||||||
|
</span> -
|
||||||
|
<span contenteditable="true" onblur="updateField({{ ticket.id }}, 'name', this)">
|
||||||
|
{{ ticket.name }}
|
||||||
|
</span> -
|
||||||
|
<span contenteditable="true" onblur="updateField({{ ticket.id }}, 'subject', this)">
|
||||||
|
{{ ticket.subject }}
|
||||||
|
</span> -
|
||||||
|
<span contenteditable="true" onblur="updateField({{ ticket.id }}, 'ticket_number', this)">
|
||||||
|
{{ ticket.ticket_number }}
|
||||||
|
</span>
|
||||||
|
<a href="{{ url_for('delete_ticket', ticket_id=ticket.id) }}">(Delete)</a>
|
||||||
|
<a href="{{ url_for('reopen_ticket', ticket_id=ticket.id) }}">(Reopen)</a>
|
||||||
|
<ul>
|
||||||
|
{% for note in ticket.notes %}
|
||||||
|
|
||||||
|
<li>
|
||||||
|
{{ note.note }} - {{ note.created_at.strftime('%Y-%m-%d %H:%M:%S') }}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Open Tickets</h2>
|
||||||
|
<ul>
|
||||||
|
{% for ticket in open_tickets %}
|
||||||
|
<li>
|
||||||
|
<span contenteditable="true" onblur="updateField({{ ticket.id }}, 'company_id', this)">
|
||||||
|
{{ ticket.company.name }}
|
||||||
|
</span> -
|
||||||
|
<span contenteditable="true" onblur="updateField({{ ticket.id }}, 'name', this)">
|
||||||
|
{{ ticket.name }}
|
||||||
|
</span> -
|
||||||
|
<span contenteditable="true" onblur="updateField({{ ticket.id }}, 'subject', this)">
|
||||||
|
{{ ticket.subject }}
|
||||||
|
</span> -
|
||||||
|
<span contenteditable="true" onblur="updateField({{ ticket.id }}, 'ticket_number', this)">
|
||||||
|
{{ ticket.ticket_number }}
|
||||||
|
</span>
|
||||||
|
<a href="{{ url_for('delete_ticket', ticket_id=ticket.id) }}">(Delete)</a>
|
||||||
|
<a href="{{ url_for('close_ticket', ticket_id=ticket.id) }}">(Close)</a>
|
||||||
|
<ul>
|
||||||
|
{% for note in ticket.notes %}
|
||||||
|
|
||||||
|
<li>
|
||||||
|
{{ note.note }} - {{ note.created_at.strftime('%Y-%m-%d %H:%M:%S') }}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function updateField(ticketId, fieldName, element) {
|
||||||
|
const newValue = element.innerText;
|
||||||
|
fetch(`/update_ticket/${ticketId}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': '{{ csrf_token() }}'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ field: fieldName, value: newValue })
|
||||||
|
}).then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
alert('Failed to update the field.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>{% block title %}Daily Overview{% endblock %}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Daily Overview</h1>
|
||||||
|
<nav>
|
||||||
|
<a href="{{ url_for('index') }}">Home</a> |
|
||||||
|
<a href="{{ url_for('all_tickets') }}">All Tickets</a> |
|
||||||
|
<a href="{{ url_for('add_ticket') }}">Add Ticket</a> |
|
||||||
|
<a href="{{ url_for('companies') }}">Companies</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Companies{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Companies</h1>
|
||||||
|
<ul>
|
||||||
|
{% for company in companies %}
|
||||||
|
<li>{{ company.name }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<h2>Add a New Company</h2>
|
||||||
|
<form method="POST" action="">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
{{ form.name.label }} {{ form.name() }}<br>
|
||||||
|
{{ form.submit() }}
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Daily Overview - Home{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h2>Open Tickets</h2>
|
||||||
|
<ul>
|
||||||
|
{% for ticket in open_tickets %}
|
||||||
|
<li>
|
||||||
|
{{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }}
|
||||||
|
<a href="{{ url_for('close_ticket', ticket_id=ticket.id) }}">(Close)</a>
|
||||||
|
<ul>
|
||||||
|
{% for note in ticket.notes %}
|
||||||
|
{% if not note.archive %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ url_for('archive_note', note_id=note.id) }}">X</a>
|
||||||
|
{{ note.note }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<a href="{{ url_for('add_note', ticket_id=ticket.id) }}">Add Note</a>
|
||||||
|
</li><br>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<h2>Closed Tickets</h2>
|
||||||
|
<ul>
|
||||||
|
{% for ticket in closed_tickets %}
|
||||||
|
<li>
|
||||||
|
{{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }}
|
||||||
|
<a href="{{ url_for('archive_ticket', ticket_id=ticket.id) }}">(Archive)</a>
|
||||||
|
<a href="{{ url_for('reopen_ticket', ticket_id=ticket.id) }}">(Reopen)</a>
|
||||||
|
<ul>
|
||||||
|
{% for note in ticket.notes %}
|
||||||
|
{% if not note.archive %}
|
||||||
|
<li>
|
||||||
|
{{ note.note }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<a href="{{ url_for('add_note', ticket_id=ticket.id) }}">Add Note</a>
|
||||||
|
</li><br>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<h2>Onsite Schedule</h2>
|
||||||
|
<p>
|
||||||
|
<a href="{{ url_for('add_onsite') }}">Add Onsite</a>
|
||||||
|
</p>
|
||||||
|
{% for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] %}
|
||||||
|
{% if onsites[day]|length > 0 %}
|
||||||
|
<h3>{{ day }}</h3>
|
||||||
|
<ul>
|
||||||
|
{% for onsite in onsites[day] %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ url_for('delete_onsite', onsite_id=onsite.id) }}">X</a>
|
||||||
|
{{ onsite.company.name }} - {{ onsite.note }}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="{{ url_for('overview') }}">Printable Overview</a>
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Printable Overview{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Open Tickets</h2>
|
||||||
|
<ul>
|
||||||
|
{% for ticket in open_tickets %}
|
||||||
|
<li>
|
||||||
|
{{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }}
|
||||||
|
<ul>
|
||||||
|
{% for note in ticket.notes %}
|
||||||
|
{% if not note.archive %}
|
||||||
|
<li>
|
||||||
|
{{ note.note }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Closed Tickets</h2>
|
||||||
|
<ul>
|
||||||
|
{% for ticket in closed_tickets %}
|
||||||
|
<li>
|
||||||
|
{{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }}
|
||||||
|
<ul>
|
||||||
|
{% for note in ticket.notes %}
|
||||||
|
{% if not note.archive %}
|
||||||
|
<li>
|
||||||
|
{{ note.note }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Onsite Schedule</h2>
|
||||||
|
{% for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] %}
|
||||||
|
{% if onsites[day]|length > 0 %}
|
||||||
|
<h3>{{ day }}</h3>
|
||||||
|
<ul>
|
||||||
|
{% for onsite in onsites[day] %}
|
||||||
|
<li>
|
||||||
|
{{ onsite.company.name }} - {{ onsite.note }}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Daily Overview - Home{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container py-5">
|
||||||
|
<h2 class="mb-4">Open Tickets</h2>
|
||||||
|
<ul class="list-group mb-5">
|
||||||
|
{% for ticket in open_tickets %}
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<strong>{{ ticket.company.name }}</strong> -
|
||||||
|
<span class="text-muted">{{ ticket.name }}</span> -
|
||||||
|
{{ ticket.subject }} -
|
||||||
|
<span class="badge bg-primary">{{ ticket.ticket_number }}</span>
|
||||||
|
</div>
|
||||||
|
<a href="{{ url_for('close_ticket', ticket_id=ticket.id) }}" class="btn btn-sm btn-danger">Close</a>
|
||||||
|
</div>
|
||||||
|
<ul class="mt-2 ps-3">
|
||||||
|
{% for note in ticket.notes %}
|
||||||
|
{% if not note.archive %}
|
||||||
|
<li class="d-flex align-items-center mb-2">
|
||||||
|
<a href="{{ url_for('archive_note', note_id=note.id) }}" class="text-danger me-2">X</a>
|
||||||
|
{{ note.note }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<a href="{{ url_for('add_note', ticket_id=ticket.id) }}" class="btn btn-link btn-sm">Add Note</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 class="mb-4">Closed Tickets</h2>
|
||||||
|
<ul class="list-group mb-5">
|
||||||
|
{% for ticket in closed_tickets %}
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<strong>{{ ticket.company.name }}</strong> -
|
||||||
|
<span class="text-muted">{{ ticket.name }}</span> -
|
||||||
|
{{ ticket.subject }} -
|
||||||
|
<span class="badge bg-primary">{{ ticket.ticket_number }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group">
|
||||||
|
<a href="{{ url_for('archive_ticket', ticket_id=ticket.id) }}" class="btn btn-sm btn-warning">Archive</a>
|
||||||
|
<a href="{{ url_for('reopen_ticket', ticket_id=ticket.id) }}" class="btn btn-sm btn-success">Reopen</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul class="mt-2 ps-3">
|
||||||
|
{% for note in ticket.notes %}
|
||||||
|
{% if not note.archive %}
|
||||||
|
<li class="text-muted">{{ note.note }}</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<a href="{{ url_for('add_note', ticket_id=ticket.id) }}" class="btn btn-link btn-sm">Add Note</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 class="mb-4">Onsite Schedule</h2>
|
||||||
|
<p class="mb-3">
|
||||||
|
<a href="{{ url_for('add_onsite') }}" class="btn btn-primary">Add Onsite</a>
|
||||||
|
</p>
|
||||||
|
{% for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] %}
|
||||||
|
{% if onsites[day]|length > 0 %}
|
||||||
|
<h3 class="h5">{{ day }}</h3>
|
||||||
|
<ul class="list-group mb-3">
|
||||||
|
{% for onsite in onsites[day] %}
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
<span>{{ onsite.company.name }} - {{ onsite.note }}</span>
|
||||||
|
<a href="{{ url_for('delete_onsite', onsite_id=onsite.id) }}" class="text-danger">X</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<p class="mt-4">
|
||||||
|
<a href="{{ url_for('overview') }}" class="btn btn-outline-secondary">Printable Overview</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Loading…
Reference in New Issue