1import { Controller } from "@hotwired/stimulus" 2import { sendAnalyticsEvent } from "services/analytics_service" 3 4export default class extends Controller { 5 static targets = ["formScreen", "sendingScreen", "successScreen", "requestedArtistsList"] 6 static values = { cancelRedirects: Boolean } 7 8 async submit(e) { 9 e?.preventDefault() 10 const names = this.getArtistNames() 11 if (names.length === 0) return 12 13 this.submittedArtists = names 14 this.showScreen("sending") 15 16 const csrfToken = document.querySelector("meta[name=\"csrf-token\"]")?.getAttribute("content") 17 const formData = new FormData() 18 formData.append("authenticity_token", csrfToken) 19 names.forEach((name) => formData.append("artist_names[]", name)) 20 21 try { 22 const res = await fetch("/artist-requests.json", { 23 method: "POST", 24 body: formData, 25 headers: { "X-Requested-With": "XMLHttpRequest", "Accept": "application/json" }, 26 }) 27 const data = await res.json().catch(() => ({})) 28 if (res.ok && data.success) { 29 sendAnalyticsEvent("artist_request_submitted", { requested_count: names.length }) 30 this.renderRequestedArtists(names) 31 this.showScreen("success") 32 } else { 33 sendAnalyticsEvent("artist_request_failed", { 34 requested_count: names.length, 35 error_type: res.ok ? "server_rejected" : "http_error" 36 }) 37 this.showScreen("form") 38 alert(data.error || "Something went wrong. Please try again.") 39 } 40 } catch (err) { 41 sendAnalyticsEvent("artist_request_failed", { requested_count: names.length, error_type: "network_error" }) 42 this.showScreen("form") 43 alert("Something went wrong. Please try again.") 44 } 45 } 46 47 getArtistNames() { 48 const inputs = this.formScreenTarget.querySelectorAll(".request-artist-input") 49 return Array.from(inputs) 50 .map((el) => el.value?.trim()) 51 .filter(Boolean) 52 } 53 54 renderRequestedArtists(names) { 55 this.requestedArtistsListTarget.innerHTML = "" 56 const spanClass = "request-artist-input w-full h-10 pl-3 pr-3 py-2.5 rounded-lg bg-[rgba(62,62,62,0.52)] border border-[#262626] text-white text-sm font-chivo flex items-center" 57 names.forEach((name) => { 58 const span = document.createElement("span") 59 span.className = spanClass 60 span.textContent = name 61 this.requestedArtistsListTarget.appendChild(span) 62 }) 63 } 64 65 handleCancel(e) { 66 e?.preventDefault() 67 if (this.cancelRedirectsValue) { 68 window.location.href = "/artists" 69 } else { 70 const modalController = this.application.getControllerForElementAndIdentifier(this.element, "modal") 71 if (modalController) modalController.close() 72 } 73 } 74 75 showForm(e) { 76 e?.preventDefault() 77 const formEl = this.formScreenTarget.querySelector('[data-controller*="request-artist-form"]') 78 if (formEl) { 79 const formController = this.application.getControllerForElementAndIdentifier(formEl, "request-artist-form") 80 if (formController?.resetForm) formController.resetForm() 81 } 82 this.showScreen("form") 83 } 84 85 showScreen(name) { 86 this.formScreenTarget.style.display = name === "form" ? "block" : "none" 87 this.sendingScreenTarget.style.display = name === "sending" ? "block" : "none" 88 if (this.hasSuccessScreenTarget) { 89 this.successScreenTarget.style.display = name === "success" ? "block" : "none" 90 } 91 } 92}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.