final
This commit is contained in:
57
app.py
Executable file
57
app.py
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
from flask import Flask, render_template, request
|
||||||
|
import math
|
||||||
|
from profiles import profiles
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# --- Matching logic (server-side copy, used for initial render only) ---
|
||||||
|
def euclidean_distance(a, b):
|
||||||
|
return math.sqrt(sum((x - y) ** 2 for x, y in zip(a, b)))
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def index():
|
||||||
|
return render_template("about.html")
|
||||||
|
|
||||||
|
@app.route("/inactive")
|
||||||
|
def inactive():
|
||||||
|
# Default user values (all 20). Client-side UI will live-update from there.
|
||||||
|
user_values = [20, 20, 20, 20, 20, 20]
|
||||||
|
sorted_matches = sorted(
|
||||||
|
profiles.items(), key=lambda p: euclidean_distance(user_values, p[1]['values'])
|
||||||
|
)
|
||||||
|
top_matches = sorted_matches[:3]
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"inactive.html",
|
||||||
|
user=user_values,
|
||||||
|
top_matches=top_matches,
|
||||||
|
profiles=profiles,
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.route("/profiles")
|
||||||
|
def profiles_page():
|
||||||
|
return render_template("profiles.html", profiles=profiles)
|
||||||
|
|
||||||
|
@app.route("/quiz", methods=["GET", "POST"])
|
||||||
|
def quiz():
|
||||||
|
if request.method == "POST":
|
||||||
|
user_values = [
|
||||||
|
int(request.form.get(f"point{i}", 20)) for i in range(1, 7)
|
||||||
|
]
|
||||||
|
sorted_matches = sorted(
|
||||||
|
profiles.items(),
|
||||||
|
key=lambda p: euclidean_distance(user_values, p[1]['values'])
|
||||||
|
)
|
||||||
|
top_matches = sorted_matches[:3]
|
||||||
|
return render_template("result.html",
|
||||||
|
user=user_values,
|
||||||
|
top_matches=top_matches,
|
||||||
|
profiles=profiles)
|
||||||
|
return render_template("quiz.html", profiles=profiles)
|
||||||
|
|
||||||
|
@app.route("/result")
|
||||||
|
def result():
|
||||||
|
return render_template("result.html", profiles=profiles)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(host='localhost', port=1337, debug=False)
|
107
profiles.py
Normal file
107
profiles.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
# Predefined radar profiles
|
||||||
|
profiles = {
|
||||||
|
"Alpenglow": {
|
||||||
|
"values": [25, 35, 45, 70, 50, 25],
|
||||||
|
"info_text0": "15%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "spicy-fruity, sour apple",
|
||||||
|
"info_text4": "relaxing, sleep promoting"
|
||||||
|
},
|
||||||
|
"Amnesia Haze": {
|
||||||
|
"values": [35, 65, 40, 35, 30, 35],
|
||||||
|
"info_text0": "20%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Sativa-dominant Hybrid",
|
||||||
|
"info_text3": "incense, citrus fruits",
|
||||||
|
"info_text4": "energizing, calming"
|
||||||
|
},
|
||||||
|
"Blue Dream": {
|
||||||
|
"values": [25, 35, 40, 41, 75, 35],
|
||||||
|
"info_text0": "15%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Sativa-dominant Hybrid",
|
||||||
|
"info_text3": "berries, sweet, slighty hazy",
|
||||||
|
"info_text4": "focusing, relaxing, slightly sleep promoting"
|
||||||
|
},
|
||||||
|
"Bubba Kush": {
|
||||||
|
"values": [30, 61, 40, 41, 31, 35],
|
||||||
|
"info_text0": "15%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "lavender, cocoa, roasted coffee",
|
||||||
|
"info_text4": "calming, relaxing"
|
||||||
|
},
|
||||||
|
"Cherry Valley Cake": {
|
||||||
|
"values": [30, 59, 65, 35, 30, 55],
|
||||||
|
"info_text0": "18%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "creamy, spicy, fermented cherry",
|
||||||
|
"info_text4": "inspring, relaxing, calming"
|
||||||
|
},
|
||||||
|
"Ciskei": {
|
||||||
|
"values": [70, 40, 35, 39, 41, 40],
|
||||||
|
"info_text0": "5%",
|
||||||
|
"info_text1": "7.5%",
|
||||||
|
"info_text2": "Sativa",
|
||||||
|
"info_text3": "herbaceous, cheesy, spicy",
|
||||||
|
"info_text4": "energizing, focusing"
|
||||||
|
},
|
||||||
|
"Cleopatra's Milk": {
|
||||||
|
"values": [30, 65, 45, 35, 35, 55],
|
||||||
|
"info_text0": "18%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "cake, oriental spices, fruit",
|
||||||
|
"info_text4": "inspring, calming"
|
||||||
|
},
|
||||||
|
"Grape Waves": {
|
||||||
|
"values": [25, 75, 45, 45, 35, 45],
|
||||||
|
"info_text0": "20%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "creamy, heavy, raisins",
|
||||||
|
"info_text4": "calming, inspiring"
|
||||||
|
},
|
||||||
|
"JBT Kush": {
|
||||||
|
"values": [30, 55, 50, 55, 45, 41],
|
||||||
|
"info_text0": "20%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "gassy (fuel), cheesy, pungent",
|
||||||
|
"info_text4": "calming, relaxing, sleep promoting"
|
||||||
|
},
|
||||||
|
"Mother's Choice": {
|
||||||
|
"values": [35, 35, 55, 35, 50, 61],
|
||||||
|
"info_text0": "15%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "spicy-sweet, candy",
|
||||||
|
"info_text4": "inspiring, focusing, relaxing"
|
||||||
|
},
|
||||||
|
"Royal Feast": {
|
||||||
|
"values": [25, 50, 55, 70, 55, 45],
|
||||||
|
"info_text0": "15%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "earthy, sour, heavy apple aroma",
|
||||||
|
"info_text4": "focusing, calming, sleep promoting"
|
||||||
|
},
|
||||||
|
"Strawberry Sundae": {
|
||||||
|
"values": [28, 32, 50, 42, 41, 47],
|
||||||
|
"info_text0": "15%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "fruity-creamy, strawberry yogurt",
|
||||||
|
"info_text4": "inspiring, relaxing"
|
||||||
|
},
|
||||||
|
"Wedding Cake": {
|
||||||
|
"values": [25, 70, 60, 50, 41, 50],
|
||||||
|
"info_text0": "20%",
|
||||||
|
"info_text1": "<1%",
|
||||||
|
"info_text2": "Indica-dominant Hybrid",
|
||||||
|
"info_text3": "creamy, lemon, vanilla, pastry",
|
||||||
|
"info_text4": "calming, relaxing, inspiring"
|
||||||
|
}
|
||||||
|
}
|
BIN
static/cic.png
Normal file
BIN
static/cic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
BIN
static/cm.png
Normal file
BIN
static/cm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
BIN
static/favicon.png
Executable file
BIN
static/favicon.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
168
static/js/inactive.js
Executable file
168
static/js/inactive.js
Executable file
@ -0,0 +1,168 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const labels = ['Energy','Calm','Relax','Sleep','Focus','Inspire'];
|
||||||
|
let userData = [...window.appData.initialUserData];
|
||||||
|
const profiles = window.appData.profiles;
|
||||||
|
|
||||||
|
const youLineColor = 'rgba(255,255,255,0.69)';
|
||||||
|
const pointBgColor = 'rgba(0,0,0,0.1)';
|
||||||
|
const youFillColor = 'rgba(255,255,255,0.1)';
|
||||||
|
const matchLineColor = 'rgb(191,181,49)';
|
||||||
|
const matchFillColor = 'rgba(191,181,49,0.420)';
|
||||||
|
const radialLineColor= 'rgba(255,255,255,0.420)';
|
||||||
|
|
||||||
|
function distance(a,b){ return Math.sqrt(a.reduce((s,v,i)=>s+(v-b[i])**2,0)); }
|
||||||
|
|
||||||
|
function getTopMatches() {
|
||||||
|
return Object.entries(profiles)
|
||||||
|
.sort((a,b)=>distance(userData,a[1].values)-distance(userData,b[1].values))
|
||||||
|
.slice(0,3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFontSize(){ return window.innerWidth < 600 ? 10 : 39; }
|
||||||
|
function getBottomChartFontSize(){ return window.innerWidth < 600 ? 8 : 24; }
|
||||||
|
function getPointRadius(){ return window.innerWidth < 600 ? 11 : 24; }
|
||||||
|
function getPointHoverRadius(){ return window.innerWidth < 600 ? 12 : 39; }
|
||||||
|
function getPointBorderColor(){ return '#BFB531'; }
|
||||||
|
|
||||||
|
// === Initialize charts ===
|
||||||
|
const charts = [1,2,3].map(i => {
|
||||||
|
const ctx = document.getElementById(`chart-${i}`).getContext('2d');
|
||||||
|
const isBest = i === 1;
|
||||||
|
|
||||||
|
const datasets = isBest ? [
|
||||||
|
{
|
||||||
|
label:'You',
|
||||||
|
data:[...userData],
|
||||||
|
fill:true,
|
||||||
|
borderColor: youLineColor,
|
||||||
|
backgroundColor: youFillColor,
|
||||||
|
pointBackgroundColor: pointBgColor,
|
||||||
|
pointBorderColor: getPointBorderColor(),
|
||||||
|
pointRadius: getPointRadius(),
|
||||||
|
pointHoverRadius: getPointHoverRadius()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label:'Match',
|
||||||
|
data:[],
|
||||||
|
fill:true,
|
||||||
|
borderColor: matchLineColor,
|
||||||
|
backgroundColor: matchFillColor,
|
||||||
|
pointBackgroundColor: matchLineColor,
|
||||||
|
pointRadius:0
|
||||||
|
}
|
||||||
|
] : [
|
||||||
|
{
|
||||||
|
label:'Match',
|
||||||
|
data:[],
|
||||||
|
fill:true,
|
||||||
|
borderColor: matchLineColor,
|
||||||
|
backgroundColor: matchFillColor,
|
||||||
|
pointBackgroundColor: matchLineColor,
|
||||||
|
pointRadius:0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return new Chart(ctx,{
|
||||||
|
type:'radar',
|
||||||
|
data:{ labels, datasets },
|
||||||
|
options:{
|
||||||
|
responsive:true,
|
||||||
|
maintainAspectRatio:false,
|
||||||
|
scales:{
|
||||||
|
r:{
|
||||||
|
min:0, max:80,
|
||||||
|
ticks:{ display:false, stepSize:20 },
|
||||||
|
pointLabels:{ font:{ size:isBest?getFontSize():getBottomChartFontSize(), weight:'bold' }, color:'#FFF' },
|
||||||
|
angleLines:{ color:radialLineColor },
|
||||||
|
grid:{ drawticks:false, drawOnChartArea:false, circular:false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins:{
|
||||||
|
tooltip:{ enabled:false },
|
||||||
|
legend:{ display:false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// === Update charts & DOM ===
|
||||||
|
function updateCharts(){
|
||||||
|
const matches = getTopMatches();
|
||||||
|
|
||||||
|
matches.forEach(([name, prof], idx)=>{
|
||||||
|
const chart = charts[idx];
|
||||||
|
if(idx===0){
|
||||||
|
chart.data.datasets[0].data = [...userData];
|
||||||
|
chart.data.datasets[1].data = prof.values;
|
||||||
|
chart.data.datasets[1].label = name;
|
||||||
|
} else {
|
||||||
|
chart.data.datasets[0].data = prof.values;
|
||||||
|
chart.data.datasets[0].label = name;
|
||||||
|
}
|
||||||
|
chart.options.scales.r.pointLabels.font.size = idx===0 ? getFontSize() : getBottomChartFontSize();
|
||||||
|
chart.update('none');
|
||||||
|
|
||||||
|
// Update main card
|
||||||
|
document.getElementById(`name-${idx+1}`).textContent = name;
|
||||||
|
for (let i = 0; i <= 4; i++) {
|
||||||
|
const el = document.getElementById(`info${i}-${idx+1}`);
|
||||||
|
if (el) el.textContent = prof[`info_text${i}`] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update popup
|
||||||
|
const popupName = document.getElementById(`popup-name-${idx+1}`);
|
||||||
|
if (popupName) popupName.textContent = name;
|
||||||
|
|
||||||
|
for (let i = 0; i <= 4; i++) {
|
||||||
|
const popupEl = document.getElementById(`popup-info${i}-${idx+1}`);
|
||||||
|
if (popupEl) popupEl.textContent = prof[`info_text${i}`] || '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCharts();
|
||||||
|
|
||||||
|
// === Reset button ===
|
||||||
|
document.getElementById('resetBtn').addEventListener('click', ()=>{
|
||||||
|
userData = [...window.appData.initialUserData];
|
||||||
|
updateCharts();
|
||||||
|
});
|
||||||
|
|
||||||
|
// === Dragging top chart ===
|
||||||
|
const topChart = charts[0];
|
||||||
|
const topCanvas = document.getElementById('chart-1');
|
||||||
|
topCanvas.style.touchAction = 'none';
|
||||||
|
let draggingIndex = null;
|
||||||
|
|
||||||
|
topCanvas.addEventListener('pointerdown', e => {
|
||||||
|
const points = topChart.getElementsAtEventForMode(e, 'nearest', {intersect:true}, true);
|
||||||
|
if(points.length && points[0].datasetIndex===0) draggingIndex = points[0].index;
|
||||||
|
});
|
||||||
|
|
||||||
|
topCanvas.addEventListener('pointermove', e => {
|
||||||
|
if(draggingIndex===null) return;
|
||||||
|
const rect = topCanvas.getBoundingClientRect();
|
||||||
|
const x = e.clientX - rect.left;
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
const cx = rect.width/2;
|
||||||
|
const cy = rect.height/2;
|
||||||
|
const dx = x-cx;
|
||||||
|
const dy = cy-y;
|
||||||
|
const dist = Math.sqrt(dx*dx + dy*dy);
|
||||||
|
const rMax = Math.min(rect.width, rect.height)/2 * 0.9;
|
||||||
|
let value = (dist/rMax)*80;
|
||||||
|
value = Math.min(Math.max(Math.round(value),0),80);
|
||||||
|
userData[draggingIndex] = value;
|
||||||
|
updateCharts();
|
||||||
|
});
|
||||||
|
|
||||||
|
topCanvas.addEventListener('pointerup', ()=> draggingIndex=null);
|
||||||
|
topCanvas.addEventListener('pointerleave', ()=> draggingIndex=null);
|
||||||
|
|
||||||
|
// bottom charts scroll normally
|
||||||
|
[2,3].forEach(i => {
|
||||||
|
document.getElementById(`chart-${i}`).style.touchAction='auto';
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('resize', updateCharts);
|
||||||
|
});
|
16
static/js/popup.js
Executable file
16
static/js/popup.js
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
function togglePopup(event, id) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const popup = document.getElementById(id);
|
||||||
|
|
||||||
|
if (popup.classList.contains('active')) {
|
||||||
|
popup.classList.remove('active');
|
||||||
|
} else {
|
||||||
|
document.querySelectorAll('.popup').forEach(p => p.classList.remove('active'));
|
||||||
|
popup.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close on outside click
|
||||||
|
document.addEventListener('click', () => {
|
||||||
|
document.querySelectorAll('.popup').forEach(p => p.classList.remove('active'));
|
||||||
|
});
|
BIN
static/scc.png
Normal file
BIN
static/scc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 414 KiB |
687
static/style.css
Normal file
687
static/style.css
Normal file
@ -0,0 +1,687 @@
|
|||||||
|
/* === Reset & Base Styles === */
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
color: #FFFFFF;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
line-height: 1.3;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-match h2 {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-match h3 {
|
||||||
|
margin-top: 0.25em;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-row h3 {
|
||||||
|
margin-top: 0.25em;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-match p {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0.5em 0 0.5em;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-row h2 {
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-row p {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
margin: 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-block {
|
||||||
|
#border: 3px solid #BFB531;
|
||||||
|
border-radius: 25px;
|
||||||
|
padding: 0.5em 0.5em;
|
||||||
|
margin: 0.5em 4em;
|
||||||
|
background-color: rgba(191,181,49,0.1);
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-info-block {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ABOUT PAGE*/
|
||||||
|
.aboutbody {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abouttext a, p {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abouttextcontainer {
|
||||||
|
margin-top: 3vh;
|
||||||
|
margin-bottom: 3vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aboutlink {
|
||||||
|
color: #BFB531;
|
||||||
|
}
|
||||||
|
|
||||||
|
.made {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #FF6600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aboutlogocontainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aboutlogo {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
max-height: 150px;
|
||||||
|
max-width: 95%;
|
||||||
|
width: auto;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aboutlogo img {
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="hidden"] { display: none; }
|
||||||
|
|
||||||
|
/* === Body & Sticky Footer === */
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 95vh; /* ensures footer sticks at bottom */
|
||||||
|
background: #1A202C;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Page container fills remaining space === */
|
||||||
|
.page-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Logo === */
|
||||||
|
.logo-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Grid Layout (profiles page) === */
|
||||||
|
.profile-grid {
|
||||||
|
display: grid;
|
||||||
|
padding: 20px;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(420px, 420px));
|
||||||
|
gap: 25px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-grid canvas {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
height: auto;
|
||||||
|
max-height: 420px;
|
||||||
|
display: block;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Footer === */
|
||||||
|
.footer {
|
||||||
|
padding: 15px;
|
||||||
|
flex-shrink: 0; /* ensure footer doesn't shrink */
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.footer p {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Cards === */
|
||||||
|
.profile-card {
|
||||||
|
background: rgba(0,0,0,0.39);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-profile-card {
|
||||||
|
padding: 1em;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(0,0,0,0.39);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
|
||||||
|
display: flex;
|
||||||
|
max-width: 420px;
|
||||||
|
max-height: 690px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-profile-card canvas {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-width: 420px;
|
||||||
|
max-height: 420px;
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Canvas sizing */
|
||||||
|
.profile-card canvas {
|
||||||
|
margin: 3em 0;
|
||||||
|
touch-action: pan-y;
|
||||||
|
#height: auto;
|
||||||
|
max-height: 840px;
|
||||||
|
margin: 0.5em;
|
||||||
|
display: block;
|
||||||
|
object-fit: contain;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Matches container === */
|
||||||
|
.matches-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 18px;
|
||||||
|
align-items: stretch;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top row: full width */
|
||||||
|
.best-row {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bottom row: use CSS grid for auto-equal height columns */
|
||||||
|
.bottom-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr; /* mobile default: one column */
|
||||||
|
gap: 15px;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
#align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Profile-card inside grid cells fill the cell */
|
||||||
|
.bottom-row .profile-card {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
align-items: stretch;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top match card styling */
|
||||||
|
.best-card {
|
||||||
|
border: 2px solid #BFB531;
|
||||||
|
box-shadow: 0 6px 24px rgba(0,0,0,0.18);
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Side match default layout (stacked) */
|
||||||
|
.side-match {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Buttons === */
|
||||||
|
button[type="submit"],
|
||||||
|
button.magenta-button {
|
||||||
|
display: block;
|
||||||
|
margin: 16px 0;
|
||||||
|
padding: 11px 22px;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(0,0,0,1);
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 4px 10px rgba(0,0,0,0.18);
|
||||||
|
transition: background-color 0.18s ease, transform 0.12s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[type="submit"] { background-color: #808080; }
|
||||||
|
button[type="submit"]:hover { background-color: #FFFFFF; transform: translateY(1px); }
|
||||||
|
button[type="submit"]:active { background-color: #000000; transform: translateY(0); }
|
||||||
|
|
||||||
|
button.magenta-button { background-color: #BFB531; }
|
||||||
|
button.magenta-button:hover { background-color: #9E9928; transform: translateY(1px); }
|
||||||
|
button.magenta-button:active { background-color: #E6E66B; transform: translateY(0); }
|
||||||
|
|
||||||
|
.button-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Popup hidden initially */
|
||||||
|
.popup {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0,0,0,0.6);
|
||||||
|
z-index: 9999;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show when active */
|
||||||
|
.popup.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content {
|
||||||
|
background: #262626;
|
||||||
|
padding: 1.5em 0;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 2px solid #BFB531;
|
||||||
|
max-width: 500px;
|
||||||
|
width: 90%;
|
||||||
|
color: #FFF;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 8px 28px rgba(0,0,0,0.5);
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glow-text {
|
||||||
|
text-shadow:
|
||||||
|
0 0 5px #BFB531,
|
||||||
|
0 0 10px #BFB531,
|
||||||
|
0 0 20px #BFB531,
|
||||||
|
0 0 40px #BFB531;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glow-info {
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
text-shadow:
|
||||||
|
0 0 5px #000000,
|
||||||
|
0 0 10px #000000,
|
||||||
|
0 0 20px #000000,
|
||||||
|
0 0 40px #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-text {
|
||||||
|
padding: 0.1em 0;
|
||||||
|
margin: 0.1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content h3 {
|
||||||
|
font-size: 3rem;
|
||||||
|
margin: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content h4 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
|
color: rgba(181,191,49,0.42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content p {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 0 0 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Circle "i" info icon */
|
||||||
|
.info-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 69px;
|
||||||
|
height: 69px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: rgba(191,181,49,0.25); /* golden circle */
|
||||||
|
color: #BFB531;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
font-size: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-info-icon {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-row .info-icon {
|
||||||
|
width: 32px; /* smaller circle */
|
||||||
|
height: 32px;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-row .info-icon::before {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-icon::before {
|
||||||
|
content: "i"; /* the actual "i" */
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover effect */
|
||||||
|
.info-icon:hover {
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-with-icon {
|
||||||
|
display: block;
|
||||||
|
#align-items: center;
|
||||||
|
margin: 0.25em 1em 0.25em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Animations === */
|
||||||
|
@keyframes gradientMove {
|
||||||
|
0% { background-position: 0% 50%; }
|
||||||
|
50% { background-position: 100% 50%; }
|
||||||
|
100% { background-position: 0% 50%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Small mobile tweaks (≤600px) === */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.profile-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-card canvas {
|
||||||
|
max-height: 360px;
|
||||||
|
}
|
||||||
|
.top-match h2, .bottom-row h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-match h3, .bottom-row h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.top-match p, .bottom-row p {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.info-block {
|
||||||
|
padding: 0.25em 2em;
|
||||||
|
margin: 0.25em;
|
||||||
|
}
|
||||||
|
.button-row {
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
button.magenta-button {
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="hidden"] { display: none; }
|
||||||
|
.info-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
.info-icon::before {
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
.bottom-row .info-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
.bottom-row .info-icon::before {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.popup-content h3 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
|
color: rgba(181,191,49,0.42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content p {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0 0 1em 0;
|
||||||
|
}
|
||||||
|
.cards-container {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.footer p {
|
||||||
|
font-size: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Large screen width (≥1000px) === */
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
/* Bottom row: two equal columns */
|
||||||
|
.bottom-row {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top card canvas larger */
|
||||||
|
.best-card canvas {
|
||||||
|
max-height: 50vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Side match canvas */
|
||||||
|
.bottom-row .profile-card canvas {
|
||||||
|
}
|
||||||
|
.cards-container {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Landscape Center Hero + Sidecards layout === */
|
||||||
|
@media only screen and (orientation: landscape) {
|
||||||
|
.aboutbody {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.abouttextcontainer {
|
||||||
|
min-width: 500px;
|
||||||
|
}
|
||||||
|
.matches-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 2fr 1fr; /* left, center, right */
|
||||||
|
gap: 20px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
.best-row {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.best-card {
|
||||||
|
max-width: 50vw;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.info-icon {
|
||||||
|
height: 39px;
|
||||||
|
width: 39px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
.title-with-icon h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
.top-match p {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.title-with-icon p {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.profile-card h3 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Place children explicitly */
|
||||||
|
.best-row {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-row {
|
||||||
|
display: contents; /* flatten so its children can go left/right */
|
||||||
|
}
|
||||||
|
|
||||||
|
#card-2 {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#card-3 {
|
||||||
|
grid-column: 3;
|
||||||
|
grid-row: 1;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scale down side cards relative to the hero */
|
||||||
|
#card-2, #card-3 {
|
||||||
|
transform: scale(0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.best-card canvas {
|
||||||
|
max-height: 39vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-card canvas {
|
||||||
|
max-height: 45vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* === Category Cards === */
|
||||||
|
.cards-container {
|
||||||
|
display: grid;
|
||||||
|
gap: 15px;
|
||||||
|
padding: 20px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-card {
|
||||||
|
background: rgba(0, 0, 0, 0.24);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 3px solid transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
user-select: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-card.active {
|
||||||
|
border-color: #;
|
||||||
|
background: rgba(0, 0, 0, 0.69);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-card.emphasis {
|
||||||
|
border-color: #BFB531;
|
||||||
|
background: rgba(0, 0, 0, 0.69);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-card .checkmark {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 16px;
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #BFB531;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-card .checkmark span {
|
||||||
|
opacity: 0.39;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-card.active .checkmark span.first {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-card.emphasis .checkmark span.first,
|
||||||
|
.category-card.emphasis .checkmark span.second {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
/* === Confetti canvas fix === */
|
||||||
|
canvas.confetti-canvas {
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
pointer-events: none; /* don’t block buttons/links */
|
||||||
|
z-index: 9999; /* stays above your charts/cards */
|
||||||
|
}
|
42
templates/about.html
Executable file
42
templates/about.html
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>cannamatch about</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}">
|
||||||
|
</head>
|
||||||
|
<body class="profiles-page">
|
||||||
|
<div class="page-container">
|
||||||
|
<p style="display:none">HUEEEEEEEEEEEEED 42069</p>
|
||||||
|
<div class="logo-container">
|
||||||
|
<a href="{{ url_for('index') }}">
|
||||||
|
<img src="{{ url_for('static', filename='cm.png') }}" alt="Logo" class="logo">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="button-row">
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('index') }}'">About</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('profiles_page') }}'">All Profiles</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('inactive') }}'">Interactive</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('quiz') }}'">Quiz</button>
|
||||||
|
</div>
|
||||||
|
<div class="aboutbody">
|
||||||
|
<div class="abouttextcontainer">
|
||||||
|
<p class="abouttext">Made for:</p>
|
||||||
|
<div class="aboutlogocontainer">
|
||||||
|
<a target="_blank" href="https://cicada.je"><img class="aboutlogo" src="static/scc.png" alt="scc"></img></a>
|
||||||
|
</div>
|
||||||
|
<p class="abouttext">Learn about Swiss Cannabis Research <a class="aboutlink" target="_blank" href="https://swisscannabis-research.ch">here</a>.</p>
|
||||||
|
</div>
|
||||||
|
<div class="abouttextcontainer">
|
||||||
|
<p class="abouttext">Inspired by:</p>
|
||||||
|
<div class="aboutlogocontainer">
|
||||||
|
<a target="blank" href="https://cicada.je"><img class="aboutlogo" src="static/cic.png" alt="cic"></img></a>
|
||||||
|
</div>
|
||||||
|
<p class="abouttext">Learn about the Terpene Wheel <a class="aboutlink" target="blank" href="https://cicada.je/terpene-wheel/">here</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
139
templates/inactive.html
Executable file
139
templates/inactive.html
Executable file
@ -0,0 +1,139 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>cannamatch interactive</title>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-dragdata@2.0.2/dist/chartjs-plugin-dragdata.min.js"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/popup.js') }}"></script>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}">
|
||||||
|
</head>
|
||||||
|
<body class="profiles-page">
|
||||||
|
|
||||||
|
<div class="page-container">
|
||||||
|
<div class="logo-container">
|
||||||
|
<a href="{{ url_for('index') }}">
|
||||||
|
<img src="{{ url_for('static', filename='cm.png') }}" alt="Logo" class="logo">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button-row">
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('index') }}'">About</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('profiles_page') }}'">All Profiles</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('inactive') }}'">Interactive</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('quiz') }}'">Quiz</button>
|
||||||
|
<button type="button" class="magenta-button" id="resetBtn" style="display:none">Reset</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="matches-container">
|
||||||
|
<!-- Top Match -->
|
||||||
|
<div class="matches-row best-row">
|
||||||
|
<div class="profile-card best-card top-match" id="card-1">
|
||||||
|
<h3>Best</h3>
|
||||||
|
<div class="info-block">
|
||||||
|
<div class="title-with-icon">
|
||||||
|
<h2 class="glow-info" id="name-1"></h2>
|
||||||
|
<span class="info-icon glow-text" onclick="togglePopup(event, 'popup-1')"></span>
|
||||||
|
</div>
|
||||||
|
<p class="info-text" id="info0-1"></p>
|
||||||
|
<p class="info-text" id="info2-1"></p>
|
||||||
|
</div>
|
||||||
|
<canvas id="chart-1"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Popup for Top Match -->
|
||||||
|
<div class="popup" id="popup-1">
|
||||||
|
<div class="popup-content">
|
||||||
|
<h3 class="info-text glow-info" id="popup-name-1"></h3>
|
||||||
|
<h4 class="info-text glow-info">THC</h4>
|
||||||
|
<p class="info-text" id="popup-info0-1"></p>
|
||||||
|
<h4 class="info-text glow-info">CBD</h4>
|
||||||
|
<p class="info-text" id="popup-info1-1"></p>
|
||||||
|
<h4 class="info-text glow-info">GENETICS</h4>
|
||||||
|
<p class="info-text" id="popup-info2-1"></p>
|
||||||
|
<h4 class="info-text glow-info">TASTE</h4>
|
||||||
|
<p class="info-text" id="popup-info3-1"></p>
|
||||||
|
<h4 class="info-text glow-info">EFFECT</h4>
|
||||||
|
<p class="info-text" id="popup-info4-1"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom Row Matches -->
|
||||||
|
<div class="matches-row bottom-row">
|
||||||
|
<!-- Runner-up -->
|
||||||
|
<div class="profile-card side-match" id="card-2">
|
||||||
|
<h3>Runner-up</h3>
|
||||||
|
<div class="info-block">
|
||||||
|
<div class="title-with-icon">
|
||||||
|
<h2 class="glow-info" id="name-2"></h2>
|
||||||
|
<span class="info-icon glow-text" onclick="togglePopup(event, 'popup-2')"></span>
|
||||||
|
</div>
|
||||||
|
<p class="info-text" id="info0-2"></p>
|
||||||
|
<p class="info-text" id="info2-2"></p>
|
||||||
|
</div>
|
||||||
|
<canvas id="chart-2"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Popup for Runner-up -->
|
||||||
|
<div class="popup" id="popup-2">
|
||||||
|
<div class="popup-content">
|
||||||
|
<h3 class="info-text glow-info" id="popup-name-2"></h3>
|
||||||
|
<h4 class="info-text glow-info">THC</h4>
|
||||||
|
<p class="info-text" id="popup-info0-2"></p>
|
||||||
|
<h4 class="info-text glow-info">CBD</h4>
|
||||||
|
<p class="info-text" id="popup-info1-2"></p>
|
||||||
|
<h4 class="info-text glow-info">GENETICS</h4>
|
||||||
|
<p class="info-text" id="popup-info2-2"></p>
|
||||||
|
<h4 class="info-text glow-info">TASTE</h4>
|
||||||
|
<p class="info-text" id="popup-info3-2"></p>
|
||||||
|
<h4 class="info-text glow-info">EFFECT</h4>
|
||||||
|
<p class="info-text" id="popup-info4-2"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Maybe -->
|
||||||
|
<div class="profile-card side-match" id="card-3">
|
||||||
|
<h3>Maybe...</h3>
|
||||||
|
<div class="info-block">
|
||||||
|
<div class="title-with-icon">
|
||||||
|
<h2 class="glow-info" id="name-3"></h2>
|
||||||
|
<span class="info-icon glow-text" onclick="togglePopup(event, 'popup-3')"></span>
|
||||||
|
</div>
|
||||||
|
<p class="info-text" id="info0-3"></p>
|
||||||
|
<p class="info-text" id="info2-3"></p>
|
||||||
|
</div>
|
||||||
|
<canvas id="chart-3"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Popup for Maybe -->
|
||||||
|
<div class="popup" id="popup-3">
|
||||||
|
<div class="popup-content">
|
||||||
|
<h3 class="info-text glow-info" id="popup-name-3"></h3>
|
||||||
|
<h4 class="info-text glow-info">THC</h4>
|
||||||
|
<p class="info-text" id="popup-info0-3"></p>
|
||||||
|
<h4 class="info-text glow-info">CBD</h4>
|
||||||
|
<p class="info-text" id="popup-info1-3"></p>
|
||||||
|
<h4 class="info-text glow-info">GENETICS</h4>
|
||||||
|
<p class="info-text" id="popup-info2-3"></p>
|
||||||
|
<h4 class="info-text glow-info">TASTE</h4>
|
||||||
|
<p class="info-text" id="popup-info3-3"></p>
|
||||||
|
<h4 class="info-text glow-info">EFFECT</h4>
|
||||||
|
<p class="info-text" id="popup-info4-3"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.appData = {
|
||||||
|
profiles: {{ profiles | tojson }},
|
||||||
|
initialUserData: [39,39,39,39,39,39]
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script src="{{ url_for('static', filename='js/inactive.js') }}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
102
templates/profiles.html
Executable file
102
templates/profiles.html
Executable file
@ -0,0 +1,102 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>cannamatch profiles</title>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<!-- Popup helper (same as inactive) -->
|
||||||
|
<script src="{{ url_for('static', filename='js/popup.js') }}"></script>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}">
|
||||||
|
</head>
|
||||||
|
<body class="profiles-page">
|
||||||
|
<div class="page-container">
|
||||||
|
<div class="logo-container">
|
||||||
|
<a href="{{ url_for('index') }}">
|
||||||
|
<img src="{{ url_for('static', filename='cm.png') }}" alt="Logo" class="logo">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="button-row">
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('index') }}'">About</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('profiles_page') }}'">All Profiles</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('inactive') }}'">Interactive</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('quiz') }}'">Quiz</button>
|
||||||
|
</div>
|
||||||
|
<div class="profile-grid">
|
||||||
|
{% for name, values in profiles.items() %}
|
||||||
|
<div class="profile-profile-card">
|
||||||
|
<div class="info-block profile-info-block">
|
||||||
|
<div class="title-with-icon">
|
||||||
|
<div class="profile-name"><h2 class="glow-info">{{ name }}</h2></div>
|
||||||
|
<span class="info-icon profile-info-icon glow-text" onclick="togglePopup(event, 'popup-{{ loop.index }}')"></span>
|
||||||
|
<p class="info-text">{{ values.info_text0 }}</p>
|
||||||
|
<p class="info-text">{{ values.info_text2 }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<canvas id="chart-{{ loop.index }}"></canvas>
|
||||||
|
<div class="popup" id="popup-{{ loop.index }}">
|
||||||
|
<div class="popup-content profile-popup-content">
|
||||||
|
<h3 class="info-text" id="popup-name-{{ loop.index }}">{{ name }}</h3>
|
||||||
|
<h4 class="info-text glow-info">THC</h4>
|
||||||
|
<p class="info-text" id="popup-info0-1">{{ values.info_text0 }}</p>
|
||||||
|
<h4 class="info-text glow-info">CBD</h4>
|
||||||
|
<p class="info-text" id="popup-info1-1">{{ values.info_text1 }}</p>
|
||||||
|
<h4 class="info-text glow-info">GENETICS</h4>
|
||||||
|
<p class="info-text" id="popup-info2-1">{{ values.info_text2 }}</p>
|
||||||
|
<h4 class="info-text glow-info">TASTE</h4>
|
||||||
|
<p class="info-text" id="popup-info3-1">{{ values.info_text3 }}</p>
|
||||||
|
<h4 class="info-text glow-info">EFFECT</h4>
|
||||||
|
<p class="info-text" id="popup-info4-1">{{ values.info_text4 }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const labels = ['Energy', 'Calm', 'Relax', 'Sleep', 'Focus', 'Inspire'];
|
||||||
|
|
||||||
|
const profiles = {{ profiles | tojson }};
|
||||||
|
|
||||||
|
Object.entries(profiles).forEach(([name, data], index) => {
|
||||||
|
const ctx = document.getElementById(`chart-${index + 1}`).getContext('2d');
|
||||||
|
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'radar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
label: name,
|
||||||
|
data: data.values,
|
||||||
|
fill: true,
|
||||||
|
borderColor: 'rgba(191,181,49,1)',
|
||||||
|
pointBackgroundColor: 'rgba(0,0,0,0)',
|
||||||
|
pointBorderColor: 'rgba(191,181,49,1)',
|
||||||
|
backgroundColor: 'rgba(191,181,49,0.420)',
|
||||||
|
pointRadius: 0,
|
||||||
|
pointHoverRadius: 0
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
r: {
|
||||||
|
min: 0,
|
||||||
|
max: 80,
|
||||||
|
ticks: { stepSize: 20, display: false },
|
||||||
|
pointLabels: { font: { size: 15, weight: 'bold' }, color: '#FFF' },
|
||||||
|
grid: { color: 'rgba(255,255,255,0.420)', circular: false },
|
||||||
|
angleLines: { color: 'rgba(255,255,255,0.69)' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: { tooltip: { enabled: false}, legend: { display: false } }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
67
templates/quiz.html
Executable file
67
templates/quiz.html
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>cannamatch quiz</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}">
|
||||||
|
</head>
|
||||||
|
<body class="profiles-page">
|
||||||
|
<div class="page-container">
|
||||||
|
<div class="logo-container">
|
||||||
|
<a href="{{ url_for('index') }}">
|
||||||
|
<img src="{{ url_for('static', filename='cm.png') }}" alt="Logo" class="logo">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="button-row">
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('index') }}'">About</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('profiles_page') }}'">All Profiles</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('inactive') }}'">Interactive</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('quiz') }}'">Quiz</button>
|
||||||
|
</div>
|
||||||
|
<h2>What are you looking for?</h2>
|
||||||
|
<form method="POST">
|
||||||
|
<div class="cards-container">
|
||||||
|
{% set categories = [
|
||||||
|
{'name': 'Energy', 'desc': 'Boost endurance and alertness.'},
|
||||||
|
{'name': 'Calm', 'desc': 'Stay grounded and centered.'},
|
||||||
|
{'name': 'Relax', 'desc': 'Ease tension in body and mind.'},
|
||||||
|
{'name': 'Sleep', 'desc': 'Support restfulness and recovery.'},
|
||||||
|
{'name': 'Focus', 'desc': 'Enhance concentration and clarity.'},
|
||||||
|
{'name': 'Inspire', 'desc': 'Spark creativity and new ideas.'}
|
||||||
|
] %}
|
||||||
|
{% for cat in categories %}
|
||||||
|
<div class="category-card" onclick="toggleCard(this, {{ loop.index }})">
|
||||||
|
<h3>{{ cat.name }}</h3>
|
||||||
|
<p>{{ cat.desc }}</p>
|
||||||
|
<input type="hidden" id="point{{ loop.index }}" name="point{{ loop.index }}" value="20">
|
||||||
|
<div class="checkmark">
|
||||||
|
<span class="first">+</span><span class="second">+</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="button-row">
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function toggleCard(card, index) {
|
||||||
|
const input = document.getElementById(`point${index}`);
|
||||||
|
const values = [20, 40, 80];
|
||||||
|
let value = parseInt(input.value, 10);
|
||||||
|
if (isNaN(value)) value = 20;
|
||||||
|
|
||||||
|
// Find next value in the cycle
|
||||||
|
const nextValue = values[(values.indexOf(value) + 1) % values.length];
|
||||||
|
input.value = nextValue;
|
||||||
|
|
||||||
|
card.classList.remove("active", "emphasis");
|
||||||
|
if (nextValue === 40) card.classList.add("active");
|
||||||
|
else if (nextValue === 80) card.classList.add("emphasis");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
299
templates/result.html
Executable file
299
templates/result.html
Executable file
@ -0,0 +1,299 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>cannamatch result</title>
|
||||||
|
|
||||||
|
<!-- Chart.js & confetti -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.5.1/dist/confetti.browser.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Popup helper (same as inactive) -->
|
||||||
|
<script src="{{ url_for('static', filename='js/popup.js') }}"></script>
|
||||||
|
|
||||||
|
<!-- Shared styles / favicon (same as inactive) -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}" />
|
||||||
|
</head>
|
||||||
|
<body class="profiles-page">
|
||||||
|
<div class="page-container">
|
||||||
|
|
||||||
|
<!-- Logo (same block as inactive) -->
|
||||||
|
<div class="logo-container">
|
||||||
|
<a href="{{ url_for('index') }}">
|
||||||
|
<img src="{{ url_for('static', filename='cm.png') }}" alt="Logo" class="logo">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Buttons row (kept minimal for results page) -->
|
||||||
|
<div class="button-row">
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('index') }}'">About</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('profiles_page') }}'">All Profiles</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('inactive') }}'">Interactive</button>
|
||||||
|
<button type="button" class="magenta-button" onclick="location.href='{{ url_for('quiz') }}'">Quiz</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="matches-container">
|
||||||
|
<!-- Top Match -->
|
||||||
|
<div class="matches-row best-row">
|
||||||
|
<div class="profile-card best-card top-match" id="card-1">
|
||||||
|
<h3>Best</h3>
|
||||||
|
<div class="info-block">
|
||||||
|
<div class="title-with-icon">
|
||||||
|
<h2 class="glow-info" id="name-1">{{ top_matches[0][0] if top_matches|length > 0 else '' }}</h2>
|
||||||
|
<span class="info-icon glow-text" onclick="togglePopup(event, 'popup-1')"></span>
|
||||||
|
</div>
|
||||||
|
{% if top_matches|length > 0 %}
|
||||||
|
<p class="info-text" id="info0-1">{{ top_matches[0][1].get('info_text0','') }}</p>
|
||||||
|
<p class="info-text" id="info2-1">{{ top_matches[0][1].get('info_text2','') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<canvas id="chart-1"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Popup for Top Match -->
|
||||||
|
<div class="popup" id="popup-1">
|
||||||
|
<div class="popup-content">
|
||||||
|
<h3 class="info-text" id="popup-name-1">{{ top_matches[0][0] if top_matches|length > 0 else '' }}</h3>
|
||||||
|
{% if top_matches|length > 0 %}
|
||||||
|
<h4 class="info-text glow-info">THC</h4>
|
||||||
|
<p class="info-text" id="popup-info0-1">{{ top_matches[0][1].get('info_text0','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">CBD</h4>
|
||||||
|
<p class="info-text" id="popup-info1-1">{{ top_matches[0][1].get('info_text1','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">GENETICS</h4>
|
||||||
|
<p class="info-text" id="popup-info2-1">{{ top_matches[0][1].get('info_text2','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">TASTE</h4>
|
||||||
|
<p class="info-text" id="popup-info3-1">{{ top_matches[0][1].get('info_text3','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">EFFECT</h4>
|
||||||
|
<p class="info-text" id="popup-info4-1">{{ top_matches[0][1].get('info_text4','') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom Row Matches -->
|
||||||
|
<div class="matches-row bottom-row">
|
||||||
|
<!-- Runner-up -->
|
||||||
|
<div class="profile-card side-match" id="card-2">
|
||||||
|
<h3>Runner-up</h3>
|
||||||
|
<div class="info-block">
|
||||||
|
<div class="title-with-icon">
|
||||||
|
<h2 class="glow-info" id="name-2">{{ top_matches[1][0] if top_matches|length > 1 else '' }}</h2>
|
||||||
|
<span class="info-icon glow-text" onclick="togglePopup(event, 'popup-2')"></span>
|
||||||
|
</div>
|
||||||
|
{% if top_matches|length > 1 %}
|
||||||
|
<p class="info-text" id="info0-2">{{ top_matches[1][1].get('info_text0','') }}</p>
|
||||||
|
<p class="info-text" id="info2-2">{{ top_matches[1][1].get('info_text2','') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<canvas id="chart-2"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Popup for Runner-up -->
|
||||||
|
<div class="popup" id="popup-2">
|
||||||
|
<div class="popup-content">
|
||||||
|
<h3 class="info-text" id="popup-name-2">{{ top_matches[1][0] if top_matches|length > 1 else '' }}</h3>
|
||||||
|
{% if top_matches|length > 1 %}
|
||||||
|
<h4 class="info-text glow-info">THC</h4>
|
||||||
|
<p class="info-text" id="popup-info0-2">{{ top_matches[1][1].get('info_text0','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">CBD</h4>
|
||||||
|
<p class="info-text" id="popup-info1-2">{{ top_matches[1][1].get('info_text1','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">GENETICS</h4>
|
||||||
|
<p class="info-text" id="popup-info2-2">{{ top_matches[1][1].get('info_text2','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">TASTE</h4>
|
||||||
|
<p class="info-text" id="popup-info3-2">{{ top_matches[1][1].get('info_text3','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">EFFECT</h4>
|
||||||
|
<p class="info-text" id="popup-info4-2">{{ top_matches[1][1].get('info_text4','') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Maybe -->
|
||||||
|
<div class="profile-card side-match" id="card-3">
|
||||||
|
<h3>Maybe...</h3>
|
||||||
|
<div class="info-block">
|
||||||
|
<div class="title-with-icon">
|
||||||
|
<h2 class="glow-info" id="name-3">{{ top_matches[2][0] if top_matches|length > 2 else '' }}</h2>
|
||||||
|
<span class="info-icon glow-text" onclick="togglePopup(event, 'popup-3')"></span>
|
||||||
|
</div>
|
||||||
|
{% if top_matches|length > 2 %}
|
||||||
|
<p class="info-text" id="info0-3">{{ top_matches[2][1].get('info_text0','') }}</p>
|
||||||
|
<p class="info-text" id="info2-3">{{ top_matches[2][1].get('info_text2','') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<canvas id="chart-3"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Popup for Maybe -->
|
||||||
|
<div class="popup" id="popup-3">
|
||||||
|
<div class="popup-content">
|
||||||
|
<h3 class="info-text" id="popup-name-3">{{ top_matches[2][0] if top_matches|length > 2 else '' }}</h3>
|
||||||
|
{% if top_matches|length > 2 %}
|
||||||
|
<h4 class="info-text glow-info">THC</h4>
|
||||||
|
<p class="info-text" id="popup-info0-3">{{ top_matches[2][1].get('info_text0','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">CBD</h4>
|
||||||
|
<p class="info-text" id="popup-info1-3">{{ top_matches[2][1].get('info_text1','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">GENETICS</h4>
|
||||||
|
<p class="info-text" id="popup-info2-3">{{ top_matches[2][1].get('info_text2','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">TASTE</h4>
|
||||||
|
<p class="info-text" id="popup-info3-3">{{ top_matches[2][1].get('info_text3','') }}</p>
|
||||||
|
<h4 class="info-text glow-info">EFFECT</h4>
|
||||||
|
<p class="info-text" id="popup-info4-3">{{ top_matches[2][1].get('info_text4','') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Same visual language as inactive.js
|
||||||
|
const labels = ['Energy','Calm','Relax','Sleep','Focus','Inspire'];
|
||||||
|
const userData = {{ user | tojson }};
|
||||||
|
const matches = {{ top_matches | tojson }};
|
||||||
|
|
||||||
|
const youLineColor = 'rgba(255,255,255,0.5)';
|
||||||
|
const youFillColor = 'rgba(255,255,255,0.1)';
|
||||||
|
const matchLineColor = 'rgb(191,181,49)';
|
||||||
|
const matchFillColor = 'rgba(191,181,49,0.420)';
|
||||||
|
const radialLineColor= 'rgba(255,255,255,0.420)';
|
||||||
|
|
||||||
|
function getFontSize(){ return window.innerWidth < 600 ? 10 : 42; }
|
||||||
|
function getBottomChartFontSize(){ return window.innerWidth < 600 ? 8 : 24; }
|
||||||
|
function getPointRadius(){ return window.innerWidth < 600 ? 11 : 24; }
|
||||||
|
function getPointHoverRadius(){ return window.innerWidth < 600 ? 12 : 39; }
|
||||||
|
|
||||||
|
const charts = [];
|
||||||
|
|
||||||
|
// Build top chart (You + best match), if present
|
||||||
|
if (matches.length > 0) {
|
||||||
|
const [bestName, bestProf] = matches[0];
|
||||||
|
const ctx1 = document.getElementById('chart-1').getContext('2d');
|
||||||
|
charts[0] = new Chart(ctx1, {
|
||||||
|
type: 'radar',
|
||||||
|
data: {
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: bestName,
|
||||||
|
data: bestProf.values,
|
||||||
|
fill: true,
|
||||||
|
borderColor: matchLineColor,
|
||||||
|
backgroundColor: matchFillColor,
|
||||||
|
pointBackgroundColor: matchLineColor,
|
||||||
|
pointRadius: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'You',
|
||||||
|
data: userData,
|
||||||
|
fill: true,
|
||||||
|
borderColor: youLineColor,
|
||||||
|
backgroundColor: youFillColor,
|
||||||
|
pointBackgroundColor: youLineColor,
|
||||||
|
pointBorderColor: matchLineColor,
|
||||||
|
pointRadius: 0,
|
||||||
|
pointHoverRadius: 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
r: {
|
||||||
|
min: 0,
|
||||||
|
max: 80,
|
||||||
|
ticks: { display: false, stepSize: 20 },
|
||||||
|
pointLabels: { font: { size: getFontSize(), weight: 'bold' }, color: '#FFF' },
|
||||||
|
angleLines: { color: radialLineColor },
|
||||||
|
grid: { drawticks: false, drawOnChartArea: false, circular: false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: { tooltip: { enabled: false }, legend: { display: false } }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build bottom charts (matches only)
|
||||||
|
for (let i = 1; i < Math.min(matches.length, 3); i++) {
|
||||||
|
const [name, prof] = matches[i];
|
||||||
|
const ctx = document.getElementById(`chart-${i+1}`).getContext('2d');
|
||||||
|
charts[i] = new Chart(ctx, {
|
||||||
|
type: 'radar',
|
||||||
|
data: {
|
||||||
|
labels,
|
||||||
|
datasets: [{
|
||||||
|
label: name,
|
||||||
|
data: prof.values,
|
||||||
|
fill: true,
|
||||||
|
borderColor: matchLineColor,
|
||||||
|
backgroundColor: matchFillColor,
|
||||||
|
pointBackgroundColor: matchLineColor,
|
||||||
|
pointRadius: 0
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
r: {
|
||||||
|
min: 0,
|
||||||
|
max: 80,
|
||||||
|
ticks: { display: false, stepSize: 20 },
|
||||||
|
pointLabels: { font: { size: getBottomChartFontSize(), weight: 'bold' }, color: '#FFF' },
|
||||||
|
angleLines: { color: radialLineColor },
|
||||||
|
grid: { drawticks: false, drawOnChartArea: false, circular: false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: { tooltip: { enabled: false }, legend: { display: false } }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust label font sizes on resize to mirror inactive page
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
if (charts[0]) {
|
||||||
|
charts[0].options.scales.r.pointLabels.font.size = getFontSize();
|
||||||
|
charts[0].update('none');
|
||||||
|
}
|
||||||
|
if (charts[1]) {
|
||||||
|
charts[1].options.scales.r.pointLabels.font.size = getBottomChartFontSize();
|
||||||
|
charts[1].update('none');
|
||||||
|
}
|
||||||
|
if (charts[2]) {
|
||||||
|
charts[2].options.scales.r.pointLabels.font.size = getBottomChartFontSize();
|
||||||
|
charts[2].update('none');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Confetti like your previous result page: from left & right of best card
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const bestCard = document.querySelector('.best-card');
|
||||||
|
if (!bestCard) return;
|
||||||
|
|
||||||
|
const rect = bestCard.getBoundingClientRect();
|
||||||
|
const colors = ['#BFB531', '#FFFFFF'];
|
||||||
|
|
||||||
|
function fire(xOrigin) {
|
||||||
|
confetti({
|
||||||
|
particleCount: 69,
|
||||||
|
angle: xOrigin < 0.5 ? 60 : 120,
|
||||||
|
spread: 69,
|
||||||
|
origin: {
|
||||||
|
x: xOrigin,
|
||||||
|
y: (rect.top + rect.height / 2) / window.innerHeight
|
||||||
|
},
|
||||||
|
colors
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const leftOrigin = (rect.left) / window.innerWidth;
|
||||||
|
const rightOrigin = (rect.right) / window.innerWidth;
|
||||||
|
|
||||||
|
fire(leftOrigin);
|
||||||
|
fire(rightOrigin);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user