mirror of
https://github.com/sstent/foodplanner.git
synced 2026-01-25 11:11:36 +00:00
days implemented
This commit is contained in:
Binary file not shown.
124
main.py
124
main.py
@@ -538,15 +538,15 @@ async def plan_page(request: Request, person: str = "Person A", week_start_date:
|
|||||||
if not week_start_date:
|
if not week_start_date:
|
||||||
today = datetime.now().date()
|
today = datetime.now().date()
|
||||||
# Find Monday of current week
|
# Find Monday of current week
|
||||||
week_start_date = (today - timedelta(days=today.weekday())).isoformat()
|
week_start_date_obj = (today - timedelta(days=today.weekday()))
|
||||||
else:
|
else:
|
||||||
week_start_date = datetime.fromisoformat(week_start_date).date()
|
week_start_date_obj = datetime.fromisoformat(week_start_date).date()
|
||||||
|
|
||||||
# Generate 7 days starting from Monday
|
# Generate 7 days starting from Monday
|
||||||
days = []
|
days = []
|
||||||
day_names = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
day_names = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
||||||
for i in range(7):
|
for i in range(7):
|
||||||
day_date = week_start_date + timedelta(days=i)
|
day_date = week_start_date_obj + timedelta(days=i)
|
||||||
days.append({
|
days.append({
|
||||||
'date': day_date,
|
'date': day_date,
|
||||||
'name': day_names[i],
|
'name': day_names[i],
|
||||||
@@ -572,15 +572,19 @@ async def plan_page(request: Request, person: str = "Person A", week_start_date:
|
|||||||
meals = db.query(Meal).all()
|
meals = db.query(Meal).all()
|
||||||
|
|
||||||
# Calculate previous and next week dates
|
# Calculate previous and next week dates
|
||||||
prev_week = (week_start_date - timedelta(days=7)).isoformat()
|
prev_week = (week_start_date_obj - timedelta(days=7)).isoformat()
|
||||||
next_week = (week_start_date + timedelta(days=7)).isoformat()
|
next_week = (week_start_date_obj + timedelta(days=7)).isoformat()
|
||||||
|
|
||||||
|
# Debug logging
|
||||||
|
print(f"DEBUG: days structure: {days}")
|
||||||
|
print(f"DEBUG: first day: {days[0] if days else 'No days'}")
|
||||||
|
|
||||||
return templates.TemplateResponse("plan.html", {
|
return templates.TemplateResponse("plan.html", {
|
||||||
"request": request, "person": person, "days": days,
|
"request": request, "person": person, "days": days,
|
||||||
"plans": plans, "daily_totals": daily_totals, "meals": meals,
|
"plans": plans, "daily_totals": daily_totals, "meals": meals,
|
||||||
"week_start_date": week_start_date.isoformat(),
|
"week_start_date": week_start_date_obj.isoformat(),
|
||||||
"prev_week": prev_week, "next_week": next_week,
|
"prev_week": prev_week, "next_week": next_week,
|
||||||
"week_range": f"{days[0]['display']} - {days[-1]['display']}, {week_start_date.year}"
|
"week_range": f"{days[0]['display']} - {days[-1]['display']}, {week_start_date_obj.year}"
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.post("/plan/add")
|
@app.post("/plan/add")
|
||||||
@@ -660,8 +664,110 @@ async def remove_from_plan(plan_id: int, db: Session = Depends(get_db)):
|
|||||||
return {"status": "error", "message": str(e)}
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
@app.get("/detailed", response_class=HTMLResponse)
|
@app.get("/detailed", response_class=HTMLResponse)
|
||||||
async def detailed(request: Request):
|
async def detailed(request: Request, person: str = "Person A", plan_date: str = None, template_id: int = None, db: Session = Depends(get_db)):
|
||||||
return templates.TemplateResponse("detailed.html", {"request": request, "title": "Detailed"})
|
from datetime import datetime
|
||||||
|
|
||||||
|
if template_id:
|
||||||
|
# Show template details
|
||||||
|
template = db.query(Template).filter(Template.id == template_id).first()
|
||||||
|
if not template:
|
||||||
|
return templates.TemplateResponse("detailed.html", {
|
||||||
|
"request": request, "title": "Template Not Found",
|
||||||
|
"error": "Template not found"
|
||||||
|
})
|
||||||
|
|
||||||
|
template_meals = db.query(TemplateMeal).filter(TemplateMeal.template_id == template_id).all()
|
||||||
|
|
||||||
|
# Calculate template nutrition
|
||||||
|
template_nutrition = {'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0, 'fiber': 0, 'sugar': 0, 'sodium': 0, 'calcium': 0}
|
||||||
|
|
||||||
|
meal_details = []
|
||||||
|
for tm in template_meals:
|
||||||
|
meal_nutrition = calculate_meal_nutrition(tm.meal, db)
|
||||||
|
meal_details.append({
|
||||||
|
'plan': {'meal': tm.meal},
|
||||||
|
'nutrition': meal_nutrition,
|
||||||
|
'foods': [] # Template view doesn't show individual foods
|
||||||
|
})
|
||||||
|
|
||||||
|
for key in template_nutrition:
|
||||||
|
if key in meal_nutrition:
|
||||||
|
template_nutrition[key] += meal_nutrition[key]
|
||||||
|
|
||||||
|
# Calculate percentages
|
||||||
|
total_cals = template_nutrition['calories']
|
||||||
|
if total_cals > 0:
|
||||||
|
template_nutrition['protein_pct'] = round((template_nutrition['protein'] * 4 / total_cals) * 100, 1)
|
||||||
|
template_nutrition['carbs_pct'] = round((template_nutrition['carbs'] * 4 / total_cals) * 100, 1)
|
||||||
|
template_nutrition['fat_pct'] = round((template_nutrition['fat'] * 9 / total_cals) * 100, 1)
|
||||||
|
template_nutrition['net_carbs'] = template_nutrition['carbs'] - template_nutrition['fiber']
|
||||||
|
|
||||||
|
return templates.TemplateResponse("detailed.html", {
|
||||||
|
"request": request, "title": f"Template: {template.name}",
|
||||||
|
"person": person, "meal_details": meal_details, "day_totals": template_nutrition,
|
||||||
|
"selected_day": template.name, "view_mode": "template"
|
||||||
|
})
|
||||||
|
|
||||||
|
elif plan_date:
|
||||||
|
# Show planned day details
|
||||||
|
plan_date_obj = datetime.fromisoformat(plan_date).date()
|
||||||
|
plans = db.query(Plan).filter(Plan.person == person, Plan.date == plan_date_obj).all()
|
||||||
|
|
||||||
|
meal_details = []
|
||||||
|
day_totals = {'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0, 'fiber': 0, 'sugar': 0, 'sodium': 0, 'calcium': 0}
|
||||||
|
|
||||||
|
for plan in plans:
|
||||||
|
meal_nutrition = calculate_meal_nutrition(plan.meal, db)
|
||||||
|
|
||||||
|
# Get meal foods for detailed breakdown
|
||||||
|
meal_foods = []
|
||||||
|
for meal_food in plan.meal.meal_foods:
|
||||||
|
meal_foods.append({
|
||||||
|
'food': meal_food.food,
|
||||||
|
'quantity': meal_food.quantity
|
||||||
|
})
|
||||||
|
|
||||||
|
meal_details.append({
|
||||||
|
'plan': plan,
|
||||||
|
'nutrition': meal_nutrition,
|
||||||
|
'foods': meal_foods
|
||||||
|
})
|
||||||
|
|
||||||
|
for key in day_totals:
|
||||||
|
if key in meal_nutrition:
|
||||||
|
day_totals[key] += meal_nutrition[key]
|
||||||
|
|
||||||
|
# Calculate percentages
|
||||||
|
total_cals = day_totals['calories']
|
||||||
|
if total_cals > 0:
|
||||||
|
day_totals['protein_pct'] = round((day_totals['protein'] * 4 / total_cals) * 100, 1)
|
||||||
|
day_totals['carbs_pct'] = round((day_totals['carbs'] * 4 / total_cals) * 100, 1)
|
||||||
|
day_totals['fat_pct'] = round((day_totals['fat'] * 9 / total_cals) * 100, 1)
|
||||||
|
day_totals['net_carbs'] = day_totals['carbs'] - day_totals['fiber']
|
||||||
|
|
||||||
|
selected_day = plan_date_obj.strftime('%A, %B %d, %Y')
|
||||||
|
|
||||||
|
return templates.TemplateResponse("detailed.html", {
|
||||||
|
"request": request, "title": f"Detailed View - {selected_day}",
|
||||||
|
"person": person, "meal_details": meal_details, "day_totals": day_totals,
|
||||||
|
"selected_day": selected_day, "view_mode": "day"
|
||||||
|
})
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Default view - show current week
|
||||||
|
templates_list = db.query(Template).all()
|
||||||
|
context = {
|
||||||
|
"request": request, "title": "Detailed View",
|
||||||
|
"person": person, "view_mode": "select", "templates": templates_list,
|
||||||
|
"meal_details": [], # Empty list for default view
|
||||||
|
"day_totals": { # Default empty nutrition totals
|
||||||
|
'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0,
|
||||||
|
'fiber': 0, 'sugar': 0, 'sodium': 0, 'calcium': 0,
|
||||||
|
'protein_pct': 0, 'carbs_pct': 0, 'fat_pct': 0, 'net_carbs': 0
|
||||||
|
},
|
||||||
|
"selected_day": "Select a date or template above"
|
||||||
|
}
|
||||||
|
return templates.TemplateResponse("detailed.html", context)
|
||||||
|
|
||||||
@app.get("/templates", response_class=HTMLResponse)
|
@app.get("/templates", response_class=HTMLResponse)
|
||||||
async def templates_page(request: Request, db: Session = Depends(get_db)):
|
async def templates_page(request: Request, db: Session = Depends(get_db)):
|
||||||
|
|||||||
128
migrate_to_dates.py
Normal file
128
migrate_to_dates.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Migration script to convert Plan.date from String to Date type
|
||||||
|
and convert existing "DayX" values to actual calendar dates.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
def migrate_plans_to_dates():
|
||||||
|
"""Convert existing DayX plans to actual dates"""
|
||||||
|
|
||||||
|
# Connect to database
|
||||||
|
conn = sqlite3.connect('meal_planner.db')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Check if migration is needed
|
||||||
|
cursor.execute("PRAGMA table_info(plans)")
|
||||||
|
columns = cursor.fetchall()
|
||||||
|
date_column_type = None
|
||||||
|
|
||||||
|
for col in columns:
|
||||||
|
if col[1] == 'date': # column name
|
||||||
|
date_column_type = col[2] # column type
|
||||||
|
break
|
||||||
|
|
||||||
|
if date_column_type and 'DATE' in date_column_type.upper():
|
||||||
|
print("Migration already completed - date column is already DATE type")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Starting migration from String to Date...")
|
||||||
|
|
||||||
|
# Create backup
|
||||||
|
print("Creating backup of plans table...")
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE plans_backup AS
|
||||||
|
SELECT * FROM plans
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Add new date column
|
||||||
|
print("Adding new date column...")
|
||||||
|
cursor.execute("""
|
||||||
|
ALTER TABLE plans ADD COLUMN date_new DATE
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Convert DayX to actual dates (starting from today as Day1)
|
||||||
|
print("Converting DayX values to dates...")
|
||||||
|
today = datetime.now().date()
|
||||||
|
|
||||||
|
# Get all unique day values
|
||||||
|
cursor.execute("SELECT DISTINCT date FROM plans WHERE date LIKE 'Day%'")
|
||||||
|
day_values = cursor.fetchall()
|
||||||
|
|
||||||
|
for (day_str,) in day_values:
|
||||||
|
if day_str.startswith('Day'):
|
||||||
|
try:
|
||||||
|
day_num = int(day_str[3:]) # Extract number from "Day1", "Day2", etc.
|
||||||
|
# Convert to date (Day1 = today, Day2 = tomorrow, etc.)
|
||||||
|
actual_date = today + timedelta(days=day_num - 1)
|
||||||
|
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE plans
|
||||||
|
SET date_new = ?
|
||||||
|
WHERE date = ?
|
||||||
|
""", (actual_date.isoformat(), day_str))
|
||||||
|
|
||||||
|
print(f"Converted {day_str} to {actual_date.isoformat()}")
|
||||||
|
|
||||||
|
except (ValueError, IndexError) as e:
|
||||||
|
print(f"Error converting {day_str}: {e}")
|
||||||
|
|
||||||
|
# Handle any non-DayX dates (if they exist)
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE plans
|
||||||
|
SET date_new = date
|
||||||
|
WHERE date NOT LIKE 'Day%' AND date_new IS NULL
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Recreate table with new structure (SQLite doesn't support DROP COLUMN with indexes)
|
||||||
|
print("Recreating table with new structure...")
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE plans_new (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
person VARCHAR NOT NULL,
|
||||||
|
date DATE NOT NULL,
|
||||||
|
meal_id INTEGER NOT NULL REFERENCES meals(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Copy data to new table
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO plans_new (id, person, date, meal_id)
|
||||||
|
SELECT id, person, date_new, meal_id FROM plans
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Drop old table and rename new one
|
||||||
|
cursor.execute("DROP TABLE plans")
|
||||||
|
cursor.execute("ALTER TABLE plans_new RENAME TO plans")
|
||||||
|
|
||||||
|
# Create index on new date column
|
||||||
|
cursor.execute("CREATE INDEX ix_plans_date ON plans(date)")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
print("Migration completed successfully!")
|
||||||
|
|
||||||
|
# Show summary
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM plans")
|
||||||
|
total_plans = cursor.fetchone()[0]
|
||||||
|
print(f"Total plans migrated: {total_plans}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Migration failed: {e}")
|
||||||
|
conn.rollback()
|
||||||
|
|
||||||
|
# Restore backup if something went wrong
|
||||||
|
try:
|
||||||
|
cursor.execute("DROP TABLE IF EXISTS plans")
|
||||||
|
cursor.execute("ALTER TABLE plans_backup RENAME TO plans")
|
||||||
|
print("Restored from backup")
|
||||||
|
except Exception as backup_error:
|
||||||
|
print(f"Failed to restore backup: {backup_error}")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
migrate_plans_to_dates()
|
||||||
@@ -3,17 +3,39 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h3>Detailed View for {{ person }}</h3>
|
<h3>Detailed View for {{ person }}</h3>
|
||||||
<form method="get" class="d-flex">
|
<div class="mb-3">
|
||||||
<input type="hidden" name="person" value="{{ person }}">
|
<div class="btn-group" role="group">
|
||||||
<select class="form-control me-2" name="plan_day">
|
<input type="radio" class="btn-check" name="viewMode" id="dayView" autocomplete="off" {% if view_mode == 'day' or not view_mode %}checked{% endif %}>
|
||||||
{% for day in days %}
|
<label class="btn btn-outline-primary" for="dayView">View Planned Day</label>
|
||||||
<option value="{{ day }}" {% if day == selected_day %}selected{% endif %}>{{ day }}</option>
|
<input type="radio" class="btn-check" name="viewMode" id="templateView" autocomplete="off" {% if view_mode == 'template' %}checked{% endif %}>
|
||||||
{% endfor %}
|
<label class="btn btn-outline-primary" for="templateView">View Template</label>
|
||||||
</select>
|
</div>
|
||||||
<button class="btn btn-primary" type="submit">
|
</div>
|
||||||
<i class="bi bi-search"></i> View
|
|
||||||
</button>
|
<div id="daySelector" {% if view_mode == 'template' %}style="display: none;"{% endif %}>
|
||||||
</form>
|
<form method="get" class="d-flex">
|
||||||
|
<input type="hidden" name="person" value="{{ person }}">
|
||||||
|
<input type="date" class="form-control me-2" name="plan_date" value="{% if view_mode == 'day' %}{{ plan_date }}{% else %}{{ today|default('') }}{% endif %}" required>
|
||||||
|
<button class="btn btn-primary" type="submit">
|
||||||
|
<i class="bi bi-search"></i> View Day
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="templateSelector" {% if view_mode != 'template' %}style="display: none;"{% endif %}>
|
||||||
|
<form method="get" class="d-flex">
|
||||||
|
<input type="hidden" name="person" value="{{ person }}">
|
||||||
|
<select class="form-control me-2" name="template_id" required>
|
||||||
|
<option value="">Select Template...</option>
|
||||||
|
{% for template in templates %}
|
||||||
|
<option value="{{ template.id }}" {% if template_id == template.id %}selected{% endif %}>{{ template.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<button class="btn btn-primary" type="submit">
|
||||||
|
<i class="bi bi-search"></i> View Template
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 text-end">
|
<div class="col-md-6 text-end">
|
||||||
<h4 class="mb-0">{{ selected_day }}</h4>
|
<h4 class="mb-0">{{ selected_day }}</h4>
|
||||||
@@ -259,8 +281,43 @@
|
|||||||
|
|
||||||
{% if not meal_details %}
|
{% if not meal_details %}
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<i class="bi bi-info-circle"></i> No meals planned for this date. Go to the Plan tab to add meals.
|
<i class="bi bi-info-circle"></i>
|
||||||
|
{% if view_mode == 'template' %}
|
||||||
|
No template selected. Please select a template to view.
|
||||||
|
{% else %}
|
||||||
|
No meals planned for this date. Go to the Plan tab to add meals.
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Handle view mode switching
|
||||||
|
const dayViewRadio = document.getElementById('dayView');
|
||||||
|
const templateViewRadio = document.getElementById('templateView');
|
||||||
|
const daySelector = document.getElementById('daySelector');
|
||||||
|
const templateSelector = document.getElementById('templateSelector');
|
||||||
|
|
||||||
|
function switchViewMode() {
|
||||||
|
if (dayViewRadio.checked) {
|
||||||
|
daySelector.style.display = 'block';
|
||||||
|
templateSelector.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
daySelector.style.display = 'none';
|
||||||
|
templateSelector.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dayViewRadio.addEventListener('change', switchViewMode);
|
||||||
|
templateViewRadio.addEventListener('change', switchViewMode);
|
||||||
|
|
||||||
|
// Set default date to today if no date is selected
|
||||||
|
const dateInput = document.querySelector('input[name="plan_date"]');
|
||||||
|
if (dateInput && !dateInput.value) {
|
||||||
|
const today = new Date().toISOString().split('T')[0];
|
||||||
|
dateInput.value = today;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -289,6 +289,10 @@ function createTemplate() {
|
|||||||
|
|
||||||
function useTemplateModal(templateId) {
|
function useTemplateModal(templateId) {
|
||||||
currentTemplateId = templateId;
|
currentTemplateId = templateId;
|
||||||
|
// Set default date to today
|
||||||
|
const today = new Date().toISOString().split('T')[0];
|
||||||
|
document.getElementById('startDate').value = today;
|
||||||
|
document.getElementById('overwriteWarning').style.display = 'none';
|
||||||
new bootstrap.Modal(document.getElementById('useTemplateModal')).show();
|
new bootstrap.Modal(document.getElementById('useTemplateModal')).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +302,46 @@ function useTemplate() {
|
|||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
person: formData.get('person'),
|
person: formData.get('person'),
|
||||||
start_day: formData.get('start_day')
|
start_date: formData.get('start_date')
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(`/templates/${currentTemplateId}/use`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: new URLSearchParams(data)
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === 'confirm_overwrite') {
|
||||||
|
// Show overwrite warning
|
||||||
|
document.getElementById('overwriteWarning').style.display = 'block';
|
||||||
|
// Change button text to confirm overwrite
|
||||||
|
const submitBtn = document.querySelector('#useTemplateForm button[type="submit"]');
|
||||||
|
submitBtn.textContent = 'Overwrite Existing Meals';
|
||||||
|
submitBtn.onclick = () => confirmOverwrite(data);
|
||||||
|
} else if (data.status === 'success') {
|
||||||
|
bootstrap.Modal.getInstance(document.getElementById('useTemplateModal')).hide();
|
||||||
|
alert('Template applied to your plan successfully!');
|
||||||
|
} else {
|
||||||
|
alert('Error using template: ' + data.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
alert('Error using template');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmOverwrite(overwriteData) {
|
||||||
|
const form = document.getElementById('useTemplateForm');
|
||||||
|
const formData = new FormData(form);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
person: formData.get('person'),
|
||||||
|
start_date: formData.get('start_date'),
|
||||||
|
confirm_overwrite: 'true'
|
||||||
};
|
};
|
||||||
|
|
||||||
fetch(`/templates/${currentTemplateId}/use`, {
|
fetch(`/templates/${currentTemplateId}/use`, {
|
||||||
|
|||||||
Reference in New Issue
Block a user