PageSourceSearch

https://my.pressable.com/assets/controllers/collaborators/show/update_on_sites_controller-5b463251.js

js pressable.com collected 2026-09-24 15:23:44 UTC 4,063 bytes, 112 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3const FILTER_DEBOUNCE_MS = 800
4
5// Update-collaborator-permissions-on-multiple-sites offcanvas: fetch the collaborator's
6// updatable sites, render checkbox rows, debounced search + environment filter,
7// select-all, and CSV-serialize the selected site ids into the hidden field on submit
8// (disabling the site_ids[] checkboxes so they are not also sent as query params).
9export default class extends Controller {
10  static targets = ["searchTerm", "testSite", "selectAll", "siteListing", "csv"]
11  // The finished endpoint URL, built server-side by the route helper. It already carries the
12  // account context and the escaped collaborator email, so this controller neither reconstructs
13  // the /a/:account_ctx prefix from window.location nor interpolates an email into a path.
14  static values = { endpoint: String }
15
16  connect() {
17    this.loadSites()
18  }
19
20  disconnect() {
21    if (this.filterTimeout) clearTimeout(this.filterTimeout)
22  }
23
24  loadSites() {
25    if (!this.endpointValue) {
26      console.error("Missing sites endpoint")
27      this.siteListingTarget.innerHTML =
28        '<div class="alert alert-warning">Unable to load sites. Please refresh the page.</div>'
29      return
30    }
31
32    fetch(this.endpointValue, {
33      method: "GET",
34      headers: { Accept: "application/json", "Content-Type": "application/json" },
35    })
36      .then((response) => {
37        if (!response.ok) throw new Error(`Server responded with ${response?.status}`)
38        return response.json()
39      })
40      .then((sites) => this.renderSites(sites))
41      .catch((error) => {
42        if (error instanceof SyntaxError) return
43        toastr.error("Could not retrieve updatable sites for this collaborator. Please try refreshing this page.")
44      })
45  }
46
47  renderSites(sites) {
48    if (!Array.isArray(sites)) sites = []
49    sites.sort((a, b) => (a.url > b.url ? 1 : -1))
50    this.siteListingTarget.innerHTML = ""
51    sites.forEach((site) => this.siteListingTarget.append(this.buildSiteRow(site)))
52  }
53
54  buildSiteRow(site) {
55    const wrapper = document.createElement("div")
56    wrapper.className = "form-check"
57    wrapper.dataset.siteRow = ""
58    wrapper.dataset.sitePattern = `${site.id}||${site.url}`
59    wrapper.dataset.testEnvironment = site.testSite
60
61    const input = document.createElement("input")
62    input.className = "form-check-input"
63    input.dataset.siteCheck = ""
64    input.type = "checkbox"
65    input.id = `update-collaborator-on-multiple-sites-${site.id}`
66    input.name = "site_ids[]"
67    input.value = site.id
68
69    const label = document.createElement("label")
70    label.className = "form-check-label"
71    label.htmlFor = input.id
72    label.textContent = site.url
73
74    wrapper.append(input, label)
75    return wrapper
76  }
77
78  search() {
79    clearTimeout(this.filterTimeout)
80    this.filterTimeout = setTimeout(() => this.applyFilter(), FILTER_DEBOUNCE_MS)
81  }
82
83  applyFilter() {
84    const term = this.searchTermTarget.value.trim()
85    const environment = this.testSiteTarget.value
86    this.siteListingTarget.querySelectorAll("[data-site-row]").forEach((row) => {
87      const matchesTerm = term === "" || row.dataset.sitePattern.includes(term)
88      const matchesEnv = environment === "" || row.dataset.testEnvironment === environment
89      row.style.display = matchesTerm && matchesEnv ? "" : "none"
90    })
91  }
92
93  selectAllSites() {
94    const checked = this.selectAllTarget.checked
95    this.siteListingTarget.querySelectorAll("[data-site-check]").forEach((checkbox) => {
96      checkbox.checked = checked
97    })
98  }
99
100  submit(event) {
101    const checked = this.siteListingTarget.querySelectorAll("[data-site-check]:checked")
102    if (checked.length === 0) {
103      toastr.error("Please select at least one site")
104      event.preventDefault()
105      return
106    }
107
108    this.csvTarget.value = Array.from(checked).map((checkbox) => checkbox.value).join(",")
109    // disable the site_ids[] checkboxes so they are not also submitted as query params
110    checked.forEach((checkbox) => { checkbox.disabled = true })
111  }
112}

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.