mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 21:50:28 +02:00
feat: Enhance dashboard and prepare wizard functionality with speaker step handling and UI updates
This commit is contained in:
@@ -205,14 +205,19 @@ const initDashboard = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("click", (event) => {
|
document.addEventListener("click", (event) => {
|
||||||
const readerClose = event.target.closest('[data-role="reader-modal-close"]');
|
const target = event.target;
|
||||||
|
if (!(target instanceof Element)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const readerClose = target.closest('[data-role="reader-modal-close"]');
|
||||||
if (readerClose) {
|
if (readerClose) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
closeReaderModal();
|
closeReaderModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const readerTriggerBtn = event.target.closest('[data-role="open-reader"]');
|
const readerTriggerBtn = target.closest('[data-role="open-reader"]');
|
||||||
if (readerTriggerBtn) {
|
if (readerTriggerBtn) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
openReaderModal(readerTriggerBtn);
|
openReaderModal(readerTriggerBtn);
|
||||||
|
|||||||
@@ -244,6 +244,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
const panels = new Map();
|
const panels = new Map();
|
||||||
const initialStep = wizard.dataset.initialStep || "chapters";
|
const initialStep = wizard.dataset.initialStep || "chapters";
|
||||||
const speakerModeSelect = form.querySelector("#speaker_mode");
|
const speakerModeSelect = form.querySelector("#speaker_mode");
|
||||||
|
const speakerIndicator = indicator ? indicator.querySelector('[data-step-key="speakers"]') : null;
|
||||||
|
const chapterActions = wizard.querySelector('[data-role="chapter-actions"]');
|
||||||
|
const continueButton = chapterActions ? chapterActions.querySelector('[data-chapter-action="continue"]') : null;
|
||||||
|
const finalizeButton = chapterActions ? chapterActions.querySelector('[data-chapter-action="finalize"]') : null;
|
||||||
stepOrder.forEach((step) => {
|
stepOrder.forEach((step) => {
|
||||||
const panel = wizard.querySelector(`[data-step-panel="${step}"]`);
|
const panel = wizard.querySelector(`[data-step-panel="${step}"]`);
|
||||||
if (panel) {
|
if (panel) {
|
||||||
@@ -311,23 +315,27 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setStep = (step) => {
|
const setStep = (step) => {
|
||||||
if (!panels.has(step)) {
|
let targetStep = step;
|
||||||
|
if (targetStep === "speakers" && shouldSkipSpeakersStep()) {
|
||||||
|
targetStep = "chapters";
|
||||||
|
}
|
||||||
|
if (!panels.has(targetStep)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentStep = step;
|
currentStep = targetStep;
|
||||||
wizard.dataset.activeStep = step;
|
wizard.dataset.activeStep = targetStep;
|
||||||
if (activeStepInput) {
|
if (activeStepInput) {
|
||||||
activeStepInput.value = step;
|
activeStepInput.value = targetStep;
|
||||||
}
|
}
|
||||||
panels.forEach((panel, key) => {
|
panels.forEach((panel, key) => {
|
||||||
const isActive = key === step;
|
const isActive = key === targetStep;
|
||||||
panel.hidden = !isActive;
|
panel.hidden = !isActive;
|
||||||
panel.setAttribute("aria-hidden", isActive ? "false" : "true");
|
panel.setAttribute("aria-hidden", isActive ? "false" : "true");
|
||||||
});
|
});
|
||||||
navButtons.forEach((button) => {
|
navButtons.forEach((button) => {
|
||||||
const target = button.dataset.stepTarget;
|
const target = button.dataset.stepTarget;
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
const isActive = target === step;
|
const isActive = target === targetStep;
|
||||||
button.classList.toggle("is-active", isActive);
|
button.classList.toggle("is-active", isActive);
|
||||||
if (button.dataset.state === "locked" && !unlockedSteps.has(target)) {
|
if (button.dataset.state === "locked" && !unlockedSteps.has(target)) {
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
@@ -337,7 +345,46 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
button.removeAttribute("aria-disabled");
|
button.removeAttribute("aria-disabled");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
updateIndicator(step);
|
updateIndicator(targetStep);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSpeakerControls = () => {
|
||||||
|
const skipSpeakers = shouldSkipSpeakersStep();
|
||||||
|
if (wizard) {
|
||||||
|
wizard.dataset.speakerStep = skipSpeakers ? "disabled" : "enabled";
|
||||||
|
}
|
||||||
|
if (speakerIndicator) {
|
||||||
|
if (skipSpeakers) {
|
||||||
|
speakerIndicator.hidden = true;
|
||||||
|
speakerIndicator.setAttribute("aria-hidden", "true");
|
||||||
|
} else {
|
||||||
|
speakerIndicator.hidden = false;
|
||||||
|
speakerIndicator.removeAttribute("aria-hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (continueButton) {
|
||||||
|
if (skipSpeakers) {
|
||||||
|
continueButton.hidden = true;
|
||||||
|
continueButton.setAttribute("aria-hidden", "true");
|
||||||
|
continueButton.setAttribute("tabindex", "-1");
|
||||||
|
} else {
|
||||||
|
continueButton.hidden = false;
|
||||||
|
continueButton.removeAttribute("aria-hidden");
|
||||||
|
continueButton.removeAttribute("tabindex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (finalizeButton) {
|
||||||
|
if (skipSpeakers) {
|
||||||
|
finalizeButton.hidden = false;
|
||||||
|
finalizeButton.removeAttribute("aria-hidden");
|
||||||
|
} else {
|
||||||
|
finalizeButton.hidden = true;
|
||||||
|
finalizeButton.setAttribute("aria-hidden", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skipSpeakers && currentStep === "speakers") {
|
||||||
|
setStep("chapters");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitForAnalysis = () => {
|
const submitForAnalysis = () => {
|
||||||
@@ -399,7 +446,23 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (speakerModeSelect) {
|
||||||
|
speakerModeSelect.addEventListener("change", () => {
|
||||||
|
updateSpeakerControls();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalizeButton) {
|
||||||
|
finalizeButton.addEventListener("click", (event) => {
|
||||||
|
if (shouldSkipSpeakersStep()) {
|
||||||
|
event.preventDefault();
|
||||||
|
submitFinalize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setStep(currentStep);
|
setStep(currentStep);
|
||||||
|
updateSpeakerControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
const voiceModal = document.querySelector('[data-role="voice-modal"]');
|
const voiceModal = document.querySelector('[data-role="voice-modal"]');
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
padding: 0 2rem 2rem;
|
padding: 1.25rem 2rem 2.75rem;
|
||||||
border-top: 1px solid rgba(148, 163, 184, 0.12);
|
border-top: 1px solid rgba(148, 163, 184, 0.12);
|
||||||
background: linear-gradient(180deg, transparent, rgba(15, 23, 42, 0.3));
|
background: linear-gradient(180deg, transparent, rgba(15, 23, 42, 0.3));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,7 +169,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="modal__footer">
|
<footer class="modal__footer">
|
||||||
<button type="button" class="button button--ghost" data-role="upload-modal-close">Cancel</button>
|
<button type="button" class="button button--ghost" data-role="upload-modal-close">Cancel</button>
|
||||||
<button type="submit" class="button">Queue Conversion</button>
|
<button type="submit" class="button">Chapter Selection</button>
|
||||||
</footer>
|
</footer>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,9 +4,13 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% set wizard_step = (active_step or 'chapters') %}
|
{% set wizard_step = (active_step or 'chapters') %}
|
||||||
|
{% set is_multi_speaker = pending.speaker_mode == 'multi' %}
|
||||||
|
{% if wizard_step == 'speakers' and not is_multi_speaker %}
|
||||||
|
{% set wizard_step = 'chapters' %}
|
||||||
|
{% endif %}
|
||||||
{% set is_chapters = wizard_step == 'chapters' %}
|
{% set is_chapters = wizard_step == 'chapters' %}
|
||||||
{% set is_speakers = wizard_step == 'speakers' %}
|
{% set is_speakers = wizard_step == 'speakers' %}
|
||||||
<section class="card" data-role="prepare-wizard" data-initial-step="{{ wizard_step }}">
|
<section class="card" data-role="prepare-wizard" data-initial-step="{{ wizard_step }}" data-speaker-step="{{ 'enabled' if is_multi_speaker else 'disabled' }}">
|
||||||
<div class="step-indicator" aria-label="Audiobook workflow" data-role="wizard-indicator">
|
<div class="step-indicator" aria-label="Audiobook workflow" data-role="wizard-indicator">
|
||||||
<span class="step-indicator__item is-complete" data-role="wizard-step" data-step-key="setup">
|
<span class="step-indicator__item is-complete" data-role="wizard-step" data-step-key="setup">
|
||||||
<span class="step-indicator__index">1</span>
|
<span class="step-indicator__index">1</span>
|
||||||
@@ -16,7 +20,7 @@
|
|||||||
<span class="step-indicator__index">2</span>
|
<span class="step-indicator__index">2</span>
|
||||||
<span class="step-indicator__label">Chapters</span>
|
<span class="step-indicator__label">Chapters</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="step-indicator__item{% if is_speakers %} is-active{% endif %}" data-role="wizard-step" data-step-key="speakers">
|
<span class="step-indicator__item{% if is_speakers %} is-active{% endif %}" data-role="wizard-step" data-step-key="speakers" {% if not is_multi_speaker %}hidden aria-hidden="true"{% endif %}>
|
||||||
<span class="step-indicator__index">3</span>
|
<span class="step-indicator__index">3</span>
|
||||||
<span class="step-indicator__label">Speakers</span>
|
<span class="step-indicator__label">Speakers</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -148,12 +152,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="prepare-step__actions">
|
<div class="prepare-step__actions" data-role="chapter-actions">
|
||||||
<button type="button" class="button" data-role="step-next" data-step-target="speakers">Continue to speakers</button>
|
<button type="button"
|
||||||
|
class="button"
|
||||||
|
data-role="step-next"
|
||||||
|
data-step-target="speakers"
|
||||||
|
data-chapter-action="continue"
|
||||||
|
{% if not is_multi_speaker %}hidden aria-hidden="true"{% endif %}>
|
||||||
|
Continue to speakers
|
||||||
|
</button>
|
||||||
|
<button type="submit"
|
||||||
|
class="button"
|
||||||
|
data-chapter-action="finalize"
|
||||||
|
{% if is_multi_speaker %}hidden aria-hidden="true"{% endif %}>
|
||||||
|
Queue conversion
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="prepare-step" data-step-panel="speakers" hidden>
|
<section class="prepare-step" data-step-panel="speakers" data-speaker-panel="true" hidden>
|
||||||
<header class="prepare-step__header">
|
<header class="prepare-step__header">
|
||||||
<h2>Step 3 · Select speakers</h2>
|
<h2>Step 3 · Select speakers</h2>
|
||||||
<p class="hint">Assign voices, audition samples, and finalize your roster.</p>
|
<p class="hint">Assign voices, audition samples, and finalize your roster.</p>
|
||||||
@@ -408,9 +425,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="prepare-actions">
|
<div class="prepare-actions">
|
||||||
<button type="button" class="button button--ghost" data-role="step-prev" data-step-target="chapters">Back to chapters</button>
|
<button type="button" class="button button--ghost" data-role="step-prev" data-step-target="chapters">Back to upload & settings</button>
|
||||||
<button type="submit" class="button">Queue conversion</button>
|
<button type="submit" class="button">Queue conversion</button>
|
||||||
<button type="submit" class="button button--ghost" form="cancel-form">Cancel</button>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user