Segmented progress bars?

Progress bar:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0"
    />
    <title>Segmented Progress Bar</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script
      defer
      src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
    ></script>
  </head>
  <body
    class="bg-gray-100 min-h-screen flex items-center justify-center"
  >
    <div
      x-data="progressBar()"
      x-init="init()"
      class="w-full max-w-3xl p-6 bg-white rounded-lg shadow-lg"
    >
      <div class="flex mb-4 space-x-1">
        <template
          x-for="(width, index) in segmentWidths"
          :key="index"
        >
          <div
            class="flex-1 h-2 bg-gray-200 rounded-full overflow-hidden"
          >
            <div
              class="h-full bg-blue-500 transition-all duration-300 ease-in-out"
              :style="{ width: width }"
            ></div>
          </div>
        </template>
      </div>
      <h2
        class="text-2xl font-bold text-center text-gray-800"
        x-text="getCurrentStepTitle()"
      ></h2>
      <p
        class="text-center text-gray-600"
        x-text="getCurrentStepDescription()"
      ></p>
      <p
        class="text-center text-gray-600 mt-2"
        x-text="`${progress}% completed`"
      ></p>
    </div>

    <script>
      function progressBar() {
        return {
          progress: 0,
          steps: {},
          segmentWidths: [],
          init() {
            this.progress = parseInt(S.progress);
            this.steps = JSON.parse(S.steps);
            this.calculateSegmentWidths();
          },
          calculateSegmentWidths() {
            const stepCount = Object.keys(
              this.steps
            ).length;
            const segmentSize = 100 / stepCount;
            this.segmentWidths = Array.from(
              { length: stepCount },
              (_, index) => {
                const segmentProgress = Math.min(
                  Math.max(
                    this.progress - index * segmentSize,
                    0
                  ),
                  segmentSize
                );
                return `${
                  (segmentProgress / segmentSize) * 100
                }%`;
              }
            );
          },
          getCurrentStepTitle() {
            const currentStepIndex = Math.floor(
              this.progress /
                (100 / Object.keys(this.steps).length)
            );
            const stepKeys = Object.keys(this.steps);
            return stepKeys[
              Math.min(
                currentStepIndex,
                stepKeys.length - 1
              )
            ];
          },
          getCurrentStepDescription() {
            const currentStepIndex = Math.floor(
              this.progress /
                (100 / Object.keys(this.steps).length)
            );
            const stepKeys = Object.keys(this.steps);
            return this.steps[
              stepKeys[
                Math.min(
                  currentStepIndex,
                  stepKeys.length - 1
                )
              ]
            ];
          },
        };
      }
    </script>
  </body>
</html>

