onboarding

This commit is contained in:
Celina Lopez
2025-08-29 12:58:57 -07:00
parent 936d2a745a
commit 14e4651e28
7 changed files with 184 additions and 7 deletions

View File

@@ -16,4 +16,6 @@
- [ ] Pull request preview apps
- [ ] Update vocabulary on landing page
- [ ] Clear our historical logs
- [ ] log drain from application
- [ ] log drain from application
- [ ] clean this up - celina - app/controllers/local/pages_controller.rb

View File

@@ -0,0 +1,5 @@
class Local::OnboardingController < ApplicationController
layout "onboarding"
def index
end
end

View File

@@ -36,13 +36,17 @@ class Local::PagesController < ApplicationController
def update_portainer_configuration
stack_manager = current_account.stack_manager || current_account.build_stack_manager
stack_manager.update!(provider_url: params[:provider_url])
result = Portainer::Authenticate.execute(stack_manager:, user: current_user, auth_code: params[:password], username: params[:username])
if result.success?
flash[:notice] = "The Portainer configuration has been updated"
else
flash[:error] = result.message
stack_manager.update!(provider_url: params[:stack_manager][:provider_url])
# TODO: clean this up - celina
if params[:password].present? && params[:username].present?
result = Portainer::Authenticate.execute(stack_manager:, user: current_user, auth_code: params[:password], username: params[:username])
if result.success?
flash[:notice] = "The Portainer configuration has been updated"
else
flash[:error] = result.message
end
end
flash[:notice] = "The Portainer configuration has been updated"
redirect_to root_path
end

View File

@@ -0,0 +1,55 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "stepHeader", "portainerUrlForm", "githubForm", "step"]
static values = {
currentStep: Number
}
connect() {
this.currentStepValue = 0
this.activeClass = "step-primary"
}
disconnect() {
}
next() {
if (this.currentStepValue == 1) {
this.submit(this.portainerUrlFormTarget)
}
if (this.currentStepValue == 2) {
this.submit(this.githubFormTarget)
}
if (this.currentStepValue < 2) {
this.currentStepValue++
this.stepHeaderTargets.forEach(step => step.classList.remove(this.activeClass))
this.stepHeaderTargets[this.currentStepValue].classList.add(this.activeClass)
this.stepTargets.forEach(step => step.classList.add("hidden"))
this.stepTargets[this.currentStepValue].classList.remove("hidden")
}
}
submit(target) {
const formData = new FormData(target)
fetch(target.action, {
method: 'PUT',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': document.querySelector('[name="csrf-token"]').content
}
})
.then(response => response.json())
.then(data => {
// Handle success - maybe show next step or success message
console.log('Success:', data)
})
.catch(error => {
console.error('Error:', error)
})
}
}

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html class="h-full antialiased dark" data-theme="dark" lang="en">
<head>
<%= render 'shared/head' %>
</head>
<body data-controller="theme" data-theme-preference-value="dark">
<div class="size-full">
<div class="flex">
<main class="main-wrapper overflow-x-hidden overflow-y-auto">
<div class="flex h-full flex-col">
<div class="content-wrapper max-w-full lg:max-w-3/4">
<%= yield %>
</div>
</div>
</main>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,87 @@
<div class="min-h-screen py-8" data-controller="onboarding">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<!-- Header and Navigation -->
<div class="mb-8">
<h1 class="text-3xl font-bold ">
Let's get your account set up
</h1>
</div>
<!-- Progress Indicator -->
<div class="mb-8">
<ul aria-label="Steps" role="group" class="steps overflow-auto w-full">
<li aria-label="Step" class="step step-primary" data-onboarding-target="stepHeader">Choose Configuration</li>
<li aria-label="Step" class="step" data-onboarding-target="stepHeader">Setup Configuration</li>
<li aria-label="Step" class="step" data-onboarding-target="stepHeader">Additional Login</li>
</ul>
</div>
<!-- Plan Options -->
<div class="grid md:grid-cols-2 gap-6 mb-8" data-onboarding-target="step">
<!-- Starter Plan (Selected) -->
<div class="border-2 border-green-500 rounded-lg p-6 bg-white dark:bg-gray-800 shadow-sm">
<div class="flex items-start justify-between mb-4">
<div>
<h3 class="text-xl font-semibold dark:text-white">Local</h3>
<p class="mt-1 dark:text-gray-300">Continue with default settings</p>
</div>
<div class="w-6 h-6 bg-green-500 rounded-full flex items-center justify-center">
<svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
</div>
</div>
</div>
<!-- Pro Plan -->
<div class="border-2 rounded-lg p-6 bg-white dark:bg-gray-800 shadow-sm">
<div class="flex items-start justify-between mb-4">
<div>
<h3 class="text-xl font-semibold dark:text-white">Portainer</h3>
<p class="mt-1 dark:text-gray-300">Use Portainer to manage your containers</p>
</div>
</div>
</div>
</div>
<!-- Setup Configuration -->
<div class="grid md:grid-cols-2 gap-6 mb-8 hidden" data-onboarding-target="step">
<%= form_with url: portainer_configuration_path, model: current_account.stack_manager, method: :put, data: { onboarding_target: "portainerUrlForm" } do |form| %>
<div class="form-group">
<%= form.label :provider_url, "Portainer URL" %>
<%= form.text_field :provider_url, class: "input input-bordered w-full max-w-xs", placeholder: "http://portainer.portainer.svc.cluster.local:9000" %>
<label class="label">
<span class="label-text-alt">All data is saved locally and never sent to any external servers.</span>
</label>
</div>
<% end %>
</div>
<div class="grid md:grid-cols-2 gap-6 mb-8 hidden" data-onboarding-target="step">
<%= form_with url: portainer_configuration_path, model: current_account.stack_manager, method: :put, data: { onboarding_target: "githubForm" } do |form| %>
<div class="form-group" data-controller="toggle-password">
<%= form.label :username, "Username" %>
<%= form.text_field :username, class: "input input-bordered w-full max-w-xs" %>
</div>
<div class="form-group" data-controller="toggle-password">
<%= form.label :password, "Password" %>
<%= form.text_field :password, type: "password", class: "input input-bordered w-full max-w-xs", data: { toggle_password_target: "input" } %>
<button type="button" class="btn btn-outline" data-action="toggle-password#toggle">
<iconify-icon icon="mdi:eye"></iconify-icon>
</button>
</div>
<div class="text-center"> or </div>
<div>
<%= link_to "Connect via Github", Git::Github::UrlHelper.authorize_url, class: "btn btn-primary" %>
</div>
<% end %>
</div>
<!-- Call to Action -->
<div class="text-center">
<button class="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 px-8 rounded-lg text-lg transition-colors" data-action="click->onboarding#next">
Next
</button>
</div>
</div>
</div>

View File

@@ -116,6 +116,9 @@ Rails.application.routes.draw do
get "/calculator", to: "static#calculator"
# Public marketing homepage
if Rails.application.config.local_mode
namespace :local do
resources :onboarding, only: [ :index ]
end
get "/github_token", to: "local/pages#github_token"
put "/github_token", to: "local/pages#update_github_token"
get "/portainer_configuration", to: "local/pages#portainer_configuration"