first commit
This commit is contained in:
commit
32964d1d1f
227
app.py
Normal file
227
app.py
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
from flask import Flask, render_template, request, redirect, url_for, jsonify
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Setup logging to file
|
||||||
|
logging.basicConfig(filename='log.log', level=logging.DEBUG, format='%(asctime)s %(levelname)s:%(message)s')
|
||||||
|
|
||||||
|
# Load the assort data
|
||||||
|
ASSORT_FILE_PATH = 'assort.json'
|
||||||
|
QUEST_ASSORT_FILE_PATH = 'questassort.json'
|
||||||
|
CACHE_FILE_PATH = 'item_cache.json'
|
||||||
|
RUBLE_TPL_ID = '5449016a4bdc2d6f028b456f'
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(ASSORT_FILE_PATH) as f:
|
||||||
|
assort_data = json.load(f)
|
||||||
|
logging.debug("Assort data loaded successfully")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error loading assort data: {e}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(QUEST_ASSORT_FILE_PATH) as f:
|
||||||
|
quest_assort_data = json.load(f)
|
||||||
|
logging.debug("Quest assort data loaded successfully")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error loading quest assort data: {e}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
|
||||||
|
# Load cache or initialize an empty dictionary
|
||||||
|
if os.path.exists(CACHE_FILE_PATH):
|
||||||
|
try:
|
||||||
|
with open(CACHE_FILE_PATH) as f:
|
||||||
|
item_cache = json.load(f)
|
||||||
|
logging.debug("Item cache loaded successfully")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error loading item cache: {e}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
item_cache = {}
|
||||||
|
else:
|
||||||
|
item_cache = {}
|
||||||
|
logging.debug("Initialized empty item cache")
|
||||||
|
|
||||||
|
# Tarkov.dev API URL
|
||||||
|
TARKOV_API_URL = "https://api.tarkov.dev/graphql"
|
||||||
|
|
||||||
|
def get_ruble_image():
|
||||||
|
return get_item_details_cached(RUBLE_TPL_ID)
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
try:
|
||||||
|
items = get_main_items_with_details(assort_data)
|
||||||
|
ruble_image = get_ruble_image()
|
||||||
|
return render_template('index.html', items=items, ruble_image=ruble_image)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error in index route: {e}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
return "Error loading items", 500
|
||||||
|
|
||||||
|
def get_main_items_with_details(assort_data):
|
||||||
|
items = []
|
||||||
|
for item in assort_data['items']:
|
||||||
|
if item['parentId'] == 'hideout':
|
||||||
|
item_copy = item.copy() # Create a copy of the item to avoid modifying the original
|
||||||
|
item_copy['details'] = get_item_details_cached(item_copy['_tpl'])
|
||||||
|
item_copy['parts'] = get_item_parts_with_details(item_copy['_id'], assort_data)
|
||||||
|
item_copy['barter_scheme'] = get_barter_scheme_with_details(item_copy['_id'], assort_data)
|
||||||
|
item_copy['quest_requirement'] = get_quest_requirement(item_copy['_id'])
|
||||||
|
item_copy['offer_name'] = item_copy['_id'] # Add offer name
|
||||||
|
items.append(item_copy)
|
||||||
|
logging.debug(f"Main items with details: {items}")
|
||||||
|
return items
|
||||||
|
|
||||||
|
def get_item_details_cached(tpl):
|
||||||
|
if tpl in item_cache:
|
||||||
|
logging.debug(f"Cache hit for tpl {tpl}")
|
||||||
|
return item_cache[tpl]
|
||||||
|
else:
|
||||||
|
logging.debug(f"Cache miss for tpl {tpl}, fetching from API")
|
||||||
|
item_details = get_items_details([tpl])
|
||||||
|
if tpl in item_details:
|
||||||
|
item_cache[tpl] = item_details[tpl]
|
||||||
|
save_item_cache()
|
||||||
|
return item_details.get(tpl, {})
|
||||||
|
|
||||||
|
def get_items_details(tpls):
|
||||||
|
queries = "\n".join([
|
||||||
|
f'item{index}: item(id: "{tpl}") {{ id name shortName description basePrice image512pxLink wikiLink }}'
|
||||||
|
for index, tpl in enumerate(tpls)
|
||||||
|
])
|
||||||
|
query = f"{{ {queries} }}"
|
||||||
|
try:
|
||||||
|
response = requests.post(TARKOV_API_URL, json={'query': query})
|
||||||
|
data = response.json()
|
||||||
|
logging.debug(f"Item details for tpls {tpls}: {data}")
|
||||||
|
return {tpl: data['data'][f'item{index}'] for index, tpl in enumerate(tpls)}
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error fetching item details for tpls {tpls}: {e}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def get_item_parts_with_details(parent_id, assort_data):
|
||||||
|
parts = []
|
||||||
|
def fetch_parts(parent_id):
|
||||||
|
sub_parts = []
|
||||||
|
for item in assort_data['items']:
|
||||||
|
if item['parentId'] == parent_id:
|
||||||
|
part_copy = item.copy() # Create a copy of the part to avoid modifying the original
|
||||||
|
part_copy['details'] = get_item_details_cached(part_copy['_tpl'])
|
||||||
|
part_copy['parts'] = fetch_parts(part_copy['_id']) # Fetch sub-parts recursively
|
||||||
|
sub_parts.append(part_copy)
|
||||||
|
return sub_parts
|
||||||
|
parts = fetch_parts(parent_id)
|
||||||
|
logging.debug(f"Parts for parent_id {parent_id}: {parts}")
|
||||||
|
return parts
|
||||||
|
|
||||||
|
def get_barter_scheme_with_details(item_id, assort_data):
|
||||||
|
barter_scheme = []
|
||||||
|
if 'barter_scheme' in assort_data:
|
||||||
|
for scheme in assort_data['barter_scheme'].get(item_id, []):
|
||||||
|
scheme_details = []
|
||||||
|
for req in scheme:
|
||||||
|
req_copy = req.copy() # Create a copy of the req to avoid modifying the original
|
||||||
|
req_details = get_item_details_cached(req_copy['_tpl'])
|
||||||
|
req_copy['details'] = req_details
|
||||||
|
scheme_details.append(req_copy)
|
||||||
|
barter_scheme.append(scheme_details)
|
||||||
|
logging.debug(f"Barter scheme for item_id {item_id}: {barter_scheme}")
|
||||||
|
return barter_scheme
|
||||||
|
|
||||||
|
def get_quest_requirement(item_id):
|
||||||
|
for quest_type, quests in quest_assort_data.items():
|
||||||
|
if item_id in quests:
|
||||||
|
return {
|
||||||
|
"type": quest_type,
|
||||||
|
"quest": quests[item_id]
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
@app.route('/edit/<item_id>', methods=['GET', 'POST'])
|
||||||
|
def edit_item(item_id):
|
||||||
|
try:
|
||||||
|
if request.method == 'POST':
|
||||||
|
# Handle form submission
|
||||||
|
new_barter_scheme = []
|
||||||
|
tpl_list = request.form.getlist('barter_tpl')
|
||||||
|
count_list = request.form.getlist('barter_count')
|
||||||
|
scheme = []
|
||||||
|
for i in range(len(tpl_list)):
|
||||||
|
scheme.append({
|
||||||
|
'_tpl': tpl_list[i],
|
||||||
|
'count': int(count_list[i])
|
||||||
|
})
|
||||||
|
new_barter_scheme.append(scheme)
|
||||||
|
update_barter_scheme(item_id, new_barter_scheme)
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
item = next((item for item in assort_data['items'] if item['_id'] == item_id), None)
|
||||||
|
if item:
|
||||||
|
item_copy = item.copy() # Create a copy of the item to avoid modifying the original
|
||||||
|
item_copy['details'] = get_item_details_cached(item_copy['_tpl'])
|
||||||
|
item_copy['parts'] = get_item_parts_with_details(item_id, assort_data)
|
||||||
|
item_copy['barter_scheme'] = get_barter_scheme_with_details(item_id, assort_data)
|
||||||
|
item_copy['quest_requirement'] = get_quest_requirement(item_id)
|
||||||
|
ruble_image = get_ruble_image()
|
||||||
|
return render_template('edit.html', item=item_copy, ruble_image=ruble_image)
|
||||||
|
else:
|
||||||
|
return "Item not found", 404
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error in edit_item route: {e}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
return "Error editing item", 500
|
||||||
|
|
||||||
|
def update_barter_scheme(item_id, new_barter_scheme):
|
||||||
|
try:
|
||||||
|
assort_data['barter_scheme'][item_id] = new_barter_scheme
|
||||||
|
save_assort_data()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error updating barter scheme for item {item_id}: {e}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
|
||||||
|
def save_assort_data():
|
||||||
|
try:
|
||||||
|
# Deep copy the assort data and remove 'details' key before saving
|
||||||
|
cleaned_assort_data = json.loads(json.dumps(assort_data)) # Deep copy
|
||||||
|
|
||||||
|
# Clean items
|
||||||
|
for item in cleaned_assort_data['items']:
|
||||||
|
if 'details' in item:
|
||||||
|
del item['details']
|
||||||
|
|
||||||
|
# Clean barter_scheme
|
||||||
|
for item_id, schemes in cleaned_assort_data.get('barter_scheme', {}).items():
|
||||||
|
for scheme in schemes:
|
||||||
|
for part in scheme:
|
||||||
|
if 'details' in part:
|
||||||
|
del part['details']
|
||||||
|
|
||||||
|
with open(ASSORT_FILE_PATH, 'w') as f:
|
||||||
|
json.dump(cleaned_assort_data, f, indent=2) # Use 2 spaces for indentation
|
||||||
|
logging.debug("Assort data saved successfully")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error saving assort data: {e}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
|
||||||
|
def save_item_cache():
|
||||||
|
try:
|
||||||
|
with open(CACHE_FILE_PATH, 'w') as f:
|
||||||
|
json.dump(item_cache, f, indent=2) # Use 2 spaces for indentation
|
||||||
|
logging.debug("Item cache saved successfully")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error saving item cache: {e}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
|
||||||
|
@app.route('/item_image/<tpl>')
|
||||||
|
def item_image(tpl):
|
||||||
|
details = get_item_details_cached(tpl)
|
||||||
|
return jsonify({'image512pxLink': details.get('image512pxLink', ''), 'name': details.get('name', '')})
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug=True)
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Flask>=2.0.3
|
||||||
|
requests>=2.26.0
|
179
templates/edit.html
Normal file
179
templates/edit.html
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Edit Item</title>
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
.price-box {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
.price-box img {
|
||||||
|
width: 30px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.price-box span {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.price-box small {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.card-header img {
|
||||||
|
width: 45px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.parts-tree {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.parts-tree > li {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.part-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.part-item img {
|
||||||
|
width: 30px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Edit {{ item.details.name }}</h1>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="main-item">Main Item</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<img src="{{ item.details.image512pxLink }}" alt="{{ item.details.name }}" style="width: 50px;">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="form-control" id="main-item" name="main-item" value="{{ item.details.name }}" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="price-box">
|
||||||
|
<img src="{{ ruble_image.image512pxLink }}" alt="RUB">
|
||||||
|
<div>
|
||||||
|
<span>{{ item.details.basePrice }} RUB</span>
|
||||||
|
<small>Base Price (for reference)</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if item.parts|length > 0 %}
|
||||||
|
<h2>
|
||||||
|
<button class="btn btn-outline-primary-custom" type="button" data-toggle="collapse" data-target="#parts-{{ item._id }}" aria-expanded="false" aria-controls="parts-{{ item._id }}">
|
||||||
|
Parts
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="parts-{{ item._id }}" class="collapse">
|
||||||
|
<ul class="parts-tree">
|
||||||
|
{% include 'parts.html' with context %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<h2>Barter Scheme</h2>
|
||||||
|
<form method="POST" id="barter-form">
|
||||||
|
<div id="barter-scheme">
|
||||||
|
{% for scheme in item.barter_scheme %}
|
||||||
|
<div class="form-group barter-item" data-index="{{ loop.index0 }}">
|
||||||
|
{% for req in scheme %}
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<img src="{{ req.details.image512pxLink }}" alt="{{ req.details.name }}" style="width: 30px;" id="barter_img_{{ loop.index0 }}">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="form-control barter_tpl" id="barter_tpl_{{ loop.index0 }}" name="barter_tpl" value="{{ req._tpl }}" required>
|
||||||
|
<input type="number" class="form-control barter_count" id="barter_count_{{ loop.index0 }}" name="barter_count" value="{{ req.count }}" required>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-danger remove-barter-item" type="button">Remove</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary" id="add-barter-item">Add Barter Item</button>
|
||||||
|
<button type="submit" class="btn btn-success">Save</button>
|
||||||
|
<a href="{{ url_for('index') }}" class="btn btn-secondary">Cancel</a>
|
||||||
|
</form>
|
||||||
|
{% if item.quest_requirement %}
|
||||||
|
<h2>Quest Requirement</h2>
|
||||||
|
<p>
|
||||||
|
<strong>Quest Requirement:</strong>
|
||||||
|
{{ item.quest_requirement.quest }}
|
||||||
|
{% if item.quest_requirement.type == 'success' %}
|
||||||
|
<i class="fas fa-check-circle text-success"></i>
|
||||||
|
{% elif item.quest_requirement.type == 'fail' %}
|
||||||
|
<i class="fas fa-times-circle text-danger"></i>
|
||||||
|
{% elif item.quest_requirement.type == 'started' %}
|
||||||
|
<i class="fas fa-question-circle text-primary"></i>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||||
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
let barterIndex = {{ item.barter_scheme|length }};
|
||||||
|
|
||||||
|
$('#add-barter-item').click(function() {
|
||||||
|
const newItem = `
|
||||||
|
<div class="form-group barter-item" data-index="${barterIndex}">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<img src="" alt="Barter Item" style="width: 30px;" id="barter_img_${barterIndex}">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="form-control barter_tpl" id="barter_tpl_${barterIndex}" name="barter_tpl" required>
|
||||||
|
<input type="number" class="form-control barter_count" id="barter_count_${barterIndex}" name="barter_count" required>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-danger remove-barter-item" type="button">Remove</button>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
$('#barter-scheme').append(newItem);
|
||||||
|
barterIndex++;
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.remove-barter-item', function() {
|
||||||
|
if ($('.barter-item').length > 1) {
|
||||||
|
$(this).closest('.barter-item').remove();
|
||||||
|
} else {
|
||||||
|
alert("You must have at least one barter item.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('change', '.barter_tpl', function() {
|
||||||
|
const index = $(this).closest('.barter-item').data('index');
|
||||||
|
const tpl = $(this).val();
|
||||||
|
$.get(`/item_image/${tpl}`, function(data) {
|
||||||
|
$(`#barter_img_${index}`).attr('src', data.image512pxLink);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
142
templates/index.html
Normal file
142
templates/index.html
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Trader Assort</title>
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
.price-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.price-box img {
|
||||||
|
width: 30px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.price-box span {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.price-box small {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.card-header img {
|
||||||
|
width: 45px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.barter-list {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
.barter-list li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.barter-list li img {
|
||||||
|
width: 45px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.parts-tree {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.parts-tree > li {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.part-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.part-item img {
|
||||||
|
width: 30px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Trader Assort</h1>
|
||||||
|
<div class="list-group">
|
||||||
|
{% for item in items %}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header d-flex align-items-center">
|
||||||
|
<img src="{{ item.details.image512pxLink }}" alt="{{ item.details.name }}">
|
||||||
|
<strong>{{ item.details.name }}</strong>
|
||||||
|
<div class="ml-2 text-muted">{{ item.offer_name }}</div> <!-- Add offer name here -->
|
||||||
|
<div class="price-box ml-auto">
|
||||||
|
<div><img src="{{ ruble_image.image512pxLink }}" alt="RUB"><span>{{ item.details.basePrice }} RUB</span></div>
|
||||||
|
<small>Base Price (for reference)</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<a href="{{ url_for('edit_item', item_id=item._id) }}" class="btn btn-primary btn-sm float-right">Edit</a>
|
||||||
|
|
||||||
|
{% if item.parts|length > 0 %}
|
||||||
|
<h2>
|
||||||
|
<button class="btn btn-outline-primary" type="button" data-toggle="collapse" data-target="#parts-{{ item._id }}" aria-expanded="false" aria-controls="parts-{{ item._id }}">
|
||||||
|
Parts
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="parts-{{ item._id }}" class="collapse">
|
||||||
|
<ul class="parts-tree">
|
||||||
|
{% include 'parts.html' with context %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if item.barter_scheme|length > 0 %}
|
||||||
|
<div><strong>Barter Requirements:</strong></div>
|
||||||
|
<ul class="barter-list">
|
||||||
|
{% for scheme in item.barter_scheme %}
|
||||||
|
{% for req in scheme %}
|
||||||
|
<li>
|
||||||
|
<span>{{ req.count }} ×</span>
|
||||||
|
<img src="{{ req.details.image512pxLink }}" alt="{{ req.details.name }}">
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if item.quest_requirement %}
|
||||||
|
<p>
|
||||||
|
<strong>Quest Requirement:</strong>
|
||||||
|
{{ item.quest_requirement.quest }}
|
||||||
|
{% if item.quest_requirement.type == 'success' %}
|
||||||
|
<i class="fas fa-check-circle text-success"></i>
|
||||||
|
{% elif item.quest_requirement.type == 'fail' %}
|
||||||
|
<i class="fas fa-times-circle text-danger"></i>
|
||||||
|
{% elif item.quest_requirement.type == 'started' %}
|
||||||
|
<i class="fas fa-question-circle text-primary"></i>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||||
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
templates/parts.html
Normal file
16
templates/parts.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{% macro render_parts(parts) %}
|
||||||
|
{% for part in parts %}
|
||||||
|
<li class="part-item">
|
||||||
|
<img src="{{ part.details.image512pxLink }}" alt="{{ part.details.name }}">
|
||||||
|
<div>
|
||||||
|
<strong>{{ part.details.shortName }}</strong>
|
||||||
|
{% if part.parts|length > 0 %}
|
||||||
|
<ul class="parts-tree">
|
||||||
|
{{ render_parts(part.parts) }}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{{ render_parts(item.parts) }}
|
Loading…
x
Reference in New Issue
Block a user