Steps manager:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0"
    />
    <title>Steps Manager</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script
      defer
      src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
    ></script>
  </head>
  <body class="bg-gray-100 min-h-screen p-6">
    <div
      x-data="stepsManager()"
      x-init="initializeSteps()"
      class="container mx-auto bg-gray-100 rounded-lg shadow-lg p-6"
    >
      <h1
        class="text-3xl font-bold mb-6 text-center text-gray-800"
      >
        Steps Manager
      </h1>

      <div class="mb-6">
        <h2
          class="text-xl font-semibold mb-2 text-gray-700"
        >
          Add New Step
        </h2>
        <div class="flex space-x-2">
          <input
            type="text"
            x-model="newStepTitle"
            placeholder="Step Title"
            class="flex-1 p-2 border rounded"
          />
          <input
            type="text"
            x-model="newStepDescription"
            placeholder="Step Description"
            class="flex-1 p-2 border rounded"
          />
          <button
            @click="debounceAddStep()"
            class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition duration-300"
          >
            Add Step
          </button>
        </div>
      </div>

      <div>
        <h2
          class="text-xl font-semibold mb-2 text-gray-700"
        >
          Current Steps
        </h2>
        <template
          x-for="(step, index) in stepsArray"
          :key="index"
        >
          <div class="bg-white p-4 rounded-lg shadow mb-2">
            <div
              x-show="!step.editing"
              class="flex items-center"
            >
              <div class="flex-1">
                <h3
                  class="font-semibold text-lg text-gray-800"
                  x-text="step.title"
                ></h3>
                <p
                  class="text-gray-600"
                  x-text="step.description"
                ></p>
              </div>
              <div class="flex space-x-2">
                <button
                  @click="editStep(index)"
                  class="text-blue-500 hover:text-blue-700"
                >
                  ✏️
                </button>
                <button
                  @click="debounceMoveStep(index, 'up')"
                  :disabled="index === 0"
                  class="text-blue-500 hover:text-blue-700 disabled:text-gray-400"
                >
                  ↑
                </button>
                <button
                  @click="debounceMoveStep(index, 'down')"
                  :disabled="index === stepsArray.length - 1"
                  class="text-blue-500 hover:text-blue-700 disabled:text-gray-400"
                >
                  ↓
                </button>
                <button
                  @click="debounceRemoveStep(index)"
                  class="text-red-500 hover:text-red-700"
                >
                  ×
                </button>
              </div>
            </div>
            <div
              x-show="step.editing"
              class="flex items-center space-x-2"
            >
              <input
                type="text"
                x-model="step.editTitle"
                class="flex-1 p-2 border rounded"
              />
              <input
                type="text"
                x-model="step.editDescription"
                class="flex-1 p-2 border rounded"
              />
              <button
                @click="saveEdit(index)"
                class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 transition duration-300"
              >
                Save
              </button>
              <button
                @click="cancelEdit(index)"
                class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600 transition duration-300"
              >
                Cancel
              </button>
            </div>
          </div>
        </template>
      </div>

      <div class="mt-6">
        <h2
          class="text-xl font-semibold mb-2 text-gray-700"
        >
          JSON Output
        </h2>
        <pre
          class="bg-gray-800 text-green-400 p-4 rounded overflow-x-auto"
          x-text="JSON.stringify(steps, null, 2)"
        ></pre>
      </div>
    </div>

    <script>
      function debounce(func, wait) {
        let timeout;
        return function (...args) {
          const context = this;
          clearTimeout(timeout);
          timeout = setTimeout(
            () => func.apply(context, args),
            wait
          );
        };
      }

      function stepsManager() {
        return {
          steps: {},
          stepsArray: [],
          newStepTitle: "",
          newStepDescription: "",
          initializeSteps() {
            if (S && S.steps) {
              try {
                this.steps = JSON.parse(S.steps);
                this.updateStepsArray();
              } catch (error) {
                console.error(
                  "Error parsing S.steps:",
                  error
                );
                this.steps = {};
                this.stepsArray = [];
              }
            }
          },
          updateStepsArray() {
            this.stepsArray = Object.entries(
              this.steps
            ).map(([title, description]) => ({
              title,
              description,
              editing: false,
              editTitle: title,
              editDescription: description,
            }));
          },
          updateSteps() {
            this.steps = Object.fromEntries(
              this.stepsArray.map((step) => [
                step.title,
                step.description,
              ])
            );
            S.steps = JSON.stringify(this.steps);
          },
          addStep() {
            if (
              this.newStepTitle &&
              this.newStepDescription
            ) {
              this.stepsArray.push({
                title: this.newStepTitle,
                description: this.newStepDescription,
                editing: false,
                editTitle: this.newStepTitle,
                editDescription: this.newStepDescription,
              });
              this.newStepTitle = "";
              this.newStepDescription = "";
              this.updateSteps();
            }
          },
          debounceAddStep: debounce(function () {
            this.addStep();
          }, 300),
          removeStep(index) {
            this.stepsArray.splice(index, 1);
            this.updateSteps();
          },
          debounceRemoveStep: debounce(function (index) {
            this.removeStep(index);
          }, 300),
          moveStep(index, direction) {
            if (
              (direction === "up" && index > 0) ||
              (direction === "down" &&
                index < this.stepsArray.length - 1)
            ) {
              const newIndex =
                direction === "up" ? index - 1 : index + 1;
              const temp = this.stepsArray[index];
              this.stepsArray[index] =
                this.stepsArray[newIndex];
              this.stepsArray[newIndex] = temp;
              this.updateSteps();
            }
          },
          debounceMoveStep: debounce(function (
            index,
            direction
          ) {
            this.moveStep(index, direction);
          },
          300),
          editStep(index) {
            this.stepsArray[index].editing = true;
          },
          saveEdit(index) {
            const step = this.stepsArray[index];
            step.title = step.editTitle;
            step.description = step.editDescription;
            step.editing = false;
            this.updateSteps();
          },
          cancelEdit(index) {
            const step = this.stepsArray[index];
            step.editTitle = step.title;
            step.editDescription = step.description;
            step.editing = false;
          },
        };
      }
    </script>
  </body>
</html>