Adds analysis tab and panel to journal interface

Introduces new analysis functionality for hair and scalp tracking
Adds dynamic preview, form interactions, and styling for analysis entries
Enables users to log detailed hair and scalp condition assessments
This commit is contained in:
Janus C. H. Knudsen 2025-12-21 22:34:23 +01:00
parent 99cd1c532f
commit b408b83cac

View file

@ -652,6 +652,10 @@
background: var(--color-text-secondary);
}
.tab-indicator.purple {
background: #8b5cf6;
}
/* Tab Content Container */
swp-journal-tab-content {
display: none;
@ -752,6 +756,11 @@
background: color-mix(in srgb, var(--color-teal) 15%, var(--b-mix));
}
swp-journal-entry-type.analyse {
color: #7c3aed;
background: color-mix(in srgb, #8b5cf6 15%, var(--b-mix));
}
swp-journal-entry-date {
font-size: 12px;
color: var(--color-text-secondary);
@ -1940,6 +1949,111 @@
outline: none;
border-color: var(--color-teal);
}
/* ==========================================
ANALYSE PANEL (extends add-note styles)
========================================== */
swp-analyse-panel {
position: fixed;
top: 0;
right: 340px;
bottom: 0;
width: 440px;
background: var(--color-surface);
border-left: 1px solid var(--color-border);
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
transform: translateX(0);
visibility: hidden;
transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1),
visibility 0ms 250ms;
display: flex;
flex-direction: column;
z-index: 980;
}
swp-analyse-panel.open {
transform: translateX(-100%);
visibility: visible;
transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1),
visibility 0ms 0ms;
}
swp-analyse-panel input[type="text"] {
padding: 10px 12px;
font-size: 13px;
border: 1px solid var(--color-border);
border-radius: 6px;
background: var(--color-surface);
}
swp-analyse-panel input[type="text"]:focus {
outline: none;
border-color: #8b5cf6;
}
swp-analyse-panel select {
padding: 10px 12px;
font-size: 13px;
border: 1px solid var(--color-border);
border-radius: 6px;
background: var(--color-surface);
cursor: pointer;
}
swp-analyse-panel select:focus {
outline: none;
border-color: #8b5cf6;
}
swp-analyse-panel textarea {
width: 100%;
min-height: 80px;
padding: 12px;
font-family: inherit;
font-size: 13px;
border: 1px solid var(--color-border);
border-radius: 6px;
background: var(--color-surface);
resize: vertical;
line-height: 1.5;
}
swp-analyse-panel textarea:focus {
outline: none;
border-color: #8b5cf6;
}
/* Analyse tags */
swp-add-note-tag-option[data-tag="analyse"].selected {
background: color-mix(in srgb, #8b5cf6 15%, var(--b-mix));
border-color: #8b5cf6;
color: #7c3aed;
}
swp-add-note-tag-option[data-tag="hovedbund"].selected {
background: color-mix(in srgb, var(--b-color-amber) 15%, var(--b-mix));
border-color: var(--b-color-amber);
color: #b45309;
}
swp-add-note-tag-option[data-tag="analyse"].selected::before {
background: #8b5cf6;
border-color: #8b5cf6;
}
swp-add-note-tag-option[data-tag="hovedbund"].selected::before {
background: var(--b-color-amber);
border-color: var(--b-color-amber);
}
/* Journal tag for analyse */
swp-journal-tag.tag-analyse {
background: color-mix(in srgb, #8b5cf6 15%, var(--b-mix));
border-color: #8b5cf6;
color: #7c3aed;
}
swp-journal-tag.tag-hovedbund {
background: color-mix(in srgb, var(--b-color-amber) 15%, var(--b-mix));
border-color: var(--b-color-amber);
color: #b45309;
}
</style>
</head>
<body>
@ -1998,7 +2112,7 @@
<swp-journal-tabs>
<swp-journal-tab class="active" data-tab="noter" onclick="switchJournalTab('noter')"><span class="tab-indicator blue"></span>Noter</swp-journal-tab>
<swp-journal-tab data-tab="farveformler" onclick="switchJournalTab('farveformler')"><span class="tab-indicator amber"></span>Farveformler</swp-journal-tab>
<swp-journal-tab class="disabled"><span class="tab-indicator gray"></span>Analyse</swp-journal-tab>
<swp-journal-tab data-tab="analyse" onclick="switchJournalTab('analyse')"><span class="tab-indicator purple"></span>Analyse</swp-journal-tab>
</swp-journal-tabs>
<!-- Tab: Noter -->
@ -2132,6 +2246,66 @@
</swp-journal-content>
</swp-journal-tab-content>
<!-- Tab: Analyse -->
<swp-journal-tab-content id="tabAnalyse">
<swp-journal-tab-header>
<swp-journal-tab-title>Hår & hovedbund <span>(analyse)</span></swp-journal-tab-title>
<swp-journal-tab-desc>Struktureret analyse af hår og hovedbund. Kan blive til advarsel.</swp-journal-tab-desc>
<swp-journal-add-btn onclick="openAnalyse()">+ Tilføj analyse</swp-journal-add-btn>
</swp-journal-tab-header>
<swp-journal-content>
<!-- Preview Entry for Analyse -->
<swp-journal-entry id="previewEntryAnalyse" class="preview hidden">
<swp-journal-entry-meta>
<swp-journal-entry-type class="analyse">Analyse</swp-journal-entry-type>
<swp-journal-entry-tags id="previewTagsAnalyse"></swp-journal-entry-tags>
</swp-journal-entry-meta>
<swp-journal-entry-text id="previewTextAnalyse">(Udfyld felter...)</swp-journal-entry-text>
<swp-journal-entry-footer>
<swp-journal-entry-date>I dag</swp-journal-entry-date>
<swp-journal-entry-icons>
<swp-journal-entry-visibility>
<img src="icons/eye.svg" class="entry-icon" alt="">
<span>Alle</span>
</swp-journal-entry-visibility>
</swp-journal-entry-icons>
</swp-journal-entry-footer>
</swp-journal-entry>
<swp-journal-entry>
<swp-journal-entry-meta>
<swp-journal-entry-type class="analyse">Analyse</swp-journal-entry-type>
<swp-journal-entry-tags>
<swp-journal-tag class="tag-analyse">Hovedbund</swp-journal-tag>
<swp-journal-tag class="tag-sensitiv">Sensitiv</swp-journal-tag>
</swp-journal-entry-tags>
<swp-journal-entry-delete title="Slet">🗑</swp-journal-entry-delete>
</swp-journal-entry-meta>
<swp-journal-entry-text>
• Hovedbund: Let irriteret<br>
• Hår: <span class="mono">Medium · Bølget</span><br>
• Porøsitet: <span class="mono">Medium</span><br>
• Kemisk beh.: Nej<br><br>
Let rødme ved hårgrænsen. Anbefalet parfumefri shampoo.
</swp-journal-entry-text>
<swp-journal-entry-footer>
<swp-journal-entry-date>2. sep 2025 · Af: Nina</swp-journal-entry-date>
<swp-journal-entry-icons>
<swp-journal-entry-visibility>
<img src="icons/eye.svg" class="entry-icon" alt="">
<span>Alle</span>
</swp-journal-entry-visibility>
<swp-journal-entry-marking class="warning">
<img src="icons/warning.svg" class="entry-icon" alt="">
<span>Advarsel</span>
</swp-journal-entry-marking>
</swp-journal-entry-icons>
</swp-journal-entry-footer>
</swp-journal-entry>
</swp-journal-content>
</swp-journal-tab-content>
<!-- Kundeprofil Toggle -->
<swp-journal-profile-section>
<swp-journal-profile-toggle onclick="toggleJournalProfile()">
@ -2294,6 +2468,130 @@
</swp-add-note-footer>
</swp-farveformel-panel>
<!-- Analyse Panel -->
<swp-analyse-panel id="analysePanel">
<swp-add-note-header>
<swp-add-note-title>Shampoo & hovedbund-analyse</swp-add-note-title>
<swp-add-note-close onclick="closeAnalyse()"></swp-add-note-close>
</swp-add-note-header>
<swp-add-note-content>
<swp-add-note-desc>Hurtige valg + bemærkning. Kan blive til "sticky warning".</swp-add-note-desc>
<!-- Row 1: Hovedbund + Irritation -->
<swp-add-note-row>
<swp-add-note-field>
<swp-add-note-label>Hovedbund (tilstand)</swp-add-note-label>
<select id="anHovedbund">
<option value="">Vælg...</option>
<option value="Normal">Normal</option>
<option value="Tør">Tør</option>
<option value="Fedtet">Fedtet</option>
<option value="Let irriteret">Let irriteret</option>
<option value="Irriteret">Irriteret</option>
</select>
</swp-add-note-field>
<swp-add-note-field>
<swp-add-note-label>Irritation</swp-add-note-label>
<select id="anIrritation">
<option value="">Vælg...</option>
<option value="Ingen">Ingen</option>
<option value="Let">Let</option>
<option value="Moderat">Moderat</option>
<option value="Kraftig">Kraftig</option>
</select>
</swp-add-note-field>
</swp-add-note-row>
<!-- Row 2: Hår tykkelse + struktur -->
<swp-add-note-row>
<swp-add-note-field>
<swp-add-note-label>Hår (tykkelse)</swp-add-note-label>
<select id="anHaarTykkelse">
<option value="">Vælg...</option>
<option value="Fint">Fint</option>
<option value="Medium">Medium</option>
<option value="Tykt">Tykt</option>
</select>
</swp-add-note-field>
<swp-add-note-field>
<swp-add-note-label>Hår (struktur)</swp-add-note-label>
<select id="anHaarStruktur">
<option value="">Vælg...</option>
<option value="Glat">Glat</option>
<option value="Bølget">Bølget</option>
<option value="Krøllet">Krøllet</option>
</select>
</swp-add-note-field>
</swp-add-note-row>
<!-- Row 3: Porøsitet + Kemisk behandling -->
<swp-add-note-row>
<swp-add-note-field>
<swp-add-note-label>Porøsitet</swp-add-note-label>
<select id="anPorositet">
<option value="">Vælg...</option>
<option value="Lav">Lav</option>
<option value="Medium">Medium</option>
<option value="Høj">Høj</option>
</select>
</swp-add-note-field>
<swp-add-note-field>
<swp-add-note-label>Tidligere kemisk behandling?</swp-add-note-label>
<select id="anKemisk">
<option value="">Vælg...</option>
<option value="Nej">Nej</option>
<option value="Ja">Ja</option>
</select>
</swp-add-note-field>
</swp-add-note-row>
<!-- Row 4: Shampoo + Hjemmepleje -->
<swp-add-note-row>
<swp-add-note-field>
<swp-add-note-label>Anvendt shampoo</swp-add-note-label>
<input type="text" id="anShampoo" placeholder="Fx: Kerastase Bain">
</swp-add-note-field>
<swp-add-note-field>
<swp-add-note-label>Anbefalet hjemmepleje</swp-add-note-label>
<input type="text" id="anHjemmepleje" placeholder="Fx: Parfumefri shampoo">
</swp-add-note-field>
</swp-add-note-row>
<!-- Bemærkning -->
<swp-add-note-field class="full-width">
<swp-add-note-label>Bemærkning</swp-add-note-label>
<textarea id="anBemaerkning" placeholder="Fx: Let rødme ved hårgrænsen. Undgå parfume og stærke sulfater."></textarea>
</swp-add-note-field>
<!-- Row 5: Advarsel + Tags -->
<swp-add-note-row>
<swp-add-note-field>
<swp-add-note-label>Gør til advarsel på kundekort?</swp-add-note-label>
<select id="anAdvarsel">
<option value="Nej">Nej</option>
<option value="Ja">Ja</option>
</select>
<swp-add-note-help>Brug "Ja" ved fx allergi/sensitivitet.</swp-add-note-help>
</swp-add-note-field>
<swp-add-note-field>
<swp-add-note-label>Tags</swp-add-note-label>
<swp-add-note-tags>
<swp-add-note-tag-option class="selected" data-tag="analyse">Analyse</swp-add-note-tag-option>
<swp-add-note-tag-option data-tag="hovedbund">Hovedbund</swp-add-note-tag-option>
<swp-add-note-tag-option data-tag="sensitiv">Sensitiv</swp-add-note-tag-option>
<swp-add-note-tag-option data-tag="allergi">Allergi</swp-add-note-tag-option>
</swp-add-note-tags>
</swp-add-note-field>
</swp-add-note-row>
</swp-add-note-content>
<swp-add-note-footer>
<swp-add-note-btn class="secondary" onclick="closeAnalyse()">Annuller</swp-add-note-btn>
<swp-add-note-btn class="primary" onclick="saveAnalyse()">Gem analyse</swp-add-note-btn>
</swp-add-note-footer>
</swp-analyse-panel>
<!-- Customer Details Panel -->
<swp-customer-panel id="customerPanel">
<swp-customer-panel-header>
@ -2642,6 +2940,7 @@
document.getElementById('journalLink').classList.remove('panel-open');
closeAddNote();
closeFarveformel();
closeAnalyse();
}
function switchJournalTab(tabName) {
@ -2652,9 +2951,11 @@
// Update tab content
document.getElementById('tabNoter').classList.toggle('active', tabName === 'noter');
document.getElementById('tabFarveformler').classList.toggle('active', tabName === 'farveformler');
document.getElementById('tabAnalyse').classList.toggle('active', tabName === 'analyse');
// Close any open panels
closeAddNote();
closeFarveformel();
closeAnalyse();
}
function openAddNote() {
@ -2958,6 +3259,145 @@
originalCloseAddNote();
activePanel = null;
};
// ==========================================
// ANALYSE PANEL
// ==========================================
// Analyse form elements
const anHovedbund = document.getElementById('anHovedbund');
const anIrritation = document.getElementById('anIrritation');
const anHaarTykkelse = document.getElementById('anHaarTykkelse');
const anHaarStruktur = document.getElementById('anHaarStruktur');
const anPorositet = document.getElementById('anPorositet');
const anKemisk = document.getElementById('anKemisk');
const anShampoo = document.getElementById('anShampoo');
const anHjemmepleje = document.getElementById('anHjemmepleje');
const anBemaerkning = document.getElementById('anBemaerkning');
const anAdvarsel = document.getElementById('anAdvarsel');
// Analyse preview elements
const previewEntryAnalyse = document.getElementById('previewEntryAnalyse');
const previewTextAnalyse = document.getElementById('previewTextAnalyse');
const previewTagsAnalyse = document.getElementById('previewTagsAnalyse');
function openAnalyse() {
document.getElementById('analysePanel').classList.add('open');
activePanel = 'analyse';
showAnalysePreview();
}
function closeAnalyse() {
document.getElementById('analysePanel').classList.remove('open');
hideAnalysePreview();
resetAnalyseForm();
activePanel = null;
}
function showAnalysePreview() {
previewEntryAnalyse.classList.remove('hidden');
updateAnalysePreview();
updateAnalysePreviewTags();
}
function hideAnalysePreview() {
previewEntryAnalyse.classList.add('hidden');
}
function updateAnalysePreview() {
const lines = [];
if (anHovedbund.value) {
lines.push(`• Hovedbund: ${anHovedbund.value}`);
}
if (anIrritation.value) {
lines.push(`• Irritation: ${anIrritation.value}`);
}
if (anHaarTykkelse.value || anHaarStruktur.value) {
const haarParts = [];
if (anHaarTykkelse.value) haarParts.push(anHaarTykkelse.value);
if (anHaarStruktur.value) haarParts.push(anHaarStruktur.value);
lines.push(`• Hår: <span class="mono">${haarParts.join(' · ')}</span>`);
}
if (anPorositet.value) {
lines.push(`• Porøsitet: <span class="mono">${anPorositet.value}</span>`);
}
if (anKemisk.value) {
lines.push(`• Kemisk beh.: ${anKemisk.value}`);
}
if (anShampoo.value) {
lines.push(`• Shampoo: ${anShampoo.value}`);
}
if (anHjemmepleje.value) {
lines.push(`• Hjemmepleje: ${anHjemmepleje.value}`);
}
if (anBemaerkning.value) {
lines.push(`<br>${anBemaerkning.value}`);
}
previewTextAnalyse.innerHTML = lines.length > 0 ? lines.join('<br>') : '(Udfyld felter...)';
}
function updateAnalysePreviewTags() {
const analysePanelTags = document.querySelectorAll('#analysePanel swp-add-note-tag-option.selected');
previewTagsAnalyse.innerHTML = '';
analysePanelTags.forEach(tag => {
const tagEl = document.createElement('swp-journal-tag');
tagEl.textContent = tag.textContent;
tagEl.classList.add('tag-' + tag.dataset.tag);
previewTagsAnalyse.appendChild(tagEl);
});
}
function resetAnalyseForm() {
anHovedbund.value = '';
anIrritation.value = '';
anHaarTykkelse.value = '';
anHaarStruktur.value = '';
anPorositet.value = '';
anKemisk.value = '';
anShampoo.value = '';
anHjemmepleje.value = '';
anBemaerkning.value = '';
anAdvarsel.value = 'Nej';
// Reset tags - only "Analyse" selected by default
document.querySelectorAll('#analysePanel swp-add-note-tag-option').forEach(tag => {
if (tag.dataset.tag === 'analyse') {
tag.classList.add('selected');
} else {
tag.classList.remove('selected');
}
});
}
function saveAnalyse() {
// In a real app, this would save to backend
alert('Analyse gemt!');
closeAnalyse();
}
// Add event listeners for analyse form fields
[anHovedbund, anIrritation, anHaarTykkelse, anHaarStruktur, anPorositet, anKemisk, anShampoo, anHjemmepleje, anBemaerkning, anAdvarsel].forEach(el => {
el.addEventListener('input', () => {
if (activePanel === 'analyse') {
updateAnalysePreview();
}
});
el.addEventListener('change', () => {
if (activePanel === 'analyse') {
updateAnalysePreview();
}
});
});
// Add tag toggle listeners for analyse panel
document.querySelectorAll('#analysePanel swp-add-note-tag-option').forEach(tag => {
tag.onclick = () => {
tag.classList.toggle('selected');
updateAnalysePreviewTags();
};
});
</script>
<!-- Chart initialization -->