WIP Feedback
This commit is contained in:
41
web/src/css/components/feedback.css
Normal file
41
web/src/css/components/feedback.css
Normal file
@@ -0,0 +1,41 @@
|
||||
.feedback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.feedback-text {
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.feedback-buttons {
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.feedback-btn {
|
||||
font-size: 1rem;
|
||||
padding: 0.2rem 0.55rem;
|
||||
background: transparent;
|
||||
border: 1px solid var(--info-border);
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
color: inherit;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.feedback-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.feedback-btn--selected {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-color: var(--info-text);
|
||||
}
|
||||
|
||||
.feedback-thanks {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ select + .fallback-hint {
|
||||
}
|
||||
|
||||
.restart-hint {
|
||||
margin-top: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
font-size: 0.78rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
@import './components/info-card.css';
|
||||
@import './components/step-nav.css';
|
||||
@import './components/modal.css';
|
||||
@import './components/feedback.css';
|
||||
@import './layout/hero.css';
|
||||
@import './layout/steps.css';
|
||||
@import './layout/footer.css';
|
||||
|
||||
@@ -305,7 +305,15 @@
|
||||
<strong>Safely eject your Kobo and let it reboot.</strong> Please be patient, NickelMenu will be automatically removed during the reboot.
|
||||
A "glitchy" horizontal line may briefly appear on screen after restarting — this is normal, as NickelMenu removes itself.
|
||||
</p>
|
||||
<p class="restart-hint">You can always restart the entire flow by reloading the page.</p>
|
||||
<p class="restart-hint">You can always restart the entire flow by reloading the page, if you want to try again for another configuration or undo the changes that were made.</p>
|
||||
<div class="banner banner--info feedback" hidden>
|
||||
<span class="feedback-text">Did you find the wizard easy to use?</span>
|
||||
<span class="feedback-thanks" hidden>Thank you for your feedback!</span>
|
||||
<span class="feedback-buttons">
|
||||
<button class="feedback-btn" data-vote="up" type="button">👍</button>
|
||||
<button class="feedback-btn" data-vote="down" type="button">👎</button>
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Step 2 (patches path): Configure patches -->
|
||||
@@ -378,7 +386,15 @@
|
||||
<li>The device will reboot and apply the patches automatically.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<p class="restart-hint">You can always restart the entire flow by reloading the page.</p>
|
||||
<p class="restart-hint">You can always restart the entire flow by reloading the page, if you want to try again for another configuration or undo the changes that were made.</p>
|
||||
<div class="banner banner--info feedback" hidden>
|
||||
<span class="feedback-text">Did you find the wizard easy to use?</span>
|
||||
<span class="feedback-thanks" hidden>Thank you for your feedback!</span>
|
||||
<span class="feedback-buttons">
|
||||
<button class="feedback-btn" data-vote="up" type="button">👍</button>
|
||||
<button class="feedback-btn" data-vote="down" type="button">👎</button>
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Error state -->
|
||||
|
||||
@@ -97,6 +97,33 @@ export async function fetchOrThrow(url, errorPrefix = 'Fetch failed') {
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wire up a .feedback banner inside a container element.
|
||||
* Shows text + vote buttons; clicking one replaces all with a thank-you message.
|
||||
* @param {HTMLElement} container - element containing the .feedback widget
|
||||
* @param {function} onVote - callback receiving 'up' or 'down'
|
||||
*/
|
||||
export function setupFeedback(container, onVote) {
|
||||
const widget = container.querySelector('.feedback');
|
||||
if (!widget) return;
|
||||
widget.hidden = false;
|
||||
const text = widget.querySelector('.feedback-text');
|
||||
const buttons = widget.querySelectorAll('.feedback-btn');
|
||||
const thanks = widget.querySelector('.feedback-thanks');
|
||||
text.hidden = false;
|
||||
thanks.hidden = true;
|
||||
buttons.forEach((btn) => {
|
||||
btn.hidden = false;
|
||||
btn.disabled = false;
|
||||
btn.addEventListener('click', () => {
|
||||
text.hidden = true;
|
||||
buttons.forEach((b) => { b.hidden = true; });
|
||||
thanks.hidden = false;
|
||||
onVote(btn.dataset.vote);
|
||||
}, { once: true });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a browser download of in-memory data.
|
||||
* Creates a temporary object URL, clicks a hidden <a>, then revokes it.
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
* `resetNickelMenuState`.
|
||||
*/
|
||||
|
||||
import { $, $q, $qa, triggerDownload, renderNmCheckboxList, populateList } from '../dom.js';
|
||||
import { $, $q, $qa, triggerDownload, renderNmCheckboxList, populateList, setupFeedback } from '../dom.js';
|
||||
import { showStep, setNavStep } from '../nav.js';
|
||||
import { ALL_FEATURES } from '../../nickelmenu/installer.js';
|
||||
import { TL } from '../strings.js';
|
||||
import { track } from '../analytics.js';
|
||||
import { isEnabled as analyticsEnabled, track } from '../analytics.js';
|
||||
|
||||
export function initNickelMenu(state) {
|
||||
|
||||
@@ -366,6 +366,12 @@ export function initNickelMenu(state) {
|
||||
track('flow-end', { result: 'nm-download' });
|
||||
}
|
||||
|
||||
if (analyticsEnabled()) {
|
||||
setupFeedback(stepNmDone, (vote) => {
|
||||
track('feedback', { vote });
|
||||
});
|
||||
}
|
||||
|
||||
setNavStep(5);
|
||||
showStep(stepNmDone);
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
* `updatePatchCount`, and `configureFirmwareStep`.
|
||||
*/
|
||||
|
||||
import { $, formatMB, triggerDownload, populateList } from '../dom.js';
|
||||
import { $, formatMB, triggerDownload, populateList, setupFeedback } from '../dom.js';
|
||||
import { showStep, setNavLabels, setNavStep } from '../nav.js';
|
||||
import { KoboModels } from '../services/kobo-device.js';
|
||||
import { TL } from '../strings.js';
|
||||
import { track } from '../analytics.js';
|
||||
import { isEnabled as analyticsEnabled, track } from '../analytics.js';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
export function initPatchesFlow(state) {
|
||||
@@ -249,6 +249,12 @@ export function initPatchesFlow(state) {
|
||||
downloadInstructions.hidden = true;
|
||||
existingTgzWarning.hidden = true;
|
||||
|
||||
if (analyticsEnabled()) {
|
||||
setupFeedback(stepDone, (vote) => {
|
||||
track('feedback', { vote });
|
||||
});
|
||||
}
|
||||
|
||||
setNavStep(5);
|
||||
showStep(stepDone);
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ export const TL = {
|
||||
STATUS: {
|
||||
DEVICE_RECOGNIZED: 'Your device has been recognized. You can continue to the next step!',
|
||||
NM_REMOVED_ON_REBOOT: 'NickelMenu will be removed on next reboot.',
|
||||
NM_INSTALLED: 'NickelMenu has been installed on your Kobo.',
|
||||
NM_DOWNLOAD_READY: 'Your NickelMenu package is ready to download.',
|
||||
NM_INSTALLED: 'NickelMenu has been prepared for your Kobo. To complete the installation, follow the instructions below.',
|
||||
NM_DOWNLOAD_READY: 'Your NickelMenu package is ready to download. After downloading, a list of installation steps will be displayed.',
|
||||
NM_WILL_BE_REMOVED: 'NickelMenu will be updated and marked for removal. It will uninstall itself when your Kobo reboots.',
|
||||
NM_WILL_BE_INSTALLED: 'The following will be installed on your Kobo:',
|
||||
NM_NICKEL_ROOT_TGZ: 'NickelMenu (KoboRoot.tgz)',
|
||||
|
||||
Reference in New Issue
Block a user