Full Code of gbarillot/rails-vue-demo-app for AI

main 04ab3db3253c cached
162 files
342.8 KB
116.7k tokens
725 symbols
1 requests
Download .txt
Showing preview only (382K chars total). Download the full file or copy to clipboard to get everything.
Repository: gbarillot/rails-vue-demo-app
Branch: main
Commit: 04ab3db3253c
Files: 162
Total size: 342.8 KB

Directory structure:
gitextract_56eg6khq/

├── .browserslistrc
├── .gitignore
├── .tool-versions
├── Gemfile
├── Procfile
├── README.md
├── Rakefile
├── app/
│   ├── assets/
│   │   ├── config/
│   │   │   └── manifest.js
│   │   ├── images/
│   │   │   └── .keep
│   │   └── stylesheets/
│   │       └── application.css
│   ├── channels/
│   │   ├── application_cable/
│   │   │   ├── channel.rb
│   │   │   └── connection.rb
│   │   └── chat_channel.rb
│   ├── controllers/
│   │   ├── admin_controller.rb
│   │   ├── api/
│   │   │   ├── admin/
│   │   │   │   ├── admin_controller.rb
│   │   │   │   ├── dashboard_controller.rb
│   │   │   │   └── musicians_controller.rb
│   │   │   ├── api_controller.rb
│   │   │   └── musicians_controller.rb
│   │   ├── application_controller.rb
│   │   └── concerns/
│   │       └── .keep
│   ├── helpers/
│   │   └── application_helper.rb
│   ├── javascript/
│   │   ├── admin/
│   │   │   ├── routes.js
│   │   │   ├── stores/
│   │   │   │   ├── dashboard_store.js
│   │   │   │   └── musician_store.js
│   │   │   └── views/
│   │   │       ├── dashboard/
│   │   │       │   └── index.vue
│   │   │       ├── musicians/
│   │   │       │   ├── _filters.vue
│   │   │       │   ├── _form.vue
│   │   │       │   ├── edit.vue
│   │   │       │   ├── index.vue
│   │   │       │   └── new.vue
│   │   │       ├── shared/
│   │   │       │   ├── _errors.vue
│   │   │       │   ├── _footer.vue
│   │   │       │   ├── _nav.vue
│   │   │       │   ├── _pagination.vue
│   │   │       │   └── layout.vue
│   │   │       └── websockets/
│   │   │           └── index.vue
│   │   ├── entrypoints/
│   │   │   ├── admin.js
│   │   │   └── front.js
│   │   ├── front/
│   │   │   ├── routes.js
│   │   │   ├── stores/
│   │   │   │   └── musician_store.js
│   │   │   └── views/
│   │   │       ├── musicians/
│   │   │       │   ├── index.vue
│   │   │       │   └── show.vue
│   │   │       ├── pages/
│   │   │       │   └── index.vue
│   │   │       └── shared/
│   │   │           ├── _footer.vue
│   │   │           ├── _nav.vue
│   │   │           └── layout.vue
│   │   └── plugins/
│   │       ├── api.js
│   │       └── cable.js
│   ├── jobs/
│   │   └── application_job.rb
│   ├── mailers/
│   │   └── application_mailer.rb
│   ├── models/
│   │   ├── application_record.rb
│   │   ├── concerns/
│   │   │   └── .keep
│   │   ├── musician.rb
│   │   └── user.rb
│   └── views/
│       ├── admin.html.erb
│       ├── api/
│       │   ├── admin/
│       │   │   ├── musicians/
│       │   │   │   ├── edit.json.jbuilder
│       │   │   │   ├── index.json.jbuilder
│       │   │   │   └── new.json.jbuilder
│       │   │   ├── shared/
│       │   │   │   └── _pagination.json.jbuilder
│       │   │   └── users/
│       │   │       ├── edit.json.jbuilder
│       │   │       ├── index.json.jbuilder
│       │   │       └── new.json.jbuilder
│       │   └── musicians/
│       │       ├── index.json.jbuilder
│       │       └── show.json.jbuilder
│       ├── application.html.erb
│       ├── devise/
│       │   └── sessions/
│       │       └── new.html.erb
│       └── layouts/
│           ├── devise.html.erb
│           ├── mailer.html.erb
│           └── mailer.text.erb
├── bin/
│   ├── bootsnap
│   ├── bundle
│   ├── foreman
│   ├── importmap
│   ├── irb
│   ├── nokogiri
│   ├── puma
│   ├── pumactl
│   ├── racc
│   ├── rackup
│   ├── rails
│   ├── rake
│   ├── rdbg
│   ├── setup
│   ├── spring
│   ├── sprockets
│   ├── thor
│   ├── tilt
│   └── vite
├── config/
│   ├── application.rb
│   ├── boot.rb
│   ├── cable.yml
│   ├── credentials.yml.enc
│   ├── database.yml
│   ├── environment.rb
│   ├── environments/
│   │   ├── development.rb
│   │   ├── production.rb
│   │   └── test.rb
│   ├── importmap.rb
│   ├── initializers/
│   │   ├── assets.rb
│   │   ├── content_security_policy.rb
│   │   ├── cypress_rails_initializer.rb
│   │   ├── devise.rb
│   │   ├── filter_parameter_logging.rb
│   │   ├── inflections.rb
│   │   ├── locales.rb
│   │   ├── new_framework_defaults_7_1.rb
│   │   └── permissions_policy.rb
│   ├── locales/
│   │   ├── devise.en.yml
│   │   ├── en.yml
│   │   └── fr.yml
│   ├── puma.rb
│   ├── routes.rb
│   ├── storage.yml
│   └── vite.json
├── config.ru
├── cypress/
│   ├── e2e/
│   │   └── home.cy.js
│   └── support/
│       └── e2e.js
├── cypress.config.js
├── db/
│   ├── migrate/
│   │   ├── 20220424120800_base_setup.rb
│   │   ├── 20240102193807_add_service_name_to_active_storage_blobs.active_storage.rb
│   │   ├── 20240102193808_create_active_storage_variant_records.active_storage.rb
│   │   └── 20240102193809_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
│   ├── schema.rb
│   └── seeds.rb
├── docker-compose.yml
├── lib/
│   ├── assets/
│   │   └── .keep
│   └── tasks/
│       └── .keep
├── log/
│   └── .keep
├── package.json
├── public/
│   ├── 404.html
│   ├── 422.html
│   ├── 500.html
│   ├── css/
│   │   └── development/
│   │       ├── admin/
│   │       │   └── forms.css
│   │       ├── admin.css
│   │       ├── devise.css
│   │       ├── front.css
│   │       ├── grid.css
│   │       ├── main.css
│   │       ├── themes/
│   │       │   ├── dark.css
│   │       │   └── light.css
│   │       └── utilities.css
│   ├── robots.txt
│   └── vite-test/
│       ├── assets/
│       │   ├── admin-ebb17751.js
│       │   ├── front-f6acb49a.js
│       │   └── vue-i18n-6b73e0ca.js
│       ├── manifest-assets.json
│       └── manifest.json
├── storage/
│   └── .keep
├── test/
│   ├── application_system_test_case.rb
│   ├── controllers/
│   │   └── .keep
│   ├── fixtures/
│   │   └── files/
│   │       └── .keep
│   ├── helpers/
│   │   └── .keep
│   ├── javascript/
│   │   └── app.test.js
│   ├── mailers/
│   │   └── .keep
│   ├── models/
│   │   └── .keep
│   ├── system/
│   │   └── .keep
│   └── test_helper.rb
├── tmp/
│   └── .keep
├── vendor/
│   ├── .keep
│   └── javascript/
│       └── .keep
└── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .browserslistrc
================================================
defaults


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
#   git config --global core.excludesfile '~/.gitignore_global'

# Ignore bundler config.
/.bundle

# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
/db/*.sqlite3-*

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore uploaded files in development.
/storage/*
!/storage/.keep

/public/assets
.byebug_history

# Ignore master key for decrypting credentials and more.
/config/master.key

# Ignore Redis dump
/dump.rdb

/public/packs
/public/packs-test
/node_modules
/yarn-error.log
yarn-debug.log*
.yarn-integrity


================================================
FILE: .tool-versions
================================================
ruby 3.2.2
nodejs 20.2.0


================================================
FILE: Gemfile
================================================
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 7.1.2"

gem 'sprockets-rails'
gem 'sqlite3'
gem 'puma'
gem 'jbuilder'
gem 'redis'
gem 'bootsnap', require: false
gem 'vite_rails'
gem 'foreman'
gem 'route_translator'
gem 'kaminari'
gem 'ransack'
gem 'devise'
gem 'cypress-rails'

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]

group :development, :test do
  # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
  gem "debug", platforms: %i[ mri mingw x64_mingw ]
end

group :development do
  # Use console on exceptions pages [https://github.com/rails/web-console]
  gem "web-console"
end

group :test do
  gem 'capybara'
  gem 'minitest'
  gem 'minitest-rails'
  gem 'minitest-spec-rails'
  gem 'minitest-focus'
  gem 'capybara-email'
  gem 'json-schema'
  gem 'warden'
  gem 'selenium-webdriver'
  gem 'webdrivers'
end


================================================
FILE: Procfile
================================================
vite: bin/vite dev
web: bin/rails s --port 3000 -b 0.0.0.0


================================================
FILE: README.md
================================================
# Rails + Vite + Vue + Pina Demo App

### This repo is no longer maintained 
Feel free to grab some ideas, but I will no longer update this codebase. 

## Description

Demo Single Page Application based on Ruby on Rails 7.0.2, using Vue 3 + Pina, compiled with Vite.
All the basic features you need to build a real world app with:

- **Front / Admin namespaces**
- **I18n** (server side + client side)
- **Forms** (with progress and error handling)
- **Authentication** (Devise)
- **Pagination** (Kaminari)
- **Dynamic search filters** (Ransack)
- **Websockets** (ActionCable)
- **Bootstrap like grid** (using CSS Grid layout)

All of this is designed with maintainability and readability in mind, slightly inspired by the Rails conventions.

This is the follow up of the previous Rails + Vue + Vuex app from 2017 which is still
available on the Rails6 branch.  

A lot of things has been updated/rewrote for this version, notably:

- Webpacker is now replaced by Vite
- Vue app in using Composition API 
- Vuex is now replaced by Pinia to handle state management
- No longer depends on JQuery for API calls (replaced by Axios)

Nonetheless, a lot of opinions and conventions from the previous version remain valid, you may have a look at the original blog post for details (https://guillaume.barillot.me/2017/12/02/how-to-organize-your-vue-files-in-a-rails-5-1-project-using-webpack/). 

Boostrapping the plumbing for basic stuff can take some time, but once you get the basic it's
pretty easy to extend and to get really efficient with Vue + Rails. With this example you have 
all you need to build up your new project in minutes.

## Installation

```
git clone git@github.com:gbarillot/rails-vue-demo-app.git
cd rails-vue-demo-app
bundle install
yarn install
bundle exec rails db:migrate
bundle exec rails db:seed
```

## Booting the app

```
foreman start
```

Hot Module Reloading (HMR) is enabled by default. If you prefer to turn it off, set "hmr" to false 
in /vite.config.ts.
## Running tests

### Rails side

```
rails test
```

### JS side

```
yarn test
```

## A note on CSS
CSS is done right in the public/css/development directory.

Pros:
- No compile time, greatly speed up page load time in dev
- Allows CSS editing + saving directly from Chrome Devtools
- Easier / more fexible to manage imports

Cons:
- You should disable cache in DevTools in Dev
- As of today, not suitable for production!

The purpose of this repo is Vue + Vite + Rails, not CSS, so feel free to use whatever method 
you'd prefer to handle CSS. Sprocket is left as is and still works.

## Contributions

PR and feedbacks welcome!

## Licence

MIT


================================================
FILE: Rakefile
================================================
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require_relative "config/application"

Rails.application.load_tasks


================================================
FILE: app/assets/config/manifest.js
================================================
//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../../javascript .js
//= link_tree ../../../vendor/javascript .js


================================================
FILE: app/assets/images/.keep
================================================


================================================
FILE: app/assets/stylesheets/application.css
================================================
/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *
 *= require_self
 */


================================================
FILE: app/channels/application_cable/channel.rb
================================================
module ApplicationCable
  class Channel < ActionCable::Channel::Base
  end
end


================================================
FILE: app/channels/application_cable/connection.rb
================================================
module ApplicationCable
  class Connection < ActionCable::Connection::Base
  end
end


================================================
FILE: app/channels/chat_channel.rb
================================================
class ChatChannel < ApplicationCable::Channel

  def subscribed
    stream_from "ChatChannel"
  end

  def receive(data)
    ActionCable.server.broadcast("ChatChannel", {
      message: data['message'].upcase
    })
  end
end

================================================
FILE: app/controllers/admin_controller.rb
================================================
class AdminController < ApplicationController
  before_action :auth_user! 

  def index
    render template: 'admin'
  end

  private 

  # Proper redirect using I18n.locale
  # Devise's authenticate_user! doesn't handle I18n properly here
  def auth_user!
    redirect_to new_user_session_path unless current_user 
  end
end


================================================
FILE: app/controllers/api/admin/admin_controller.rb
================================================
class Api::Admin::AdminController < Api::ApiController
  before_action :authenticate_user!

  def search_params
    params[:q] ||= {} if params[:q] != ''
  end
end


================================================
FILE: app/controllers/api/admin/dashboard_controller.rb
================================================
class Api::Admin::DashboardController < Api::Admin::AdminController
  def index
    render json: {metrics: {musicians: Musician.count}}
  end
end


================================================
FILE: app/controllers/api/admin/musicians_controller.rb
================================================
class Api::Admin::MusiciansController < Api::Admin::AdminController
  # DELETE ME: Dummy emulation of a slow network so you can see the UI animation in dev. mode
  before_action :slow, only: [:create, :update] 
  before_action :load_musician, except: [:index, :new, :create]  

  def index
    @musicians = Musician.ransack(search_params)
                         .result
                         .page(params[:page])
                         .per(params[:per_page])
  end

  def new
    @musician = Musician.new
  end

  def create
    @musician = Musician.create(musician_params)

    if @musician.errors.empty?
      render template: '/api/admin/musicians/edit'
    else
      render json: {errors: @musician.errors.messages}.to_json, status: 422      
    end
  end

  def edit
  end

  def update
    if @musician.update(musician_params)
      head :ok
    else
      render json: {errors: @musician.errors.messages}.to_json, status: 422
    end
  end

  def destroy
    if @musician.destroy    
      head :ok
    else
      render json: {errors: @musician.errors.messages}.to_json, status: 422
    end
  end

  private

  def musician_params
    params.require(:musician).permit(
      :name,
      :band
    )
  end

  def load_musician
    @musician = Musician.find(params[:id])
  end

  def slow
    sleep 1
  end
end


================================================
FILE: app/controllers/api/api_controller.rb
================================================
class Api::ApiController < ApplicationController

end


================================================
FILE: app/controllers/api/musicians_controller.rb
================================================
class Api::MusiciansController < Api::ApiController

  def index
    @musicians = Musician.all
  end

  def show    
    if params[:id] == "this-will-trigger-a-500"
      # Render a 500 to demonstrate how the front-end handles server side errors
      render json: {error: "Internal server error"}, status: 500
    elsif params[:id] == "this-will-trigger-a-401"
      # Render a 401 to demonstrate how the front-end handles server side errors
      render json: {error: "Not authenticated"}, status: 401
    else
      @musician = Musician.find(params[:id])
    end
  end

end


================================================
FILE: app/controllers/application_controller.rb
================================================
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  around_action :set_locale_from_url
  #rescue_from Exception, with: :render_error

  def index
    render template: 'application'
  end

  def set_locale
    I18n.locale = params[:locale] || session[:locale] || I18n.default_locale
    session[:locale] = I18n.locale
  end

  def self.default_url_options
    { locale: I18n.locale }
  end

  def after_sign_in_path_for(resource) 
    admin_root_path
  end

  private
  def render_error(e)
    if e.class.name == "ActiveRecord::RecordNotFound"
      render json: {error: "Not found"}.to_json, status: 404
    else
      render json: {error: "Internal server error"}.to_json, status: 500
    end
  end
end


================================================
FILE: app/controllers/concerns/.keep
================================================


================================================
FILE: app/helpers/application_helper.rb
================================================
module ApplicationHelper

  def ui_translations(section)
    translations = {current: I18n.t('.')[:vue][section]}
    translations.to_json.html_safe
  end

  def paginate(scope, default_per_page = scope.default_per_page)
    collection = scope.page(params[:page]).per((params[:per_page] || default_per_page).to_i)
    current, total, per_page = collection.current_page, collection.total_pages, collection.limit_value

    {
      current:  current,
      previous: (current > 1 ? (current - 1) : nil),
      next:     (current == total ? nil : (current + 1)),
      per_page: per_page,
      pages:    total,
      count:    collection.total_count
    }
  end
end


================================================
FILE: app/javascript/admin/routes.js
================================================
import { createWebHistory, createRouter } from 'vue-router'

import DashboardIndex from '@/admin/views/dashboard/index.vue';
import MusicianIndex from '@/admin/views/musicians/index.vue';
import MusicianNew from '@/admin/views/musicians/new.vue';
import MusicianEdit from '@/admin/views/musicians/edit.vue';
import WebsocketIndex from '@/admin/views/websockets/index.vue';

const router = createRouter({
  history: createWebHistory(`/${I18n.prefix}admin`),
  routes: [
    { path: '/', component: DashboardIndex, name: 'root_path' },
    { path: '/musicians', component: MusicianIndex, name: 'musicians_path' },
    { path: '/musicians/new', component: MusicianNew, name: 'new_musician_path' },
    { path: '/musicians/:id/edit', component: MusicianEdit, name: 'edit_musician_path' },
    { path: '/websockets', component: WebsocketIndex, name: 'websockets_path' }
  ]
});

// Handles 404 Not found
router.beforeEach((to, from, next) => {
  if (!to.matched.length) {
    window.location.href = '/404'
  } else {
    next();
  }
});

export default router;

================================================
FILE: app/javascript/admin/stores/dashboard_store.js
================================================
import { defineStore } from 'pinia'

export const DashboardStore = defineStore('dashboard', {
  state: () => {
    return {
      metrics: {}
    }
  },

  actions: {
    async index() {
      return this.Api.get('/dashboard').then(response => {    
        this.metrics = response.data.metrics;        
      })  
    }
  }
})

================================================
FILE: app/javascript/admin/stores/musician_store.js
================================================
import { defineStore } from 'pinia'

export const MusicianStore = defineStore('musicians', {
  state: () => {
    return {
      progress: '',
      errors: {},
      bands: [],
      musician: {},
      musicians: [],
      pagination: {}
    }
  },

  actions: {
    async index(path) {
      return this.Api.get(path).then(response => {  
        this.pagination = response.data.pagination;
        this.bands = response.data.bands;    
        this.musicians = response.data.musicians;        
      })  
    },
    async new() {
      this.errors = {}; 
      this.musician = {};
      return this.Api.get(`/musicians/new`).then(response => {             
        this.musician = response.data.musician;
      })  
    },
    async create() {
      this.errors = {};
      this.progress = 'loading';
      return this.Api.post(`/musicians`, this.musician).then(response => {        
        this.musician = response.data.musician;
        return true;
      }).catch(error => {
        this.errors = error.response.data.errors;
        return false;
      }).finally(() => {
        this.progress = '';
      })
    },
    async edit(id) {
      this.errors = {};
      this.musician = {};
      return this.Api.get(`/musicians/${id}/edit`).then(response => {             
        this.musician = response.data.musician;
      })  
    },
    async update(id) {
      this.errors = {};
      this.progress = 'loading';
      return this.Api.put(`/musicians/${id}`, this.musician).then(response => {        
        this.errors = {};
      }).catch(error => {
        this.errors = error.response.data.errors;
      }).finally(() => {
        this.progress = '';
      })
    },
    async destroy(id) {      
      return this.Api.destroy(`/musicians/${id}`).then(response => {  
        this.errors = {};      
      }).catch(error => {
        this.errors = error.response.data.errors;
      }) 
    }
  }
})

================================================
FILE: app/javascript/admin/views/dashboard/index.vue
================================================
<template>
  <section class="container">
    <div class="row">
      <div class="col-xs-12 col-md-3 card">
        <h3>Rails 7</h3>
        <p><a href="https://edgeguides.rubyonrails.org/" target="_blank">An awesome server side Framework</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>Vue 3</h3>
        <p><a href="https://vuejs.org/guide/" target="_blank">SPA without the pain</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>Pinia</h3>
        <p><a href="https://pinia.vuejs.org/" target="_blank">State manager</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>Vue Router</h3>
        <p><a href="https://router.vuejs.org/" target="_blank">Flexible</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>Axios</h3>
        <p><a href="https://axios-http.com/docs/intro" target="_blank">Handles AJAX requests, no JQuery</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>Vite</h3>
        <p><a href="https://vitejs.dev/" target="_blank">Next generation Frontend tooling</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>Devise</h3>
        <p><a href="https://github.com/heartcombo/devise" target="_blank">Simple authentication using regular cookies that just works</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>I18n</h3>
        <p><a href="https://kazupon.github.io/vue-i18n/" target="_blank">
          {{ $t('dashboard.musicians', 0) }}, 
          {{ $t('dashboard.musicians', 1) }}, 
          {{ store.metrics.musicians + ' ' + $t('dashboard.musicians', store.metrics.musicians) }}</a>
        </p> 
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>ActionCable</h3>
        <p><a href="https://guides.rubyonrails.org/action_cable_overview.html" target="_blank">Websockets with 2 way bindings</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>Kaminari</h3>
        <p><a href="https://github.com/kaminari/kaminari" target="_blank">Efficient Pagination</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>Ransack</h3>
        <p><a href="https://github.com/activerecord-hackery/ransack" target="_blank">ActiveRecord wizardry for dynamic search filters</a></p>
      </div>
      <div class="col-xs-12 col-md-3 card">
        <h3>Bootstrap grid</h3>
        <p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout" target="_blank">... but using native CSS Grid layout</a></p>
      </div>
    </div>
  </section>
</template>

<script setup>
import { DashboardStore } from "@/admin/stores/dashboard_store.js";
const store = DashboardStore();

onMounted(() => {
  store.index();    
})
</script>


================================================
FILE: app/javascript/admin/views/musicians/_filters.vue
================================================
<template>  
  <section class="filters">
    <a href="#" @click.prevent="toggleForm" class="openable">{{ $t('filter') }}</a>

    <form @submit.prevent="search" ref="filters" accept-charset="UTF-8" class="card hidden">  
      <div class="row">      
        <div class="col-xs-12 col-md-6 col-xl-5"> 
          <label>{{ $t('musicians.form.name') }}</label>    
          <input type="text" v-model="form.name_cont" placeholder="name">
        </div>  
        <div class="col-xs-12 col-md-6 col-xl-4">     
          <label>{{ $t('musicians.form.band') }}</label>
          <select v-model="form.band_eq">
            <option :value="null">{{ $t('any') }}</option>
            <option v-for="band in bands" :key="band.key" :value="band.key">{{ band.name }}</option>
          </select>
        </div> 
        
        <div class="col-xs-12 col-md-2 col-xl-3">
          <label class="hidden-sm hidden-md hidden-lg">&nbsp;</label>
          <input type="submit" role="button" class="button button-primary" :value="$t('filter')">
        </div>
        <div class="col-xs-12 col-md-5">
          <a @click="reset" href="#" class="secondary outline fill">{{ $t('reset_filters') }}</a>
        </div>  
      </div>      
    </form>

  </section>
</template>

<script setup>
const location = useRoute();
const router = useRouter();
const props = defineProps(['bands']);
const filters = ref(null);

const form = () => {
  return {
    name_cont: '',
    band_eq: null
  } 
}

const toggleForm = (e) => {
  e.target.classList.toggle('open');
  filters.value.classList.toggle('hidden');  
}

const search = () => {
  const query = Object.fromEntries(
    Object.entries(form).map(entry => [`q[${entry[0]}]`, entry[1]])
  );
  router.push({path: location.path, query: query});
}

const reset = () => {
  form.name_cont = '';
  form.band_eq = null;
  router.push({path: location.path, query: ''})
}
</script>


================================================
FILE: app/javascript/admin/views/musicians/_form.vue
================================================
<template>  
  <fieldset>
    <label>{{ $t('musicians.form.name') }}</label>
    <errors attr="name" :messages="data.errors" />
    <input type="text" v-model="data.musician.name" :aria-invalid="data.errors.name ? true : ''">
  </fieldset>

  <fieldset>
    <label>{{ $t('musicians.form.band') }}</label>
    <errors attr="band" :messages="data.errors" />
    <select v-model="data.musician.band" :aria-invalid="data.errors.band ? true : ''">
      <option v-for="band in data.musician.bands" :key="band.key" :value="band.key">{{ band.name }}</option>
    </select>
  </fieldset>     
</template>

<script setup>
import errors from "../shared/_errors.vue";
defineProps(['data'])
</script>


================================================
FILE: app/javascript/admin/views/musicians/edit.vue
================================================
<template>  
  <section class="container">
    <ul class="breadcrumb">
      <li><router-link :to="{ name: 'root_path' }">{{ $t('title') }}</router-link></li>
      <li><router-link :to="{ name: 'musicians_path' }">{{ $t('nav.musicians') }}</router-link></li>
      <li>{{ store.musician.name }}</li>
    </ul>

    <form @submit.prevent="update" class="card" :class="store.progress">
      <MusicianForm :data="store" /> 

      <div class="row">
        <div class="col-xs-12 col-sm-8 col-md-9 col-lg-10 hidden-xs">
          <a @click="destroy" href="#" role="button" class="secondary outline">{{ $t('delete') }}</a> 
        </div>
        <div class="col-xs-12 col-sm-4 col-md-3 col-lg-2 ta-right">
          <input type="submit" :value="$t('save')" />      
        </div>
        <a @click="destroy" href="#" class="visible-xs">{{ $t('delete') }}</a>
      </div>
    </form>
 
    <div class="row">
      <div class="col-xs-12">
        <p>{{ $t('musicians.comment') }}</p>
      </div>
    </div>
  </section>
</template>

<script setup>
import MusicianForm from "./_form.vue";
import { MusicianStore } from "@/admin/stores/musician_store.js";

const store = MusicianStore();
const router = useRouter();
const location = useRoute();

const update = (event => {
  store.update(location.params.id);
});

const destroy = (event => {
  if(confirm(t('confirm'))) {
    store.destroy(location.params.id).then(response => {
      router.push({name: 'musicians_path'});
    })
  }  
});

onMounted(() => {
  store.edit(location.params.id)
});
</script>


================================================
FILE: app/javascript/admin/views/musicians/index.vue
================================================
<template>
  <section class="container">
    <div class="row">
      <div class="col-xs-12 col-lg-8 col-xl-9">      
        <ul class="breadcrumb">
          <li><router-link :to="{name: 'root_path'}">{{ $t('title') }}</router-link></li>
          <li>{{ $t('nav.musicians') }}</li>
        </ul>
      </div>
      <div class="col-xs-12 col-lg-4 col-xl-3 ta-right">
        <router-link :to="{name: 'new_musician_path'}" role="button" class="outline" style="width: 100%">{{ $t('musicians.create') }}</router-link> 
      </div>
    </div>

    <filters :bands="store.bands" />
 
    <div ref="listing" >
      <table v-if="store.musicians && store.musicians.length > 0">
        <thead>
          <tr>
            <th>{{ $t('musicians.form.id') }}</th>
            <th>{{ $t('musicians.form.name') }}</th>
            <th>{{ $t('musicians.form.band') }}</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="musician in store.musicians" :key="musician.id">
            <td><router-link :to="{ name: 'edit_musician_path', params: {id: musician.id}}">{{ musician.id }}</router-link></td>
            <td><router-link :to="{ name: 'edit_musician_path', params: {id: musician.id}}">{{ musician.name }}</router-link></td>
            <td><router-link :to="{ name: 'edit_musician_path', params: {id: musician.id}}">{{ musician.band }}</router-link></td>
          </tr>
        </tbody>
      </table>
      <div v-else>
        <h3 class="card ta-center">{{ $t('no_result') }}</h3>
      </div>

      <pagination v-if="store.pagination" :store="store" @clicked="index"></pagination>
    </div>
  </section>
</template>

<script setup>
import Filters from './_filters.vue';
import Pagination from '../shared/_pagination.vue';

import { MusicianStore } from "@/admin/stores/musician_store.js";
const store = MusicianStore();

const location = useRoute();
const index = (event => {  
  store.index(location.fullPath)
});

onMounted(() => {
  index(); 
});
</script>


================================================
FILE: app/javascript/admin/views/musicians/new.vue
================================================
<template>  
  <section class="container">
    <ul class="breadcrumb">
      <li><router-link :to="{ name: 'root_path' }">{{ $t('title') }}</router-link></li>
      <li><router-link :to="{ name: 'musicians_path' }">{{ $t('nav.musicians') }}</router-link></li>
      <li><span>{{ $t('musicians.new') }}</span></li>
    </ul>

    <div ref="animation">
      <form @submit.prevent="create" accept-charset="UTF-8" class="card">
        <MusicianForm :data="store" /> 

        <div class="row">
          <div class="col-sm-4 col-start-sm-21 ta-right">
            <input type="submit" :value="$t('save')" />      
          </div>
        </div>
      </form>
    </div> 
  </section>
</template>

<script setup>
import MusicianForm from "./_form.vue";
import { MusicianStore } from "@/admin/stores/musician_store.js";
const store = MusicianStore();

const location = useRoute();

const create = (event => {
  store.create(location.params.id);
});

onMounted(() => {
  store.new(location.params.id)
});
</script>


================================================
FILE: app/javascript/admin/views/shared/_errors.vue
================================================
<template>  
  <span class="error" v-if="message != ''">{{ message }}</span>
</template>

<script setup>
const props = defineProps(['attr', 'messages'])

const message = computed(() => {
  return !!props.messages[props.attr] ? props.messages[props.attr].join(',') : ''
})
</script>


================================================
FILE: app/javascript/admin/views/shared/_footer.vue
================================================
<template>    
  <footer>
    <div class="container">
      <div class="row">
        <div class="col-xs-12 col-lg-8">
          Guillaume Barillot 2024 - 
          <a href="https://github.com/gbarillot">Github</a> | 
          <a href="https://twitter.com/gbarillot">Twitter</a>
        </div>
        <div class="col-xs-12 col-lg-4 ta-right">
          <b>Theme: </b> 
          <a href="#" @click.prevent="setTheme('light')">light</a> | 
          <a href="#" @click.prevent="setTheme('dark')">dark</a>
        </div>
      </div>
    </div>
  </footer>
</template>

<script setup>
const setTheme = ((theme) => {
  let root = document.querySelector('html');
  root.setAttribute('data-theme', theme);
  document.cookie = `theme=${theme}; path=/`;
})

const getCookie = ((name) => {
	let value = '; ' + document.cookie;
	let parts = value.split(`; ${name}=`);
	if (parts.length == 2) return parts.pop().split(';').shift();
})

onMounted(() => {
  const theme = getCookie('theme');
  theme ? setTheme(theme) : setTheme('light');
})
</script>


================================================
FILE: app/javascript/admin/views/shared/_nav.vue
================================================
<template>
  <section class="top-nav">
    <div class="container">
      <div class="row">
        <div class="col-xs-12 col-lg-10">
          <nav>
            <ul>
              <li :class="activeOn(['root_path'])">
                <router-link :to="{name: 'root_path'}">{{ $t('nav.dashboard') }}</router-link>
              </li>
              <li :class="activeOn(['musicians_path', 'edit_musician_path', 'new_musician_path'])">
                <router-link :to="{name: 'musicians_path'}">{{ $t('nav.musicians') }}</router-link>
              </li>
              <li :class="activeOn(['websockets_path'])">
                <router-link :to="{name: 'websockets_path'}">{{ $t('nav.websockets') }}</router-link>
              </li>
              <li>
                <a :href="'/' + localePrefix() + 'users/sign_out'">{{ $t('nav.logout') }}</a>
              </li>
            </ul>
          </nav>
        </div>
        <div class="col-xs-12 col-lg-2 hidden-xs hidden-sm hidden-md">
          <select v-model="locale">
            <option v-for="locale in availableLocales" :value="locale" :key="locale">
              {{ locale.toUpperCase() }}
            </option>
          </select> 
        </div>
      </div>
    </div>
  </section>
</template>

<script setup>
const location = useRoute();
const { t } = useI18n({});

const availableLocales = ref(window.I18n.availableLocales);
const locale = ref(window.I18n.locale);

const activeOn = ((paths) => {
  return paths.includes(location.name) ? 'active' : ''
})
const localePrefix = ((paths) => {
  return window.I18n.prefix;
})

watch(locale, (newLocale, _oldLocale) => {
  let redirectTo = `/${newLocale}/admin${location.path}`;
  if (newLocale === availableLocales.value[0]) {
    redirectTo = `/admin${location.path}`;
  }
  window.location.href = redirectTo;
});
</script>


================================================
FILE: app/javascript/admin/views/shared/_pagination.vue
================================================
<template>
  <section clas="container">    
    <div v-if="store.pagination.next || store.pagination.previous">      
      <ul class="pagination">
        <li v-if="store.pagination.previous != null">
          <router-link :to="{path: $route.path, query: setQuery(store.pagination.previous)}" v-if="store.pagination.previous">
            <span>&laquo;</span>
          </router-link>
        </li>

        <li v-for="page in store.pagination.pages" :key="page" :class="[store.pagination.current == page ? 'active' : '']">
          <router-link :to="{path: $route.path, query: setQuery(page)}" v-if="page != store.pagination.current && (page == 1 || page == 2 || page == 3 || page == store.pagination.pages || page == store.pagination.pages - 1 || page == store.pagination.pages - 2)">{{ page }}</router-link>

          <span v-if="page != store.pagination.current && page == 4">...</span>
          <span v-if="page == store.pagination.current">{{ page }}</span>
          <span v-if="page == store.pagination.current && page == 4">...</span>
        </li>

        <li v-if="store.pagination.next != null && store.pagination.pages > 0">
          <router-link :to="{path: $route.path, query: setQuery(store.pagination.next)}"  v-if="store.pagination.next">
            <span>&raquo;</span>
          </router-link>
        </li>
      </ul>
    </div>
  </section>
</template>

<script setup>
const clickEvent = defineEmits(['clicked'])
const location = useRoute();
const props = defineProps(['store']);

watch(() => location.query, () => {
  clickEvent('clicked', true);
});

const setQuery = (page) => {
  const query = JSON.parse(JSON.stringify(location.query));
  query['page'] = page;
  return query;
}
</script>


================================================
FILE: app/javascript/admin/views/shared/layout.vue
================================================
<template>    
  <main>
    <nav-bar />
    <router-view />
  </main>
  <bottom />
</template>

<script setup>
import NavBar from './_nav.vue';
import Bottom from './_footer.vue';
</script>


================================================
FILE: app/javascript/admin/views/websockets/index.vue
================================================
<template>
  <section class="container">
    <ul class="breadcrumb">
      <li><router-link :to="{name: 'root_path'}">{{ $t('title') }}</router-link></li>
      <li>{{ $t('nav.websockets') }}</li>
    </ul>

    <div class="row">
      <div class="col-xs-12 col-sm-6 card">
        <form @submit.prevent="publish" accept-charset="UTF-8">
          <input type="input" v-model="message" :placeholder="$t('websockets.placeholder')" />
          <br /><br />
          <input type="submit" :value="$t('websockets.publish')" />
        </form>

        <div class="card">
          <p>{{ $t('websockets.comment1') }}</p>
          <code>{{ $t('websockets.code_example') }}</code>
        </div>
      </div>

      <div class="col-xs-12 col-sm-6 card">
        <p>{{ $t('websockets.comment2') }}
           <b>{{ $t('websockets.server_side') }}</b>
           {{ $t('websockets.comment3') }}
        </p>
        
        <div v-if="messages.length > 0">
          <p v-for="(message, index) in messages" :key="index"><i>{{ message }}</i></p>
        </div>
        <div v-else>
          <p><i>{{ $t('websockets.waiting_messages') }}</i></p>
        </div> 
      </div>
    </div>
  </section>
</template>

<script setup>
const messages = ref([]);
const message = ref('');
const cable = inject('cable')

const publish = (() => {
  cable.send(message.value);
  message.value = '';
})

onMounted(() => {
  cable.on('chat', (event) => {
    messages.value.unshift(event['message']);
  })
});
</script>


================================================
FILE: app/javascript/entrypoints/admin.js
================================================
import { createApp } from 'vue';
const app = createApp(Layout);

import Router from '@/admin/routes.js';
import Layout from '@/admin/views/shared/layout.vue';
import Axios from "axios";

// ActionCable setup
import { createCable } from '@/plugins/cable';
const Cable = createCable({channel: 'ChatChannel'})

// Pinia + Axios setup
import { createApi } from '@/plugins/api';
import { createPinia } from 'pinia';
const Pinia = createPinia();
Pinia.use(({ store }) => { store.Api = createApi({handler: Axios, namespace: '/admin'}) })

// I18n loader
import { createI18n } from 'vue-i18n';
const I18n = createI18n({locale: 'current',  messages: translations, legacy: false});

app.use(Router)
   .use(Pinia)
   .use(I18n)
   .use(Cable)
   .mount('#app')

================================================
FILE: app/javascript/entrypoints/front.js
================================================
import { createApp } from 'vue';
const app = createApp(Layout);

import Router from '@/front/routes.js';
import Layout from '@/front/views/shared/layout.vue';
import Axios from "axios";

// API + Axios wrapper
import { createApi } from '@/plugins/api';
const Api = createApi({handler: Axios, namespace: ''});

// Pinia + Axios setup
import { createPinia } from 'pinia';
const Pinia = createPinia();
Pinia.use(({ store }) => { store.Api = Api })

// I18n loader
import { createI18n } from 'vue-i18n';
const I18n = createI18n({locale: 'current',  messages: translations, legacy: false});

app.use(Router)
   .use(Pinia)
   .use(I18n)
   .mount('#app')

================================================
FILE: app/javascript/front/routes.js
================================================
import { createWebHistory, createRouter } from 'vue-router'

import PageIndex from './views/pages/index.vue';
import MusicianIndex from './views/musicians/index.vue';
import MusicianShow from './views/musicians/show.vue';

const router = createRouter({
  history: createWebHistory(`/${I18n.prefix}`),
  routes: [
    { path: '/', component: MusicianIndex, name: 'root_path' },
    { path: '/pages', component: PageIndex, name: 'pages_path' },
    { path: '/musicians', component: MusicianIndex, name: 'musicians_path' },
    { path: '/musicians/:id', component: MusicianShow, name: 'musician_path' },
  ]
});

// Handles 404 Not found
router.beforeEach((to, from, next) => {
  if (!to.matched.length) {
    window.location.href = '/404'
  } else {
    next();
  }
});

export default router;

================================================
FILE: app/javascript/front/stores/musician_store.js
================================================
import { defineStore } from 'pinia'

export const MusicianStore = defineStore('musicians', {
  state: () => {
    return {
      musicians: [],
      musician: {}      
    }
  },

  actions: {
    async index(path) {
      return this.Api.get('/musicians').then(response => {      
        this.musicians = response.data.musicians;
      })  
    },
    async show(id) {
      return this.Api.get(`/musicians/${id}`).then(response => {      
        this.musician = response.data.musician;
      })  
    }
  }
})

================================================
FILE: app/javascript/front/views/musicians/index.vue
================================================
<template>
  <section class="container">
    <h1>{{ $t('home.title') }}</h1>

    <ul class="breadcrumb">
      <li><span>{{ $t('home.breadcrumb') }}</span></li>
    </ul>

    <div class="row">
      <div v-for="musician in store.musicians" :key="musician.id" class="col-xs-12 col-md-3 card">
        <router-link :to="{ name: 'musician_path', params: {id: musician.id}}">{{ musician.name }}</router-link>
      </div>
    </div>
  </section>
</template>

<script setup>  
import { MusicianStore } from "../../stores/musician_store.js";
const store = MusicianStore();

onMounted(() => {
  store.index()
});
</script>


================================================
FILE: app/javascript/front/views/musicians/show.vue
================================================
<template>  
  <section class="container">
    <h1>{{ $t('home.title') }}</h1>

    <ul class="breadcrumb">
      <li><router-link :to="{ name: 'root_path' }">{{ $t('home.breadcrumb') }}</router-link></li>
      <li>{{ store.musician.name }}</li>
    </ul>

    <h2>{{ $t('musicians.title') }}</h2>
    <p>
      <b>{{ $t('musicians.id') }}:</b> {{ store.musician.id }}<br />
      <b>{{ $t('musicians.name') }}:</b> {{ store.musician.name }}<br /> 
      <b>{{ $t('musicians.band') }}:</b> {{ store.musician.band }}
    </p>
  </section>
</template>

<script setup>
import { MusicianStore } from "../../stores/musician_store.js";
const store = MusicianStore();
const location = useRoute();

onMounted(() => {
  store.show(location.params.id)
});
</script>

================================================
FILE: app/javascript/front/views/pages/index.vue
================================================
<template>
  <section class="container">
    <h1>{{ $t('pages.title') }}</h1>

    <p><a href="/dead-link">{{ $t('pages.server_404') }}</a></p>
    <p><router-link to="/dead-link">{{ $t('pages.client_404') }}</router-link></p>
    <p><a @click.prevent="unauthorized" href="#">{{ $t('pages.server_401') }}</a></p>
    <p><a @click.prevent="crash" href="#">{{ $t('pages.server_500') }}</a></p>
    <br />
    <b><a href="admin">{{ $t('pages.admin_link') }}</a></b>
  </section>
</template>

<script setup>
import { MusicianStore } from "../../stores/musician_store";
const store = MusicianStore();

const unauthorized = (() => {
  store.show('this-will-trigger-a-401');        
})
const crash = (() => {
  store.show('this-will-trigger-a-500'); 
})
</script>


================================================
FILE: app/javascript/front/views/shared/_footer.vue
================================================
<template>    
  <footer>
    <div class="container">
      <div class="row">
        <div class="col-xs-12 col-lg-8">
          Guillaume Barillot 2024 - 
          <a href="https://github.com/gbarillot">Github</a> | 
          <a href="https://twitter.com/gbarillot">Twitter</a>
        </div>
        <div class="col-xs-12 col-lg-4 ta-right">
          <b>Theme: </b> 
          <a href="#" @click.prevent="setTheme('light')">light</a> | 
          <a href="#" @click.prevent="setTheme('dark')">dark</a>
        </div>
      </div>
    </div>
  </footer>
</template>

<script setup>
const setTheme = ((theme) => {
  let root = document.querySelector('html');
  root.setAttribute('data-theme', theme);
  document.cookie = `theme=${theme}; path=/`;
})

const getCookie = ((name) => {
	let value = '; ' + document.cookie;
	let parts = value.split(`; ${name}=`);
	if (parts.length == 2) return parts.pop().split(';').shift();
})

onMounted(() => {
  const theme = getCookie('theme');
  theme ? setTheme(theme) : setTheme('light');
})
</script>


================================================
FILE: app/javascript/front/views/shared/_nav.vue
================================================
<template>
  <section class="top-nav">
    <div class="container">
      <div class="row">
        <div class="col-xs-8 col-sm-9">
          <nav>
            <ul>
              <li :class="activeOn(['root_path', 'musicians_path', 'musician_path'])">
                <router-link to="/" >{{ $t('nav.homepage') }}</router-link>
              </li>
              <li :class="activeOn(['pages_path'])">
                <router-link :to="{name: 'pages_path'}">{{ $t('nav.pages') }}</router-link>
              </li>
            </ul>
          </nav>
        </div>
        <div class="col-xs-4 col-sm-3">
          <select v-model="locale">
            <option v-for="locale in availableLocales" :value="locale" :key="locale">
              {{ locale.toUpperCase() }}
            </option>
          </select> 
        </div>
      </div>
    </div>

  </section>
</template>

<script setup>
const location = useRoute();
const { t } = useI18n({});

const availableLocales = ref(window.I18n.availableLocales);
const locale = ref(window.I18n.locale);

const activeOn = ((paths) => {
  return paths.includes(location.name) ? 'active' : ''
})

watch(locale, (newLocale, _oldLocale) => {
  let redirectTo = `/${newLocale}${location.path}`;
  if (newLocale === availableLocales.value[0]) {
    redirectTo = `${location.path}`;
  }
  window.location.href = redirectTo;
});
</script>


================================================
FILE: app/javascript/front/views/shared/layout.vue
================================================
<template>    
  <main>
    <nav-bar /> 
    <router-view />
  </main>  
  <bottom />
</template>

<script setup>
import NavBar from './_nav.vue';
import Bottom from './_footer.vue';
</script>


================================================
FILE: app/javascript/plugins/api.js
================================================
import Axios from "axios";

function Api() {
  const get = function (route) {
    return Axios.get(route);
  };
  const post = function (route, params) {
    return Axios.post(route, params);
  };
  const put = function (route, params) {
    return Axios.put(route, params);
  };
  const destroy = function (route) {
    return Axios.delete(route);
  };

  return { get, post, put, destroy };
}

export function createApi(args) {
  args.handler.defaults.baseURL = `/${window.I18n.prefix}api${args.namespace}/`;
  args.handler.defaults.headers.common["X-CSRF-Token"] = document
    .querySelector('meta[name="csrf-token"]')
    .getAttribute("content");
  args.handler.interceptors.response.use(
    (response) => {
      return response;
    },
    (error) => {
      switch (error.response.status) {
        case 500:
          window.location.href = "/500";
          break;
        case 401:
          alert("not authenticated");
          break;
      }

      return Promise.reject(error);
    }
  );

  return new Api();
}


================================================
FILE: app/javascript/plugins/cable.js
================================================
import { createConsumer } from "@rails/actioncable"
import mitt from 'mitt';

const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
const consumer = createConsumer(`${protocol}://${window.location.host}/cable`);
const emitter = mitt();
let channel = null;

function Cable() {}

Cable.prototype.on = function(channel, callback) {
  return emitter.on(channel, callback);
};

Cable.prototype.send = function(message) {
  channel.send({message: message})
};

Cable.prototype.install = function(app) {
  app.plugin = this;
  app.provide('cable', this)
}

export function createCable(options) {
  channel = consumer.subscriptions.create({ channel: options.channel}, {
    received(data) {    
      emitter.emit('chat', data)
    }
  })

  return new Cable();
}


================================================
FILE: app/jobs/application_job.rb
================================================
class ApplicationJob < ActiveJob::Base
  # Automatically retry jobs that encountered a deadlock
  # retry_on ActiveRecord::Deadlocked

  # Most jobs are safe to ignore if the underlying records are no longer available
  # discard_on ActiveJob::DeserializationError
end


================================================
FILE: app/mailers/application_mailer.rb
================================================
class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout "mailer"
end


================================================
FILE: app/models/application_record.rb
================================================
class ApplicationRecord < ActiveRecord::Base
  primary_abstract_class
end


================================================
FILE: app/models/concerns/.keep
================================================


================================================
FILE: app/models/musician.rb
================================================
class Musician < ApplicationRecord
  paginates_per 5

  validates_presence_of :name, :band

  enum band: [:rolling_stones, :beatles, :acdc]

  def self.ransackable_attributes(auth_object = nil)
    ["band", "name"]
  end
end


================================================
FILE: app/models/user.rb
================================================
class User < ApplicationRecord
  paginates_per 10

  devise :database_authenticatable

  validates_presence_of :email
end


================================================
FILE: app/views/admin.html.erb
================================================
<html data-theme="light">
  <head>
    <title>Boilerplate | Admin</title>
    <meta charset="UTF-8">
    <%= csrf_meta_tags %>
    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
    <link rel="stylesheet" href="/css/development/front.css">
    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />

    <script type='text/javascript'>
      const translations = <%= ui_translations(:admin) %>;
    </script>
    <script type='text/javascript'>
      I18n = {
        prefix: '<%= I18n.locale == I18n.default_locale ? "" : "#{I18n.locale}/" %>',
        locale: '<%= I18n.locale %>',
        availableLocales: <%= raw(I18n.available_locales.map {|loc| loc.to_s}) %>
      }
    </script>
  </head>
  <body>
    <div id="app"></div>
    <%= vite_javascript_tag 'admin' %>
  </body>
</html>


================================================
FILE: app/views/api/admin/musicians/edit.json.jbuilder
================================================
json.musician do
  json.id @musician.id
  json.name @musician.name
  json.band @musician.band
  json.bands Musician.bands.each do |name, _|
    json.key name
    json.name t(name, scope: 'bands')
  end
end


================================================
FILE: app/views/api/admin/musicians/index.json.jbuilder
================================================
json.musicians @musicians.each do |musician|
  json.id musician.id
  json.created_at l(musician.created_at, format: :default)
  json.name musician.name
  json.band t(musician.band, scope: 'bands')
end

json.bands Musician.bands.each do |band, key|
  json.key key
  json.name t(band, scope: 'bands')
end

json.partial! partial: '/api/admin/shared/pagination', locals: {
  kind: @musicians
}


================================================
FILE: app/views/api/admin/musicians/new.json.jbuilder
================================================
json.musician do
  json.name @musician.name
  json.bands Musician.bands.each do |name, _|
    json.key name
    json.name t(name, scope: 'bands')
  end
end


================================================
FILE: app/views/api/admin/shared/_pagination.json.jbuilder
================================================
obj = paginate(kind)
json.pagination do
  json.current obj[:current]
  json.previous obj[:previous]
  json.next obj[:next]
  json.per_page obj[:per_page]
  json.pages obj[:pages]
  json.count obj[:count]
end


================================================
FILE: app/views/api/admin/users/edit.json.jbuilder
================================================
json.user do
  json.id @user.id
  json.email @user.email
end


================================================
FILE: app/views/api/admin/users/index.json.jbuilder
================================================
json.users @users.each do |user|
  json.id user.id
  json.created_at l(user.created_at, format: :default)
  json.email user.email
end

json.partial! partial: '/api/admin/shared/pagination', locals: {
  kind: @users,
  callback: 'UserStore/index'
}


================================================
FILE: app/views/api/admin/users/new.json.jbuilder
================================================
json.user do
  json.email @user.email
end


================================================
FILE: app/views/api/musicians/index.json.jbuilder
================================================
json.musicians @musicians.each do |musician|
  json.id musician.id
  json.created_at l(musician.created_at, format: :default)
  json.name musician.name
  json.band t(musician.band, scope: 'bands')
end


================================================
FILE: app/views/api/musicians/show.json.jbuilder
================================================
json.musician do
  json.id @musician.id
  json.name @musician.name
  json.band t(@musician.band, scope: 'bands')
end


================================================
FILE: app/views/application.html.erb
================================================
<html data-theme="light">
  <head>
    <title>Boilerplate</title>
    <meta charset="UTF-8">
    <%= csrf_meta_tags %>
    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
    <link rel="stylesheet" href="/css/development/front.css">
    
    <script type="text/javascript">
      const translations = <%= ui_translations(:front) %>;
    </script>

    <script type="text/javascript">
      I18n = {
        prefix: '<%= I18n.locale == I18n.default_locale ? "" : "#{I18n.locale}/" %>',
        locale: '<%= I18n.locale %>',
        availableLocales: <%= raw(I18n.available_locales.map {|loc| loc.to_s}) %>
      }
    </script>
  </head>
  <body>
    <div id="app"></div>
    
    <%= vite_javascript_tag 'front' %>
  </body>
</html>


================================================
FILE: app/views/devise/sessions/new.html.erb
================================================
<section class="container devise">
  <div class="row">
    <div class="col-xs-12 col-sm-10 col-md-6">

      <div class="card">
        <h2><%= t('devise.sign_in') %></h2>
        <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
          <fieldset>
            <%= f.label :email %>
            <%= f.email_field :email, value: "admin@domain.com", autofocus: true, class: "uk-input" %>
          </fieldset>

          <fieldset>
            <%= f.label :password %>
            <%= f.password_field :password, value: "password", autocomplete: "off", class: "uk-input" %>
          </fieldset>

          <div class="actions">
            <%= f.submit t('devise.sign_in'), class: "button button-primary" %>
          </div>
        <% end %>
      </div>

    </div>
  </div>
</section>


================================================
FILE: app/views/layouts/devise.html.erb
================================================
<html data-theme="light">
  <head>
    <title>Boilerplate</title>
    <%= csrf_meta_tags %>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
    <link rel="stylesheet" href="/css/development/front.css">

    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
  </head>
  <body>
    <%= yield %>
  </body>
</html>


================================================
FILE: app/views/layouts/mailer.html.erb
================================================
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style>
      /* Email styles need to be inline */
    </style>
  </head>

  <body>
    <%= yield %>
  </body>
</html>


================================================
FILE: app/views/layouts/mailer.text.erb
================================================
<%= yield %>


================================================
FILE: bin/bootsnap
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'bootsnap' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("bootsnap", "bootsnap")


================================================
FILE: bin/bundle
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'bundle' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require "rubygems"

m = Module.new do
  module_function

  def invoked_as_script?
    File.expand_path($0) == File.expand_path(__FILE__)
  end

  def env_var_version
    ENV["BUNDLER_VERSION"]
  end

  def cli_arg_version
    return unless invoked_as_script? # don't want to hijack other binstubs
    return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
    bundler_version = nil
    update_index = nil
    ARGV.each_with_index do |a, i|
      if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
        bundler_version = a
      end
      next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
      bundler_version = $1
      update_index = i
    end
    bundler_version
  end

  def gemfile
    gemfile = ENV["BUNDLE_GEMFILE"]
    return gemfile if gemfile && !gemfile.empty?

    File.expand_path("../Gemfile", __dir__)
  end

  def lockfile
    lockfile =
      case File.basename(gemfile)
      when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
      else "#{gemfile}.lock"
      end
    File.expand_path(lockfile)
  end

  def lockfile_version
    return unless File.file?(lockfile)
    lockfile_contents = File.read(lockfile)
    return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
    Regexp.last_match(1)
  end

  def bundler_requirement
    @bundler_requirement ||=
      env_var_version || cli_arg_version ||
        bundler_requirement_for(lockfile_version)
  end

  def bundler_requirement_for(version)
    return "#{Gem::Requirement.default}.a" unless version

    bundler_gem_version = Gem::Version.new(version)

    requirement = bundler_gem_version.approximate_recommendation

    return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0")

    requirement += ".a" if bundler_gem_version.prerelease?

    requirement
  end

  def load_bundler!
    ENV["BUNDLE_GEMFILE"] ||= gemfile

    activate_bundler
  end

  def activate_bundler
    gem_error = activation_error_handling do
      gem "bundler", bundler_requirement
    end
    return if gem_error.nil?
    require_error = activation_error_handling do
      require "bundler/version"
    end
    return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
    warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
    exit 42
  end

  def activation_error_handling
    yield
    nil
  rescue StandardError, LoadError => e
    e
  end
end

m.load_bundler!

if m.invoked_as_script?
  load Gem.bin_path("bundler", "bundle")
end


================================================
FILE: bin/foreman
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'foreman' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("foreman", "foreman")


================================================
FILE: bin/importmap
================================================
#!/usr/bin/env ruby

require_relative "../config/application"
require "importmap/commands"


================================================
FILE: bin/irb
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'irb' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("irb", "irb")


================================================
FILE: bin/nokogiri
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'nokogiri' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("nokogiri", "nokogiri")


================================================
FILE: bin/puma
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'puma' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("puma", "puma")


================================================
FILE: bin/pumactl
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'pumactl' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("puma", "pumactl")


================================================
FILE: bin/racc
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'racc' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("racc", "racc")


================================================
FILE: bin/rackup
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'rackup' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("rack", "rackup")


================================================
FILE: bin/rails
================================================
#!/usr/bin/env ruby
APP_PATH = File.expand_path("../config/application", __dir__)
require_relative "../config/boot"
require "rails/commands"


================================================
FILE: bin/rake
================================================
#!/usr/bin/env ruby
require_relative "../config/boot"
require "rake"
Rake.application.run


================================================
FILE: bin/rdbg
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'rdbg' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("debug", "rdbg")


================================================
FILE: bin/setup
================================================
#!/usr/bin/env ruby
require "fileutils"

# path to your application root.
APP_ROOT = File.expand_path("..", __dir__)

def system!(*args)
  system(*args, exception: true)
end

FileUtils.chdir APP_ROOT do
  # This script is a way to set up or update your development environment automatically.
  # This script is idempotent, so that you can run it at any time and get an expectable outcome.
  # Add necessary setup steps to this file.

  puts "== Installing dependencies =="
  system! "gem install bundler --conservative"
  system("bundle check") || system!("bundle install")

  # puts "\n== Copying sample files =="
  # unless File.exist?("config/database.yml")
  #   FileUtils.cp "config/database.yml.sample", "config/database.yml"
  # end

  puts "\n== Preparing database =="
  system! "bin/rails db:prepare"

  puts "\n== Removing old logs and tempfiles =="
  system! "bin/rails log:clear tmp:clear"

  puts "\n== Restarting application server =="
  system! "bin/rails restart"
end


================================================
FILE: bin/spring
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'spring' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("spring", "spring")


================================================
FILE: bin/sprockets
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'sprockets' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("sprockets", "sprockets")


================================================
FILE: bin/thor
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'thor' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("thor", "thor")


================================================
FILE: bin/tilt
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'tilt' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("tilt", "tilt")


================================================
FILE: bin/vite
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'vite' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("vite_ruby", "vite")


================================================
FILE: config/application.rb
================================================
require_relative "boot"

require "rails/all"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Next
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 7.0

    # Please, add to the `ignore` list any other `lib` subdirectories that do
    # not contain `.rb` files, or that should not be reloaded or eager loaded.
    # Common ones are `templates`, `generators`, or `middleware`, for example.
    config.autoload_lib(ignore: %w(assets tasks))

    # Configuration for the application, engines, and railties goes here.
    #
    # These settings can be overridden in specific environments using the files
    # in config/environments, which are processed later.
    #
    # config.time_zone = "Central Time (US & Canada)"
    # config.eager_load_paths << Rails.root.join("extras")
  end
end


================================================
FILE: config/boot.rb
================================================
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

require "bundler/setup" # Set up gems listed in the Gemfile.
require "bootsnap/setup" # Speed up boot time by caching expensive operations.


================================================
FILE: config/cable.yml
================================================
development:
  adapter: redis
  url: redis://<%= ENV["REDIS_URL"] || 'localhost' %>:6379/1

test:
  adapter: test

production:
  adapter: redis
  url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
  channel_prefix: next_production


================================================
FILE: config/credentials.yml.enc
================================================
z50kuuEHj1CEUcKRpJWPamB52/q1tUa818uvcrvMpkdqctOJIuFJlK1CSQvI9roMpaEdrydKRM2yubgNJX5C4Bj5qsI5uW3O77bkjiSbVjjSrMOCE4iogYxF2LS9aJ3OPny29Dgx3BzAtDR9GN6FiBytcvOjYIUNTYZrbqYFLFW2zjAeb8NElTqBdjePEHWitR5813D8e4VzQqLsaM/fcbiiwXs48YyFU1vN9uPCpL+Ljn54vyFmrAox1sbjtxYIr97YNsQNvouGdpt92zzkhckcnXQ6OWv1J/thPHzGbtJO5Ej9XmtlWnUWTFAI713C7SptEmyky/Q8OdO7ueKx16dZWucPAGoLm2lFst7OvVkWLM0r6sxGM4UrJLwG9IZhwt9UdxUyCd0VM/w9w+1bSdaUGsMAWnheTeLe--LR73FGyHeOp0ATJ0--+C76DQyMTGNE1y/Y8/G92Q==

================================================
FILE: config/database.yml
================================================
# SQLite version 3.x
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem 'sqlite3'
#
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  database: db/production.sqlite3


================================================
FILE: config/environment.rb
================================================
# Load the Rails application.
require_relative "application"

# Initialize the Rails application.
Rails.application.initialize!


================================================
FILE: config/environments/development.rb
================================================
require "active_support/core_ext/integer/time"

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # In the development environment your application's code is reloaded any time
  # it changes. This slows down response time but is perfect for development
  # since you don't have to restart the web server when you make code changes.
  config.enable_reloading = true

  # Do not eager load code on boot.
  config.eager_load = false

  # Show full error reports.
  config.consider_all_requests_local = true

  # Enable server timing
  config.server_timing = true

  # Enable/disable caching. By default caching is disabled.
  # Run rails dev:cache to toggle caching.
  if Rails.root.join("tmp/caching-dev.txt").exist?
    config.action_controller.perform_caching = true
    config.action_controller.enable_fragment_cache_logging = true

    config.cache_store = :memory_store
    config.public_file_server.headers = {
      "Cache-Control" => "public, max-age=#{2.days.to_i}"
    }
  else
    config.action_controller.perform_caching = false

    config.cache_store = :null_store
  end

  # Store uploaded files on the local file system (see config/storage.yml for options).
  config.active_storage.service = :local

  # Don't care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = false

  config.action_mailer.perform_caching = false

  # Print deprecation notices to the Rails logger.
  config.active_support.deprecation = :log

  # Raise exceptions for disallowed deprecations.
  config.active_support.disallowed_deprecation = :raise

  # Tell Active Support which deprecation messages to disallow.
  config.active_support.disallowed_deprecation_warnings = []

  # Raise an error on page load if there are pending migrations.
  config.active_record.migration_error = :page_load

  # Highlight code that triggered database queries in logs.
  config.active_record.verbose_query_logs = true

  # Highlight code that enqueued background job in logs.
  config.active_job.verbose_enqueue_logs = true

  # Suppress logger output for asset requests.
  config.assets.quiet = true

  # Raises error for missing translations.
  # config.i18n.raise_on_missing_translations = true

  # Annotate rendered view with file names.
  # config.action_view.annotate_rendered_view_with_filenames = true

  # Uncomment if you wish to allow Action Cable access from any origin.
  # config.action_cable.disable_request_forgery_protection = true

  # Raise error when a before_action's only/except options reference missing actions
  config.action_controller.raise_on_missing_callback_actions = true
end


================================================
FILE: config/environments/production.rb
================================================
require "active_support/core_ext/integer/time"

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # Code is not reloaded between requests.
  config.enable_reloading = false

  # Eager load code on boot. This eager loads most of Rails and
  # your application in memory, allowing both threaded web servers
  # and those relying on copy on write to perform better.
  # Rake tasks automatically ignore this option for performance.
  config.eager_load = true

  # Full error reports are disabled and caching is turned on.
  config.consider_all_requests_local = false
  config.action_controller.perform_caching = true

  # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment
  # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files).
  # config.require_master_key = true

  # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead.
  # config.public_file_server.enabled = false

  # Compress CSS using a preprocessor.
  # config.assets.css_compressor = :sass

  # Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = false

  # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  # config.asset_host = "http://assets.example.com"

  # Specifies the header that your server uses for sending files.
  # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
  # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX

  # Store uploaded files on the local file system (see config/storage.yml for options).
  config.active_storage.service = :local

  # Mount Action Cable outside main process or domain.
  # config.action_cable.mount_path = nil
  # config.action_cable.url = "wss://example.com/cable"
  # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]

  # Assume all access to the app is happening through a SSL-terminating reverse proxy.
  # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
  # config.assume_ssl = true

  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  config.force_ssl = true

  # Log to STDOUT by default
  config.logger = ActiveSupport::Logger.new(STDOUT)
    .tap  { |logger| logger.formatter = ::Logger::Formatter.new }
    .then { |logger| ActiveSupport::TaggedLogging.new(logger) }

  # Prepend all log lines with the following tags.
  config.log_tags = [ :request_id ]

  # Info include generic and useful information about system operation, but avoids logging too much
  # information to avoid inadvertent exposure of personally identifiable information (PII). If you
  # want to log everything, set the level to "debug".
  config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")

  # Use a different cache store in production.
  # config.cache_store = :mem_cache_store

  # Use a real queuing backend for Active Job (and separate queues per environment).
  # config.active_job.queue_adapter = :resque
  # config.active_job.queue_name_prefix = "next_production"

  config.action_mailer.perform_caching = false

  # Ignore bad email addresses and do not raise email delivery errors.
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
  # config.action_mailer.raise_delivery_errors = false

  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  # the I18n.default_locale when a translation cannot be found).
  config.i18n.fallbacks = true

  # Don't log any deprecations.
  config.active_support.report_deprecations = false

  # Do not dump schema after migrations.
  config.active_record.dump_schema_after_migration = false

  # Enable DNS rebinding protection and other `Host` header attacks.
  # config.hosts = [
  #   "example.com",     # Allow requests from example.com
  #   /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
  # ]
  # Skip DNS rebinding protection for the default health check endpoint.
  # config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
end


================================================
FILE: config/environments/test.rb
================================================
require "active_support/core_ext/integer/time"

# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # While tests run files are not watched, reloading is not necessary.
  config.enable_reloading = false

  # Eager loading loads your entire application. When running a single test locally,
  # this is usually not necessary, and can slow down your test suite. However, it's
  # recommended that you enable it in continuous integration systems to ensure eager
  # loading is working properly before deploying your code.
  config.eager_load = ENV["CI"].present?

  # Configure public file server for tests with Cache-Control for performance.
  config.public_file_server.enabled = true
  config.public_file_server.headers = {
    "Cache-Control" => "public, max-age=#{1.hour.to_i}"
  }

  # Show full error reports and disable caching.
  config.consider_all_requests_local = true
  config.action_controller.perform_caching = false
  config.cache_store = :null_store

  # Render exception templates for rescuable exceptions and raise for other exceptions.
  config.action_dispatch.show_exceptions = :rescuable

  # Disable request forgery protection in test environment.
  config.action_controller.allow_forgery_protection = false

  # Store uploaded files on the local file system in a temporary directory.
  config.active_storage.service = :test

  config.action_mailer.perform_caching = false

  # Tell Action Mailer not to deliver emails to the real world.
  # The :test delivery method accumulates sent emails in the
  # ActionMailer::Base.deliveries array.
  config.action_mailer.delivery_method = :test

  # Print deprecation notices to the stderr.
  config.active_support.deprecation = :stderr

  # Raise exceptions for disallowed deprecations.
  config.active_support.disallowed_deprecation = :raise

  # Tell Active Support which deprecation messages to disallow.
  config.active_support.disallowed_deprecation_warnings = []

  # Raises error for missing translations.
  # config.i18n.raise_on_missing_translations = true

  # Annotate rendered view with file names.
  # config.action_view.annotate_rendered_view_with_filenames = true

  # Raise error when a before_action's only/except options reference missing actions
  config.action_controller.raise_on_missing_callback_actions = true
end


================================================
FILE: config/importmap.rb
================================================
# Pin npm packages by running ./bin/importmap

pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"


================================================
FILE: config/initializers/assets.rb
================================================
# Be sure to restart your server when you modify this file.

# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = "1.0"

# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path

# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# Rails.application.config.assets.precompile += %w( admin.js admin.css )


================================================
FILE: config/initializers/content_security_policy.rb
================================================
# Be sure to restart your server when you modify this file.

# Define an application-wide content security policy.
# See the Securing Rails Applications Guide for more information:
# https://guides.rubyonrails.org/security.html#content-security-policy-header

# Rails.application.configure do
#   config.content_security_policy do |policy|
#     policy.default_src :self, :https
#     policy.font_src    :self, :https, :data
#     policy.img_src     :self, :https, :data
#     policy.object_src  :none
#     policy.script_src  :self, :https
#     policy.style_src   :self, :https
#     # Specify URI for violation reports
#     # policy.report_uri "/csp-violation-report-endpoint"
#   end
#
#   # Generate session nonces for permitted importmap, inline scripts, and inline styles.
#   config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
#   config.content_security_policy_nonce_directives = %w(script-src style-src)
#
#   # Report violations without enforcing the policy.
#   # config.content_security_policy_report_only = true
# end


================================================
FILE: config/initializers/cypress_rails_initializer.rb
================================================
return unless Rails.env.test?
#require "./lib/external_service"

Rails.application.load_tasks unless defined?(Rake::Task)

CypressRails.hooks.before_server_start do
  # Add our fixtures before the resettable transaction is started
  Rake::Task["db:fixtures:load"].invoke
end

CypressRails.hooks.after_server_start do
  # Start up external service
  #ExternalService.start_service
end

CypressRails.hooks.after_transaction_start do
  # After each transaction, add this compliment (will be rolled back on reset)
  #Compliment.create(text: "You are courageous")
end

CypressRails.hooks.after_state_reset do
  # if Compliment.count != 4
  #   raise "Wait I was expecting exactly 4 compliments!"
  # end
end

CypressRails.hooks.before_server_stop do
  # Purge and reload the test database so we don't leave our fixtures in there
  Rake::Task["db:test:prepare"].invoke
end

================================================
FILE: config/initializers/devise.rb
================================================
# frozen_string_literal: true

# Assuming you have not yet modified this file, each configuration option below
# is set to its default value. Note that some are commented out while others
# are not: uncommented lines are intended to protect your configuration from
# breaking changes in upgrades (i.e., in the event that future versions of
# Devise change the default values for those options).
#
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
  # The secret key used by Devise. Devise uses this key to generate
  # random tokens. Changing this key will render invalid all existing
  # confirmation, reset password and unlock tokens in the database.
  # Devise will use the `secret_key_base` as its `secret_key`
  # by default. You can change it below and use your own secret key.
  # config.secret_key = 'fe7dbda89d8b5e2b6f07c0b413456bb1ab362afc0bf446ddfe789c0819a46d2f14a779adc845025ac3db2900b0167c7b028b400c4343a1536ae7e1695cef1a66'

  # ==> Controller configuration
  # Configure the parent class to the devise controllers.
  # config.parent_controller = 'DeviseController'

  # ==> Mailer Configuration
  # Configure the e-mail address which will be shown in Devise::Mailer,
  # note that it will be overwritten if you use your own mailer class
  # with default "from" parameter.
  config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'

  # Configure the class responsible to send e-mails.
  # config.mailer = 'Devise::Mailer'

  # Configure the parent class responsible to send e-mails.
  # config.parent_mailer = 'ActionMailer::Base'

  # ==> ORM configuration
  # Load and configure the ORM. Supports :active_record (default) and
  # :mongoid (bson_ext recommended) by default. Other ORMs may be
  # available as additional gems.
  require 'devise/orm/active_record'

  # ==> Configuration for any authentication mechanism
  # Configure which keys are used when authenticating a user. The default is
  # just :email. You can configure it to use [:username, :subdomain], so for
  # authenticating a user, both parameters are required. Remember that those
  # parameters are used only when authenticating and not when retrieving from
  # session. If you need permissions, you should implement that in a before filter.
  # You can also supply a hash where the value is a boolean determining whether
  # or not authentication should be aborted when the value is not present.
  # config.authentication_keys = [:email]

  # Configure parameters from the request object used for authentication. Each entry
  # given should be a request method and it will automatically be passed to the
  # find_for_authentication method and considered in your model lookup. For instance,
  # if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
  # The same considerations mentioned for authentication_keys also apply to request_keys.
  # config.request_keys = []

  # Configure which authentication keys should be case-insensitive.
  # These keys will be downcased upon creating or modifying a user and when used
  # to authenticate or find a user. Default is :email.
  config.case_insensitive_keys = [:email]

  # Configure which authentication keys should have whitespace stripped.
  # These keys will have whitespace before and after removed upon creating or
  # modifying a user and when used to authenticate or find a user. Default is :email.
  config.strip_whitespace_keys = [:email]

  # Tell if authentication through request.params is enabled. True by default.
  # It can be set to an array that will enable params authentication only for the
  # given strategies, for example, `config.params_authenticatable = [:database]` will
  # enable it only for database (email + password) authentication.
  # config.params_authenticatable = true

  # Tell if authentication through HTTP Auth is enabled. False by default.
  # It can be set to an array that will enable http authentication only for the
  # given strategies, for example, `config.http_authenticatable = [:database]` will
  # enable it only for database authentication.
  # For API-only applications to support authentication "out-of-the-box", you will likely want to
  # enable this with :database unless you are using a custom strategy.
  # The supported strategies are:
  # :database      = Support basic authentication with authentication key + password
  # config.http_authenticatable = false

  # If 401 status code should be returned for AJAX requests. True by default.
  # config.http_authenticatable_on_xhr = true

  # The realm used in Http Basic Authentication. 'Application' by default.
  # config.http_authentication_realm = 'Application'

  # It will change confirmation, password recovery and other workflows
  # to behave the same regardless if the e-mail provided was right or wrong.
  # Does not affect registerable.
  # config.paranoid = true

  # By default Devise will store the user in session. You can skip storage for
  # particular strategies by setting this option.
  # Notice that if you are skipping storage for all authentication paths, you
  # may want to disable generating routes to Devise's sessions controller by
  # passing skip: :sessions to `devise_for` in your config/routes.rb
  config.skip_session_storage = [:http_auth]

  # By default, Devise cleans up the CSRF token on authentication to
  # avoid CSRF token fixation attacks. This means that, when using AJAX
  # requests for sign in and sign up, you need to get a new CSRF token
  # from the server. You can disable this option at your own risk.
  # config.clean_up_csrf_token_on_authentication = true

  # When false, Devise will not attempt to reload routes on eager load.
  # This can reduce the time taken to boot the app but if your application
  # requires the Devise mappings to be loaded during boot time the application
  # won't boot properly.
  # config.reload_routes = true

  # ==> Configuration for :database_authenticatable
  # For bcrypt, this is the cost for hashing the password and defaults to 12. If
  # using other algorithms, it sets how many times you want the password to be hashed.
  # The number of stretches used for generating the hashed password are stored
  # with the hashed password. This allows you to change the stretches without
  # invalidating existing passwords.
  #
  # Limiting the stretches to just one in testing will increase the performance of
  # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
  # a value less than 10 in other environments. Note that, for bcrypt (the default
  # algorithm), the cost increases exponentially with the number of stretches (e.g.
  # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
  config.stretches = Rails.env.test? ? 1 : 12

  # Set up a pepper to generate the hashed password.
  # config.pepper = '8f57964ea0a55bf7585f2098daf8877ad19337cb2c530ffc776f792f0a13be6648060a3e0696b9652cf748a033fb512df777b2cf36f2423f6a7a4e17181c3c26'

  # Send a notification to the original email when the user's email is changed.
  # config.send_email_changed_notification = false

  # Send a notification email when the user's password is changed.
  # config.send_password_change_notification = false

  # ==> Configuration for :confirmable
  # A period that the user is allowed to access the website even without
  # confirming their account. For instance, if set to 2.days, the user will be
  # able to access the website for two days without confirming their account,
  # access will be blocked just in the third day.
  # You can also set it to nil, which will allow the user to access the website
  # without confirming their account.
  # Default is 0.days, meaning the user cannot access the website without
  # confirming their account.
  # config.allow_unconfirmed_access_for = 2.days

  # A period that the user is allowed to confirm their account before their
  # token becomes invalid. For example, if set to 3.days, the user can confirm
  # their account within 3 days after the mail was sent, but on the fourth day
  # their account can't be confirmed with the token any more.
  # Default is nil, meaning there is no restriction on how long a user can take
  # before confirming their account.
  # config.confirm_within = 3.days

  # If true, requires any email changes to be confirmed (exactly the same way as
  # initial account confirmation) to be applied. Requires additional unconfirmed_email
  # db field (see migrations). Until confirmed, new email is stored in
  # unconfirmed_email column, and copied to email column on successful confirmation.
  config.reconfirmable = true

  # Defines which key will be used when confirming an account
  # config.confirmation_keys = [:email]

  # ==> Configuration for :rememberable
  # The time the user will be remembered without asking for credentials again.
  # config.remember_for = 2.weeks

  # Invalidates all the remember me tokens when the user signs out.
  config.expire_all_remember_me_on_sign_out = true

  # If true, extends the user's remember period when remembered via cookie.
  # config.extend_remember_period = false

  # Options to be passed to the created cookie. For instance, you can set
  # secure: true in order to force SSL only cookies.
  # config.rememberable_options = {}

  # ==> Configuration for :validatable
  # Range for password length.
  config.password_length = 6..128

  # Email regex used to validate email formats. It simply asserts that
  # one (and only one) @ exists in the given string. This is mainly
  # to give user feedback and not to assert the e-mail validity.
  config.email_regexp = /\A[^@\s]+@[^@\s]+\z/

  # ==> Configuration for :timeoutable
  # The time you want to timeout the user session without activity. After this
  # time the user will be asked for credentials again. Default is 30 minutes.
  # config.timeout_in = 30.minutes

  # ==> Configuration for :lockable
  # Defines which strategy will be used to lock an account.
  # :failed_attempts = Locks an account after a number of failed attempts to sign in.
  # :none            = No lock strategy. You should handle locking by yourself.
  # config.lock_strategy = :failed_attempts

  # Defines which key will be used when locking and unlocking an account
  # config.unlock_keys = [:email]

  # Defines which strategy will be used to unlock an account.
  # :email = Sends an unlock link to the user email
  # :time  = Re-enables login after a certain amount of time (see :unlock_in below)
  # :both  = Enables both strategies
  # :none  = No unlock strategy. You should handle unlocking by yourself.
  # config.unlock_strategy = :both

  # Number of authentication tries before locking an account if lock_strategy
  # is failed attempts.
  # config.maximum_attempts = 20

  # Time interval to unlock the account if :time is enabled as unlock_strategy.
  # config.unlock_in = 1.hour

  # Warn on the last attempt before the account is locked.
  # config.last_attempt_warning = true

  # ==> Configuration for :recoverable
  #
  # Defines which key will be used when recovering the password for an account
  # config.reset_password_keys = [:email]

  # Time interval you can reset your password with a reset password key.
  # Don't put a too small interval or your users won't have the time to
  # change their passwords.
  config.reset_password_within = 6.hours

  # When set to false, does not sign a user in automatically after their password is
  # reset. Defaults to true, so a user is signed in automatically after a reset.
  # config.sign_in_after_reset_password = true

  # ==> Configuration for :encryptable
  # Allow you to use another hashing or encryption algorithm besides bcrypt (default).
  # You can use :sha1, :sha512 or algorithms from others authentication tools as
  # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20
  # for default behavior) and :restful_authentication_sha1 (then you should set
  # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).
  #
  # Require the `devise-encryptable` gem when using anything other than bcrypt
  # config.encryptor = :sha512

  # ==> Scopes configuration
  # Turn scoped views on. Before rendering "sessions/new", it will first check for
  # "users/sessions/new". It's turned off by default because it's slower if you
  # are using only default views.
  # config.scoped_views = false

  # Configure the default scope given to Warden. By default it's the first
  # devise role declared in your routes (usually :user).
  # config.default_scope = :user

  # Set this configuration to false if you want /users/sign_out to sign out
  # only the current scope. By default, Devise signs out all scopes.
  # config.sign_out_all_scopes = true

  # ==> Navigation configuration
  # Lists the formats that should be treated as navigational. Formats like
  # :html, should redirect to the sign in page when the user does not have
  # access, but formats like :xml or :json, should return 401.
  #
  # If you have any extra navigational formats, like :iphone or :mobile, you
  # should add them to the navigational formats lists.
  #
  # The "*/*" below is required to match Internet Explorer requests.
  # config.navigational_formats = ['*/*', :html]

  # The default HTTP method used to sign out a resource. Default is :delete.
  config.sign_out_via = :get

  # ==> OmniAuth
  # Add a new OmniAuth provider. Check the wiki for more information on setting
  # up on your models and hooks.
  # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'

  # ==> Warden configuration
  # If you want to use other strategies, that are not supported by Devise, or
  # change the failure app, you can configure them inside the config.warden block.
  #
  # config.warden do |manager|
  #   manager.intercept_401 = false
  #   manager.default_strategies(scope: :user).unshift :some_external_strategy
  # end

  # ==> Mountable engine configurations
  # When using Devise inside an engine, let's call it `MyEngine`, and this engine
  # is mountable, there are some extra configurations to be taken into account.
  # The following options are available, assuming the engine is mounted as:
  #
  #     mount MyEngine, at: '/my_engine'
  #
  # The router that invoked `devise_for`, in the example above, would be:
  # config.router_name = :my_engine
  #
  # When using OmniAuth, Devise cannot automatically set OmniAuth path,
  # so you need to do it manually. For the users scope, it would be:
  # config.omniauth_path_prefix = '/my_engine/users/auth'

  # ==> Turbolinks configuration
  # If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly:
  #
  # ActiveSupport.on_load(:devise_failure_app) do
  #   include Turbolinks::Controller
  # end

  # ==> Configuration for :registerable

  # When set to false, does not sign a user in automatically after their password is
  # changed. Defaults to true, so a user is signed in automatically after changing a password.
  # config.sign_in_after_change_password = true
end


================================================
FILE: config/initializers/filter_parameter_logging.rb
================================================
# Be sure to restart your server when you modify this file.

# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
# Use this to limit dissemination of sensitive information.
# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
Rails.application.config.filter_parameters += [
  :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
]


================================================
FILE: config/initializers/inflections.rb
================================================
# Be sure to restart your server when you modify this file.

# Add new inflection rules using the following format. Inflections
# are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
#   inflect.plural /^(ox)$/i, "\\1en"
#   inflect.singular /^(ox)en/i, "\\1"
#   inflect.irregular "person", "people"
#   inflect.uncountable %w( fish sheep )
# end

# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
#   inflect.acronym "RESTful"
# end


================================================
FILE: config/initializers/locales.rb
================================================
I18n.available_locales = [:en, :fr]

================================================
FILE: config/initializers/new_framework_defaults_7_1.rb
================================================
# Be sure to restart your server when you modify this file.
#
# This file eases your Rails 7.1 framework defaults upgrade.
#
# Uncomment each configuration one by one to switch to the new default.
# Once your application is ready to run with all new defaults, you can remove
# this file and set the `config.load_defaults` to `7.1`.
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html

###
# No longer add autoloaded paths into `$LOAD_PATH`. This means that you won't be able
# to manually require files that are managed by the autoloader, which you shouldn't do anyway.
#
# This will reduce the size of the load path, making `require` faster if you don't use bootsnap, or reduce the size
# of the bootsnap cache if you use it.
#++
# Rails.application.config.add_autoload_paths_to_load_path = false

###
# Remove the default X-Download-Options headers since it is used only by Internet Explorer.
# If you need to support Internet Explorer, add back `"X-Download-Options" => "noopen"`.
#++
# Rails.application.config.action_dispatch.default_headers = {
#   "X-Frame-Options" => "SAMEORIGIN",
#   "X-XSS-Protection" => "0",
#   "X-Content-Type-Options" => "nosniff",
#   "X-Permitted-Cross-Domain-Policies" => "none",
#   "Referrer-Policy" => "strict-origin-when-cross-origin"
# }

###
# Do not treat an `ActionController::Parameters` instance
# as equal to an equivalent `Hash` by default.
#++
# Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality = false

###
# Active Record Encryption now uses SHA-256 as its hash digest algorithm.
# 
# There are 3 scenarios to consider.
#
# 1. If you have data encrypted with previous Rails versions, and you have
# +config.active_support.key_generator_hash_digest_class+ configured as SHA1 (the default
# before Rails 7.0), you need to configure SHA-1 for Active Record Encryption too:
#++
# Rails.application.config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA1
#
# 2. If you have +config.active_support.key_generator_hash_digest_class+ configured as SHA256 (the new default
# in 7.0), then you need to configure SHA-256 for Active Record Encryption:
#++
# Rails.application.config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA256
#
# 3. If you don't currently have data encrypted with Active Record encryption, you can disable this setting to
# configure the default behavior starting 7.1+:
#++
# Rails.application.config.active_record.encryption.support_sha1_for_non_deterministic_encryption = false

###
# No longer run after_commit callbacks on the first of multiple Active Record
# instances to save changes to the same database row within a transaction.
# Instead, run these callbacks on the instance most likely to have internal
# state which matches what was committed to the database, typically the last
# instance to save.
#++
# Rails.application.config.active_record.run_commit_callbacks_on_first_saved_instances_in_transaction = false

###
# Configures SQLite with a strict strings mode, which disables double-quoted string literals.
#
# SQLite has some quirks around double-quoted string literals.
# It first tries to consider double-quoted strings as identifier names, but if they don't exist
# it then considers them as string literals. Because of this, typos can silently go unnoticed.
# For example, it is possible to create an index for a non existing column.
# See https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted for more details.
#++
# Rails.application.config.active_record.sqlite3_adapter_strict_strings_by_default = true

###
# Disable deprecated singular associations names.
#++
# Rails.application.config.active_record.allow_deprecated_singular_associations_name = false

###
# Enable the Active Job `BigDecimal` argument serializer, which guarantees
# roundtripping. Without this serializer, some queue adapters may serialize
# `BigDecimal` arguments as simple (non-roundtrippable) strings.
#
# When deploying an application with multiple replicas, old (pre-Rails 7.1)
# replicas will not be able to deserialize `BigDecimal` arguments from this
# serializer. Therefore, this setting should only be enabled after all replicas
# have been successfully upgraded to Rails 7.1.
#++
# Rails.application.config.active_job.use_big_decimal_serializer = true

###
# Specify if an `ArgumentError` should be raised if `Rails.cache` `fetch` or
# `write` are given an invalid `expires_at` or `expires_in` time.
# Options are `true`, and `false`. If `false`, the exception will be reported
# as `handled` and logged instead.
#++
# Rails.application.config.active_support.raise_on_invalid_cache_expiration_time = true

###
# Specify whether Query Logs will format tags using the SQLCommenter format
# (https://open-telemetry.github.io/opentelemetry-sqlcommenter/), or using the legacy format.
# Options are `:legacy` and `:sqlcommenter`.
#++
# Rails.application.config.active_record.query_log_tags_format = :sqlcommenter

###
# Specify the default serializer used by `MessageEncryptor` and `MessageVerifier`
# instances.
#
# The legacy default is `:marshal`, which is a potential vector for
# deserialization attacks in cases where a message signing secret has been
# leaked.
#
# In Rails 7.1, the new default is `:json_allow_marshal` which serializes and
# deserializes with `ActiveSupport::JSON`, but can fall back to deserializing
# with `Marshal` so that legacy messages can still be read.
#
# In Rails 7.2, the default will become `:json` which serializes and
# deserializes with `ActiveSupport::JSON` only.
#
# Alternatively, you can choose `:message_pack` or `:message_pack_allow_marshal`,
# which serialize with `ActiveSupport::MessagePack`. `ActiveSupport::MessagePack`
# can roundtrip some Ruby types that are not supported by JSON, and may provide
# improved performance, but it requires the `msgpack` gem.
#
# For more information, see
# https://guides.rubyonrails.org/v7.1/configuring.html#config-active-support-message-serializer
#
# If you are performing a rolling deploy of a Rails 7.1 upgrade, wherein servers
# that have not yet been upgraded must be able to read messages from upgraded
# servers, first deploy without changing the serializer, then set the serializer
# in a subsequent deploy.
#++
# Rails.application.config.active_support.message_serializer = :json_allow_marshal

###
# Enable a performance optimization that serializes message data and metadata
# together. This changes the message format, so messages serialized this way
# cannot be read by older versions of Rails. However, messages that use the old
# format can still be read, regardless of whether this optimization is enabled.
#
# To perform a rolling deploy of a Rails 7.1 upgrade, wherein servers that have
# not yet been upgraded must be able to read messages from upgraded servers,
# leave this optimization off on the first deploy, then enable it on a
# subsequent deploy.
#++
# Rails.application.config.active_support.use_message_serializer_for_metadata = true

###
# Set the maximum size for Rails log files.
#
# `config.load_defaults 7.1` does not set this value for environments other than
# development and test.
#++
# if Rails.env.local?
#   Rails.application.config.log_file_size = 100 * 1024 * 1024
# end

###
# Enable raising on assignment to attr_readonly attributes. The previous
# behavior would allow assignment but silently not persist changes to the
# database.
#++
# Rails.application.config.active_record.raise_on_assign_to_attr_readonly = true

###
# Enable validating only parent-related columns for presence when the parent is mandatory.
# The previous behavior was to validate the presence of the parent record, which performed an extra query
# to get the parent every time the child record was updated, even when parent has not changed.
#++
# Rails.application.config.active_record.belongs_to_required_validates_foreign_key = false

###
# Enable precompilation of `config.filter_parameters`. Precompilation can
# improve filtering performance, depending on the quantity and types of filters.
#++
# Rails.application.config.precompile_filter_parameters = true

###
# Enable before_committed! callbacks on all enrolled records in a transaction.
# The previous behavior was to only run the callbacks on the first copy of a record
# if there were multiple copies of the same record enrolled in the transaction.
#++
# Rails.application.config.active_record.before_committed_on_all_records = true

###
# Disable automatic column serialization into YAML.
# To keep the historic behavior, you can set it to `YAML`, however it is
# recommended to explicitly define the serialization method for each column
# rather than to rely on a global default.
#++
# Rails.application.config.active_record.default_column_serializer = nil

###
# Enable a performance optimization that serializes Active Record models
# in a faster and more compact way.
#
# To perform a rolling deploy of a Rails 7.1 upgrade, wherein servers that have
# not yet been upgraded must be able to read caches from upgraded servers,
# leave this optimization off on the first deploy, then enable it on a
# subsequent deploy.
#++
# Rails.application.config.active_record.marshalling_format_version = 7.1

###
# Run `after_commit` and `after_*_commit` callbacks in the order they are defined in a model.
# This matches the behaviour of all other callbacks.
# In previous versions of Rails, they ran in the inverse order.
#++
# Rails.application.config.active_record.run_after_transaction_callbacks_in_order_defined = true

###
# Whether a `transaction` block is committed or rolled back when exited via `return`, `break` or `throw`.
#++
# Rails.application.config.active_record.commit_transaction_on_non_local_return = true

###
# Controls when to generate a value for <tt>has_secure_token</tt> declarations.
#++
# Rails.application.config.active_record.generate_secure_token_on = :initialize

###
# ** Please read carefully, this must be configured in config/application.rb **
#
# Change the format of the cache entry.
#
# Changing this default means that all new cache entries added to the cache
# will have a different format that is not supported by Rails 7.0
# applications.
#
# Only change this value after your application is fully deployed to Rails 7.1
# and you have no plans to rollback.
# When you're ready to change format, add this to `config/application.rb` (NOT
# this file):
#   config.active_support.cache_format_version = 7.1


###
# Configure Action View to use HTML5 standards-compliant sanitizers when they are supported on your
# platform.
#
# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action View to use HTML5-compliant
# sanitizers if they are supported, else fall back to HTML4 sanitizers.
#
# In previous versions of Rails, Action View always used `Rails::HTML4::Sanitizer` as its vendor.
#++
# Rails.application.config.action_view.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor


###
# Configure Action Text to use an HTML5 standards-compliant sanitizer when it is supported on your
# platform.
#
# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action Text to use HTML5-compliant
# sanitizers if they are supported, else fall back to HTML4 sanitizers.
#
# In previous versions of Rails, Action Text always used `Rails::HTML4::Sanitizer` as its vendor.
#++
# Rails.application.config.action_text.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor


###
# Configure the log level used by the DebugExceptions middleware when logging
# uncaught exceptions during requests.
#++
# Rails.application.config.action_dispatch.debug_exception_log_level = :error


###
# Configure the test helpers in Action View, Action Dispatch, and rails-dom-testing to use HTML5
# parsers.
#
# Nokogiri::HTML5 isn't supported on JRuby, so JRuby applications must set this to :html4.
#
# In previous versions of Rails, these test helpers always used an HTML4 parser.
#++
# Rails.application.config.dom_testing_default_html_version = :html5


================================================
FILE: config/initializers/permissions_policy.rb
================================================
# Be sure to restart your server when you modify this file.

# Define an application-wide HTTP permissions policy. For further
# information see: https://developers.google.com/web/updates/2018/06/feature-policy

# Rails.application.config.permissions_policy do |policy|
#   policy.camera      :none
#   policy.gyroscope   :none
#   policy.microphone  :none
#   policy.usb         :none
#   policy.fullscreen  :self
#   policy.payment     :self, "https://secure.example.com"
# end


================================================
FILE: config/locales/devise.en.yml
================================================
# Additional translations at https://github.com/plataformatec/devise/wiki/I18n

en:
  devise:
    confirmations:
      confirmed: "Your email address has been successfully confirmed."
      send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
      send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
    failure:
      already_authenticated: "You are already signed in."
      inactive: "Your account is not activated yet."
      invalid: "Invalid %{authentication_keys} or password."
      locked: "Your account is locked."
      last_attempt: "You have one more attempt before your account is locked."
      not_found_in_database: "Invalid %{authentication_keys} or password."
      timeout: "Your session expired. Please sign in again to continue."
      unauthenticated: "You need to sign in or sign up before continuing."
      unconfirmed: "You have to confirm your email address before continuing."
    mailer:
      confirmation_instructions:
        subject: "Confirmation instructions"
      reset_password_instructions:
        subject: "Reset password instructions"
      unlock_instructions:
        subject: "Unlock instructions"
      email_changed:
        subject: "Email Changed"
      password_change:
        subject: "Password Changed"
    omniauth_callbacks:
      failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
      success: "Successfully authenticated from %{kind} account."
    passwords:
      no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
      send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
      send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
      updated: "Your password has been changed successfully. You are now signed in."
      updated_not_active: "Your password has been changed successfully."
    registrations:
      destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
      signed_up: "Welcome! You have signed up successfully."
      signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
      signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
      signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
      update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address."
      updated: "Your account has been updated successfully."
    sessions:
      signed_in: "Signed in successfully."
      signed_out: "Signed out successfully."
      already_signed_out: "Signed out successfully."
    unlocks:
      send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
      send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
      unlocked: "Your account has been unlocked successfully. Please sign in to continue."
  errors:
    messages:
      already_confirmed: "was already confirmed, please try signing in"
      confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
      expired: "has expired, please request a new one"
      not_found: "not found"
      not_locked: "was not locked"
      not_saved:
        one: "1 error prohibited this %{resource} from being saved:"
        other: "%{count} errors prohibited this %{resource} from being saved:"


================================================
FILE: config/locales/en.yml
================================================
en:
  time:
    formats:
      default: "%B %d %Y %H:%M"
  bands:
    rolling_stones: "The Rolling Stones"
    beatles: "The Beatles"
    acdc: "AC/DC"
  devise:
    sign_in: "Sign in"
  vue:
    front:
      title: "Front end"
      nav:
        homepage: "Home"
        pages: "Pages"
      home:
        title: "Musicians"
        breadcrumb: "All musicians"        
      pages:
        title: "Pages"
        server_404: "This link triggers a Server side routing 404"
        client_404: "This link triggers a Client side routing 404"
        server_401: "This link performs an API call that responds a 401"
        server_500: "This link performs an API call that responds a 500"
        admin_link: "» Wanna go to the admin?"
      musicians:
        title: "Details about this musician"
        id: "Id"
        name: "Name"
        band: "Band"        
      errors:
        title: "Error pages"
    admin:
      title: "Admin"
      save: "Save"
      cancel: "Cancel"
      filter: "Filter"
      reset_filters: "Reset filters"
      delete: "Delete"
      no_result: "No result :-/"
      any: "Any"
      nav:
        dashboard: "Dashboard"
        musicians: "Musicians"  
        websockets: "Websockets"   
        logout: "Logout"
      dashboard:
        title: "Dashboard"
        musicians: "No musician | 1 musician | musicians" 
      musicians:
        create: "+ Add a new musician"
        new: "New musician"
        form:
          id: "Id"
          name: "Name"
          band: "Band"
        comment: "This whole CRUD section is deliberatly slowed down so you can actually see the animations. Comment out the \"slow\" method in Api::Admin::MusiciansController to use the app at full speed"     
      confirm: "Are you sure?"
      websockets: 
        placeholder: "Type your message..."
        publish: "Publish"
        comment1: "You can also publish straight out from the Rails console:"
        # Beware of string containing: {} @ $ |
        # See: https://vue-i18n.intlify.dev/guide/essentials/syntax#special-characters
        code_example: "{'ActionCable.server.broadcast(\"ChatChannel\", { message: \"hey!\" })'}"
        comment2: "All messages are upcased"
        server_side: " server side. "
        comment3: "If you open up multiple tabs, you messsages will be upcased in each of these tabs"
        waiting_messages: "Waiting for messages..."
  activerecord:
    attributes:        
      user:
        email: "Email address"
        password: "Password"
    errors:
      models:
        musician:
          attributes:
            name:
              blank: "Mandatory field"
            band:
              blank: "Please select a band"

================================================
FILE: config/locales/fr.yml
================================================
fr:
  time:
    formats:
      default: "%B %d %Y %H:%M"
  bands:
    rolling_stones: "The Rolling Stones"
    beatles: "The Beatles"
    acdc: "AC/DC"
  devise:
    sign_in: "Authentification"
  vue:
    front:
      title: "Front end"
      nav:
        homepage: "Accueil"
        pages: "Pages"
      home:
        title: "Musiciens"
        breadcrumb: "Tous les musiciens"        
      pages:
        title: "Pages"
        server_404: "Ce lien provoque une 404 venant du routage serveur"
        client_404: "Ce lien provoque un 404 venant du routage côté client"
        server_401: "Ce lien appelle l'API qui lui répond une 401"
        server_500: "Ce lien appelle l'API qui lui répond une 500"
        admin_link: "» Envie de voir l'admin ?"
      musicians:
        title: "Details concernant ce musicien"
        id: "Id"
        name: "Nom"
        band: "Groupe"        
      errors:
        title: "Pages d'erreurs"
    admin:
      title: "Admin"
      save: "Enregistrer"
      cancel: "Annuler"
      filter: "Filtrer"
      reset_filters: "Reseter les filtres"
      delete: "Supprimer"
      no_result: "Aucun resultat :-/"
      any: "Tous"
      nav:
        dashboard: "Dashboard"
        musicians: "Musiciens"
        websockets: "Websockets"      
        logout: "Déconnection"
      dashboard:
        title: "Dashboard"
        musicians: "Aucun musician | Un musicien | musiciens" 
      musicians:
        create: "+ Ajouter un musicien"
        new: "Nouveau musicien"        
        form:
          id: "Id"
          name: "Nom"
          band: "Groupe"   
        comment: "This whole CRUD section is deliberatly slowed down so you can actually see the animations. Comment out the \"slow\" method in Api::Admin::MusiciansController to use the app at full speed"     
      confirm: "Etes-vous sur ?"
      websockets: 
        placeholder: "Tapez un message..."
        publish: "Publier"
        comment1: "Vous pouvez aussi publier un message depuis la console Rails :"
        # Beware of string containing: {} @ $ |
        # See: https://vue-i18n.intlify.dev/guide/essentials/syntax#special-characters
        code_example: "{'ActionCable.server.broadcast(\"ChatChannel\", { message: \"hey!\" })'}"
        comment2: "Tous les messages que vous publiez sont passés en majuscule"
        server_side: " côté serveur. "
        comment3: "Si vous ouvrez plusieurs onglets, les messages seront en majuscules sur chacun d'eux."
        waiting_messages: "En attente de messages..."
  activerecord:
    attributes:        
      user:
        email: "Adresse Email"
        password: "Mot de passe"
    errors:
      models:
        musician:
          attributes:
            name:
              blank: "Saisie requise"
            band:
              blank: "Merci de sélectionner un groupe"

================================================
FILE: config/puma.rb
================================================
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

# Specifies the `worker_timeout` threshold that Puma will use to wait before
# terminating a worker in development environments.
#
worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port ENV.fetch("PORT") { 3000 }

# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }

# Specifies the `pidfile` that Puma will use.
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!

# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart


================================================
FILE: config/routes.rb
================================================
Rails.application.routes.draw do
  mount ActionCable.server => '/cable'

  localized do
    devise_for :users, only: [:sessions]

    namespace :api, :defaults => { :format => 'json' } do
      resources :musicians, only: [:index, :show]

      namespace :admin do
        resources :dashboard, only: :index
        resources :musicians, except: :show
      end
    end

    get '/admin', to: 'admin#index', as: 'admin_root'
    match "/admin/*path", to: "admin#index", format: false, via: :get

    root :to => "application#index"
    match "*path", to: "application#index", format: false, via: :get
  end
end


================================================
FILE: config/storage.yml
================================================
test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# amazon:
#   service: S3
#   access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
#   secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
#   region: us-east-1
#   bucket: your_own_bucket-<%= Rails.env %>

# Remember not to checkin your GCS keyfile to a repository
# google:
#   service: GCS
#   project: your_project
#   credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
#   bucket: your_own_bucket-<%= Rails.env %>

# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
# microsoft:
#   service: AzureStorage
#   storage_account_name: your_account_name
#   storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
#   container: your_container_name-<%= Rails.env %>

# mirror:
#   service: Mirror
#   primary: local
#   mirrors: [ amazon, google, microsoft ]


================================================
FILE: config/vite.json
================================================
{
  "all": {
    "sourceCodeDir": "app/javascript",
    "watchAdditionalPaths": []
  },
  "development": {
    "autoBuild": true,
    "publicOutputDir": "vite-dev",
    "port": 3036,
    "host": "0.0.0.0"
  },
  "test": {
    "autoBuild": true,
    "publicOutputDir": "vite-test",
    "port": 3037
  }
}


================================================
FILE: config.ru
================================================
# This file is used by Rack-based servers to start the application.

require_relative "config/environment"

run Rails.application
Rails.application.load_server


================================================
FILE: cypress/e2e/home.cy.js
================================================
describe('Main navigation', () => {
  it('passes', () => {
    cy.visit('http://localhost:5100')
    cy.get('.row > :nth-child(1) > a').click();
    cy.get('.breadcrumb > :nth-child(1) > a').click();
    cy.get('ul > :nth-child(2) > a').click();
    cy.get(':nth-child(1) > a').click();
    cy.get('.row > :nth-child(1) > a').click();
    cy.get('p').click();
    cy.get('p').should('have.text', 'Id: 1Name: John LennonBand: The Beatles');
    cy.get('.active > a').click();
    cy.get('ul > :nth-child(2) > a').click();
    cy.get('section.container > :nth-child(2) > a').should('have.text', 'This link triggers a Server side routing 404');
    cy.get('section.container > :nth-child(2) > a').click();
    cy.get(':nth-child(1) > a').click();
    cy.get('h1').should('have.text', 'Musicians');
    cy.visit('http://localhost:5100/');
    cy.get('select').select('fr');
    cy.get('h1').click();
    cy.get('h1').should('have.text', 'Musiciens');
    cy.get('span').click();
    cy.get('span').should('have.text', 'Tous les musiciens');
    cy.get('select').select('en');
    cy.get('.row > :nth-child(1) > a').click();
    cy.get('.breadcrumb > :nth-child(1) > a').click();  
  })
})

================================================
FILE: cypress/support/e2e.js
================================================


================================================
FILE: cypress.config.js
================================================
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    experimentalStudio: true,
    setupNodeEvents(on, config) {
      on('before:browser:launch', (browser = {}, launchOptions) => {
        /* ... */
      })
    },
  },
  screenshotsFolder: "tmp/cypress_screenshots",
  videosFolder: "tmp/cypress_videos",
  trashAssetsBeforeRuns: false
})


================================================
FILE: db/migrate/20220424120800_base_setup.rb
================================================
class BaseSetup < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.timestamps
      t.string :email
    end

    create_table :musicians do |t|
      t.timestamps
      t.string :name
      t.integer :band
    end

    change_table :users do |t|
      ## Database authenticatable
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
  end
end


================================================
FILE: db/migrate/20240102193807_add_service_name_to_active_storage_blobs.active_storage.rb
================================================
# This migration comes from active_storage (originally 20190112182829)
class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
  def up
    return unless table_exists?(:active_storage_blobs)

    unless column_exists?(:active_storage_blobs, :service_name)
      add_column :active_storage_blobs, :service_name, :string

      if configured_service = ActiveStorage::Blob.service.name
        ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
      end

      change_column :active_storage_blobs, :service_name, :string, null: false
    end
  end

  def down
    return unless table_exists?(:active_storage_blobs)

    remove_column :active_storage_blobs, :service_name
  end
end


================================================
FILE: db/migrate/20240102193808_create_active_storage_variant_records.active_storage.rb
================================================
# This migration comes from active_storage (originally 20191206030411)
class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
  def change
    return unless table_exists?(:active_storage_blobs)

    # Use Active Record's configured type for primary key
    create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t|
      t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type
      t.string :variation_digest, null: false

      t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
      t.foreign_key :active_storage_blobs, column: :blob_id
    end
  end

  private
    def primary_key_type
      config = Rails.configuration.generators
      config.options[config.orm][:primary_key_type] || :primary_key
    end

    def blobs_primary_key_type
      pkey_name = connection.primary_key(:active_storage_blobs)
      pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name }
      pkey_column.bigint? ? :bigint : pkey_column.type
    end
end


================================================
FILE: db/migrate/20240102193809_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
================================================
# This migration comes from active_storage (originally 20211119233751)
class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]
  def change
    return unless table_exists?(:active_storage_blobs)

    change_column_null(:active_storage_blobs, :checksum, true)
  end
end


================================================
FILE: db/schema.rb
================================================
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2024_01_02_193809) do
  create_table "musicians", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "name"
    t.integer "band"
  end

  create_table "users", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "email"
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer "sign_in_count", default: 0, null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string "current_sign_in_ip"
    t.string "last_sign_in_ip"
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

end


================================================
FILE: db/seeds.rb
================================================
User.create!(email: "admin@domain.com", password: "password", password_confirmation: "password")
Musician.create!(name: "John Lennon", band: "beatles")
Musician.create!(name: "Paul McCartney", band: "beatles")
Musician.create!(name: "Georges Harrison", band: "beatles")
Musician.create!(name: "Ringo Starr", band: "beatles")
Musician.create!(name: "Mick Jagger", band: "rolling_stones")
Musician.create!(name: "Keith Richards", band: "rolling_stones")
Musician.create!(name: "Mick Taylor", band: "rolling_stones")
Musician.create!(name: "Bill Wyman", band: "rolling_stones")
Musician.create!(name: "Charlie Watts", band: "rolling_stones")
Musician.create!(name: "Angus Young", band: "acdc")
Musician.create!(name: "Malcom Young", band: "acdc")
Musician.create!(name: "Bon Scott", band: "acdc")
Musician.create!(name: "Phil Rudd", band: "acdc")


================================================
FILE: docker-compose.yml
================================================
version: "3.9"

services:
  redis:
    image: 'redis'
    restart: always
    command: redis-server
    ports:
      - 6379:6379
    environment:
      - ALLOW_EMPTY_PASSWORD=yes

  web:
    build:
      context: .
    image: 'rails-vue-demo-app'
    working_dir: /app
    command: sh -c "yarn install && rm -rf tmp/pids && export REDIS_URL='redis' && foreman start"
    volumes:
      - type: bind
        source: .
        target: /app
    ports:
      - 3000:3000
      - 3036:3036

================================================
FILE: lib/assets/.keep
================================================


================================================
FILE: lib/tasks/.keep
================================================


================================================
FILE: log/.keep
================================================


================================================
FILE: package.json
================================================
{
  "type": "module",
  "devDependencies": {
    "cypress": "^13.6.0",
    "eslint": "^8.7.0",
    "lightningcss-cli": "^1.22.1",
    "prettier": "^2.5.1",
    "vite": "^5.0.0",
    "vite-plugin-ruby": "^5.0.0",
    "vue-eslint-parser": "^8.0.0"
  },
  "dependencies": {
    "@rails/actioncable": "^7.0.2-4",
    "@vitejs/plugin-vue": "^2.3.1",
    "axios": "^1.6.2",
    "lightningcss": "^1.22.1",
    "mitt": "^3.0.1",
    "pinia": "^2.0.13",
    "rollup": "^4.9.5",
    "unplugin-auto-import": "^0.17.2",
    "vue": "^3.2.33",
    "vue-axios": "^3.4.1",
    "vue-i18n": "^9.1.9",
    "vue-router": "^4.0.14",
    "yarn": "^1.22.21"
  }
}


================================================
FILE: public/404.html
================================================
<!DOCTYPE html>
<html>
<head>
  <title>The page you were looking for doesn't exist (404)</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <style>
  .rails-default-error-page {
    background-color: #EFEFEF;
    color: #2E2F30;
    text-align: center;
    font-family: arial, sans-serif;
    margin: 0;
  }

  .rails-default-error-page div.dialog {
    width: 95%;
    max-width: 33em;
    margin: 4em auto 0;
  }

  .rails-default-error-page div.dialog > div {
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #BBB;
    border-top: #B00100 solid 4px;
    border-top-left-radius: 9px;
    border-top-right-radius: 9px;
    background-color: white;
    padding: 7px 12% 0;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }

  .rails-default-error-page h1 {
    font-size: 100%;
    color: #730E15;
    line-height: 1.5em;
  }

  .rails-default-error-page div.dialog > p {
    margin: 0 0 1em;
    padding: 1em;
    background-color: #F7F7F7;
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #999;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    border-top-color: #DADADA;
    color: #666;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }
  </style>
</head>

<body class="rails-default-error-page">
  <!-- This file lives in public/404.html -->
  <div class="dialog">
    <div>
      <h1>The page you were looking for doesn't exist.</h1>
      <p>You may have mistyped the address or the page may have moved.</p>
    </div>
    <p>If you are the application owner check the logs for more information.</p>
  </div>
</body>
</html>


================================================
FILE: public/422.html
================================================
<!DOCTYPE html>
<html>
<head>
  <title>The change you wanted was rejected (422)</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <style>
  .rails-default-error-page {
    background-color: #EFEFEF;
    color: #2E2F30;
    text-align: center;
    font-family: arial, sans-serif;
    margin: 0;
  }

  .rails-default-error-page div.dialog {
    width: 95%;
    max-width: 33em;
    margin: 4em auto 0;
  }

  .rails-default-error-page div.dialog > div {
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #BBB;
    border-top: #B00100 solid 4px;
    border-top-left-radius: 9px;
    border-top-right-radius: 9px;
    background-color: white;
    padding: 7px 12% 0;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }

  .rails-default-error-page h1 {
    font-size: 100%;
    color: #730E15;
    line-height: 1.5em;
  }

  .rails-default-error-page div.dialog > p {
    margin: 0 0 1em;
    padding: 1em;
    background-color: #F7F7F7;
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #999;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    border-top-color: #DADADA;
    color: #666;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }
  </style>
</head>

<body class="rails-default-error-page">
  <!-- This file lives in public/422.html -->
  <div class="dialog">
    <div>
      <h1>The change you wanted was rejected.</h1>
      <p>Maybe you tried to change something you didn't have access to.</p>
    </div>
    <p>If you are the application owner check the logs for more information.</p>
  </div>
</body>
</html>


================================================
FILE: public/500.html
================================================
<!DOCTYPE html>
<html>
<head>
  <title>We're sorry, but something went wrong (500)</title>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <style>
  .rails-default-error-page {
    background-color: #EFEFEF;
    color: #2E2F30;
    text-align: center;
    font-family: arial, sans-serif;
    margin: 0;
  }

  .rails-default-error-page div.dialog {
    width: 95%;
    max-width: 33em;
    margin: 4em auto 0;
  }

  .rails-default-error-page div.dialog > div {
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #BBB;
    border-top: #B00100 solid 4px;
    border-top-left-radius: 9px;
    border-top-right-radius: 9px;
    background-color: white;
    padding: 7px 12% 0;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }

  .rails-default-error-page h1 {
    font-size: 100%;
    color: #730E15;
    line-height: 1.5em;
  }

  .rails-default-error-page div.dialog > p {
    margin: 0 0 1em;
    padding: 1em;
    background-color: #F7F7F7;
    border: 1px solid #CCC;
    border-right-color: #999;
    border-left-color: #999;
    border-bottom-color: #999;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    border-top-color: #DADADA;
    color: #666;
    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
  }
  </style>
</head>

<body class="rails-default-error-page">
  <!-- This file lives in public/500.html -->
  <div class="dialog">
    <div>
      <h1>We're sorry, but something went wrong.</h1>
    </div>
    <p>If you are the application owner check the logs for more information.</p>
  </div>
</body>
</html>


================================================
FILE: public/css/development/admin/forms.css
================================================
.loading:not(form) * {
  display: none;
}
.loading:not(form) {
  width: 100%;
  height: 40px;
  margin: 100px auto 100px auto;
  position: relative;
  &::before, &::after{
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 4em;
    height: 4em;
    border-radius: 50%;
    transform: translate(-50%, -50%) scale(0);
  }
  &::before {
    background: #2c6ed1;
    animation: pulse 2s ease-in-out infinite;
  }
  &::after {
    background: #2c6ed1;
    animation: pulse 2s 1s ease-in-out infinite;
  }  
}

@keyframes pulse {
  0%, 100%{
    transform: translate(-50%, -50%) scale(0);
    opacity: 1;
  }
  50%{
    transform: translate(-50%, -50%) scale(1.0);
    opacity: 0;
  }
}

form {
  position: relative;
  z-index: 1;
  fieldset {
    border: none;
    padding: 0;
    position: relative;
  }
  fieldset {
    position: relative;
  }
  label {
    color: var(--text-color);
    font-weight: bold;
  }
  input, select {
    background-color: var(--body-bg);
    color: var(--text-color);
  }
  .error {
    position: absolute;
    top: 0;
    right: 0;
    color: red;
  }
  &.loading {
    display: block;
  }
  /* Create a screen so the user cannot double submit/interact */
  &.loading:after {
    content: " ";
    display: block;
    position: absolute;
    z-index: 2;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba(255, 255, 255, 0.3);  
  }
  /* Facebook style spinner: processing */
  &.loading input[type=submit] {
    color: transparent;
    background-image: url(data:image/gif;utf8;base64,R0lGODlhEAALAPQAAP////////7+/v7+/v7+/v7+/v////7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/gAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCwAAACwAAAAAEAALAAAFLSAgjmRpnqSgCuLKAq5AEIM4zDVw03ve27ifDgfkEYe04kDIDC5zrtYKRa2WQgAh+QQJCwAAACwAAAAAEAALAAAFJGBhGAVgnqhpHIeRvsDawqns0qeN5+y967tYLyicBYE7EYkYAgAh+QQJCwAAACwAAAAAEAALAAAFNiAgjothLOOIJAkiGgxjpGKiKMkbz7SN6zIawJcDwIK9W/HISxGBzdHTuBNOmcJVCyoUlk7CEAAh+QQJCwAAACwAAAAAEAALAAAFNSAgjqQIRRFUAo3jNGIkSdHqPI8Tz3V55zuaDacDyIQ+YrBH+hWPzJFzOQQaeavWi7oqnVIhACH5BAkLAAAALAAAAAAQAAsAAAUyICCOZGme1rJY5kRRk7hI0mJSVUXJtF3iOl7tltsBZsNfUegjAY3I5sgFY55KqdX1GgIAIfkECQsAAAAsAAAAABAACwAABTcgII5kaZ4kcV2EqLJipmnZhWGXaOOitm2aXQ4g7P2Ct2ER4AMul00kj5g0Al8tADY2y6C+4FIIACH5BAkLAAAALAAAAAAQAAsAAAUvICCOZGme5ERRk6iy7qpyHCVStA3gNa/7txxwlwv2isSacYUc+l4tADQGQ1mvpBAAIfkECQsAAAAsAAAAABAACwAABS8gII5kaZ7kRFGTqLLuqnIcJVK0DeA1r/u3HHCXC/aKxJpxhRz6Xi0ANAZDWa+kEAA7AAAAAAAAAAAA);
    background-position: center;
    background-repeat: no-repeat;
  }
  /* Check icon: success */
  &.success input[type=submit] {
    background-color: lime !important;
    color: transparent;
    background-repeat: no-repeat;
    background-position: center 16px;
    background-size: 25px;
    background-image: url(data:image/svg+xml;utf8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTcgMTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgY2xhc3M9InNpLWdseXBoIHNpLWdseXBoLWNoZWNrZWQiPg0KICAgIDxnIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPg0KICAgICAgICA8cGF0aCBkPSJNMy40MzIsNi4xODkgQzMuODI0LDUuNzk4IDQuNDU1LDUuNzk4IDQuODQ3LDYuMTg5IEw2Ljk2OCw4LjMxIEwxMy4xNDcsMi4xMzEgQzEzLjUzMSwxLjc0NyAxNC4xNTcsMS43NTMgMTQuNTQ4LDIuMTQ0IEwxNi42Nyw0LjI2NiBDMTcuMDYsNC42NTcgMTcuMDY2LDUuMjg0IDE2LjY4NCw1LjY2NiBMNy42NjIsMTQuNjg3IEM3LjI3OCwxNS4wNyA2LjY1MSwxNS4wNjQgNi4yNjEsMTQuNjczIEwxLjMxMSw5LjcyMyBDMC45Miw5LjMzMyAwLjkyLDguNyAxLjMxMSw4LjMxIEwzLjQzMiw2LjE4OSBaIiBmaWxsPSIjZmZmIiBjbGFzcz0ic2ktZ2x5cGgtZmlsbCI+PC9wYXRoPg0KICAgIDwvZz4NCjwvc3ZnPg==);
  }
  /* Cross icon: failure */
  &.failed input[type=submit] {
    background-color: red !important;
    color: transparent;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 21px;
    background-image: url(data:image/svg+xml;utf8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTcgMTciIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgY2xhc3M9InNpLWdseXBoIHNpLWdseXBoLWRlbGV0ZSI+DQogICAgPGcgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+DQogICAgICAgIDxwYXRoIGQ9Ik0xMi41NjYsOCBMMTUuNjExLDQuOTU2IEMxNi4wMzEsNC41MzUgMTYuMDMxLDMuODUzIDE1LjYxMSwzLjQzNCBMMTIuNTY2LDAuMzg5IEMxMi4xNDYsLTAuMDMxIDExLjQ2NCwtMC4wMzEgMTEuMDQzLDAuMzg5IEw3Ljk5OSwzLjQzMyBMNC45NTUsMC4zODkgQzQuNTM0LC0wLjAzMSAzLjg1MiwtMC4wMzEgMy40MzIsMC4zODkgTDAuMzg4LDMuNDM0IEMtMC4wMzQsMy44NTQgLTAuMDM0LDQuNTM2IDAuMzg3LDQuOTU2IEwzLjQzMSw4IEwwLjM4NywxMS4wNDQgQy0wLjAzNCwxMS40NjUgLTAuMDM0LDEyLjE0NyAwLjM4OCwxMi41NjcgTDMuNDMyLDE1LjYxMSBDMy44NTIsMTYuMDMyIDQuNTM0LDE2LjAzMiA0Ljk1NSwxNS42MTEgTDcuOTk5LDEyLjU2NyBMMTEuMDQzLDE1LjYxMSBDMTEuNDY0LDE2LjAzMiAxMi4xNDYsMTYuMDMyIDEyLjU2NiwxNS42MTEgTDE1LjYxMSwxMi41NjcgQzE2LjAzMSwxMi4xNDYgMTYuMDMxLDExLjQ2NCAxNS42MTEsMTEuMDQ0IEwxMi41NjYsOCBMMTIuNTY2LDggWiIgZmlsbD0iI2ZmZmZmZiIgY2xhc3M9InNpLWdseXBoLWZpbGwiPjwvcGF0aD4NCiAgICA8L2c+DQo8L3N2Zz4=);
  }
}

================================================
FILE: public/css/development/admin.css
================================================
@import url("grid.css");
@import url("themes/light.css");
@import url("themes/dark.css");
@import url("main.css");
@import url("admin/forms.css");
@import url("utilities.css");

================================================
FILE: public/css/development/devise.css
================================================
.devise {
  > .row {
    margin-top: 50px;
    @media (min-width: 576px) and (max-width: 991px) {
      > div {
        grid-column: 2/span 10 !important;
      }
    }
    @media (min-width: 992px) {
      > div {
        grid-column: 4/span 6 !important;
      }
    }
  }
}

================================================
FILE: public/css/development/front.css
================================================
@import url("grid.css");
@import url("themes/light.css");
@import url("themes/dark.css");
@import url("main.css");
@import url("devise.css");
@import url("utilities.css");

================================================
FILE: public/css/development/grid.css
================================================
:root {
  --gutter: 20px;
}
[class^="col-"] {
  position: relative;
}
.wrapper {
  width: calc(100% - var(--gutter));
  margin: 0 auto;
}
.row {
  clear: both;
  display: grid;
  grid-template-columns: repeat(12,1fr);
  grid-gap: var(--gutter);
}

@media (max-width: 575px) {
  :root {
    --gutter: 10px;
  } 
}

.col-xs-1 { grid-column-start: span 1 }
.col-xs-2 { grid-column-start: span 2 }
.col-xs-3 { grid-column-start: span 3 }
.col-xs-4 { grid-column-start: span 4 }
.col-xs-5 { grid-column-start: span 5 }
.col-xs-6 { grid-column-start: span 6 }
.col-xs-7 { grid-column-start: span 7 }
.col-xs-8 { grid-column-start: span 8 }
.col-xs-9 { grid-column-start: span 9 }
.col-xs-10 { grid-column-start: span 10 }
.col-xs-11 { grid-column-start: span 11 }
.col-xs-12 { grid-column-start: span 12 }

@media (min-width: 576px) {
  .col-sm-1 { grid-column-start: span 1 !important }
  .col-sm-2 { grid-column-start: span 2 !important }
  .col-sm-3 { grid-column-start: span 3 !important }
  .col-sm-4 { grid-column-start: span 4 !important }
  .col-sm-5 { grid-column-start: span 5 !important }
  .col-sm-6 { grid-column-start: span 6 !important }
  .col-sm-7 { grid-column-start: span 7 !important }
  .col-sm-8 { grid-column-start: span 8 !important }
  .col-sm-9 { grid-column-start: span 9 !important }
  .col-sm-10 { grid-column-start: span 10 !important }
  .col-sm-11 { grid-column-start: span 11 !important }
  .col-sm-12 { grid-column-start: span 12 !important }
}
@media (min-width: 768px) {
  .col-md-1 { grid-column-start: span 1 !important }
  .col-md-2 { grid-column-start: span 2 !important }
  .col-md-3 { grid-column-start: span 3 !important }
  .col-md-4 { grid-column-start: span 4 !important }
  .col-md-5 { grid-column-start: span 5 !important }
  .col-md-6 { grid-column-start: span 6 !important }
  .col-md-7 { grid-column-start: span 7 !important }
  .col-md-8 { grid-column-start: span 8 !important }
  .col-md-9 { grid-column-start: span 9 !important }
  .col-md-10 { grid-column-start: span 10 !important }
  .col-md-11 { grid-column-start: span 11 !important }
  .col-md-12 { grid-column-start: span 12 !important }
}
@media (min-width: 992px) {
  .col-lg-1 { grid-column-start: span 1 !important }
  .col-lg-2 { grid-column-start: span 2 !important }
  .col-lg-3 { grid-column-start: span 3 !important }
  .col-lg-4 { grid-column-start: span 4 !important}
  .col-lg-5 { grid-column-start: span 5 !important }
  .col-lg-6 { grid-column-start: span 6 !important }
  .col-lg-7 { grid-column-start: span 7 !important }
  .col-lg-8 { grid-column-start: span 8 !important }
  .col-lg-9 { grid-column-start: span 9 !important }
  .col-lg-10 { grid-column-start: span 10 !important }
  .col-lg-11 { grid-column-start: span 11 !important }
  .col-lg-12 { grid-column-start: span 12 !important }
}
@media (min-width: 1200px) {
  .col-xl-1 { grid-column-start: span 1 !important }
  .col-xl-2 { grid-column-start: span 2 !important }
  .col-xl-3 { grid-column-start: span 3 !important }
  .col-xl-4 { grid-column-start: span 4 !important }
  .col-xl-5 { grid-column-start: span 5 !important }
  .col-xl-6 { grid-column-start: span 6 !important }
  .col-xl-7 { grid-column-start: span 7 !important }
  .col-xl-8 { grid-column-start: span 8 !important }
  .col-xl-9 { grid-column-start: span 9 !important }
  .col-xl-10 { grid-column-start: span 10 !important }
  .col-xl-11 { grid-column-start: span 11 !important }
  .col-xl-12 { grid-column-start: span 12 !important }
}
@media (min-width: 1400px) {
  .col-xxl-1 { grid-column-start: span 1 !important }
  .col-xxl-2 { grid-column-start: span 2 !important }
  .col-xxl-3 { grid-column-start: span 3 !important }
  .col-xxl-4 { grid-column-start: span 4 !important }
  .col-xxl-5 { grid-column-start: span 5 !important }
  .col-xxl-6 { grid-column-start: span 6 !important }
  .col-xxl-7 { grid-column-start: span 7 !important }
  .col-xxl-8 { grid-column-start: span 8 !important }
  .col-xxl-9 { grid-column-start: span 9 !important }
  .col-xxl-10 { grid-column-start: span 10 !important }
  .col-xxl-11 { grid-column-start: span 11 !important }
  .col-xxl-12 { grid-column-start: span 12 !important }
}

.visible-xs { display: none; }
.visible-sm { display: none; }
.visible-md { display: none; }
.visible-lg { display: none; }
.visible-xl { display: none; }
.visible-xxl { display: none; }

@media (min-width: 0) and (max-width: 575px) {
  .hidden-xs { display: none; }
  .visible-xs { display: block; }
}
@media (min-width: 576px) and (max-width: 767px) {
  .hidden-sm { display: none; }
  .visible-sm { display: block; }
}
@media (min-width: 768px) and (max-width: 992px) {
  .hidden-md { display: none; }
  .visible-md { display: block; }
}
@media (min-width: 992px) and (max-width: 1199px) {
  .hidden-lg { display: none; }
  .visible-lg { display: block; }
}
@media (min-width: 1200px) and (max-width: 1399px) {
  .hidden-xl { display: none; }
  .visible-xl { display: block; }
}
@media (min-width: 1400px)  {
  .hidden-xxl { display: none; }
  .visible-xxl { display: block; }
}

================================================
FILE: public/css/development/main.css
================================================
html {
  min-width: 375px;
}
body, html {
  background-color: var(--body-bg);
}
h1, h2, h3, h4, h5, h6 {
  color: var(--title-color);
}
p, span, li, table, tr, td, th, label {
  color: var(--text-color);
}
.top-nav {
  background-color: var(--accent);
  select {
    top: 5px;
    right: 0;
    position: absolute;
    max-width: 100px;
  }
  ul li.active {
    border-bottom: 1px solid var(--border-active);
  }
}
.filters {
  @media(max-width: 992px) {
    margin-top: 30px;
  }
}
.breadcrumb {
  display: inline-block;
  height: 40px;
  margin: 0 auto 20px auto;
  background-color: var(--accent);
  li {
    list-style-type: none;
    display: inline-block;
    padding-top: 5px;
    color: var(--light-text-color);
  }
  li:after {
    content: '/';
    margin: 0 10px 0 10px;
  }
  li:last-child {
    &:after {
      content: '';
    }
  }
}

.card {
  background-color: var(--accent);
  padding: 20px;
  box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.2);
}

.pagination {
  list-style-type: none;
  float: right;
  padding: 0;
  height: 40px;
  li {
    list-style-type: none;
    display: inline-block;
    width: 40px;
    height: 40px;
    line-height: 20px;
    text-align: center;
    padding: 10px;
    margin-left: 5px;
    a {
      text-decoration: none !important;
    }
  }
}

main {
  min-height: calc(100vh - 60px);
}
footer {
  height: 50px;
  text-align: left;
  padding-top: 8px;
  color: var(--light-text-color);
  font-size: 14px;
  & a {
    color: var(--light-text-color);
    text-decoration: underline;
  }
  @media(max-width: 991px) {
    * {
      text-align: center !important;
    }
  }
}

================================================
FILE: public/css/development/themes/dark.css
================================================
[data-theme="dark"] {
  --body-bg: #000;
  --title-color: #fff;
  --text-color: #ddd;
  --light-text-color: #666; 
  --accent: rgb(37, 45, 52); 
  --border-active: #fffe;
}


================================================
FILE: public/css/development/themes/light.css
================================================
[data-theme="light"] {
  --body-bg: #f5f5f5;
  --title-color: #333;
  --text-color: #666;
  --light-text-color: #999; 
  --accent: #fff; 
  --border-active: blue;
}


================================================
FILE: public/css/development/utilities.css
================================================
.hidden {
  display: none;
}
.ta-left {
  text-align: left;
}
.ta-center {
  text-align: center;
}
.ta-right {
  text-align: right;
}
.openable {
  position: relative;
  padding-left: 20px;
  &:before {
    width: 10px;
    height: 10px;
    display: inline-block;
    border: solid black;
    content: '';
    border-width: 0 1px 1px 0;
    transform: rotate(-45deg);
    margin-right: 10px;
    position: absolute;
    top: 7px;
    left: 0;
  }
  &.open:before {
    top: 5px;
    transform: rotate(45deg);
  }
}


================================================
FILE: public/robots.txt
================================================
# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file


================================================
FILE: public/vite-test/assets/admin-ebb17751.js
================================================
import{d as q,_ as v,o as r,c,a as t,t as i,b as V,w,e as g,v as S,f as F,F as y,r as k,g as p,h as d,i as m,j as x,k as f,l as _,m as I,n as T,E as L,p as C,q as O,s as P,u as E,x as R,A as M,y as B}from"./vue-i18n-6b73e0ca.js";const D=q("dashboard",{state:()=>({metrics:{}}),actions:{async index(){this.axios.get("/dashboard").then(e=>{this.metrics=e.data.metrics})}}}),Q={setup(){return{store:D()}},created(){this.$api.call(this.store.index())}},W={class:"container"},z={class:"row"},J=V('<div class="col-xs-24 col-md-6 card"><h3>Rails 7</h3><p><a href="https://edgeguides.rubyonrails.org/" target="_blank">An awesome server side Framework</a></p></div><div class="col-xs-24 col-md-6 card"><h3>Vue 3</h3><p><a href="https://vuejs.org/guide/" target="_blank">SPA without the pain</a></p></div><div class="col-xs-24 col-md-6 card"><h3>Pinia</h3><p><a href="https://pinia.vuejs.org/" target="_blank">State manager</a></p></div><div class="col-xs-24 col-md-6 card"><h3>Vue Router</h3><p><a href="https://router.vuejs.org/" target="_blank">Flexible</a></p></div><div class="col-xs-24 col-md-6 card"><h3>Axios</h3><p><a href="https://axios-http.com/docs/intro" target="_blank">Handles AJAX requests, no JQuery</a></p></div><div class="col-xs-24 col-md-6 card"><h3>Vite</h3><p><a href="https://vitejs.dev/" target="_blank">Next generation Frontend tooling</a></p></div><div class="col-xs-24 col-md-6 card"><h3>Devise</h3><p><a href="https://github.com/heartcombo/devise" target="_blank">Simple authentication using regular cookies that just works</a></p></div>',7),G={class:"col-xs-24 col-md-6 card"},H=t("h3",null,"I18n",-1),K={href:"https://kazupon.github.io/vue-i18n/",target:"_blank"},X=V('<div class="col-xs-24 col-md-6 card"><h3>ActionCable</h3><p><a href="https://guides.rubyonrails.org/action_cable_overview.html" target="_blank">Websockets with 2 way bindings</a></p></div><div class="col-xs-24 col-md-6 card"><h3>Kaminari</h3><p><a href="https://github.com/kaminari/kaminari" target="_blank">Efficient Pagination</a></p></div><div class="col-xs-24 col-md-6 card"><h3>Ransack</h3><p><a href="https://github.com/activerecord-hackery/ransack" target="_blank">ActiveRecord wizardry for dynamic search filters</a></p></div><div class="col-xs-24 col-md-6 card"><h3>Bootstrap grid</h3><p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout" target="_blank">... but using native CSS Grid layout</a></p></div>',4);function Y(e,s,o,u,h,l){return r(),c("section",W,[t("div",z,[J,t("div",G,[H,t("p",null,[t("a",K,i(e.$tc("dashboard.musicians",0))+", "+i(e.$tc("dashboard.musicians",1))+", "+i(u.store.metrics.musicians+" "+e.$tc("dashboard.musicians",u.store.metrics.musicians)),1)])]),X])])}const Z=v(Q,[["render",Y]]),A=q("musicians",{state:()=>({errors:{},bands:[],musician:{},musicians:[],pagination:{}}),actions:{async index(e){return this.axios.get(e).then(s=>{this.pagination=s.data.pagination,this.bands=s.data.bands,this.musicians=s.data.musicians})},async new(){return this.errors={},this.musician={},this.axios.get("/musicians/new").then(e=>{this.musician=e.data.musician})},async create(){return this.errors={},this.axios.post("/musicians",this.musician).then(e=>(this.musician=e.data.musician,!0)).catch(e=>(this.errors=e.response.data.errors,!1))},async edit(e){return this.errors={},this.musician={},this.axios.get(`/musicians/${e}/edit`).then(s=>{this.musician=s.data.musician})},async update(e){return this.errors={},this.axios.put(`/musicians/${e}`,this.musician).then(s=>!0).catch(s=>(this.errors=s.response.data.errors,!1))},async destroy(e){return this.errors={},this.axios.delete(`/musicians/${e}`).then(s=>!0).catch(s=>(this.errors=s.response.data.errors,!1))}}}),tt={props:["bands"],data:function(){return{form:{name_cont:"",band_eq:null}}},methods:{toggleForm(){this.$refs.filters.classList.toggle("hidden")},search(){const e=Object.fromEntries(Object.entries(this.form).map(s=>[`q[${s[0]}]`,s[1]]));this.$router.push({path:this.$route.path,query:e}),this.$emit("index")},reset(){this.form={name_cont:"",band_eq:null},this.$router.push({path:this.$route.path,query:""})}}},et={class:"row"},st={class:"col-xs-24 col-md-10"},at={class:"col-xs-24 col-md-10"},nt={value:null},it=["value"],ot={class:"row"},rt={class:"col-xs-24 col-md-5"},ct=["value"],lt={class:"col-xs-24 col-md-5"};function ut(e,s,o,u,h,l){return r(),c("section",null,[t("a",{href:"#",onClick:s[0]||(s[0]=w((...n)=>l.toggleForm&&l.toggleForm(...n),["prevent"]))}," "+i(e.$t("filter")),1),t("form",{onSubmit:s[4]||(s[4]=w((...n)=>l.search&&l.search(...n),["prevent"])),ref:"filters","accept-charset":"UTF-8",class:"card hidden"},[t("div",et,[t("div",st,[t("label",null,i(e.$t("musicians.form.name")),1),g(t("input",{type:"text","onUpdate:modelValue":s[1]||(s[1]=n=>e.form.name_cont=n),placeholder:"name"},null,512),[[S,e.form.name_cont]])]),t("div",at,[t("label",null,i(e.$t("musicians.form.band")),1),g(t("select",{"onUpdate:modelValue":s[2]||(s[2]=n=>e.form.band_eq=n)},[t("option",nt,i(e.$t("any")),1),(r(!0),c(y,null,k(o.bands,n=>(r(),c("option",{key:n.key,value:n.key},i(n.name),9,it))),128))],512),[[F,e.form.band_eq]])])]),t("div",ot,[t("div",rt,[t("input",{type:"submit",role:"button",class:"button button-primary",value:e.$t("filter")},null,8,ct)]),t("div",lt,[t("a",{onClick:s[3]||(s[3]=(...n)=>l.reset&&l.reset(...n)),href:"#",role:"button",class:"secondary outline fill"},i(e.$t("reset_filters")),1)])])],544)])}const dt=v(tt,[["render",ut]]),ht={components:{Filters:dt},setup(){return{store:A()}},mounted(){this.index()},methods:{index(){this.$api.call(this.store.index(this.$route.fullPath),this.$refs.listing)}}},mt={class:"container"},_t={class:"row"},pt={class:"col-xs-12"},ft={class:"breadcrumb"},vt={class:"col-xs-12 ta-right"},$t={ref:"listing"},bt={key:0},gt={key:1},yt={class:"card ta-center"},kt=t("p",null,'This whole CRUD section is deliberatly slowed down so you can actually see the animations. Comment out the "slow" method in Api::Admin::MusiciansController to use the app at full speed',-1);function wt(e,s,o,u,h,l){const n=p("router-link"),a=p("filters"),$=p("pagination");return r(),c("section",mt,[t("div",_t,[t("div",pt,[t("ul",ft,[t("li",null,[d(n,{to:{name:"root_path"}},{default:m(()=>[_(i(e.$t("title")),1)]),_:1})]),t("li",null,i(e.$t("nav.musicians")),1)])]),t("div",vt,[d(n,{to:{name:"new_musician_path"},role:"button",class:"outline"},{default:m(()=>[_(i(e.$t("musicians.create")),1)]),_:1})])]),d(a,{bands:u.store.bands},null,8,["bands"]),t("div",$t,[u.store.musicians&&u.store.musicians.length>0?(r(),c("table",bt,[t("thead",null,[t("tr",null,[t("th",null,i(e.$t("musicians.form.id")),1),t("th",null,i(e.$t("musicians.form.name")),1),t("th",null,i(e.$t("musicians.form.band")),1)])]),t("tbody",null,[(r(!0),c(y,null,k(u.store.musicians,b=>(r(),c("tr",{key:b.id},[t("td",null,[d(n,{to:{name:"edit_musician_path",params:{id:b.id}}},{default:m(()=>[_(i(b.id),1)]),_:2},1032,["to"])]),t("td",null,[d(n,{to:{name:"edit_musician_path",params:{id:b.id}}},{default:m(()=>[_(i(b.name),1)]),_:2},1032,["to"])]),t("td",null,[d(n,{to:{name:"edit_musician_path",params:{id:b.id}}},{default:m(()=>[_(i(b.band),1)]),_:2},1032,["to"])])]))),128))])])):(r(),c("div",gt,[t("h3",yt,i(e.$t("no_result")),1)])),u.store.pagination?(r(),x($,{key:2,store:u.store,onClicked:l.index},null,8,["store","onClicked"])):f("",!0)],512),kt])}const xt=v(ht,[["render",wt]]),Ct={props:["attr","messages"],data(){return{message:""}},watch:{messages:function(){this.message="",this.messages[this.attr]&&(this.message=this.messages[this.attr].join(","))}}},St={key:0,class:"error"};function Ft(e,s,o,u,h,l){return h.message!=""?(r(),c("span",St,i(h.message),1)):f("",!0)}const At=v(Ct,[["render",Ft]]),qt={props:["data"],components:{Errors:At}},Vt=["aria-invalid"],Mt=["aria-invalid"],Ut=["value"];function Nt(e,s,o,u,h,l){const n=p("errors");return r(),c("section",null,[t("fieldset",null,[t("label",null,i(e.$t("musicians.form.name")),1),d(n,{attr:"name",messages:o.data.errors},null,8,["messages"]),g(t("input",{type:"text","onUpdate:modelValue":s[0]||(s[0]=a=>o.data.musician.name=a),"aria-invalid":o.data.errors.name?!0:""},null,8,Vt),[[S,o.data.musician.name]])]),t("fieldset",null,[t("label",null,i(e.$t("musicians.form.band")),1),d(n,{attr:"band",messages:o.data.errors},null,8,["messages"]),g(t("select",{"onUpdate:modelValue":s[1]||(s[1]=a=>o.data.musician.band=a),"aria-invalid":o.data.errors.band?!0:""},[(r(!0),c(y,null,k(o.data.musician.bands,a=>(r(),c("option",{key:a.key,value:a.key},i(a.name),9,Ut))),128))],8,Mt),[[F,o.data.musician.band]])])])}const U=v(qt,[["render",Nt]]),jt={components:{MusicianForm:U},setup(){return{store:A()}},mounted(){this.$api.call(this.store.new(),this.$refs.animation)},methods:{create(e){this.$api.call(this.store.create(),e.target).then(s=>{s===!0&&this.$router.push({name:"edit_musician_path",params:{id:this.store.musician.id}})})}}},It={class:"container"},Tt={class:"breadcrumb"},Lt={ref:"animation"},Ot={class:"row"},Pt={class:"col-sm-4 col-start-sm-21 ta-right"},Et=["value"];function Rt(e,s,o,u,h,l){const n=p("router-link"),a=p("MusicianForm");return r(),c("section",It,[t("ul",Tt,[t("li",null,[d(n,{to:{name:"root_path"}},{default:m(()=>[_(i(e.$t("title")),1)]),_:1})]),t("li",null,[d(n,{to:{name:"musicians_path"}},{default:m(()=>[_(i(e.$t("nav.musicians")),1)]),_:1})]),t("li",null,[t("span",null,i(e.$t("musicians.new")),1)])]),t("div",Lt,[t("form",{onSubmit:s[0]||(s[0]=w((...$)=>l.create&&l.create(...$),["prevent"])),"accept-charset":"UTF-8",class:"card"},[d(a,{data:u.store},null,8,["data"]),t("div",Ot,[t("div",Pt,[t("input",{type:"submit",value:e.$t("save")},null,8,Et)])])],32)],512)])}const Bt=v(jt,[["render",Rt]]),Dt={components:{MusicianForm:U},setup(){return{store:A()}},mounted(){this.$api.call(this.store.edit(this.$route.params.id),this.$refs.animation)},methods:{update(e){this.$api.call(this.store.update(this.$route.params.id),e.target)},destroy(){confirm(this.$t("confirm"))&&this.$api.call(this.store.destroy(this.$route.params.id),this.$refs.animation).then(e=>{e===!0&&this.$router.push({name:"musicians_path"})})}}},Qt={class:"container"},Wt={class:"breadcrumb"},zt={ref:"animation"},Jt={class:"row"},Gt={class:"col-sm-4 secondary outline"},Ht={class:"col-sm-4 col-start-sm-21 ta-right"},Kt=["value"];function Xt(e,s,o,u,h,l){const n=p("router-link"),a=p("MusicianForm");return r(),c("section",Qt,[t("ul",Wt,[t("li",null,[d(n,{to:{name:"root_path"}},{default:m(()=>[_(i(e.$t("title")),1)]),_:1})]),t("li",null,[d(n,{to:{name:"musicians_path"}},{default:m(()=>[_(i(e.$t("nav.musicians")),1)]),_:1})]),t("li",null,i(u.store.musician.name),1)]),t("div",zt,[t("form",{ref:"form",onSubmit:s[1]||(s[1]=w((...$)=>l.update&&l.update(...$),["prevent"])),"accept-charset":"UTF-8",class:"card"},[d(a,{data:u.store},null,8,["data"]),t("div",Jt,[t("div",Gt,[t("a",{onClick:s[0]||(s[0]=(...$)=>l.destroy&&l.destroy(...$)),href:"#",role:"button",class:"secondary outline"},i(e.$t("delete")),1)]),t("div",Ht,[t("input",{type:"submit",value:e.$t("save")},null,8,Kt)])])],544)],512)])}const Yt=v(Dt,[["render",Xt]]),Zt={data(){return{message:"",messages:[]}},created(){this.$cable.on("chat",e=>{this.messages.unshift(e.message)})},methods:{publish(){this.$cable.send(this.message),this.message=""}}},te={class:"container"},ee={class:"breadcrumb"},se={class:"row"},ae={class:"col-xs-24 col-sm-12 card"},ne=t("br",null,null,-1),ie=t("br",null,null,-1),oe=t("input",{type:"submit",value:"Publish"},null,-1),re=t("div",{class:"card"},[t("p",null,"You can also push messages here from the server using the Rails console:"),t("code",null,'ActionCable.server.broadcast("ChatChannel", { message: "hey!" })')],-1),ce={class:"col-xs-24 col-sm-12 card"},le=t("p",null,[_("All messages you type are upcased "),t("b",null,"server side"),_(" after a round trip. If you open multiple tabs, messages are broadcasted on all tabs.")],-1),ue={key:0},de={key:1},he=t("p",null,[t("i",null,"Waiting for messages...")],-1),me=[he];function _e(e,s,o,u,h,l){const n=p("router-link");return r(),c("section",te,[t("ul",ee,[t("li",null,[d(n,{to:{name:"root_path"}},{default:m(()=>[_(i(e.$t("title")),1)]),_:1})]),t("li",null,i(e.$t("nav.websockets")),1)]),t("div",se,[t("div",ae,[t("form",{onSubmit:s[1]||(s[1]=w((...a)=>l.publish&&l.publish(...a),["prevent"])),"accept-charset":"UTF-8"},[g(t("input",{type:"input","onUpdate:modelValue":s[0]||(s[0]=a=>h.message=a),placeholder:"Type in a message"},null,512),[[S,h.message]]),ne,ie,oe],32),re]),t("div",ce,[le,h.messages.length>0?(r(),c("div",ue,[(r(!0),c(y,null,k(h.messages,(a,$)=>(r(),c("p",{key:$},[t("i",null,i(a),1)]))),128))])):(r(),c("div",de,me))])])])}const pe=v(Zt,[["render",_e]]),fe=I({history:T(`/${I18n.prefix}admin`),routes:[{path:"/",component:Z,name:"root_path"},{path:"/musicians",component:xt,name:"musicians_path"},{path:"/musicians/new",component:Bt,name:"new_musician_path"},{path:"/musicians/:id/edit",component:Yt,name:"edit_musician_path"},{path:"/websockets",component:pe,name:"websockets_path"},{path:"/404",component:L},{path:"/:catchAll(.*)",redirect:"/404"}]}),ve={data(){return{availableLocales:window.I18n.availableLocales,locale:window.I18n.locale}},methods:{activeOn(e){return e.includes(this.$route.name)?"active":""}},watch:{locale:function(e){let s=`/${e}${this.$route.path}`;e==this.availableLocales[0]&&(s=`${this.$route.path}`),window.location.href=s}}},$e={class:"top-nav"},be={class:"container"},ge={class:"row"},ye={class:"col-md-16 col-lg-21"},ke={href:"/users/sign_out"},we={class:"col-md-8 col-lg-3"},xe=["value"];function Ce(e,s,o,u,h,l){const n=p("router-link");return r(),c("section",$e,[t("div",be,[t("div",ge,[t("div",ye,[t("nav",null,[t("ul",null,[t("li",{class:C(l.activeOn(["root_path"]))},[d(n,{to:{name:"root_path"}},{default:m(()=>[_(i(e.$t("nav.dashboard")),1)]),_:1})],2),t("li",{class:C(l.activeOn(["musicians_path","edit_musician_path","new_musician_path"]))},[d(n,{to:{name:"musicians_path"}},{default:m(()=>[_(i(e.$t("nav.musicians")),1)]),_:1})],2),t("li",{class:C(l.activeOn(["websockets_path"]))},[d(n,{to:{name:"websockets_path"}},{default:m(()=>[_(i(e.$t("nav.websockets")),1)]),_:1})],2),t("li",null,[t("a",ke,i(e.$t("nav.logout")),1)])])])]),t("div",we,[g(t("select",{"onUpdate:modelValue":s[0]||(s[0]=a=>h.locale=a)},[(r(!0),c(y,null,k(h.availableLocales,a=>(r(),c("option",{value:a,key:a},i(a.toUpperCase()),9,xe))),128))],512),[[F,h.locale]])])])])])}const Se=v(ve,[["render",Ce]]),Fe={components:{"nav-bar":Se}};function Ae(e,s,o,u,h,l){const n=p("nav-bar"),a=p("router-view");return r(),c("section",null,[d(n),d(a)])}const qe=v(Fe,[["render",Ae]]),Ve={props:["store"],methods:{setQuery(e){let s=JSON.parse(JSON.stringify(this.$route.query));return s.page=e,s}},watch:{"$route.query":function(){this.$emit("clicked")}}},Me={clas:"container"},Ue={key:0},Ne={class:"pagination"},je={key:0},Ie=t("span",null,"«",-1),Te={key:1},Le={key:2},Oe={key:3},Pe={key:1},Ee=t("span",null,"»",-1);function Re(e,s,o,u,h,l){const n=p("router-link");return r(),c("section",Me,[o.store.pagination.next||o.store.pagination.previous?(r(),c("div",Ue,[t("ul",Ne,[o.store.pagination.previous!=null?(r(),c("li",je,[o.store.pagination.previous?(r(),x(n,{key:0,to:{path:e.$route.path,query:l.setQuery(o.store.pagination.previous)}},{default:m(()=>[Ie]),_:1},8,["to"])):f("",!0)])):f("",!0),(r(!0),c(y,null,k(o.store.pagination.pages,a=>(r(),c("li",{key:a,class:C([o.store.pagination.current==a?"active":""])},[a!=o.store.pagination.current&&(a==1||a==2||a==3||a==o.store.pagination.pages||a==o.store.pagination.pages-1||a==o.store.pagination.pages-2)?(r(),x(n,{key:0,to:{path:e.$route.path,query:l.setQuery(a)}},{default:m(()=>[_(i(a),1)]),_:2},1032,["to"])):f("",!0),a!=o.store.pagination.current&&a==4?(r(),c("span",Te,"...")):f("",!0),a==o.store.pagination.current?(r(),c("span",Le,i(a),1)):f("",!0),a==o.store.pagination.current&&a==4?(r(),c("span",Oe,"...")):f("",!0)],2))),128)),o.store.pagination.next!=null&&o.store.pagination.pages>0?(r(),c("li",Pe,[o.store.pagination.next?(r(),x(n,{key:0,to:{path:e.$route.path,query:l.setQuery(o.store.pagination.next)}},{default:m(()=>[Ee]),_:1},8,["to"])):f("",!0)])):f("",!0)])])):f("",!0)])}const Be=v(Ve,[["render",Re]]),N=O(qe),De=P({channel:"ChatChannel"}),Qe=E({handler:M,namespace:"/admin"}),j=R();j.use(({store:e})=>{e.axios=M});const We=B({locale:"current",messages:translations});N.component("pagination",Be);N.use(fe).use(j).use(We).use(Qe).use(De).mount("#app");


================================================
FILE: public/vite-test/assets/front-f6acb49a.js
================================================
import{d as I,_ as m,g as p,o as c,c as l,a as e,t as s,h,i as _,w as f,l as u,F as w,r as k,m as A,n as L,E as M,p as b,e as S,f as z,q as B,s as N,u as V,x,A as C,y as E}from"./vue-i18n-6b73e0ca.js";const v=I("musicians",{state:()=>({musician:{},musicians:[]}),actions:{async index(){return this.axios.get("/musicians").then(t=>{this.musicians=t.data.musicians})},async show(t){return this.axios.get(`/musicians/${t}`).then(n=>{this.musician=n.data.musician})}}}),O={setup(){return{store:v()}},methods:{unauthorized(){this.$api.call(this.store.show("this-will-trigger-a-401"))},crash(){this.$api.call(this.store.show("this-will-trigger-a-500"))}}},P={class:"container"},D={href:"/dead-link"},F=e("br",null,null,-1),T={href:"/admin"};function U(t,n,$,r,d,o){const i=p("router-link");return c(),l("section",P,[e("h1",null,s(t.$t("pages.title")),1),e("p",null,[e("a",D,s(t.$t("pages.server_404")),1)]),e("p",null,[h(i,{to:"/dead-link"},{default:_(()=>[u(s(t.$t("pages.client_404")),1)]),_:1})]),e("p",null,[e("a",{onClick:n[0]||(n[0]=f((...a)=>o.unauthorized&&o.unauthorized(...a),["prevent"])),href:"#"},s(t.$t("pages.server_401")),1)]),e("p",null,[e("a",{onClick:n[1]||(n[1]=f((...a)=>o.crash&&o.crash(...a),["prevent"])),href:"#"},s(t.$t("pages.server_500")),1)]),F,e("p",null,[e("a",T,s(t.$t("pages.admin_link")),1)])])}const q=m(O,[["render",U]]),H={setup(){return{store:v()}},created(){this.$api.call(this.store.index())}},R={class:"container"},W={class:"breadcrumb"},j={class:"row"};function G(t,n,$,r,d,o){const i=p("router-link");return c(),l("section",R,[e("h1",null,s(t.$t("home.title")),1),e("ul",W,[e("li",null,[e("span",null,s(t.$t("home.breadcrumb")),1)])]),e("div",j,[(c(!0),l(w,null,k(r.store.musicians,a=>(c(),l("div",{key:a.id,class:"col-xs-24 col-md-6 card"},[h(i,{to:{name:"musician_path",params:{id:a.id}}},{default:_(()=>[u(s(a.name),1)]),_:2},1032,["to"])]))),128))])])}const g=m(H,[["render",G]]),J={setup(){return{store:v()}},created(){this.$api.call(this.store.show(this.$route.params.id))}},K={class:"container"},Q={class:"breadcrumb"},X=e("br",null,null,-1),Y=e("br",null,null,-1);function Z(t,n,$,r,d,o){const i=p("router-link");return c(),l("section",K,[e("h1",null,s(t.$t("home.title")),1),e("ul",Q,[e("li",null,[h(i,{to:{name:"root_path"}},{default:_(()=>[u(s(t.$t("home.breadcrumb")),1)]),_:1})]),e("li",null,s(r.store.musician.name),1)]),e("h2",null,s(t.$t("musicians.title")),1),e("p",null,[e("b",null,s(t.$t("musicians.id"))+":",1),u(" "+s(r.store.musician.id),1),X,e("b",null,s(t.$t("musicians.name"))+":",1),u(" "+s(r.store.musician.name),1),Y,e("b",null,s(t.$t("musicians.band"))+":",1),u(" "+s(r.store.musician.band),1)])])}const ee=m(J,[["render",Z]]),te=A({history:L(`/${I18n.prefix}`),routes:[{path:"/",component:g,name:"root_path"},{path:"/pages",component:q,name:"pages_path"},{path:"/musicians",component:g,name:"musicians_path"},{path:"/musicians/:id",component:ee,name:"musician_path"},{path:"/404",component:M},{path:"/:catchAll(.*)",redirect:"/404"}]}),se={data(){return{availableLocales:window.I18n.availableLocales,locale:window.I18n.locale}},methods:{activeOn(t){return t.includes(this.$route.name)?"active":""}},watch:{locale:function(t){let n=`/${t}${this.$route.path}`;t==this.availableLocales[0]&&(n=`${this.$route.path}`),window.location.href=n}}},ne={class:"top-nav"},ae={class:"container"},oe={class:"row"},ie={class:"col-md-16 col-lg-21"},re={class:"col-md-8 col-lg-3"},ce=["value"];function le(t,n,$,r,d,o){const i=p("router-link");return c(),l("section",ne,[e("div",ae,[e("div",oe,[e("div",ie,[e("nav",null,[e("ul",null,[e("li",{class:b(o.activeOn(["root_path","musicians_path","musician_path"]))},[h(i,{to:"/"},{default:_(()=>[u(s(t.$t("nav.homepage")),1)]),_:1})],2),e("li",{class:b(o.activeOn(["pages_path"]))},[h(i,{to:{name:"pages_path"}},{default:_(()=>[u(s(t.$t("nav.pages")),1)]),_:1})],2)])])]),e("div",re,[S(e("select",{"onUpdate:modelValue":n[0]||(n[0]=a=>d.locale=a)},[(c(!0),l(w,null,k(d.availableLocales,a=>(c(),l("option",{value:a,key:a},s(a.toUpperCase()),9,ce))),128))],512),[[z,d.locale]])])])])])}const ue=m(se,[["render",le]]),de={components:{"nav-bar":ue}};function he(t,n,$,r,d,o){const i=p("nav-bar"),a=p("router-view");return c(),l("div",null,[h(i),h(a)])}const pe=m(de,[["render",he]]),_e=B(pe),me=N({channel:"ChatChannel"}),$e=V({handler:C,namespace:""}),y=x();y.use(({store:t})=>{t.axios=C});const ve=E({locale:"current",messages:translations});_e.use(te).use(y).use(ve).use($e).use(me).mount("#app");


================================================
FILE: public/vite-test/assets/vue-i18n-6b73e0ca.js
================================================
function Xs(e,t){const n=Object.create(null),r=e.split(",");for(let o=0;o<r.length;o++)n[r[o]]=!0;return t?o=>!!n[o.toLowerCase()]:o=>!!n[o]}const _e={},nn=[],nt=()=>{},xc=()=>!1,Pr=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Js=e=>e.startsWith("onUpdate:"),ke=Object.assign,zs=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Fc=Object.prototype.hasOwnProperty,ie=(e,t)=>Fc.call(e,t),G=Array.isArray,rn=e=>Jn(e)==="[object Map]",Ir=e=>Jn(e)==="[object Set]",Uo=e=>Jn(e)==="[object Date]",Q=e=>typeof e=="function",Le=e=>typeof e=="string",an=e=>typeof e=="symbol",he=e=>e!==null&&typeof e=="object",Al=e=>(he(e)||Q(e))&&Q(e.then)&&Q(e.catch),Ll=Object.prototype.toString,Jn=e=>Ll.call(e),Mc=e=>Jn(e).slice(8,-1),Cl=e=>Jn(e)==="[object Object]",Qs=e=>Le(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,ur=Xs(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),wr=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Dc=/-(\w)/g,ht=wr(e=>e.replace(Dc,(t,n)=>n?n.toUpperCase():"")),Uc=/\B([A-Z])/g,bn=wr(e=>e.replace(Uc,"-$1").toLowerCase()),kr=wr(e=>e.charAt(0).toUpperCase()+e.slice(1)),ts=wr(e=>e?`on${kr(e)}`:""),Xt=(e,t)=>!Object.is(e,t),fr=(e,t)=>{for(let n=0;n<e.length;n++)e[n](t)},yr=(e,t,n)=>{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},Or=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let $o;const ps=()=>$o||($o=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function Zs(e){if(G(e)){const t={};for(let n=0;n<e.length;n++){const r=e[n],o=Le(r)?Wc(r):Zs(r);if(o)for(const s in o)t[s]=o[s]}return t}else if(Le(e)||he(e))return e}const $c=/;(?![^(]*\))/g,jc=/:([^]+)/,Hc=/\/\*[^]*?\*\//g;function Wc(e){const t={};return e.replace(Hc,"").split($c).forEach(n=>{if(n){const r=n.split(jc);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function eo(e){let t="";if(Le(e))t=e;else if(G(e))for(let n=0;n<e.length;n++){const r=eo(e[n]);r&&(t+=r+" ")}else if(he(e))for(const n in e)e[n]&&(t+=n+" ");return t.trim()}const Bc="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",Vc=Xs(Bc);function Pl(e){return!!e||e===""}function Kc(e,t){if(e.length!==t.length)return!1;let n=!0;for(let r=0;n&&r<e.length;r++)n=xr(e[r],t[r]);return n}function xr(e,t){if(e===t)return!0;let n=Uo(e),r=Uo(t);if(n||r)return n&&r?e.getTime()===t.getTime():!1;if(n=an(e),r=an(t),n||r)return e===t;if(n=G(e),r=G(t),n||r)return n&&r?Kc(e,t):!1;if(n=he(e),r=he(t),n||r){if(!n||!r)return!1;const o=Object.keys(e).length,s=Object.keys(t).length;if(o!==s)return!1;for(const i in e){const l=e.hasOwnProperty(i),a=t.hasOwnProperty(i);if(l&&!a||!l&&a||!xr(e[i],t[i]))return!1}}return String(e)===String(t)}function Yc(e,t){return e.findIndex(n=>xr(n,t))}const j_=e=>Le(e)?e:e==null?"":G(e)||he(e)&&(e.toString===Ll||!Q(e.toString))?JSON.stringify(e,Il,2):String(e),Il=(e,t)=>t&&t.__v_isRef?Il(e,t.value):rn(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[r,o])=>(n[`${r} =>`]=o,n),{})}:Ir(t)?{[`Set(${t.size})`]:[...t.values()]}:he(t)&&!G(t)&&!Cl(t)?String(t):t;let Ge;class wl{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=Ge,!t&&Ge&&(this.index=(Ge.scopes||(Ge.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=Ge;try{return Ge=this,t()}finally{Ge=n}}}on(){Ge=this}off(){Ge=this.parent}stop(t){if(this._active){let n,r;for(n=0,r=this.effects.length;n<r;n++)this.effects[n].stop();for(n=0,r=this.cleanups.length;n<r;n++)this.cleanups[n]();if(this.scopes)for(n=0,r=this.scopes.length;n<r;n++)this.scopes[n].stop(!0);if(!this.detached&&this.parent&&!t){const o=this.parent.scopes.pop();o&&o!==this&&(this.parent.scopes[this.index]=o,o.index=this.index)}this.parent=void 0,this._active=!1}}}function to(e){return new wl(e)}function Gc(e,t=Ge){t&&t.active&&t.effects.push(e)}function kl(){return Ge}function qc(e){Ge&&Ge.cleanups.push(e)}const no=e=>{const t=new Set(e);return t.w=0,t.n=0,t},xl=e=>(e.w&Mt)>0,Fl=e=>(e.n&Mt)>0,Xc=({deps:e})=>{if(e.length)for(let t=0;t<e.length;t++)e[t].w|=Mt},Jc=e=>{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r<t.length;r++){const o=t[r];xl(o)&&!Fl(o)?o.delete(e):t[n++]=o,o.w&=~Mt,o.n&=~Mt}t.length=n}},Tr=new WeakMap;let Cn=0,Mt=1;const _s=30;let Ze;const Gt=Symbol(""),gs=Symbol("");class ro{constructor(t,n=null,r){this.fn=t,this.scheduler=n,this.active=!0,this.deps=[],this.parent=void 0,Gc(this,r)}run(){if(!this.active)return this.fn();let t=Ze,n=wt;for(;t;){if(t===this)return;t=t.parent}try{return this.parent=Ze,Ze=this,wt=!0,Mt=1<<++Cn,Cn<=_s?Xc(this):jo(this),this.fn()}finally{Cn<=_s&&Jc(this),Mt=1<<--Cn,Ze=this.parent,wt=n,this.parent=void 0,this.deferStop&&this.stop()}}stop(){Ze===this?this.deferStop=!0:this.active&&(jo(this),this.onStop&&this.onStop(),this.active=!1)}}function jo(e){const{deps:t}=e;if(t.length){for(let n=0;n<t.length;n++)t[n].delete(e);t.length=0}}let wt=!0;const Ml=[];function En(){Ml.push(wt),wt=!1}function yn(){const e=Ml.pop();wt=e===void 0?!0:e}function Ve(e,t,n){if(wt&&Ze){let r=Tr.get(e);r||Tr.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=no()),Dl(o)}}function Dl(e,t){let n=!1;Cn<=_s?Fl(e)||(e.n|=Mt,n=!xl(e)):n=!e.has(Ze),n&&(e.add(Ze),Ze.deps.push(e))}function yt(e,t,n,r,o,s){const i=Tr.get(e);if(!i)return;let l=[];if(t==="clear")l=[...i.values()];else if(n==="length"&&G(e)){const a=Number(r);i.forEach((f,d)=>{(d==="length"||!an(d)&&d>=a)&&l.push(f)})}else switch(n!==void 0&&l.push(i.get(n)),t){case"add":G(e)?Qs(n)&&l.push(i.get("length")):(l.push(i.get(Gt)),rn(e)&&l.push(i.get(gs)));break;case"delete":G(e)||(l.push(i.get(Gt)),rn(e)&&l.push(i.get(gs)));break;case"set":rn(e)&&l.push(i.get(Gt));break}if(l.length===1)l[0]&&bs(l[0]);else{const a=[];for(const f of l)f&&a.push(...f);bs(no(a))}}function bs(e,t){const n=G(e)?e:[...e];for(const r of n)r.computed&&Ho(r);for(const r of n)r.computed||Ho(r)}function Ho(e,t){(e!==Ze||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}function zc(e,t){var n;return(n=Tr.get(e))==null?void 0:n.get(t)}const Qc=Xs("__proto__,__v_isRef,__isVue"),Ul=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(an)),Wo=Zc();function Zc(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const r=le(this);for(let s=0,i=this.length;s<i;s++)Ve(r,"get",s+"");const o=r[t](...n);return o===-1||o===!1?r[t](...n.map(le)):o}}),["push","pop","shift","unshift","splice"].forEach(t=>{e[t]=function(...n){En();const r=le(this)[t].apply(this,n);return yn(),r}}),e}function eu(e){const t=le(this);return Ve(t,"has",e),t.hasOwnProperty(e)}class $l{constructor(t=!1,n=!1){this._isReadonly=t,this._shallow=n}get(t,n,r){const o=this._isReadonly,s=this._shallow;if(n==="__v_isReactive")return!o;if(n==="__v_isReadonly")return o;if(n==="__v_isShallow")return s;if(n==="__v_raw"&&r===(o?s?hu:Bl:s?Wl:Hl).get(t))return t;const i=G(t);if(!o){if(i&&ie(Wo,n))return Reflect.get(Wo,n,r);if(n==="hasOwnProperty")return eu}const l=Reflect.get(t,n,r);return(an(n)?Ul.has(n):Qc(n))||(o||Ve(t,"get",n),s)?l:Te(l)?i&&Qs(n)?l:l.value:he(l)?o?Kl(l):zn(l):l}}class jl extends $l{constructor(t=!1){super(!1,t)}set(t,n,r,o){let s=t[n];if(cn(s)&&Te(s)&&!Te(r))return!1;if(!this._shallow&&(!vr(r)&&!cn(r)&&(s=le(s),r=le(r)),!G(t)&&Te(s)&&!Te(r)))return s.value=r,!0;const i=G(t)&&Qs(n)?Number(n)<t.length:ie(t,n),l=Reflect.set(t,n,r,o);return t===le(o)&&(i?Xt(r,s)&&yt(t,"set",n,r):yt(t,"add",n,r)),l}deleteProperty(t,n){const r=ie(t,n);t[n];const o=Reflect.deleteProperty(t,n);return o&&r&&yt(t,"delete",n,void 0),o}has(t,n){const r=Reflect.has(t,n);return(!an(n)||!Ul.has(n))&&Ve(t,"has",n),r}ownKeys(t){return Ve(t,"iterate",G(t)?"length":Gt),Reflect.ownKeys(t)}}class tu extends $l{constructor(t=!1){super(!0,t)}set(t,n){return!0}deleteProperty(t,n){return!0}}const nu=new jl,ru=new tu,su=new jl(!0),so=e=>e,Fr=e=>Reflect.getPrototypeOf(e);function nr(e,t,n=!1,r=!1){e=e.__v_raw;const o=le(e),s=le(t);n||(Xt(t,s)&&Ve(o,"get",t),Ve(o,"get",s));const{has:i}=Fr(o),l=r?so:n?lo:Un;if(i.call(o,t))return l(e.get(t));if(i.call(o,s))return l(e.get(s));e!==o&&e.get(t)}function rr(e,t=!1){const n=this.__v_raw,r=le(n),o=le(e);return t||(Xt(e,o)&&Ve(r,"has",e),Ve(r,"has",o)),e===o?n.has(e):n.has(e)||n.has(o)}function sr(e,t=!1){return e=e.__v_raw,!t&&Ve(le(e),"iterate",Gt),Reflect.get(e,"size",e)}function Bo(e){e=le(e);const t=le(this);return Fr(t).has.call(t,e)||(t.add(e),yt(t,"add",e,e)),this}function Vo(e,t){t=le(t);const n=le(this),{has:r,get:o}=Fr(n);let s=r.call(n,e);s||(e=le(e),s=r.call(n,e));const i=o.call(n,e);return n.set(e,t),s?Xt(t,i)&&yt(n,"set",e,t):yt(n,"add",e,t),this}function Ko(e){const t=le(this),{has:n,get:r}=Fr(t);let o=n.call(t,e);o||(e=le(e),o=n.call(t,e)),r&&r.call(t,e);const s=t.delete(e);return o&&yt(t,"delete",e,void 0),s}function Yo(){const e=le(this),t=e.size!==0,n=e.clear();return t&&yt(e,"clear",void 0,void 0),n}function or(e,t){return function(r,o){const s=this,i=s.__v_raw,l=le(i),a=t?so:e?lo:Un;return!e&&Ve(l,"iterate",Gt),i.forEach((f,d)=>r.call(o,a(f),a(d),s))}}function ir(e,t,n){return function(...r){const o=this.__v_raw,s=le(o),i=rn(s),l=e==="entries"||e===Symbol.iterator&&i,a=e==="keys"&&i,f=o[e](...r),d=n?so:t?lo:Un;return!t&&Ve(s,"iterate",a?gs:Gt),{next(){const{value:h,done:m}=f.next();return m?{value:h,done:m}:{value:l?[d(h[0]),d(h[1])]:d(h),done:m}},[Symbol.iterator](){return this}}}}function St(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function ou(){const e={get(s){return nr(this,s)},get size(){return sr(this)},has:rr,add:Bo,set:Vo,delete:Ko,clear:Yo,forEach:or(!1,!1)},t={get(s){return nr(this,s,!1,!0)},get size(){return sr(this)},has:rr,add:Bo,set:Vo,delete:Ko,clear:Yo,forEach:or(!1,!0)},n={get(s){return nr(this,s,!0)},get size(){return sr(this,!0)},has(s){return rr.call(this,s,!0)},add:St("add"),set:St("set"),delete:St("delete"),clear:St("clear"),forEach:or(!0,!1)},r={get(s){return nr(this,s,!0,!0)},get size(){return sr(this,!0)},has(s){return rr.call(this,s,!0)},add:St("add"),set:St("set"),delete:St("delete"),clear:St("clear"),forEach:or(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(s=>{e[s]=ir(s,!1,!1),n[s]=ir(s,!0,!1),t[s]=ir(s,!1,!0),r[s]=ir(s,!0,!0)}),[e,n,t,r]}const[iu,lu,au,cu]=ou();function oo(e,t){const n=t?e?cu:au:e?lu:iu;return(r,o,s)=>o==="__v_isReactive"?!e:o==="__v_isReadonly"?e:o==="__v_raw"?r:Reflect.get(ie(n,o)&&o in r?n:r,o,s)}const uu={get:oo(!1,!1)},fu={get:oo(!1,!0)},du={get:oo(!0,!1)},Hl=new WeakMap,Wl=new WeakMap,Bl=new WeakMap,hu=new WeakMap;function mu(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function pu(e){return e.__v_skip||!Object.isExtensible(e)?0:mu(Mc(e))}function zn(e){return cn(e)?e:io(e,!1,nu,uu,Hl)}function Vl(e){return io(e,!1,su,fu,Wl)}function Kl(e){return io(e,!0,ru,du,Bl)}function io(e,t,n,r,o){if(!he(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const s=o.get(e);if(s)return s;const i=pu(e);if(i===0)return e;const l=new Proxy(e,i===2?r:n);return o.set(e,l),l}function kt(e){return cn(e)?kt(e.__v_raw):!!(e&&e.__v_isReactive)}function cn(e){return!!(e&&e.__v_isReadonly)}function vr(e){return!!(e&&e.__v_isShallow)}function Yl(e){return kt(e)||cn(e)}function le(e){const t=e&&e.__v_raw;return t?le(t):e}function Mr(e){return yr(e,"__v_skip",!0),e}const Un=e=>he(e)?zn(e):e,lo=e=>he(e)?Kl(e):e;function Gl(e){wt&&Ze&&(e=le(e),Dl(e.dep||(e.dep=no())))}function ql(e,t){e=le(e);const n=e.dep;n&&bs(n)}function Te(e){return!!(e&&e.__v_isRef===!0)}function qe(e){return Jl(e,!1)}function Xl(e){return Jl(e,!0)}function Jl(e,t){return Te(e)?e:new _u(e,t)}class _u{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:le(t),this._value=n?t:Un(t)}get value(){return Gl(this),this._value}set value(t){const n=this.__v_isShallow||vr(t)||cn(t);t=n?t:le(t),Xt(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Un(t),ql(this))}}function sn(e){return Te(e)?e.value:e}const gu={get:(e,t,n)=>sn(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const o=e[t];return Te(o)&&!Te(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function zl(e){return kt(e)?e:new Proxy(e,gu)}function bu(e){const t=G(e)?new Array(e.length):{};for(const n in e)t[n]=yu(e,n);return t}class Eu{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return zc(le(this._object),this._key)}}function yu(e,t,n){const r=e[t];return Te(r)?r:new Eu(e,t,n)}class Ou{constructor(t,n,r,o){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this._dirty=!0,this.effect=new ro(t,()=>{this._dirty||(this._dirty=!0,ql(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!o,this.__v_isReadonly=r}get value(){const t=le(this);return Gl(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function Tu(e,t,n=!1){let r,o;const s=Q(e);return s?(r=e,o=nt):(r=e.get,o=e.set),new Ou(r,o,s||!o,n)}function xt(e,t,n,r){let o;try{o=r?e(...r):e()}catch(s){Dr(s,t,n)}return o}function rt(e,t,n,r){if(Q(e)){const s=xt(e,t,n,r);return s&&Al(s)&&s.catch(i=>{Dr(i,t,n)}),s}const o=[];for(let s=0;s<e.length;s++)o.push(rt(e[s],t,n,r));return o}function Dr(e,t,n,r=!0){const o=t?t.vnode:null;if(t){let s=t.parent;const i=t.proxy,l=n;for(;s;){const f=s.ec;if(f){for(let d=0;d<f.length;d++)if(f[d](e,i,l)===!1)return}s=s.parent}const a=t.appContext.config.errorHandler;if(a){xt(a,null,10,[e,i,l]);return}}vu(e,n,o,r)}function vu(e,t,n,r=!0){console.error(e)}let $n=!1,Es=!1;const Fe=[];let ut=0;const on=[];let bt=null,Bt=0;const Ql=Promise.resolve();let ao=null;function co(e){const t=ao||Ql;return e?t.then(this?e.bind(this):e):t}function Su(e){let t=ut+1,n=Fe.length;for(;t<n;){const r=t+n>>>1,o=Fe[r],s=jn(o);s<e||s===e&&o.pre?t=r+1:n=r}return t}function uo(e){(!Fe.length||!Fe.includes(e,$n&&e.allowRecurse?ut+1:ut))&&(e.id==null?Fe.push(e):Fe.splice(Su(e.id),0,e),Zl())}function Zl(){!$n&&!Es&&(Es=!0,ao=Ql.then(ta))}function Nu(e){const t=Fe.indexOf(e);t>ut&&Fe.splice(t,1)}function Ru(e){G(e)?on.push(...e):(!bt||!bt.includes(e,e.allowRecurse?Bt+1:Bt))&&on.push(e),Zl()}function Go(e,t=$n?ut+1:0){for(;t<Fe.length;t++){const n=Fe[t];n&&n.pre&&(Fe.splice(t,1),t--,n())}}function ea(e){if(on.length){const t=[...new Set(on)];if(on.length=0,bt){bt.push(...t);return}for(bt=t,bt.sort((n,r)=>jn(n)-jn(r)),Bt=0;Bt<bt.length;Bt++)bt[Bt]();bt=null,Bt=0}}const jn=e=>e.id==null?1/0:e.id,Au=(e,t)=>{const n=jn(e)-jn(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function ta(e){Es=!1,$n=!0,Fe.sort(Au);const t=nt;try{for(ut=0;ut<Fe.length;ut++){const n=Fe[ut];n&&n.active!==!1&&xt(n,null,14)}}finally{ut=0,Fe.length=0,ea(),$n=!1,ao=null,(Fe.length||on.length)&&ta()}}function Lu(e,t,...n){if(e.isUnmounted)return;const r=e.vnode.props||_e;let o=n;const s=t.startsWith("update:"),i=s&&t.slice(7);if(i&&i in r){const d=`${i==="modelValue"?"model":i}Modifiers`,{number:h,trim:m}=r[d]||_e;m&&(o=n.map(b=>Le
Download .txt
gitextract_56eg6khq/

├── .browserslistrc
├── .gitignore
├── .tool-versions
├── Gemfile
├── Procfile
├── README.md
├── Rakefile
├── app/
│   ├── assets/
│   │   ├── config/
│   │   │   └── manifest.js
│   │   ├── images/
│   │   │   └── .keep
│   │   └── stylesheets/
│   │       └── application.css
│   ├── channels/
│   │   ├── application_cable/
│   │   │   ├── channel.rb
│   │   │   └── connection.rb
│   │   └── chat_channel.rb
│   ├── controllers/
│   │   ├── admin_controller.rb
│   │   ├── api/
│   │   │   ├── admin/
│   │   │   │   ├── admin_controller.rb
│   │   │   │   ├── dashboard_controller.rb
│   │   │   │   └── musicians_controller.rb
│   │   │   ├── api_controller.rb
│   │   │   └── musicians_controller.rb
│   │   ├── application_controller.rb
│   │   └── concerns/
│   │       └── .keep
│   ├── helpers/
│   │   └── application_helper.rb
│   ├── javascript/
│   │   ├── admin/
│   │   │   ├── routes.js
│   │   │   ├── stores/
│   │   │   │   ├── dashboard_store.js
│   │   │   │   └── musician_store.js
│   │   │   └── views/
│   │   │       ├── dashboard/
│   │   │       │   └── index.vue
│   │   │       ├── musicians/
│   │   │       │   ├── _filters.vue
│   │   │       │   ├── _form.vue
│   │   │       │   ├── edit.vue
│   │   │       │   ├── index.vue
│   │   │       │   └── new.vue
│   │   │       ├── shared/
│   │   │       │   ├── _errors.vue
│   │   │       │   ├── _footer.vue
│   │   │       │   ├── _nav.vue
│   │   │       │   ├── _pagination.vue
│   │   │       │   └── layout.vue
│   │   │       └── websockets/
│   │   │           └── index.vue
│   │   ├── entrypoints/
│   │   │   ├── admin.js
│   │   │   └── front.js
│   │   ├── front/
│   │   │   ├── routes.js
│   │   │   ├── stores/
│   │   │   │   └── musician_store.js
│   │   │   └── views/
│   │   │       ├── musicians/
│   │   │       │   ├── index.vue
│   │   │       │   └── show.vue
│   │   │       ├── pages/
│   │   │       │   └── index.vue
│   │   │       └── shared/
│   │   │           ├── _footer.vue
│   │   │           ├── _nav.vue
│   │   │           └── layout.vue
│   │   └── plugins/
│   │       ├── api.js
│   │       └── cable.js
│   ├── jobs/
│   │   └── application_job.rb
│   ├── mailers/
│   │   └── application_mailer.rb
│   ├── models/
│   │   ├── application_record.rb
│   │   ├── concerns/
│   │   │   └── .keep
│   │   ├── musician.rb
│   │   └── user.rb
│   └── views/
│       ├── admin.html.erb
│       ├── api/
│       │   ├── admin/
│       │   │   ├── musicians/
│       │   │   │   ├── edit.json.jbuilder
│       │   │   │   ├── index.json.jbuilder
│       │   │   │   └── new.json.jbuilder
│       │   │   ├── shared/
│       │   │   │   └── _pagination.json.jbuilder
│       │   │   └── users/
│       │   │       ├── edit.json.jbuilder
│       │   │       ├── index.json.jbuilder
│       │   │       └── new.json.jbuilder
│       │   └── musicians/
│       │       ├── index.json.jbuilder
│       │       └── show.json.jbuilder
│       ├── application.html.erb
│       ├── devise/
│       │   └── sessions/
│       │       └── new.html.erb
│       └── layouts/
│           ├── devise.html.erb
│           ├── mailer.html.erb
│           └── mailer.text.erb
├── bin/
│   ├── bootsnap
│   ├── bundle
│   ├── foreman
│   ├── importmap
│   ├── irb
│   ├── nokogiri
│   ├── puma
│   ├── pumactl
│   ├── racc
│   ├── rackup
│   ├── rails
│   ├── rake
│   ├── rdbg
│   ├── setup
│   ├── spring
│   ├── sprockets
│   ├── thor
│   ├── tilt
│   └── vite
├── config/
│   ├── application.rb
│   ├── boot.rb
│   ├── cable.yml
│   ├── credentials.yml.enc
│   ├── database.yml
│   ├── environment.rb
│   ├── environments/
│   │   ├── development.rb
│   │   ├── production.rb
│   │   └── test.rb
│   ├── importmap.rb
│   ├── initializers/
│   │   ├── assets.rb
│   │   ├── content_security_policy.rb
│   │   ├── cypress_rails_initializer.rb
│   │   ├── devise.rb
│   │   ├── filter_parameter_logging.rb
│   │   ├── inflections.rb
│   │   ├── locales.rb
│   │   ├── new_framework_defaults_7_1.rb
│   │   └── permissions_policy.rb
│   ├── locales/
│   │   ├── devise.en.yml
│   │   ├── en.yml
│   │   └── fr.yml
│   ├── puma.rb
│   ├── routes.rb
│   ├── storage.yml
│   └── vite.json
├── config.ru
├── cypress/
│   ├── e2e/
│   │   └── home.cy.js
│   └── support/
│       └── e2e.js
├── cypress.config.js
├── db/
│   ├── migrate/
│   │   ├── 20220424120800_base_setup.rb
│   │   ├── 20240102193807_add_service_name_to_active_storage_blobs.active_storage.rb
│   │   ├── 20240102193808_create_active_storage_variant_records.active_storage.rb
│   │   └── 20240102193809_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
│   ├── schema.rb
│   └── seeds.rb
├── docker-compose.yml
├── lib/
│   ├── assets/
│   │   └── .keep
│   └── tasks/
│       └── .keep
├── log/
│   └── .keep
├── package.json
├── public/
│   ├── 404.html
│   ├── 422.html
│   ├── 500.html
│   ├── css/
│   │   └── development/
│   │       ├── admin/
│   │       │   └── forms.css
│   │       ├── admin.css
│   │       ├── devise.css
│   │       ├── front.css
│   │       ├── grid.css
│   │       ├── main.css
│   │       ├── themes/
│   │       │   ├── dark.css
│   │       │   └── light.css
│   │       └── utilities.css
│   ├── robots.txt
│   └── vite-test/
│       ├── assets/
│       │   ├── admin-ebb17751.js
│       │   ├── front-f6acb49a.js
│       │   └── vue-i18n-6b73e0ca.js
│       ├── manifest-assets.json
│       └── manifest.json
├── storage/
│   └── .keep
├── test/
│   ├── application_system_test_case.rb
│   ├── controllers/
│   │   └── .keep
│   ├── fixtures/
│   │   └── files/
│   │       └── .keep
│   ├── helpers/
│   │   └── .keep
│   ├── javascript/
│   │   └── app.test.js
│   ├── mailers/
│   │   └── .keep
│   ├── models/
│   │   └── .keep
│   ├── system/
│   │   └── .keep
│   └── test_helper.rb
├── tmp/
│   └── .keep
├── vendor/
│   ├── .keep
│   └── javascript/
│       └── .keep
└── vite.config.ts
Download .txt
SYMBOL INDEX (725 symbols across 32 files)

FILE: app/channels/application_cable/channel.rb
  type ApplicationCable (line 1) | module ApplicationCable
    class Channel (line 2) | class Channel < ActionCable::Channel::Base

FILE: app/channels/application_cable/connection.rb
  type ApplicationCable (line 1) | module ApplicationCable
    class Connection (line 2) | class Connection < ActionCable::Connection::Base

FILE: app/channels/chat_channel.rb
  class ChatChannel (line 1) | class ChatChannel < ApplicationCable::Channel
    method subscribed (line 3) | def subscribed
    method receive (line 7) | def receive(data)

FILE: app/controllers/admin_controller.rb
  class AdminController (line 1) | class AdminController < ApplicationController
    method index (line 4) | def index
    method auth_user! (line 12) | def auth_user!

FILE: app/controllers/api/admin/admin_controller.rb
  class Api::Admin::AdminController (line 1) | class Api::Admin::AdminController < Api::ApiController
    method search_params (line 4) | def search_params

FILE: app/controllers/api/admin/dashboard_controller.rb
  class Api::Admin::DashboardController (line 1) | class Api::Admin::DashboardController < Api::Admin::AdminController
    method index (line 2) | def index

FILE: app/controllers/api/admin/musicians_controller.rb
  class Api::Admin::MusiciansController (line 1) | class Api::Admin::MusiciansController < Api::Admin::AdminController
    method index (line 6) | def index
    method new (line 13) | def new
    method create (line 17) | def create
    method edit (line 27) | def edit
    method update (line 30) | def update
    method destroy (line 38) | def destroy
    method musician_params (line 48) | def musician_params
    method load_musician (line 55) | def load_musician
    method slow (line 59) | def slow

FILE: app/controllers/api/api_controller.rb
  class Api::ApiController (line 1) | class Api::ApiController < ApplicationController

FILE: app/controllers/api/musicians_controller.rb
  class Api::MusiciansController (line 1) | class Api::MusiciansController < Api::ApiController
    method index (line 3) | def index
    method show (line 7) | def show

FILE: app/controllers/application_controller.rb
  class ApplicationController (line 1) | class ApplicationController < ActionController::Base
    method index (line 6) | def index
    method set_locale (line 10) | def set_locale
    method default_url_options (line 15) | def self.default_url_options
    method after_sign_in_path_for (line 19) | def after_sign_in_path_for(resource)
    method render_error (line 24) | def render_error(e)

FILE: app/helpers/application_helper.rb
  type ApplicationHelper (line 1) | module ApplicationHelper
    function ui_translations (line 3) | def ui_translations(section)
    function paginate (line 8) | def paginate(scope, default_per_page = scope.default_per_page)

FILE: app/javascript/admin/stores/dashboard_store.js
  method index (line 11) | async index() {

FILE: app/javascript/admin/stores/musician_store.js
  method index (line 16) | async index(path) {
  method new (line 23) | async new() {
  method create (line 30) | async create() {
  method edit (line 43) | async edit(id) {
  method update (line 50) | async update(id) {
  method destroy (line 61) | async destroy(id) {

FILE: app/javascript/front/stores/musician_store.js
  method index (line 12) | async index(path) {
  method show (line 17) | async show(id) {

FILE: app/javascript/plugins/api.js
  function Api (line 3) | function Api() {
  function createApi (line 20) | function createApi(args) {

FILE: app/javascript/plugins/cable.js
  function Cable (line 9) | function Cable() {}
  function createCable (line 24) | function createCable(options) {

FILE: app/jobs/application_job.rb
  class ApplicationJob (line 1) | class ApplicationJob < ActiveJob::Base

FILE: app/mailers/application_mailer.rb
  class ApplicationMailer (line 1) | class ApplicationMailer < ActionMailer::Base

FILE: app/models/application_record.rb
  class ApplicationRecord (line 1) | class ApplicationRecord < ActiveRecord::Base

FILE: app/models/musician.rb
  class Musician (line 1) | class Musician < ApplicationRecord
    method ransackable_attributes (line 8) | def self.ransackable_attributes(auth_object = nil)

FILE: app/models/user.rb
  class User (line 1) | class User < ApplicationRecord

FILE: config/application.rb
  type Next (line 9) | module Next
    class Application (line 10) | class Application < Rails::Application

FILE: cypress.config.js
  method setupNodeEvents (line 8) | setupNodeEvents(on, config) {

FILE: db/migrate/20220424120800_base_setup.rb
  class BaseSetup (line 1) | class BaseSetup < ActiveRecord::Migration[7.0]
    method change (line 2) | def change

FILE: db/migrate/20240102193807_add_service_name_to_active_storage_blobs.active_storage.rb
  class AddServiceNameToActiveStorageBlobs (line 2) | class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
    method up (line 3) | def up
    method down (line 17) | def down

FILE: db/migrate/20240102193808_create_active_storage_variant_records.active_storage.rb
  class CreateActiveStorageVariantRecords (line 2) | class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
    method change (line 3) | def change
    method primary_key_type (line 17) | def primary_key_type
    method blobs_primary_key_type (line 22) | def blobs_primary_key_type

FILE: db/migrate/20240102193809_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
  class RemoveNotNullOnActiveStorageBlobsChecksum (line 2) | class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migratio...
    method change (line 3) | def change

FILE: public/vite-test/assets/admin-ebb17751.js
  method index (line 1) | async index(){this.axios.get("/dashboard").then(e=>{this.metrics=e.data....
  method setup (line 1) | setup(){return{store:D()}}
  method created (line 1) | created(){this.$api.call(this.store.index())}
  function Y (line 1) | function Y(e,s,o,u,h,l){return r(),c("section",W,[t("div",z,[J,t("div",G...
  method index (line 1) | async index(e){return this.axios.get(e).then(s=>{this.pagination=s.data....
  method new (line 1) | async new(){return this.errors={},this.musician={},this.axios.get("/musi...
  method create (line 1) | async create(){return this.errors={},this.axios.post("/musicians",this.m...
  method edit (line 1) | async edit(e){return this.errors={},this.musician={},this.axios.get(`/mu...
  method update (line 1) | async update(e){return this.errors={},this.axios.put(`/musicians/${e}`,t...
  method destroy (line 1) | async destroy(e){return this.errors={},this.axios.delete(`/musicians/${e...
  method toggleForm (line 1) | toggleForm(){this.$refs.filters.classList.toggle("hidden")}
  method search (line 1) | search(){const e=Object.fromEntries(Object.entries(this.form).map(s=>[`q...
  method reset (line 1) | reset(){this.form={name_cont:"",band_eq:null},this.$router.push({path:th...
  function ut (line 1) | function ut(e,s,o,u,h,l){return r(),c("section",null,[t("a",{href:"#",on...
  method setup (line 1) | setup(){return{store:A()}}
  method mounted (line 1) | mounted(){this.index()}
  method index (line 1) | index(){this.$api.call(this.store.index(this.$route.fullPath),this.$refs...
  function wt (line 1) | function wt(e,s,o,u,h,l){const n=p("router-link"),a=p("filters"),$=p("pa...
  method data (line 1) | data(){return{message:""}}
  function Ft (line 1) | function Ft(e,s,o,u,h,l){return h.message!=""?(r(),c("span",St,i(h.messa...
  function Nt (line 1) | function Nt(e,s,o,u,h,l){const n=p("errors");return r(),c("section",null...
  method setup (line 1) | setup(){return{store:A()}}
  method mounted (line 1) | mounted(){this.$api.call(this.store.new(),this.$refs.animation)}
  method create (line 1) | create(e){this.$api.call(this.store.create(),e.target).then(s=>{s===!0&&...
  function Rt (line 1) | function Rt(e,s,o,u,h,l){const n=p("router-link"),a=p("MusicianForm");re...
  method setup (line 1) | setup(){return{store:A()}}
  method mounted (line 1) | mounted(){this.$api.call(this.store.edit(this.$route.params.id),this.$re...
  method update (line 1) | update(e){this.$api.call(this.store.update(this.$route.params.id),e.targ...
  method destroy (line 1) | destroy(){confirm(this.$t("confirm"))&&this.$api.call(this.store.destroy...
  function Xt (line 1) | function Xt(e,s,o,u,h,l){const n=p("router-link"),a=p("MusicianForm");re...
  method data (line 1) | data(){return{message:"",messages:[]}}
  method created (line 1) | created(){this.$cable.on("chat",e=>{this.messages.unshift(e.message)})}
  method publish (line 1) | publish(){this.$cable.send(this.message),this.message=""}
  function _e (line 1) | function _e(e,s,o,u,h,l){const n=p("router-link");return r(),c("section"...
  method data (line 1) | data(){return{availableLocales:window.I18n.availableLocales,locale:windo...
  method activeOn (line 1) | activeOn(e){return e.includes(this.$route.name)?"active":""}
  function Ce (line 1) | function Ce(e,s,o,u,h,l){const n=p("router-link");return r(),c("section"...
  function Ae (line 1) | function Ae(e,s,o,u,h,l){const n=p("nav-bar"),a=p("router-view");return ...
  method setQuery (line 1) | setQuery(e){let s=JSON.parse(JSON.stringify(this.$route.query));return s...
  function Re (line 1) | function Re(e,s,o,u,h,l){const n=p("router-link");return r(),c("section"...

FILE: public/vite-test/assets/front-f6acb49a.js
  method index (line 1) | async index(){return this.axios.get("/musicians").then(t=>{this.musician...
  method show (line 1) | async show(t){return this.axios.get(`/musicians/${t}`).then(n=>{this.mus...
  method setup (line 1) | setup(){return{store:v()}}
  method unauthorized (line 1) | unauthorized(){this.$api.call(this.store.show("this-will-trigger-a-401"))}
  method crash (line 1) | crash(){this.$api.call(this.store.show("this-will-trigger-a-500"))}
  function U (line 1) | function U(t,n,$,r,d,o){const i=p("router-link");return c(),l("section",...
  method setup (line 1) | setup(){return{store:v()}}
  method created (line 1) | created(){this.$api.call(this.store.index())}
  function G (line 1) | function G(t,n,$,r,d,o){const i=p("router-link");return c(),l("section",...
  method setup (line 1) | setup(){return{store:v()}}
  method created (line 1) | created(){this.$api.call(this.store.show(this.$route.params.id))}
  function Z (line 1) | function Z(t,n,$,r,d,o){const i=p("router-link");return c(),l("section",...
  method data (line 1) | data(){return{availableLocales:window.I18n.availableLocales,locale:windo...
  method activeOn (line 1) | activeOn(t){return t.includes(this.$route.name)?"active":""}
  function le (line 1) | function le(t,n,$,r,d,o){const i=p("router-link");return c(),l("section"...
  function he (line 1) | function he(t,n,$,r,d,o){const i=p("nav-bar"),a=p("router-view");return ...

FILE: public/vite-test/assets/vue-i18n-6b73e0ca.js
  function Xs (line 1) | function Xs(e,t){const n=Object.create(null),r=e.split(",");for(let o=0;...
  function Zs (line 1) | function Zs(e){if(G(e)){const t={};for(let n=0;n<e.length;n++){const r=e...
  function Wc (line 1) | function Wc(e){const t={};return e.replace(Hc,"").split($c).forEach(n=>{...
  function eo (line 1) | function eo(e){let t="";if(Le(e))t=e;else if(G(e))for(let n=0;n<e.length...
  function Pl (line 1) | function Pl(e){return!!e||e===""}
  function Kc (line 1) | function Kc(e,t){if(e.length!==t.length)return!1;let n=!0;for(let r=0;n&...
  function xr (line 1) | function xr(e,t){if(e===t)return!0;let n=Uo(e),r=Uo(t);if(n||r)return n&...
  function Yc (line 1) | function Yc(e,t){return e.findIndex(n=>xr(n,t))}
  class wl (line 1) | class wl{constructor(t=!1){this.detached=t,this._active=!0,this.effects=...
    method constructor (line 1) | constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this...
    method active (line 1) | get active(){return this._active}
    method run (line 1) | run(t){if(this._active){const n=Ge;try{return Ge=this,t()}finally{Ge=n}}}
    method on (line 1) | on(){Ge=this}
    method off (line 1) | off(){Ge=this.parent}
    method stop (line 1) | stop(t){if(this._active){let n,r;for(n=0,r=this.effects.length;n<r;n++...
  function to (line 1) | function to(e){return new wl(e)}
  function Gc (line 1) | function Gc(e,t=Ge){t&&t.active&&t.effects.push(e)}
  function kl (line 1) | function kl(){return Ge}
  function qc (line 1) | function qc(e){Ge&&Ge.cleanups.push(e)}
  class ro (line 1) | class ro{constructor(t,n=null,r){this.fn=t,this.scheduler=n,this.active=...
    method constructor (line 1) | constructor(t,n=null,r){this.fn=t,this.scheduler=n,this.active=!0,this...
    method run (line 1) | run(){if(!this.active)return this.fn();let t=Ze,n=wt;for(;t;){if(t===t...
    method stop (line 1) | stop(){Ze===this?this.deferStop=!0:this.active&&(jo(this),this.onStop&...
  function jo (line 1) | function jo(e){const{deps:t}=e;if(t.length){for(let n=0;n<t.length;n++)t...
  function En (line 1) | function En(){Ml.push(wt),wt=!1}
  function yn (line 1) | function yn(){const e=Ml.pop();wt=e===void 0?!0:e}
  function Ve (line 1) | function Ve(e,t,n){if(wt&&Ze){let r=Tr.get(e);r||Tr.set(e,r=new Map);let...
  function Dl (line 1) | function Dl(e,t){let n=!1;Cn<=_s?Fl(e)||(e.n|=Mt,n=!xl(e)):n=!e.has(Ze),...
  function yt (line 1) | function yt(e,t,n,r,o,s){const i=Tr.get(e);if(!i)return;let l=[];if(t===...
  function bs (line 1) | function bs(e,t){const n=G(e)?e:[...e];for(const r of n)r.computed&&Ho(r...
  function Ho (line 1) | function Ho(e,t){(e!==Ze||e.allowRecurse)&&(e.scheduler?e.scheduler():e....
  function zc (line 1) | function zc(e,t){var n;return(n=Tr.get(e))==null?void 0:n.get(t)}
  function Zc (line 1) | function Zc(){const e={};return["includes","indexOf","lastIndexOf"].forE...
  function eu (line 1) | function eu(e){const t=le(this);return Ve(t,"has",e),t.hasOwnProperty(e)}
  class $l (line 1) | class $l{constructor(t=!1,n=!1){this._isReadonly=t,this._shallow=n}get(t...
    method constructor (line 1) | constructor(t=!1,n=!1){this._isReadonly=t,this._shallow=n}
    method get (line 1) | get(t,n,r){const o=this._isReadonly,s=this._shallow;if(n==="__v_isReac...
  class jl (line 1) | class jl extends $l{constructor(t=!1){super(!1,t)}set(t,n,r,o){let s=t[n...
    method constructor (line 1) | constructor(t=!1){super(!1,t)}
    method set (line 1) | set(t,n,r,o){let s=t[n];if(cn(s)&&Te(s)&&!Te(r))return!1;if(!this._sha...
    method deleteProperty (line 1) | deleteProperty(t,n){const r=ie(t,n);t[n];const o=Reflect.deletePropert...
    method has (line 1) | has(t,n){const r=Reflect.has(t,n);return(!an(n)||!Ul.has(n))&&Ve(t,"ha...
    method ownKeys (line 1) | ownKeys(t){return Ve(t,"iterate",G(t)?"length":Gt),Reflect.ownKeys(t)}
  class tu (line 1) | class tu extends $l{constructor(t=!1){super(!0,t)}set(t,n){return!0}dele...
    method constructor (line 1) | constructor(t=!1){super(!0,t)}
    method set (line 1) | set(t,n){return!0}
    method deleteProperty (line 1) | deleteProperty(t,n){return!0}
  function nr (line 1) | function nr(e,t,n=!1,r=!1){e=e.__v_raw;const o=le(e),s=le(t);n||(Xt(t,s)...
  function rr (line 1) | function rr(e,t=!1){const n=this.__v_raw,r=le(n),o=le(e);return t||(Xt(e...
  function sr (line 1) | function sr(e,t=!1){return e=e.__v_raw,!t&&Ve(le(e),"iterate",Gt),Reflec...
  function Bo (line 1) | function Bo(e){e=le(e);const t=le(this);return Fr(t).has.call(t,e)||(t.a...
  function Vo (line 1) | function Vo(e,t){t=le(t);const n=le(this),{has:r,get:o}=Fr(n);let s=r.ca...
  function Ko (line 1) | function Ko(e){const t=le(this),{has:n,get:r}=Fr(t);let o=n.call(t,e);o|...
  function Yo (line 1) | function Yo(){const e=le(this),t=e.size!==0,n=e.clear();return t&&yt(e,"...
  function or (line 1) | function or(e,t){return function(r,o){const s=this,i=s.__v_raw,l=le(i),a...
  function ir (line 1) | function ir(e,t,n){return function(...r){const o=this.__v_raw,s=le(o),i=...
  function St (line 1) | function St(e){return function(...t){return e==="delete"?!1:e==="clear"?...
  function ou (line 1) | function ou(){const e={get(s){return nr(this,s)},get size(){return sr(th...
  function oo (line 1) | function oo(e,t){const n=t?e?cu:au:e?lu:iu;return(r,o,s)=>o==="__v_isRea...
  function mu (line 1) | function mu(e){switch(e){case"Object":case"Array":return 1;case"Map":cas...
  function pu (line 1) | function pu(e){return e.__v_skip||!Object.isExtensible(e)?0:mu(Mc(e))}
  function zn (line 1) | function zn(e){return cn(e)?e:io(e,!1,nu,uu,Hl)}
  function Vl (line 1) | function Vl(e){return io(e,!1,su,fu,Wl)}
  function Kl (line 1) | function Kl(e){return io(e,!0,ru,du,Bl)}
  function io (line 1) | function io(e,t,n,r,o){if(!he(e)||e.__v_raw&&!(t&&e.__v_isReactive))retu...
  function kt (line 1) | function kt(e){return cn(e)?kt(e.__v_raw):!!(e&&e.__v_isReactive)}
  function cn (line 1) | function cn(e){return!!(e&&e.__v_isReadonly)}
  function vr (line 1) | function vr(e){return!!(e&&e.__v_isShallow)}
  function Yl (line 1) | function Yl(e){return kt(e)||cn(e)}
  function le (line 1) | function le(e){const t=e&&e.__v_raw;return t?le(t):e}
  function Mr (line 1) | function Mr(e){return yr(e,"__v_skip",!0),e}
  function Gl (line 1) | function Gl(e){wt&&Ze&&(e=le(e),Dl(e.dep||(e.dep=no())))}
  function ql (line 1) | function ql(e,t){e=le(e);const n=e.dep;n&&bs(n)}
  function Te (line 1) | function Te(e){return!!(e&&e.__v_isRef===!0)}
  function qe (line 1) | function qe(e){return Jl(e,!1)}
  function Xl (line 1) | function Xl(e){return Jl(e,!0)}
  function Jl (line 1) | function Jl(e,t){return Te(e)?e:new _u(e,t)}
  class _u (line 1) | class _u{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_...
    method constructor (line 1) | constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!...
    method value (line 1) | get value(){return Gl(this),this._value}
    method value (line 1) | set value(t){const n=this.__v_isShallow||vr(t)||cn(t);t=n?t:le(t),Xt(t...
  function sn (line 1) | function sn(e){return Te(e)?e.value:e}
  function zl (line 1) | function zl(e){return kt(e)?e:new Proxy(e,gu)}
  function bu (line 1) | function bu(e){const t=G(e)?new Array(e.length):{};for(const n in e)t[n]...
  class Eu (line 1) | class Eu{constructor(t,n,r){this._object=t,this._key=n,this._defaultValu...
    method constructor (line 1) | constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,thi...
    method value (line 1) | get value(){const t=this._object[this._key];return t===void 0?this._de...
    method value (line 1) | set value(t){this._object[this._key]=t}
    method dep (line 1) | get dep(){return zc(le(this._object),this._key)}
  function yu (line 1) | function yu(e,t,n){const r=e[t];return Te(r)?r:new Eu(e,t,n)}
  class Ou (line 1) | class Ou{constructor(t,n,r,o){this._setter=n,this.dep=void 0,this.__v_is...
    method constructor (line 1) | constructor(t,n,r,o){this._setter=n,this.dep=void 0,this.__v_isRef=!0,...
    method value (line 1) | get value(){const t=le(this);return Gl(t),(t._dirty||!t._cacheable)&&(...
    method value (line 1) | set value(t){this._setter(t)}
  function Tu (line 1) | function Tu(e,t,n=!1){let r,o;const s=Q(e);return s?(r=e,o=nt):(r=e.get,...
  function xt (line 1) | function xt(e,t,n,r){let o;try{o=r?e(...r):e()}catch(s){Dr(s,t,n)}return o}
  function rt (line 1) | function rt(e,t,n,r){if(Q(e)){const s=xt(e,t,n,r);return s&&Al(s)&&s.cat...
  function Dr (line 1) | function Dr(e,t,n,r=!0){const o=t?t.vnode:null;if(t){let s=t.parent;cons...
  function vu (line 1) | function vu(e,t,n,r=!0){console.error(e)}
  function co (line 1) | function co(e){const t=ao||Ql;return e?t.then(this?e.bind(this):e):t}
  function Su (line 1) | function Su(e){let t=ut+1,n=Fe.length;for(;t<n;){const r=t+n>>>1,o=Fe[r]...
  function uo (line 1) | function uo(e){(!Fe.length||!Fe.includes(e,$n&&e.allowRecurse?ut+1:ut))&...
  function Zl (line 1) | function Zl(){!$n&&!Es&&(Es=!0,ao=Ql.then(ta))}
  function Nu (line 1) | function Nu(e){const t=Fe.indexOf(e);t>ut&&Fe.splice(t,1)}
  function Ru (line 1) | function Ru(e){G(e)?on.push(...e):(!bt||!bt.includes(e,e.allowRecurse?Bt...
  function Go (line 1) | function Go(e,t=$n?ut+1:0){for(;t<Fe.length;t++){const n=Fe[t];n&&n.pre&...
  function ea (line 1) | function ea(e){if(on.length){const t=[...new Set(on)];if(on.length=0,bt)...
  function ta (line 1) | function ta(e){Es=!1,$n=!0,Fe.sort(Au);const t=nt;try{for(ut=0;ut<Fe.len...
  function Lu (line 1) | function Lu(e,t,...n){if(e.isUnmounted)return;const r=e.vnode.props||_e;...
  function na (line 1) | function na(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(o!==void 0)retu...
  function Ur (line 1) | function Ur(e,t){return!e||!Pr(t)?!1:(t=t.slice(2).replace(/Once$/,""),i...
  function Sr (line 1) | function Sr(e){const t=Be;return Be=e,ra=e&&e.type.__scopeId||null,t}
  function Cu (line 1) | function Cu(e,t=Be,n){if(!t||e._n)return e;const r=(...o)=>{r._d&&si(-1)...
  function ns (line 1) | function ns(e){const{type:t,vnode:n,proxy:r,withProxy:o,props:s,propsOpt...
  function wu (line 1) | function wu(e,t,n){const{props:r,children:o,component:s}=e,{props:i,chil...
  function qo (line 1) | function qo(e,t,n){const r=Object.keys(t);if(r.length!==Object.keys(e).l...
  function ku (line 1) | function ku({vnode:e,parent:t},n){for(;t&&t.subTree===e;)(e=t.vnode).el=...
  function H_ (line 1) | function H_(e,t){return Fu(sa,e,!0,t)||e}
  function Fu (line 1) | function Fu(e,t,n=!0,r=!1){const o=Be||Ne;if(o){const s=o.type;if(e===sa...
  function Xo (line 1) | function Xo(e,t){return e&&(e[t]||e[ht(t)]||e[kr(ht(t))])}
  function Du (line 1) | function Du(e,t){t&&t.pendingBranch?G(e)?t.effects.push(...e):t.effects....
  function Ft (line 1) | function Ft(e,t,n){return oa(e,t,n)}
  function oa (line 1) | function oa(e,t,{immediate:n,deep:r,flush:o,onTrack:s,onTrigger:i}=_e){v...
  function Uu (line 1) | function Uu(e,t,n){const r=this.proxy,o=Le(e)?e.includes(".")?ia(r,e):()...
  function ia (line 1) | function ia(e,t){const n=t.split(".");return()=>{let r=e;for(let o=0;o<n...
  function Yt (line 1) | function Yt(e,t){if(!he(e)||e.__v_skip||(t=t||new Set,t.has(e)))return e...
  function W_ (line 1) | function W_(e,t){const n=Be;if(n===null)return e;const r=Hr(n)||n.proxy,...
  function jt (line 1) | function jt(e,t,n,r){const o=e.dirs,s=t&&t.dirs;for(let i=0;i<o.length;i...
  function Qn (line 1) | function Qn(e,t){return Q(e)?(()=>ke({name:e.name},t,{setup:e}))():e}
  function $u (line 1) | function $u(e,t){aa(e,"a",t)}
  function ju (line 1) | function ju(e,t){aa(e,"da",t)}
  function aa (line 1) | function aa(e,t,n=Ne){const r=e.__wdc||(e.__wdc=()=>{let o=n;for(;o;){if...
  function Hu (line 1) | function Hu(e,t,n,r){const o=$r(t,e,r,!0);fo(()=>{zs(r[t],o)},n)}
  function $r (line 1) | function $r(e,t,n=Ne,r=!1){if(n){const o=n[e]||(n[e]=[]),s=t.__weh||(t._...
  function qu (line 1) | function qu(e,t=Ne){$r("ec",e,t)}
  function B_ (line 1) | function B_(e,t,n,r){let o;const s=n&&n[r];if(G(e)||Le(e)){o=new Array(e...
  method get (line 1) | get({_:e},t){const{ctx:n,setupState:r,data:o,props:s,accessCache:i,type:...
  method set (line 1) | set({_:e},t,n){const{data:r,setupState:o,ctx:s}=e;return rs(o,t)?(o[t]=n...
  method has (line 1) | has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOption...
  method defineProperty (line 1) | defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:ie(n,"valu...
  function Jo (line 1) | function Jo(e){return G(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}
  function Ju (line 1) | function Ju(e){const t=ho(e),n=e.proxy,r=e.ctx;Os=!1,t.beforeCreate&&zo(...
  function zu (line 1) | function zu(e,t,n=nt){G(e)&&(e=Ts(e));for(const r in e){const o=e[r];let...
  function zo (line 1) | function zo(e,t,n){rt(G(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}
  function fa (line 1) | function fa(e,t,n,r){const o=r.includes(".")?ia(n,r):()=>n[r];if(Le(e)){...
  function ho (line 1) | function ho(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCa...
  function Nr (line 1) | function Nr(e,t,n,r=!1){const{mixins:o,extends:s}=t;s&&Nr(e,s,n,!0),o&&o...
  function Qo (line 1) | function Qo(e,t){return t?e?function(){return ke(Q(e)?e.call(this,this):...
  function Zu (line 1) | function Zu(e,t){return Pn(Ts(e),Ts(t))}
  function Ts (line 1) | function Ts(e){if(G(e)){const t={};for(let n=0;n<e.length;n++)t[e[n]]=e[...
  function De (line 1) | function De(e,t){return e?[...new Set([].concat(e,t))]:t}
  function Pn (line 1) | function Pn(e,t){return e?ke(Object.create(null),e,t):t}
  function Zo (line 1) | function Zo(e,t){return e?G(e)&&G(t)?[...new Set([...e,...t])]:ke(Object...
  function ef (line 1) | function ef(e,t){if(!e)return t;if(!t)return e;const n=ke(Object.create(...
  function da (line 1) | function da(){return{app:null,config:{isNativeTag:xc,performance:!1,glob...
  function nf (line 1) | function nf(e,t){return function(r,o=null){Q(r)||(r=ke({},r)),o!=null&&!...
  function hr (line 1) | function hr(e,t){if(Ne){let n=Ne.provides;const r=Ne.parent&&Ne.parent.p...
  function st (line 1) | function st(e,t,n=!1){const r=Ne||Be;if(r||Hn){const o=r?r.parent==null?...
  function rf (line 1) | function rf(){return!!(Ne||Be||Hn)}
  function sf (line 1) | function sf(e,t,n,r=!1){const o={},s={};yr(s,jr,1),e.propsDefaults=Objec...
  function of (line 1) | function of(e,t,n,r){const{props:o,attrs:s,vnode:{patchFlag:i}}=e,l=le(o...
  function ha (line 1) | function ha(e,t,n,r){const[o,s]=e.propsOptions;let i=!1,l;if(t)for(let a...
  function vs (line 1) | function vs(e,t,n,r,o,s){const i=e[n];if(i!=null){const l=ie(i,"default"...
  function ma (line 1) | function ma(e,t,n=!1){const r=t.propsCache,o=r.get(e);if(o)return o;cons...
  function ei (line 1) | function ei(e){return e[0]!=="$"}
  function ti (line 1) | function ti(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)...
  function ni (line 1) | function ni(e,t){return ti(e)===ti(t)}
  function ri (line 1) | function ri(e,t){return G(t)?t.findIndex(n=>ni(n,e)):Q(t)&&ni(t,e)?0:-1}
  function Ss (line 1) | function Ss(e,t,n,r,o=!1){if(G(e)){e.forEach((m,b)=>Ss(m,t&&(G(t)?t[b]:t...
  function uf (line 1) | function uf(e){return ff(e)}
  function ff (line 1) | function ff(e,t){const n=ps();n.__VUE__=!0;const{insert:r,remove:o,patch...
  function Ht (line 1) | function Ht({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}
  function df (line 1) | function df(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}
  function ba (line 1) | function ba(e,t,n=!1){const r=e.children,o=t.children;if(G(r)&&G(o))for(...
  function hf (line 1) | function hf(e){const t=e.slice(),n=[0];let r,o,s,i,l;const a=e.length;fo...
  function Ea (line 1) | function Ea(e=!1){kn.push(et=e?null:[])}
  function pf (line 1) | function pf(){kn.pop(),et=kn[kn.length-1]||null}
  function si (line 1) | function si(e){Wn+=e}
  function ya (line 1) | function ya(e){return e.dynamicChildren=Wn>0?et||nn:null,pf(),Wn>0&&et&&...
  function _f (line 1) | function _f(e,t,n,r,o,s){return ya(po(e,t,n,r,o,s,!0))}
  function gf (line 1) | function gf(e,t,n,r,o){return ya($e(e,t,n,r,o,!0))}
  function Ns (line 1) | function Ns(e){return e?e.__v_isVNode===!0:!1}
  function Sn (line 1) | function Sn(e,t){return e.type===t.type&&e.key===t.key}
  function po (line 1) | function po(e,t=null,n=null,r=0,o=null,s=e===Qe?0:1,i=!1,l=!1){const a={...
  function bf (line 1) | function bf(e,t=null,n=null,r=0,o=null,s=!1){if((!e||e===xu)&&(e=Jt),Ns(...
  function Ef (line 1) | function Ef(e){return e?Yl(e)||jr in e?ke({},e):e:null}
  function un (line 1) | function un(e,t,n=!1){const{props:r,ref:o,patchFlag:s,children:i}=e,l=t?...
  function yf (line 1) | function yf(e=" ",t=0){return $e(Zn,null,e,t)}
  function V_ (line 1) | function V_(e,t){const n=$e(mr,null,e);return n.staticCount=t,n}
  function K_ (line 1) | function K_(e="",t=!1){return t?(Ea(),gf(Jt,null,e)):$e(Jt,null,e)}
  function ct (line 1) | function ct(e){return e==null||typeof e=="boolean"?$e(Jt):G(e)?$e(Qe,nul...
  function Pt (line 1) | function Pt(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:un(e)}
  function _o (line 1) | function _o(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(...
  function Of (line 1) | function Of(...e){const t={};for(let n=0;n<e.length;n++){const r=e[n];fo...
  function lt (line 1) | function lt(e,t,n,r=null){rt(e,t,7,[n,r])}
  function Sf (line 1) | function Sf(e,t,n){const r=e.type,o=(t?t.appContext:e.appContext)||Tf,s=...
  function Ta (line 1) | function Ta(e){return e.vnode.shapeFlag&4}
  function Nf (line 1) | function Nf(e,t=!1){Vn=t;const{props:n,children:r}=e.vnode,o=Ta(e);sf(e,...
  function Rf (line 1) | function Rf(e,t){const n=e.type;e.accessCache=Object.create(null),e.prox...
  function ii (line 1) | function ii(e,t,n){Q(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=...
  function va (line 1) | function va(e,t,n){const r=e.type;if(!e.render){if(!t&&li&&!r.render){co...
  function Af (line 1) | function Af(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get...
  function Lf (line 1) | function Lf(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return Af...
  function Hr (line 1) | function Hr(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Pro...
  function Cf (line 1) | function Cf(e,t=!0){return Q(e)?e.displayName||e.name:e.name||t&&e.__name}
  function Pf (line 1) | function Pf(e){return Q(e)&&"__vccOpts"in e}
  function Wr (line 1) | function Wr(e,t,n){const r=arguments.length;return r===2?he(t)&&!G(t)?Ns...
  method setScopeId (line 1) | setScopeId(e,t){e.setAttribute(t,"")}
  method insertStaticContent (line 1) | insertStaticContent(e,t,n,r,o,s){const i=n?n.previousSibling:t.lastChild...
  function Df (line 1) | function Df(e,t,n){const r=e[Mf];r&&(t=(t?[t,...r]:[...r]).join(" ")),t=...
  function $f (line 1) | function $f(e,t,n){const r=e.style,o=Le(n);if(n&&!o){if(t&&!Le(t))for(co...
  function Rs (line 1) | function Rs(e,t,n){if(G(n))n.forEach(r=>Rs(e,t,r));else if(n==null&&(n="...
  function jf (line 1) | function jf(e,t){const n=ss[t];if(n)return n;let r=ht(t);if(r!=="filter"...
  function Hf (line 1) | function Hf(e,t,n,r,o){if(r&&t.startsWith("xlink:"))n==null?e.removeAttr...
  function Wf (line 1) | function Wf(e,t,n,r,o,s,i){if(t==="innerHTML"||t==="textContent"){r&&i(r...
  function Kt (line 1) | function Kt(e,t,n,r){e.addEventListener(t,n,r)}
  function Bf (line 1) | function Bf(e,t,n,r){e.removeEventListener(t,n,r)}
  function Vf (line 1) | function Vf(e,t,n,r,o=null){const s=e[di]||(e[di]={}),i=s[t];if(r&&i)i.v...
  function Kf (line 1) | function Kf(e){let t;if(hi.test(e)){t={};let r;for(;r=e.match(hi);)e=e.s...
  function qf (line 1) | function qf(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts...
  function Xf (line 1) | function Xf(e,t){if(G(t)){const n=e.stopImmediatePropagation;return e.st...
  function zf (line 1) | function zf(e,t,n,r){if(r)return!!(t==="innerHTML"||t==="textContent"||t...
  function Qf (line 1) | function Qf(e){e.target.composing=!0}
  function pi (line 1) | function pi(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchE...
  method created (line 1) | created(e,{modifiers:{lazy:t,trim:n,number:r}},o){e[ln]=Rr(o);const s=r|...
  method mounted (line 1) | mounted(e,{value:t}){e.value=t??""}
  method beforeUpdate (line 1) | beforeUpdate(e,{value:t,modifiers:{lazy:n,trim:r,number:o}},s){if(e[ln]=...
  method created (line 1) | created(e,{value:t,modifiers:{number:n}},r){const o=Ir(t);Kt(e,"change",...
  method mounted (line 1) | mounted(e,{value:t}){_i(e,t)}
  method beforeUpdate (line 1) | beforeUpdate(e,t,n){e[ln]=Rr(n)}
  method updated (line 1) | updated(e,{value:t}){_i(e,t)}
  function _i (line 1) | function _i(e,t){const n=e.multiple;if(!(n&&!G(t)&&!Ir(t))){for(let r=0,...
  function Ar (line 1) | function Ar(e){return"_value"in e?e._value:e.value}
  function nd (line 1) | function nd(){return gi||(gi=uf(td))}
  function rd (line 1) | function rd(e){return Le(e)?document.querySelector(e):e}
  function sd (line 5) | function sd(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}
  function is (line 5) | function is(e,t){const n={};for(const r in t){const o=t[r];n[r]=ot(o)?o....
  function ls (line 5) | function ls(e,t,n="/"){let r,o={},s="",i="";const l=t.indexOf("#");let a...
  function ld (line 5) | function ld(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+...
  function bi (line 5) | function bi(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?...
  function ad (line 5) | function ad(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;retur...
  function dn (line 5) | function dn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}
  function Sa (line 5) | function Sa(e,t){if(Object.keys(e).length!==Object.keys(t).length)return...
  function cd (line 5) | function cd(e,t){return ot(e)?Ei(e,t):ot(t)?Ei(t,e):e===t}
  function Ei (line 5) | function Ei(e,t){return ot(t)?e.length===t.length&&e.every((n,r)=>n===t[...
  function ud (line 5) | function ud(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t....
  function fd (line 5) | function fd(e){if(!e)if(Zt){const t=document.querySelector("base");e=t&&...
  function hd (line 5) | function hd(e,t){return e.replace(dd,"#")+t}
  function md (line 5) | function md(e,t){const n=document.documentElement.getBoundingClientRect(...
  function pd (line 5) | function pd(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.s...
  function yi (line 5) | function yi(e,t){return(history.state?history.state.position-t:-1)+e}
  function _d (line 5) | function _d(e,t){As.set(e,t)}
  function gd (line 5) | function gd(e){const t=As.get(e);return As.delete(e),t}
  function Na (line 5) | function Na(e,t){const{pathname:n,search:r,hash:o}=t,s=e.indexOf("#");if...
  function Ed (line 5) | function Ed(e,t,n,r){let o=[],s=[],i=null;const l=({state:m})=>{const b=...
  function Oi (line 5) | function Oi(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:...
  function yd (line 5) | function yd(e){const{history:t,location:n}=window,r={value:Na(e,n)},o={v...
  function J_ (line 5) | function J_(e){e=fd(e);const t=yd(e),n=Ed(e,t.state,t.location,t.replace...
  function Od (line 5) | function Od(e){return typeof e=="string"||e&&typeof e=="object"}
  function Ra (line 5) | function Ra(e){return typeof e=="string"||typeof e=="symbol"}
  function hn (line 5) | function hn(e,t){return ue(new Error,{type:e,[Aa]:!0},t)}
  function _t (line 5) | function _t(e,t){return e instanceof Error&&Aa in e&&(t==null||!!(e.type...
  function Sd (line 5) | function Sd(e,t){const n=ue({},Td,t),r=[];let o=n.start?"^":"";const s=[...
  function Nd (line 5) | function Nd(e,t){let n=0;for(;n<e.length&&n<t.length;){const r=t[n]-e[n]...
  function Rd (line 5) | function Rd(e,t){let n=0;const r=e.score,o=t.score;for(;n<r.length&&n<o....
  function Si (line 5) | function Si(e){const t=e[e.length-1];return e.length>0&&t[t.length-1]<0}
  function Cd (line 5) | function Cd(e){if(!e)return[[]];if(e==="/")return[[Ad]];if(!e.startsWith...
  function Pd (line 5) | function Pd(e,t,n){const r=Sd(Cd(e.path),n),o=ue(r,{record:e,parent:t,ch...
  function Id (line 5) | function Id(e,t){const n=[],r=new Map;t=Ai({strict:!1,end:!0,sensitive:!...
  function Ni (line 5) | function Ni(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}
  function wd (line 5) | function wd(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e...
  function kd (line 5) | function kd(e){const t={},n=e.props||!1;if("component"in e)t.default=n;e...
  function Ri (line 5) | function Ri(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}
  function xd (line 5) | function xd(e){return e.reduce((t,n)=>ue(t,n.meta),{})}
  function Ai (line 5) | function Ai(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];retur...
  function La (line 5) | function La(e,t){return t.children.some(n=>n===e||La(e,n))}
  function bo (line 5) | function bo(e){return encodeURI(""+e).replace(Wd,"|").replace($d,"[").re...
  function Vd (line 5) | function Vd(e){return bo(e).replace(wa,"{").replace(ka,"}").replace(Ia,"...
  function Ls (line 5) | function Ls(e){return bo(e).replace(Pa,"%2B").replace(Bd,"+").replace(Ca...
  function Kd (line 5) | function Kd(e){return Ls(e).replace(Dd,"%3D")}
  function Yd (line 5) | function Yd(e){return bo(e).replace(Ca,"%23").replace(Ud,"%3F")}
  function Gd (line 5) | function Gd(e){return e==null?"":Yd(e).replace(Md,"%2F")}
  function Lr (line 5) | function Lr(e){try{return decodeURIComponent(""+e)}catch{}return""+e}
  function qd (line 5) | function qd(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?...
  function Li (line 5) | function Li(e){let t="";for(let n in e){const r=e[n];if(n=Kd(n),r==null)...
  function Xd (line 5) | function Xd(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[...
  function Nn (line 5) | function Nn(){let e=[];function t(r){return e.push(r),()=>{const o=e.ind...
  function It (line 5) | function It(e,t,n,r,o){const s=r&&(r.enterCallbacks[o]=r.enterCallbacks[...
  function as (line 5) | function as(e,t,n,r){const o=[];for(const s of e)for(const i in s.compon...
  function zd (line 5) | function zd(e){return typeof e=="object"||"displayName"in e||"props"in e...
  function Pi (line 5) | function Pi(e){const t=st(Eo),n=st(xa),r=Oe(()=>t.resolve(sn(e.to))),o=O...
  method setup (line 5) | setup(e,{slots:t}){const n=zn(Pi(e)),{options:r}=st(Eo),o=Oe(()=>({[wi(e...
  function eh (line 5) | function eh(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defa...
  function th (line 5) | function th(e,t){for(const n in t){const r=t[n],o=e[n];if(typeof r=="str...
  function Ii (line 5) | function Ii(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}
  method setup (line 5) | setup(e,{attrs:t,slots:n}){const r=st(Cs),o=Oe(()=>e.route||r.value),s=s...
  function ki (line 5) | function ki(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}
  function z_ (line 5) | function z_(e){const t=Id(e.routes,e),n=e.parseQuery||qd,r=e.stringifyQu...
  function sh (line 5) | function sh(e,t){const n=[],r=[],o=[],s=Math.max(t.matched.length,e.matc...
  function Ps (line 9) | function Ps(e){return e&&typeof e=="object"&&Object.prototype.toString.c...
  function Q_ (line 9) | function Q_(){const e=to(!0),t=e.run(()=>qe({}));let n=[],r=[];const o=M...
  function xi (line 9) | function xi(e,t,n,r=Da){e.push(t);const o=()=>{const s=e.indexOf(t);s>-1...
  function Qt (line 9) | function Qt(e,...t){e.slice().forEach(n=>{n(...t)})}
  function Is (line 9) | function Is(e,t){e instanceof Map&&t instanceof Map&&t.forEach((n,r)=>e....
  function ah (line 9) | function ah(e){return!Ps(e)||!e.hasOwnProperty(lh)}
  function ch (line 9) | function ch(e){return!!(Te(e)&&e.effect)}
  function uh (line 9) | function uh(e,t,n,r){const{state:o,actions:s,getters:i}=t,l=n.state.valu...
  function Ua (line 9) | function Ua(e,t,n={},r,o,s){let i;const l=Ct({actions:{}},n),a={deep:!0}...
  function Z_ (line 9) | function Z_(e,t,n){let r,o;const s=typeof t=="function";typeof e=="strin...
  function _h (line 9) | function _h(e,t){return Ea(),_f("div",hh,ph)}
  function $a (line 9) | function $a(e,t){return function(){return e.apply(t,arguments)}}
  function bh (line 9) | function bh(e){return e!==null&&!Yn(e)&&e.constructor!==null&&!Yn(e.cons...
  function Eh (line 9) | function Eh(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t...
  function er (line 9) | function er(e,t,{allOwnKeys:n=!1}={}){if(e===null||typeof e>"u")return;l...
  function Wa (line 9) | function Wa(e,t){t=t.toLowerCase();const n=Object.keys(e);let r=n.length...
  function ws (line 9) | function ws(){const{caseless:e}=Va(this)&&this||{},t={},n=(r,o)=>{const ...
  function Gh (line 9) | function Gh(e){return!!(e&&ze(e.append)&&e[Symbol.toStringTag]==="FormDa...
  function oe (line 9) | function oe(e,t,n,r,o){Error.call(this),Error.captureStackTrace?Error.ca...
  function ks (line 9) | function ks(e){return P.isPlainObject(e)||P.isArray(e)}
  function Xa (line 9) | function Xa(e){return P.endsWith(e,"[]")?e.slice(0,-2):e}
  function Di (line 9) | function Di(e,t,n){return e?e.concat(t).map(function(o,s){return o=Xa(o)...
  function Qh (line 9) | function Qh(e){return P.isArray(e)&&!e.some(ks)}
  function qr (line 9) | function qr(e,t,n){if(!P.isObject(e))throw new TypeError("target must be...
  function Ui (line 9) | function Ui(e){const t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E...
  function Oo (line 9) | function Oo(e,t){this._pairs=[],e&&qr(e,this,t)}
  function em (line 9) | function em(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace...
  function za (line 9) | function za(e,t,n){if(!t)return e;const r=n&&n.encode||em,o=n&&n.seriali...
  class tm (line 9) | class tm{constructor(){this.handlers=[]}use(t,n,r){return this.handlers....
    method constructor (line 9) | constructor(){this.handlers=[]}
    method use (line 9) | use(t,n,r){return this.handlers.push({fulfilled:t,rejected:n,synchrono...
    method eject (line 9) | eject(t){this.handlers[t]&&(this.handlers[t]=null)}
    method clear (line 9) | clear(){this.handlers&&(this.handlers=[])}
    method forEach (line 9) | forEach(t){P.forEach(this.handlers,function(r){r!==null&&t(r)})}
  function cm (line 9) | function cm(e,t){return qr(e,new ft.classes.URLSearchParams,Object.assig...
  function um (line 9) | function um(e){return P.matchAll(/\w+|\[(\w*)]/g,e).map(t=>t[0]==="[]"?"...
  function fm (line 9) | function fm(e){const t={},n=Object.keys(e);let r;const o=n.length;let s;...
  function ec (line 9) | function ec(e){function t(n,r,o,s){let i=n[s++];const l=Number.isFinite(...
  function dm (line 9) | function dm(e,t,n){if(P.isString(e))try{return(t||JSON.parse)(e),P.trim(...
  function Rn (line 10) | function Rn(e){return e&&String(e).trim().toLowerCase()}
  function gr (line 10) | function gr(e){return e===!1||e==null?e:P.isArray(e)?e.map(gr):String(e)}
  function pm (line 10) | function pm(e){const t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;...
  function us (line 10) | function us(e,t,n,r,o){if(P.isFunction(r))return r.call(this,t,n);if(o&&...
  function gm (line 10) | function gm(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(...
  function bm (line 10) | function bm(e,t){const n=P.toCamelCase(" "+t);["get","set","has"].forEac...
  class Xr (line 10) | class Xr{constructor(t){t&&this.set(t)}set(t,n,r){const o=this;function ...
    method constructor (line 10) | constructor(t){t&&this.set(t)}
    method set (line 10) | set(t,n,r){const o=this;function s(l,a,f){const d=Rn(a);if(!d)throw ne...
    method get (line 10) | get(t,n){if(t=Rn(t),t){const r=P.findKey(this,t);if(r){const o=this[r]...
    method has (line 10) | has(t,n){if(t=Rn(t),t){const r=P.findKey(this,t);return!!(r&&this[r]!=...
    method delete (line 10) | delete(t,n){const r=this;let o=!1;function s(i){if(i=Rn(i),i){const l=...
    method clear (line 10) | clear(t){const n=Object.keys(this);let r=n.length,o=!1;for(;r--;){cons...
    method normalize (line 10) | normalize(t){const n=this,r={};return P.forEach(this,(o,s)=>{const i=P...
    method concat (line 10) | concat(...t){return this.constructor.concat(this,...t)}
    method toJSON (line 10) | toJSON(t){const n=Object.create(null);return P.forEach(this,(r,o)=>{r!...
    method toString (line 10) | toString(){return Object.entries(this.toJSON()).map(([t,n])=>t+": "+n)...
    method from (line 11) | static from(t){return t instanceof this?t:new this(t)}
    method concat (line 11) | static concat(t,...n){const r=new this(t);return n.forEach(o=>r.set(o)...
    method accessor (line 11) | static accessor(t){const r=(this[ji]=this[ji]={accessors:{}}).accessor...
  method [Symbol.iterator] (line 10) | [Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator...
  method [Symbol.toStringTag] (line 11) | get[Symbol.toStringTag](){return"AxiosHeaders"}
  method set (line 11) | set(r){this[n]=r}
  function fs (line 11) | function fs(e,t){const n=this||vo,r=t||n,o=Ot.from(r.headers);let s=r.da...
  function tc (line 11) | function tc(e){return!!(e&&e.__CANCEL__)}
  function tr (line 11) | function tr(e,t,n){oe.call(this,e??"canceled",oe.ERR_CANCELED,t,n),this....
  function Em (line 11) | function Em(e,t,n){const r=n.config.validateStatus;!n.status||!r||r(n.st...
  method write (line 11) | write(e,t,n,r,o,s){const i=[e+"="+encodeURIComponent(t)];P.isNumber(n)&&...
  method read (line 11) | read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]...
  method remove (line 11) | remove(e){this.write(e,"",Date.now()-864e5)}
  method write (line 11) | write(){}
  method read (line 11) | read(){return null}
  method remove (line 11) | remove(){}
  function Om (line 11) | function Om(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}
  function Tm (line 11) | function Tm(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}
  function nc (line 11) | function nc(e,t){return e&&!Om(t)?Tm(e,t):t}
  function o (line 11) | function o(s){let i=s;return t&&(n.setAttribute("href",i),i=n.href),n.se...
  function Sm (line 11) | function Sm(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1...
  function Nm (line 11) | function Nm(e,t){e=e||10;const n=new Array(e),r=new Array(e);let o=0,s=0...
  function Hi (line 11) | function Hi(e,t){let n=0;const r=Nm(50,250);return o=>{const s=o.loaded,...
  function f (line 11) | function f(){e.cancelToken&&e.cancelToken.unsubscribe(a),e.signal&&e.sig...
  function b (line 11) | function b(){if(!h)return;const S=Ot.from("getAllResponseHeaders"in h&&h...
  function ds (line 13) | function ds(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.sign...
  function Bi (line 13) | function Bi(e){return ds(e),e.headers=Ot.from(e.headers),e.data=fs.call(...
  function mn (line 13) | function mn(e,t){t=t||{};const n={};function r(f,d,h){return P.isPlainOb...
  function o (line 13) | function o(s,i){return"[Axios v"+sc+"] Transitional option '"+s+"'"+i+(r...
  function Cm (line 13) | function Cm(e,t,n){if(typeof e!="object")throw new oe("options must be a...
  method constructor (line 13) | constructor(t){this.defaults=t,this.interceptors={request:new $i,respons...
  method request (line 13) | request(t,n){typeof t=="string"?(n=n||{},n.url=t):n=t||{},n=mn(this.defa...
  method getUri (line 13) | getUri(t){t=mn(this.defaults,t);const n=nc(t.baseURL,t.url);return za(n,...
  function n (line 13) | function n(r){return function(s,i,l){return this.request(mn(l||{},{metho...
  class No (line 13) | class No{constructor(t){if(typeof t!="function")throw new TypeError("exe...
    method constructor (line 13) | constructor(t){if(typeof t!="function")throw new TypeError("executor m...
    method throwIfRequested (line 13) | throwIfRequested(){if(this.reason)throw this.reason}
    method subscribe (line 13) | subscribe(t){if(this.reason){t(this.reason);return}this._listeners?thi...
    method unsubscribe (line 13) | unsubscribe(t){if(!this._listeners)return;const n=this._listeners.inde...
    method source (line 13) | static source(){let t;return{token:new No(function(o){t=o}),cancel:t}}
  function Im (line 13) | function Im(e){return function(n){return e.apply(null,n)}}
  function wm (line 13) | function wm(e){return P.isObject(e)&&e.isAxiosError===!0}
  function oc (line 13) | function oc(e){const t=new br(e),n=$a(br.prototype.request,t);return P.e...
  method log (line 13) | log(...e){this.enabled&&(e.push(Date.now()),Er.logger.log("[ActionCable]...
  class Ro (line 13) | class Ro{constructor(t){this.visibilityDidChange=this.visibilityDidChang...
    method constructor (line 13) | constructor(t){this.visibilityDidChange=this.visibilityDidChange.bind(...
    method start (line 13) | start(){this.isRunning()||(this.startedAt=In(),delete this.stoppedAt,t...
    method stop (line 13) | stop(){this.isRunning()&&(this.stoppedAt=In(),this.stopPolling(),remov...
    method isRunning (line 13) | isRunning(){return this.startedAt&&!this.stoppedAt}
    method recordPing (line 13) | recordPing(){this.pingedAt=In()}
    method recordConnect (line 13) | recordConnect(){this.reconnectAttempts=0,this.recordPing(),delete this...
    method recordDisconnect (line 13) | recordDisconnect(){this.disconnectedAt=In(),be.log("ConnectionMonitor ...
    method startPolling (line 13) | startPolling(){this.stopPolling(),this.poll()}
    method stopPolling (line 13) | stopPolling(){clearTimeout(this.pollTimeout)}
    method poll (line 13) | poll(){this.pollTimeout=setTimeout(()=>{this.reconnectIfStale(),this.p...
    method getPollInterval (line 13) | getPollInterval(){const{staleThreshold:t,reconnectionBackoffRate:n}=th...
    method reconnectIfStale (line 13) | reconnectIfStale(){this.connectionIsStale()&&(be.log(`ConnectionMonito...
    method refreshedAt (line 13) | get refreshedAt(){return this.pingedAt?this.pingedAt:this.startedAt}
    method connectionIsStale (line 13) | connectionIsStale(){return ar(this.refreshedAt)>this.constructor.stale...
    method disconnectedRecently (line 13) | disconnectedRecently(){return this.disconnectedAt&&ar(this.disconnecte...
    method visibilityDidChange (line 13) | visibilityDidChange(){document.visibilityState==="visible"&&setTimeout...
  class Ao (line 13) | class Ao{constructor(t){this.open=this.open.bind(this),this.consumer=t,t...
    method constructor (line 13) | constructor(t){this.open=this.open.bind(this),this.consumer=t,this.sub...
    method send (line 13) | send(t){return this.isOpen()?(this.webSocket.send(JSON.stringify(t)),!...
    method open (line 13) | open(){if(this.isActive())return be.log(`Attempted to open WebSocket, ...
    method close (line 13) | close({allowReconnect:t}={allowReconnect:!0}){if(t||this.monitor.stop(...
    method reopen (line 13) | reopen(){if(be.log(`Reopening WebSocket, current state is ${this.getSt...
    method getProtocol (line 13) | getProtocol(){if(this.webSocket)return this.webSocket.protocol}
    method isOpen (line 13) | isOpen(){return this.isState("open")}
    method isActive (line 13) | isActive(){return this.isState("open","connecting")}
    method triedToReconnect (line 13) | triedToReconnect(){return this.monitor.reconnectAttempts>0}
    method isProtocolSupported (line 13) | isProtocolSupported(){return Yi.call(xm,this.getProtocol())>=0}
    method isState (line 13) | isState(...t){return Yi.call(t,this.getState())>=0}
    method getState (line 13) | getState(){if(this.webSocket){for(let t in Er.WebSocket)if(Er.WebSocke...
    method installEventHandlers (line 13) | installEventHandlers(){for(let t in this.events){const n=this.events[t...
    method uninstallEventHandlers (line 13) | uninstallEventHandlers(){for(let t in this.events)this.webSocket[`on${...
  method message (line 13) | message(e){if(!this.isProtocolSupported())return;const{identifier:t,mess...
  method open (line 13) | open(){if(be.log(`WebSocket onopen event, using '${this.getProtocol()}' ...
  method close (line 13) | close(e){if(be.log("WebSocket onclose event"),!this.disconnected)return ...
  method error (line 13) | error(){be.log("WebSocket onerror event")}
  class Mm (line 13) | class Mm{constructor(t,n={},r){this.consumer=t,this.identifier=JSON.stri...
    method constructor (line 13) | constructor(t,n={},r){this.consumer=t,this.identifier=JSON.stringify(n...
    method perform (line 13) | perform(t,n={}){return n.action=t,this.send(n)}
    method send (line 13) | send(t){return this.consumer.send({command:"message",identifier:this.i...
    method unsubscribe (line 13) | unsubscribe(){return this.consumer.subscriptions.remove(this)}
  class Dm (line 13) | class Dm{constructor(t){this.subscriptions=t,this.pendingSubscriptions=[...
    method constructor (line 13) | constructor(t){this.subscriptions=t,this.pendingSubscriptions=[]}
    method guarantee (line 13) | guarantee(t){this.pendingSubscriptions.indexOf(t)==-1?(be.log(`Subscri...
    method forget (line 13) | forget(t){be.log(`SubscriptionGuarantor forgetting ${t.identifier}`),t...
    method startGuaranteeing (line 13) | startGuaranteeing(){this.stopGuaranteeing(),this.retrySubscribing()}
    method stopGuaranteeing (line 13) | stopGuaranteeing(){clearTimeout(this.retryTimeout)}
    method retrySubscribing (line 13) | retrySubscribing(){this.retryTimeout=setTimeout(()=>{this.subscription...
  class Um (line 13) | class Um{constructor(t){this.consumer=t,this.guarantor=new Dm(this),this...
    method constructor (line 13) | constructor(t){this.consumer=t,this.guarantor=new Dm(this),this.subscr...
    method create (line 13) | create(t,n){const r=t,o=typeof r=="object"?r:{channel:r},s=new Mm(this...
    method add (line 13) | add(t){return this.subscriptions.push(t),this.consumer.ensureActiveCon...
    method remove (line 13) | remove(t){return this.forget(t),this.findAll(t.identifier).length||thi...
    method reject (line 13) | reject(t){return this.findAll(t).map(n=>(this.forget(n),this.notify(n,...
    method forget (line 13) | forget(t){return this.guarantor.forget(t),this.subscriptions=this.subs...
    method findAll (line 13) | findAll(t){return this.subscriptions.filter(n=>n.identifier===t)}
    method reload (line 13) | reload(){return this.subscriptions.map(t=>this.subscribe(t))}
    method notifyAll (line 13) | notifyAll(t,...n){return this.subscriptions.map(r=>this.notify(r,t,......
    method notify (line 13) | notify(t,n,...r){let o;return typeof t=="string"?o=this.findAll(t):o=[...
    method subscribe (line 13) | subscribe(t){this.sendCommand(t,"subscribe")&&this.guarantor.guarantee...
    method confirmSubscription (line 13) | confirmSubscription(t){be.log(`Subscription confirmed ${t}`),this.find...
    method sendCommand (line 13) | sendCommand(t,n){const{identifier:r}=t;return this.consumer.send({comm...
  class $m (line 13) | class $m{constructor(t){this._url=t,this.subscriptions=new Um(this),this...
    method constructor (line 13) | constructor(t){this._url=t,this.subscriptions=new Um(this),this.connec...
    method url (line 13) | get url(){return jm(this._url)}
    method send (line 13) | send(t){return this.connection.send(t)}
    method connect (line 13) | connect(){return this.connection.open()}
    method disconnect (line 13) | disconnect(){return this.connection.close({allowReconnect:!1})}
    method ensureActiveConnection (line 13) | ensureActiveConnection(){if(!this.connection.isActive())return this.co...
    method addSubProtocol (line 13) | addSubProtocol(t){this.subprotocols=[...this.subprotocols,t]}
  function jm (line 13) | function jm(e){if(typeof e=="function"&&(e=e()),e&&!/^wss?:/i.test(e)){c...
  function Hm (line 13) | function Hm(e=Wm("url")||ic.default_mount_path){return new $m(e)}
  function Wm (line 13) | function Wm(e){const t=document.head.querySelector(`meta[name='action-ca...
  function Bm (line 13) | function Bm(e){return{all:e=e||new Map,on:function(t,n){var r=e.get(t);r...
  function Jr (line 13) | function Jr(){}
  function rg (line 13) | function rg(e){return ac=Km.subscriptions.create({channel:e.channel},{re...
  function Lo (line 13) | function Lo(){}
  function sg (line 13) | function sg(e){return e.handler.defaults.baseURL=`${window.location.prot...
  function qi (line 17) | function qi(e){return e.replace(/</g,"&lt;").replace(/>/g,"&gt;").replac...
  function Gn (line 17) | function Gn(e,t){return Xm.call(e,t)}
  function Qm (line 17) | function Qm(e,t=""){return e.reduce((n,r,o)=>o===0?n+r:n+t+r,"")}
  function Co (line 17) | function Co(e){let t=e;return()=>++t}
  function Zm (line 17) | function Zm(e,t){typeof console<"u"&&(console.warn("[intlify] "+e),t&&co...
  function Dn (line 17) | function Dn(e,t){if(cr(e)||cr(t))throw new Error("Invalid value");for(co...
  function ep (line 21) | function ep(e,t,n){return{line:e,column:t,offset:n}}
  function $s (line 21) | function $s(e,t,n){const r={start:e,end:t};return n!=null&&(r.source=n),r}
  function np (line 21) | function np(e,...t){return t.length===1&&rp(t[0])&&(t=t[0]),(!t||!t.hasO...
  function dc (line 21) | function dc(e,t=""){return e.reduce((n,r,o)=>o===0?n+r:n+t+r,"")}
  function Tn (line 21) | function Tn(e,t,n={}){const{domain:r,messages:o,args:s}=n,i=np((o||sp)[e...
  function op (line 21) | function op(e){throw e}
  function cp (line 22) | function cp(e){const t=e;let n=0,r=1,o=1,s=0;const i=I=>t[I]===ip&&t[I+1...
  function dp (line 22) | function dp(e,t={}){const n=t.location!==!1,r=cp(e),o=()=>r.index(),s=()...
  function pp (line 22) | function pp(e,t,n){switch(e){case"\\\\":return"\\";case"\\'":return"'";d...
  function _p (line 22) | function _p(e={}){const t=e.location!==!1,{onError:n}=e;function r(g,v,L...
  function at (line 22) | function at(e){if(e.type===14)return"EOF";const t=(e.value||"").replace(...
  function gp (line 22) | function gp(e,t={}){const n={ast:e,helpers:new Set};return{context:()=>n...
  function zi (line 22) | function zi(e,t){for(let n=0;n<e.length;n++)Po(e[n],t)}
  function Po (line 22) | function Po(e,t){switch(e.type){case 1:zi(e.cases,t),t.helper("plural");...
  function bp (line 22) | function bp(e,t={}){const n=gp(e);n.helper("normalize"),e.body&&Po(e.bod...
  function Ep (line 22) | function Ep(e){const t=e.body;return t.type===2?Qi(t):t.cases.forEach(n=...
  function Qi (line 22) | function Qi(e){if(e.items.length===1){const t=e.items[0];(t.type===3||t....
  function en (line 22) | function en(e){switch(e.t=e.type,e.type){case 0:const t=e;en(t.body),t.b...
  function Tp (line 22) | function Tp(e,t){const{sourceMap:n,filename:r,breakLineCode:o,needIndent...
  function vp (line 22) | function vp(e,t){const{helper:n}=e;e.push(`${n("linked")}(`),pn(e,t.key)...
  function Sp (line 22) | function Sp(e,t){const{helper:n,needIndent:r}=e;e.push(`${n("normalize")...
  function Np (line 22) | function Np(e,t){const{helper:n,needIndent:r}=e;if(t.cases.length>1){e.p...
  function Rp (line 22) | function Rp(e,t){t.body?pn(e,t.body):e.push("null")}
  function pn (line 22) | function pn(e,t){const{helper:n}=e;switch(t.type){case 0:Rp(e,t);break;c...
  function Lp (line 23) | function Lp(e,t={}){const n=fc({},t),r=!!n.jit,o=!!n.minify,s=n.optimize...
  function Cp (line 27) | function Cp(){typeof __INTLIFY_PROD_DEVTOOLS__!="boolean"&&(Et().__INTLI...
  function Ip (line 27) | function Ip(e){return Pp.test(e)}
  function wp (line 27) | function wp(e){const t=e.charCodeAt(0),n=e.charCodeAt(e.length-1);return...
  function kp (line 27) | function kp(e){if(e==null)return"o";switch(e.charCodeAt(0)){case 91:case...
  function xp (line 27) | function xp(e){const t=e.trim();return e.charAt(0)==="0"&&isNaN(parseInt...
  function Fp (line 27) | function Fp(e){const t=[];let n=-1,r=0,o=0,s,i,l,a,f,d,h;const m=[];m[0]...
  function Mp (line 27) | function Mp(e,t){return ce(e)?e[t]:null}
  function Dp (line 27) | function Dp(e,t){if(!ce(e))return null;let n=Zi.get(t);if(n||(n=Fp(t),n&...
  function el (line 27) | function el(e,t){return e=Math.abs(e),t===2?e?e>1?1:0:1:e?Math.min(e,2):0}
  function Bp (line 27) | function Bp(e){const t=Re(e.pluralIndex)?e.pluralIndex:-1;return e.named...
  function Vp (line 27) | function Vp(e,t){t.count||(t.count=e),t.n||(t.n=e)}
  function Kp (line 27) | function Kp(e={}){const t=e.locale,n=Bp(e),r=ce(e.pluralRules)&&H(t)&&de...
  function Yp (line 27) | function Yp(e){qn=e}
  function Gp (line 27) | function Gp(e,t,n){qn&&qn.emit("i18n:init",{timestamp:Date.now(),i18n:e,...
  function Xp (line 27) | function Xp(e){return t=>qn&&qn.emit(e,t)}
  function dt (line 27) | function dt(e){return Tn(e,null,void 0)}
  function Io (line 27) | function Io(e,t){return t.locale!=null?tl(t.locale):tl(e.locale)}
  function tl (line 27) | function tl(e){if(H(e))return e;if(de(e)){if(e.resolvedOnce&&hs!=null)re...
  function zp (line 27) | function zp(e,t,n){return[...new Set([n,...ge(t)?t:ce(t)?Object.keys(t):...
  function mc (line 27) | function mc(e,t,n){const r=H(n)?n:_n,o=e;o.__localeChainCache||(o.__loca...
  function nl (line 27) | function nl(e,t,n){let r=!0;for(let o=0;o<t.length&&ee(r);o++){const s=t...
  function Qp (line 27) | function Qp(e,t,n){let r;const o=t.split("-");do{const s=o.join("-");r=Z...
  function Zp (line 27) | function Zp(e,t,n){let r=!1;if(!e.includes(t)&&(r=!0,t)){r=t[t.length-1]...
  function t_ (line 27) | function t_(){return{upper:(e,t)=>t==="text"&&H(e)?e.toUpperCase():t==="...
  function ol (line 27) | function ol(e){pc=e}
  function n_ (line 27) | function n_(e){_c=e}
  function r_ (line 27) | function r_(e){gc=e}
  function l_ (line 27) | function l_(e={}){const t=de(e.onWarn)?e.onWarn:Zm,n=H(e.version)?e.vers...
  function wo (line 27) | function wo(e,t,n,r,o){const{missing:s,onWarn:i}=e;if(s!==null){const l=...
  function Ln (line 27) | function Ln(e,t,n){const r=e;r.__localeChainCache=new Map,e.localeFallba...
  function ms (line 27) | function ms(e){return n=>a_(n,e)}
  function a_ (line 27) | function a_(e,t){const n=t.b||t.body;if((n.t||n.type)===1){const r=n,o=r...
  function al (line 27) | function al(e,t){const n=t.s||t.static;if(n)return e.type==="text"?n:e.n...
  function js (line 27) | function js(e,t){const n=t.t||t.type;switch(n){case 3:const r=t;return r...
  function Oc (line 27) | function Oc(e,t={}){let n=!1;const r=t.onError||op;return t.onError=o=>{...
  function u_ (line 27) | function u_(e,t){if(__INTLIFY_JIT_COMPILATION__&&!__INTLIFY_DROP_MESSAGE...
  function ul (line 27) | function ul(e,...t){const{fallbackFormat:n,postTranslation:r,unresolving...
  function f_ (line 27) | function f_(e){ge(e.list)?e.list=e.list.map(t=>H(t)?qi(t):t):ce(e.named)...
  function Tc (line 27) | function Tc(e,t,n,r,o,s){const{messages:i,onWarn:l,messageResolver:a,loc...
  function vc (line 27) | function vc(e,t,n,r,o,s){const{messageCompiler:i,warnHtmlMessage:l}=e;if...
  function d_ (line 27) | function d_(e,t,n){return t(n)}
  function Hs (line 27) | function Hs(...e){const[t,n,r]=e,o={};if(!H(t)&&!Re(t)&&!Je(t)&&!gn(t))t...
  function h_ (line 27) | function h_(e,t,n,r,o,s){return{locale:t,key:n,warnHtmlMessage:o,onError...
  function m_ (line 27) | function m_(e,t,n,r){const{modifiers:o,pluralRules:s,messageResolver:i,f...
  function fl (line 27) | function fl(e,...t){const{datetimeFormats:n,unresolving:r,fallbackLocale...
  function Ws (line 27) | function Ws(...e){const[t,n,r,o]=e,s={};let i={},l;if(H(t)){const a=t.ma...
  function dl (line 27) | function dl(e,t,n){const r=e;for(const o in n){const s=`${t}__${o}`;r.__...
  function hl (line 27) | function hl(e,...t){const{numberFormats:n,unresolving:r,fallbackLocale:o...
  function Bs (line 27) | function Bs(...e){const[t,n,r,o]=e,s={};let i={};if(!Re(t))throw dt(tt.I...
  function ml (line 27) | function ml(e,t,n){const r=e;for(const o in n){const s=`${t}__${o}`;r.__...
  function __ (line 31) | function __(){typeof __VUE_I18N_FULL_INSTALL__!="boolean"&&(Et().__VUE_I...
  function Ie (line 31) | function Ie(e,...t){return Tn(e,null,void 0)}
  function Xn (line 31) | function Xn(e){if(!ce(e))return e;for(const t in e)if(Gn(e,t))if(!t.incl...
  function Zr (line 31) | function Zr(e,t){const{messages:n,__i18n:r,messageResolver:o,flatJson:s}...
  function Pc (line 31) | function Pc(e){return e.type}
  function Ic (line 31) | function Ic(e,t,n){let r=ce(t.messages)?t.messages:{};"__i18nGlobal"in n...
  function pl (line 31) | function pl(e){return $e(Zn,null,e,0)}
  function El (line 31) | function El(e){return(t,n,r,o)=>e(n,r,Bn()||void 0,o)}
  function ko (line 31) | function ko(e={},t){const{__root:n,__injectWithOption:r}=e,o=n===void 0,...
  function E_ (line 31) | function E_(e){const t=H(e.locale)?e.locale:_n,n=H(e.fallbackLocale)||ge...
  function qs (line 31) | function qs(e={},t){{const n=ko(E_(e)),{__extender:r}=e,o={id:n.id,get l...
  function y_ (line 31) | function y_({slots:e},t){return t.length===1&&t[0]==="default"?(e.defaul...
  function wc (line 31) | function wc(e){return Qe}
  method setup (line 31) | setup(e,t){const{slots:n,attrs:r}=t,o=e.i18n||Fo({useScope:e.scope,__use...
  function T_ (line 31) | function T_(e){return ge(e)&&!H(e[0])}
  function kc (line 31) | function kc(e,t,n,r){const{slots:o,attrs:s}=t;return()=>{const i={part:!...
  method setup (line 31) | setup(e,t){const n=e.i18n||Fo({useScope:"parent",__useComponent:!0});ret...
  method setup (line 31) | setup(e,t){const n=e.i18n||Fo({useScope:"parent",__useComponent:!0});ret...
  function N_ (line 31) | function N_(e,t){const n=e;if(e.mode==="composition")return n.__getInsta...
  function R_ (line 31) | function R_(e){const t=i=>{const{instance:l,modifiers:a,value:f}=i;if(!l...
  function vl (line 31) | function vl(e){if(H(e))return{path:e};if(J(e)){if(!("path"in e))throw Ie...
  function Sl (line 31) | function Sl(e){const{path:t,locale:n,args:r,choice:o,plural:s}=e,i={},l=...
  function A_ (line 31) | function A_(e,t,...n){const r=J(n[0])?n[0]:{},o=!!r.useI18nComponentName...
  function L_ (line 31) | function L_(e,t,n){return{beforeCreate(){const r=Bn();if(!r)throw Ie(Ae....
  function Nl (line 31) | function Nl(e,t){e.locale=t.locale||e.locale,e.fallbackLocale=t.fallback...
  function og (line 31) | function og(e={},t){const n=__VUE_I18N_LEGACY_API__&&ee(e.legacy)?e.lega...
  function Fo (line 31) | function Fo(e={}){const t=Bn();if(t==null)throw Ie(Ae.MUST_BE_CALL_SETUP...
  function P_ (line 31) | function P_(e,t,n){const r=to();{const o=__VUE_I18N_LEGACY_API__&&t?r.ru...
  function I_ (line 31) | function I_(e){{const t=st(e.isCE?C_:e.appContext.app.__VUE_I18N_SYMBOL_...
  function w_ (line 31) | function w_(e,t){return zr(e)?"__i18n"in t?"local":"global":e.useScope?e...
  function k_ (line 31) | function k_(e){return e.mode==="composition"?e.global:e.global.__composer}
  function x_ (line 31) | function x_(e,t,n=!1){let r=null;const o=t.root;let s=F_(t,n);for(;s!=nu...
  function F_ (line 31) | function F_(e,t=!1){return e==null?null:t&&e.vnode.ctx||e.parent}
  function M_ (line 31) | function M_(e,t,n){ua(()=>{},t),fo(()=>{const r=n;e.__deleteInstance(t);...
  function D_ (line 31) | function D_(e,t,n,r={}){const o=t==="local",s=Xl(null);if(o&&e.proxy&&!(...
  function $_ (line 31) | function $_(e,t){const n=Object.create(null);return U_.forEach(o=>{const...

FILE: test/application_system_test_case.rb
  class ApplicationSystemTestCase (line 3) | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase

FILE: test/test_helper.rb
  class ActiveSupport::TestCase (line 22) | class ActiveSupport::TestCase
    method json_response (line 29) | def json_response
  class ActionController::TestCase (line 34) | class ActionController::TestCase
  class ActionDispatch::IntegrationTest (line 46) | class ActionDispatch::IntegrationTest
Condensed preview — 162 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (371K chars).
[
  {
    "path": ".browserslistrc",
    "chars": 9,
    "preview": "defaults\n"
  },
  {
    "path": ".gitignore",
    "chars": 808,
    "preview": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring t"
  },
  {
    "path": ".tool-versions",
    "chars": 25,
    "preview": "ruby 3.2.2\nnodejs 20.2.0\n"
  },
  {
    "path": "Gemfile",
    "chars": 1101,
    "preview": "source \"https://rubygems.org\"\ngit_source(:github) { |repo| \"https://github.com/#{repo}.git\" }\n\n# Bundle edge Rails inste"
  },
  {
    "path": "Procfile",
    "chars": 59,
    "preview": "vite: bin/vite dev\nweb: bin/rails s --port 3000 -b 0.0.0.0\n"
  },
  {
    "path": "README.md",
    "chars": 2637,
    "preview": "# Rails + Vite + Vue + Pina Demo App\n\n### This repo is no longer maintained \nFeel free to grab some ideas, but I will no"
  },
  {
    "path": "Rakefile",
    "chars": 227,
    "preview": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they wil"
  },
  {
    "path": "app/assets/config/manifest.js",
    "chars": 143,
    "preview": "//= link_tree ../images\n//= link_directory ../stylesheets .css\n//= link_tree ../../javascript .js\n//= link_tree ../../.."
  },
  {
    "path": "app/assets/images/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/assets/stylesheets/application.css",
    "chars": 690,
    "preview": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below"
  },
  {
    "path": "app/channels/application_cable/channel.rb",
    "chars": 79,
    "preview": "module ApplicationCable\n  class Channel < ActionCable::Channel::Base\n  end\nend\n"
  },
  {
    "path": "app/channels/application_cable/connection.rb",
    "chars": 85,
    "preview": "module ApplicationCable\n  class Connection < ActionCable::Connection::Base\n  end\nend\n"
  },
  {
    "path": "app/channels/chat_channel.rb",
    "chars": 225,
    "preview": "class ChatChannel < ApplicationCable::Channel\n\n  def subscribed\n    stream_from \"ChatChannel\"\n  end\n\n  def receive(data)"
  },
  {
    "path": "app/controllers/admin_controller.rb",
    "chars": 326,
    "preview": "class AdminController < ApplicationController\n  before_action :auth_user! \n\n  def index\n    render template: 'admin'\n  e"
  },
  {
    "path": "app/controllers/api/admin/admin_controller.rb",
    "chars": 164,
    "preview": "class Api::Admin::AdminController < Api::ApiController\n  before_action :authenticate_user!\n\n  def search_params\n    para"
  },
  {
    "path": "app/controllers/api/admin/dashboard_controller.rb",
    "chars": 146,
    "preview": "class Api::Admin::DashboardController < Api::Admin::AdminController\n  def index\n    render json: {metrics: {musicians: M"
  },
  {
    "path": "app/controllers/api/admin/musicians_controller.rb",
    "chars": 1328,
    "preview": "class Api::Admin::MusiciansController < Api::Admin::AdminController\n  # DELETE ME: Dummy emulation of a slow network so "
  },
  {
    "path": "app/controllers/api/api_controller.rb",
    "chars": 54,
    "preview": "class Api::ApiController < ApplicationController\n\nend\n"
  },
  {
    "path": "app/controllers/api/musicians_controller.rb",
    "chars": 577,
    "preview": "class Api::MusiciansController < Api::ApiController\n\n  def index\n    @musicians = Musician.all\n  end\n\n  def show    \n   "
  },
  {
    "path": "app/controllers/application_controller.rb",
    "chars": 747,
    "preview": "class ApplicationController < ActionController::Base\n  protect_from_forgery with: :exception\n  around_action :set_locale"
  },
  {
    "path": "app/controllers/concerns/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/helpers/application_helper.rb",
    "chars": 664,
    "preview": "module ApplicationHelper\n\n  def ui_translations(section)\n    translations = {current: I18n.t('.')[:vue][section]}\n    tr"
  },
  {
    "path": "app/javascript/admin/routes.js",
    "chars": 1055,
    "preview": "import { createWebHistory, createRouter } from 'vue-router'\n\nimport DashboardIndex from '@/admin/views/dashboard/index.v"
  },
  {
    "path": "app/javascript/admin/stores/dashboard_store.js",
    "chars": 327,
    "preview": "import { defineStore } from 'pinia'\n\nexport const DashboardStore = defineStore('dashboard', {\n  state: () => {\n    retur"
  },
  {
    "path": "app/javascript/admin/stores/musician_store.js",
    "chars": 1914,
    "preview": "import { defineStore } from 'pinia'\n\nexport const MusicianStore = defineStore('musicians', {\n  state: () => {\n    return"
  },
  {
    "path": "app/javascript/admin/views/dashboard/index.vue",
    "chars": 2786,
    "preview": "<template>\n  <section class=\"container\">\n    <div class=\"row\">\n      <div class=\"col-xs-12 col-md-3 card\">\n        <h3>R"
  },
  {
    "path": "app/javascript/admin/views/musicians/_filters.vue",
    "chars": 1904,
    "preview": "<template>  \n  <section class=\"filters\">\n    <a href=\"#\" @click.prevent=\"toggleForm\" class=\"openable\">{{ $t('filter') }}"
  },
  {
    "path": "app/javascript/admin/views/musicians/_form.vue",
    "chars": 689,
    "preview": "<template>  \n  <fieldset>\n    <label>{{ $t('musicians.form.name') }}</label>\n    <errors attr=\"name\" :messages=\"data.err"
  },
  {
    "path": "app/javascript/admin/views/musicians/edit.vue",
    "chars": 1554,
    "preview": "<template>  \n  <section class=\"container\">\n    <ul class=\"breadcrumb\">\n      <li><router-link :to=\"{ name: 'root_path' }"
  },
  {
    "path": "app/javascript/admin/views/musicians/index.vue",
    "chars": 1982,
    "preview": "<template>\n  <section class=\"container\">\n    <div class=\"row\">\n      <div class=\"col-xs-12 col-lg-8 col-xl-9\">      \n   "
  },
  {
    "path": "app/javascript/admin/views/musicians/new.vue",
    "chars": 1011,
    "preview": "<template>  \n  <section class=\"container\">\n    <ul class=\"breadcrumb\">\n      <li><router-link :to=\"{ name: 'root_path' }"
  },
  {
    "path": "app/javascript/admin/views/shared/_errors.vue",
    "chars": 282,
    "preview": "<template>  \n  <span class=\"error\" v-if=\"message != ''\">{{ message }}</span>\n</template>\n\n<script setup>\nconst props = d"
  },
  {
    "path": "app/javascript/admin/views/shared/_footer.vue",
    "chars": 1043,
    "preview": "<template>    \n  <footer>\n    <div class=\"container\">\n      <div class=\"row\">\n        <div class=\"col-xs-12 col-lg-8\">\n "
  },
  {
    "path": "app/javascript/admin/views/shared/_nav.vue",
    "chars": 1836,
    "preview": "<template>\n  <section class=\"top-nav\">\n    <div class=\"container\">\n      <div class=\"row\">\n        <div class=\"col-xs-12"
  },
  {
    "path": "app/javascript/admin/views/shared/_pagination.vue",
    "chars": 1725,
    "preview": "<template>\n  <section clas=\"container\">    \n    <div v-if=\"store.pagination.next || store.pagination.previous\">      \n  "
  },
  {
    "path": "app/javascript/admin/views/shared/layout.vue",
    "chars": 190,
    "preview": "<template>    \n  <main>\n    <nav-bar />\n    <router-view />\n  </main>\n  <bottom />\n</template>\n\n<script setup>\nimport Na"
  },
  {
    "path": "app/javascript/admin/views/websockets/index.vue",
    "chars": 1497,
    "preview": "<template>\n  <section class=\"container\">\n    <ul class=\"breadcrumb\">\n      <li><router-link :to=\"{name: 'root_path'}\">{{"
  },
  {
    "path": "app/javascript/entrypoints/admin.js",
    "chars": 750,
    "preview": "import { createApp } from 'vue';\nconst app = createApp(Layout);\n\nimport Router from '@/admin/routes.js';\nimport Layout f"
  },
  {
    "path": "app/javascript/entrypoints/front.js",
    "chars": 649,
    "preview": "import { createApp } from 'vue';\nconst app = createApp(Layout);\n\nimport Router from '@/front/routes.js';\nimport Layout f"
  },
  {
    "path": "app/javascript/front/routes.js",
    "chars": 791,
    "preview": "import { createWebHistory, createRouter } from 'vue-router'\n\nimport PageIndex from './views/pages/index.vue';\nimport Mus"
  },
  {
    "path": "app/javascript/front/stores/musician_store.js",
    "chars": 514,
    "preview": "import { defineStore } from 'pinia'\n\nexport const MusicianStore = defineStore('musicians', {\n  state: () => {\n    return"
  },
  {
    "path": "app/javascript/front/views/musicians/index.vue",
    "chars": 618,
    "preview": "<template>\n  <section class=\"container\">\n    <h1>{{ $t('home.title') }}</h1>\n\n    <ul class=\"breadcrumb\">\n      <li><spa"
  },
  {
    "path": "app/javascript/front/views/musicians/show.vue",
    "chars": 756,
    "preview": "<template>  \n  <section class=\"container\">\n    <h1>{{ $t('home.title') }}</h1>\n\n    <ul class=\"breadcrumb\">\n      <li><r"
  },
  {
    "path": "app/javascript/front/views/pages/index.vue",
    "chars": 757,
    "preview": "<template>\n  <section class=\"container\">\n    <h1>{{ $t('pages.title') }}</h1>\n\n    <p><a href=\"/dead-link\">{{ $t('pages."
  },
  {
    "path": "app/javascript/front/views/shared/_footer.vue",
    "chars": 1043,
    "preview": "<template>    \n  <footer>\n    <div class=\"container\">\n      <div class=\"row\">\n        <div class=\"col-xs-12 col-lg-8\">\n "
  },
  {
    "path": "app/javascript/front/views/shared/_nav.vue",
    "chars": 1373,
    "preview": "<template>\n  <section class=\"top-nav\">\n    <div class=\"container\">\n      <div class=\"row\">\n        <div class=\"col-xs-8 "
  },
  {
    "path": "app/javascript/front/views/shared/layout.vue",
    "chars": 193,
    "preview": "<template>    \n  <main>\n    <nav-bar /> \n    <router-view />\n  </main>  \n  <bottom />\n</template>\n\n<script setup>\nimport"
  },
  {
    "path": "app/javascript/plugins/api.js",
    "chars": 1029,
    "preview": "import Axios from \"axios\";\n\nfunction Api() {\n  const get = function (route) {\n    return Axios.get(route);\n  };\n  const "
  },
  {
    "path": "app/javascript/plugins/cable.js",
    "chars": 773,
    "preview": "import { createConsumer } from \"@rails/actioncable\"\nimport mitt from 'mitt';\n\nconst protocol = window.location.protocol "
  },
  {
    "path": "app/jobs/application_job.rb",
    "chars": 269,
    "preview": "class ApplicationJob < ActiveJob::Base\n  # Automatically retry jobs that encountered a deadlock\n  # retry_on ActiveRecor"
  },
  {
    "path": "app/mailers/application_mailer.rb",
    "chars": 102,
    "preview": "class ApplicationMailer < ActionMailer::Base\n  default from: \"from@example.com\"\n  layout \"mailer\"\nend\n"
  },
  {
    "path": "app/models/application_record.rb",
    "chars": 74,
    "preview": "class ApplicationRecord < ActiveRecord::Base\n  primary_abstract_class\nend\n"
  },
  {
    "path": "app/models/concerns/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "app/models/musician.rb",
    "chars": 225,
    "preview": "class Musician < ApplicationRecord\n  paginates_per 5\n\n  validates_presence_of :name, :band\n\n  enum band: [:rolling_stone"
  },
  {
    "path": "app/models/user.rb",
    "chars": 122,
    "preview": "class User < ApplicationRecord\n  paginates_per 10\n\n  devise :database_authenticatable\n\n  validates_presence_of :email\nen"
  },
  {
    "path": "app/views/admin.html.erb",
    "chars": 923,
    "preview": "<html data-theme=\"light\">\n  <head>\n    <title>Boilerplate | Admin</title>\n    <meta charset=\"UTF-8\">\n    <%= csrf_meta_t"
  },
  {
    "path": "app/views/api/admin/musicians/edit.json.jbuilder",
    "chars": 206,
    "preview": "json.musician do\n  json.id @musician.id\n  json.name @musician.name\n  json.band @musician.band\n  json.bands Musician.band"
  },
  {
    "path": "app/views/api/admin/musicians/index.json.jbuilder",
    "chars": 390,
    "preview": "json.musicians @musicians.each do |musician|\n  json.id musician.id\n  json.created_at l(musician.created_at, format: :def"
  },
  {
    "path": "app/views/api/admin/musicians/new.json.jbuilder",
    "chars": 156,
    "preview": "json.musician do\n  json.name @musician.name\n  json.bands Musician.bands.each do |name, _|\n    json.key name\n    json.nam"
  },
  {
    "path": "app/views/api/admin/shared/_pagination.json.jbuilder",
    "chars": 208,
    "preview": "obj = paginate(kind)\njson.pagination do\n  json.current obj[:current]\n  json.previous obj[:previous]\n  json.next obj[:nex"
  },
  {
    "path": "app/views/api/admin/users/edit.json.jbuilder",
    "chars": 61,
    "preview": "json.user do\n  json.id @user.id\n  json.email @user.email\nend\n"
  },
  {
    "path": "app/views/api/admin/users/index.json.jbuilder",
    "chars": 248,
    "preview": "json.users @users.each do |user|\n  json.id user.id\n  json.created_at l(user.created_at, format: :default)\n  json.email u"
  },
  {
    "path": "app/views/api/admin/users/new.json.jbuilder",
    "chars": 42,
    "preview": "json.user do\n  json.email @user.email\nend\n"
  },
  {
    "path": "app/views/api/musicians/index.json.jbuilder",
    "chars": 201,
    "preview": "json.musicians @musicians.each do |musician|\n  json.id musician.id\n  json.created_at l(musician.created_at, format: :def"
  },
  {
    "path": "app/views/api/musicians/show.json.jbuilder",
    "chars": 117,
    "preview": "json.musician do\n  json.id @musician.id\n  json.name @musician.name\n  json.band t(@musician.band, scope: 'bands')\nend\n"
  },
  {
    "path": "app/views/application.html.erb",
    "chars": 920,
    "preview": "<html data-theme=\"light\">\n  <head>\n    <title>Boilerplate</title>\n    <meta charset=\"UTF-8\">\n    <%= csrf_meta_tags %>\n "
  },
  {
    "path": "app/views/devise/sessions/new.html.erb",
    "chars": 825,
    "preview": "<section class=\"container devise\">\n  <div class=\"row\">\n    <div class=\"col-xs-12 col-sm-10 col-md-6\">\n\n      <div class="
  },
  {
    "path": "app/views/layouts/devise.html.erb",
    "chars": 448,
    "preview": "<html data-theme=\"light\">\n  <head>\n    <title>Boilerplate</title>\n    <%= csrf_meta_tags %>\n    <meta name=\"viewport\" co"
  },
  {
    "path": "app/views/layouts/mailer.html.erb",
    "chars": 229,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <style>\n  "
  },
  {
    "path": "app/views/layouts/mailer.text.erb",
    "chars": 13,
    "preview": "<%= yield %>\n"
  },
  {
    "path": "bin/bootsnap",
    "chars": 747,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'bootsnap"
  },
  {
    "path": "bin/bundle",
    "chars": 2961,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'bundle' "
  },
  {
    "path": "bin/foreman",
    "chars": 744,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'foreman'"
  },
  {
    "path": "bin/importmap",
    "chars": 91,
    "preview": "#!/usr/bin/env ruby\n\nrequire_relative \"../config/application\"\nrequire \"importmap/commands\"\n"
  },
  {
    "path": "bin/irb",
    "chars": 732,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'irb' is "
  },
  {
    "path": "bin/nokogiri",
    "chars": 747,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'nokogiri"
  },
  {
    "path": "bin/puma",
    "chars": 735,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'puma' is"
  },
  {
    "path": "bin/pumactl",
    "chars": 741,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'pumactl'"
  },
  {
    "path": "bin/racc",
    "chars": 735,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'racc' is"
  },
  {
    "path": "bin/rackup",
    "chars": 739,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'rackup' "
  },
  {
    "path": "bin/rails",
    "chars": 141,
    "preview": "#!/usr/bin/env ruby\nAPP_PATH = File.expand_path(\"../config/application\", __dir__)\nrequire_relative \"../config/boot\"\nrequ"
  },
  {
    "path": "bin/rake",
    "chars": 90,
    "preview": "#!/usr/bin/env ruby\nrequire_relative \"../config/boot\"\nrequire \"rake\"\nRake.application.run\n"
  },
  {
    "path": "bin/rdbg",
    "chars": 736,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'rdbg' is"
  },
  {
    "path": "bin/setup",
    "chars": 984,
    "preview": "#!/usr/bin/env ruby\nrequire \"fileutils\"\n\n# path to your application root.\nAPP_ROOT = File.expand_path(\"..\", __dir__)\n\nde"
  },
  {
    "path": "bin/spring",
    "chars": 741,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'spring' "
  },
  {
    "path": "bin/sprockets",
    "chars": 750,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'sprocket"
  },
  {
    "path": "bin/thor",
    "chars": 735,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'thor' is"
  },
  {
    "path": "bin/tilt",
    "chars": 735,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'tilt' is"
  },
  {
    "path": "bin/vite",
    "chars": 740,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'vite' is"
  },
  {
    "path": "config/application.rb",
    "chars": 988,
    "preview": "require_relative \"boot\"\n\nrequire \"rails/all\"\n\n# Require the gems listed in Gemfile, including any gems\n# you've limited "
  },
  {
    "path": "config/boot.rb",
    "chars": 207,
    "preview": "ENV[\"BUNDLE_GEMFILE\"] ||= File.expand_path(\"../Gemfile\", __dir__)\n\nrequire \"bundler/setup\" # Set up gems listed in the G"
  },
  {
    "path": "config/cable.yml",
    "chars": 246,
    "preview": "development:\n  adapter: redis\n  url: redis://<%= ENV[\"REDIS_URL\"] || 'localhost' %>:6379/1\n\ntest:\n  adapter: test\n\nprodu"
  },
  {
    "path": "config/credentials.yml.enc",
    "chars": 464,
    "preview": "z50kuuEHj1CEUcKRpJWPamB52/q1tUa818uvcrvMpkdqctOJIuFJlK1CSQvI9roMpaEdrydKRM2yubgNJX5C4Bj5qsI5uW3O77bkjiSbVjjSrMOCE4iogYxF"
  },
  {
    "path": "config/database.yml",
    "chars": 594,
    "preview": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\n"
  },
  {
    "path": "config/environment.rb",
    "chars": 128,
    "preview": "# Load the Rails application.\nrequire_relative \"application\"\n\n# Initialize the Rails application.\nRails.application.init"
  },
  {
    "path": "config/environments/development.rb",
    "chars": 2672,
    "preview": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take pre"
  },
  {
    "path": "config/environments/production.rb",
    "chars": 4285,
    "preview": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take pre"
  },
  {
    "path": "config/environments/test.rb",
    "chars": 2643,
    "preview": "require \"active_support/core_ext/integer/time\"\n\n# The test environment is used exclusively to run your application's\n# t"
  },
  {
    "path": "config/importmap.rb",
    "chars": 345,
    "preview": "# Pin npm packages by running ./bin/importmap\n\npin \"application\", preload: true\npin \"@hotwired/turbo-rails\", to: \"turbo."
  },
  {
    "path": "config/initializers/assets.rb",
    "chars": 502,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire"
  },
  {
    "path": "config/initializers/content_security_policy.rb",
    "chars": 1071,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Define an application-wide content security policy.\n# See"
  },
  {
    "path": "config/initializers/cypress_rails_initializer.rb",
    "chars": 866,
    "preview": "return unless Rails.env.test?\n#require \"./lib/external_service\"\n\nRails.application.load_tasks unless defined?(Rake::Task"
  },
  {
    "path": "config/initializers/devise.rb",
    "chars": 15187,
    "preview": "# frozen_string_literal: true\n\n# Assuming you have not yet modified this file, each configuration option below\n# is set "
  },
  {
    "path": "config/initializers/filter_parameter_logging.rb",
    "chars": 448,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Configure parameters to be partially matched (e.g. passw "
  },
  {
    "path": "config/initializers/inflections.rb",
    "chars": 649,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Infl"
  },
  {
    "path": "config/initializers/locales.rb",
    "chars": 35,
    "preview": "I18n.available_locales = [:en, :fr]"
  },
  {
    "path": "config/initializers/new_framework_defaults_7_1.rb",
    "chars": 12110,
    "preview": "# Be sure to restart your server when you modify this file.\n#\n# This file eases your Rails 7.1 framework defaults upgrad"
  },
  {
    "path": "config/initializers/permissions_policy.rb",
    "chars": 480,
    "preview": "# Be sure to restart your server when you modify this file.\n\n# Define an application-wide HTTP permissions policy. For f"
  },
  {
    "path": "config/locales/devise.en.yml",
    "chars": 4138,
    "preview": "# Additional translations at https://github.com/plataformatec/devise/wiki/I18n\n\nen:\n  devise:\n    confirmations:\n      c"
  },
  {
    "path": "config/locales/en.yml",
    "chars": 2689,
    "preview": "en:\n  time:\n    formats:\n      default: \"%B %d %Y %H:%M\"\n  bands:\n    rolling_stones: \"The Rolling Stones\"\n    beatles: "
  },
  {
    "path": "config/locales/fr.yml",
    "chars": 2832,
    "preview": "fr:\n  time:\n    formats:\n      default: \"%B %d %Y %H:%M\"\n  bands:\n    rolling_stones: \"The Rolling Stones\"\n    beatles: "
  },
  {
    "path": "config/puma.rb",
    "chars": 1792,
    "preview": "# Puma can serve each request in a thread from an internal thread pool.\n# The `threads` method setting takes two numbers"
  },
  {
    "path": "config/routes.rb",
    "chars": 611,
    "preview": "Rails.application.routes.draw do\n  mount ActionCable.server => '/cable'\n\n  localized do\n    devise_for :users, only: [:s"
  },
  {
    "path": "config/storage.yml",
    "chars": 1152,
    "preview": "test:\n  service: Disk\n  root: <%= Rails.root.join(\"tmp/storage\") %>\n\nlocal:\n  service: Disk\n  root: <%= Rails.root.join("
  },
  {
    "path": "config/vite.json",
    "chars": 304,
    "preview": "{\n  \"all\": {\n    \"sourceCodeDir\": \"app/javascript\",\n    \"watchAdditionalPaths\": []\n  },\n  \"development\": {\n    \"autoBuil"
  },
  {
    "path": "config.ru",
    "chars": 160,
    "preview": "# This file is used by Rack-based servers to start the application.\n\nrequire_relative \"config/environment\"\n\nrun Rails.ap"
  },
  {
    "path": "cypress/e2e/home.cy.js",
    "chars": 1184,
    "preview": "describe('Main navigation', () => {\n  it('passes', () => {\n    cy.visit('http://localhost:5100')\n    cy.get('.row > :nth"
  },
  {
    "path": "cypress/support/e2e.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "cypress.config.js",
    "chars": 464,
    "preview": "const { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  // setupNodeEvents can be defined in eith"
  },
  {
    "path": "db/migrate/20220424120800_base_setup.rb",
    "chars": 890,
    "preview": "class BaseSetup < ActiveRecord::Migration[7.0]\n  def change\n    create_table :users do |t|\n      t.timestamps\n      t.st"
  },
  {
    "path": "db/migrate/20240102193807_add_service_name_to_active_storage_blobs.active_storage.rb",
    "chars": 718,
    "preview": "# This migration comes from active_storage (originally 20190112182829)\nclass AddServiceNameToActiveStorageBlobs < Active"
  },
  {
    "path": "db/migrate/20240102193808_create_active_storage_variant_records.active_storage.rb",
    "chars": 1103,
    "preview": "# This migration comes from active_storage (originally 20191206030411)\nclass CreateActiveStorageVariantRecords < ActiveR"
  },
  {
    "path": "db/migrate/20240102193809_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb",
    "chars": 292,
    "preview": "# This migration comes from active_storage (originally 20211119233751)\nclass RemoveNotNullOnActiveStorageBlobsChecksum <"
  },
  {
    "path": "db/schema.rb",
    "chars": 1640,
    "preview": "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the mig"
  },
  {
    "path": "db/seeds.rb",
    "chars": 844,
    "preview": "User.create!(email: \"admin@domain.com\", password: \"password\", password_confirmation: \"password\")\nMusician.create!(name: "
  },
  {
    "path": "docker-compose.yml",
    "chars": 484,
    "preview": "version: \"3.9\"\n\nservices:\n  redis:\n    image: 'redis'\n    restart: always\n    command: redis-server\n    ports:\n      - 6"
  },
  {
    "path": "lib/assets/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "lib/tasks/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "log/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "package.json",
    "chars": 641,
    "preview": "{\n  \"type\": \"module\",\n  \"devDependencies\": {\n    \"cypress\": \"^13.6.0\",\n    \"eslint\": \"^8.7.0\",\n    \"lightningcss-cli\": \""
  },
  {
    "path": "public/404.html",
    "chars": 1722,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <meta name=\"viewport\""
  },
  {
    "path": "public/422.html",
    "chars": 1705,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <meta name=\"viewport\" content="
  },
  {
    "path": "public/500.html",
    "chars": 1635,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <meta name=\"viewport\" conte"
  },
  {
    "path": "public/css/development/admin/forms.css",
    "chars": 5042,
    "preview": ".loading:not(form) * {\n  display: none;\n}\n.loading:not(form) {\n  width: 100%;\n  height: 40px;\n  margin: 100px auto 100px"
  },
  {
    "path": "public/css/development/admin.css",
    "chars": 176,
    "preview": "@import url(\"grid.css\");\n@import url(\"themes/light.css\");\n@import url(\"themes/dark.css\");\n@import url(\"main.css\");\n@impo"
  },
  {
    "path": "public/css/development/devise.css",
    "chars": 276,
    "preview": ".devise {\n  > .row {\n    margin-top: 50px;\n    @media (min-width: 576px) and (max-width: 991px) {\n      > div {\n        "
  },
  {
    "path": "public/css/development/front.css",
    "chars": 171,
    "preview": "@import url(\"grid.css\");\n@import url(\"themes/light.css\");\n@import url(\"themes/dark.css\");\n@import url(\"main.css\");\n@impo"
  },
  {
    "path": "public/css/development/grid.css",
    "chars": 5056,
    "preview": ":root {\n  --gutter: 20px;\n}\n[class^=\"col-\"] {\n  position: relative;\n}\n.wrapper {\n  width: calc(100% - var(--gutter));\n  "
  },
  {
    "path": "public/css/development/main.css",
    "chars": 1616,
    "preview": "html {\n  min-width: 375px;\n}\nbody, html {\n  background-color: var(--body-bg);\n}\nh1, h2, h3, h4, h5, h6 {\n  color: var(--"
  },
  {
    "path": "public/css/development/themes/dark.css",
    "chars": 173,
    "preview": "[data-theme=\"dark\"] {\n  --body-bg: #000;\n  --title-color: #fff;\n  --text-color: #ddd;\n  --light-text-color: #666; \n  --a"
  },
  {
    "path": "public/css/development/themes/light.css",
    "chars": 165,
    "preview": "[data-theme=\"light\"] {\n  --body-bg: #f5f5f5;\n  --title-color: #333;\n  --text-color: #666;\n  --light-text-color: #999; \n "
  },
  {
    "path": "public/css/development/utilities.css",
    "chars": 516,
    "preview": ".hidden {\n  display: none;\n}\n.ta-left {\n  text-align: left;\n}\n.ta-center {\n  text-align: center;\n}\n.ta-right {\n  text-al"
  },
  {
    "path": "public/robots.txt",
    "chars": 99,
    "preview": "# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file\n"
  },
  {
    "path": "public/vite-test/assets/admin-ebb17751.js",
    "chars": 16466,
    "preview": "import{d as q,_ as v,o as r,c,a as t,t as i,b as V,w,e as g,v as S,f as F,F as y,r as k,g as p,h as d,i as m,j as x,k as"
  },
  {
    "path": "public/vite-test/assets/front-f6acb49a.js",
    "chars": 4492,
    "preview": "import{d as I,_ as m,g as p,o as c,c as l,a as e,t as s,h,i as _,w as f,l as u,F as w,r as k,m as A,n as L,E as M,p as b"
  },
  {
    "path": "public/vite-test/assets/vue-i18n-6b73e0ca.js",
    "chars": 183491,
    "preview": "function Xs(e,t){const n=Object.create(null),r=e.split(\",\");for(let o=0;o<r.length;o++)n[r[o]]=!0;return t?o=>!!n[o.toLo"
  },
  {
    "path": "public/vite-test/manifest-assets.json",
    "chars": 2,
    "preview": "{}"
  },
  {
    "path": "public/vite-test/manifest.json",
    "chars": 442,
    "preview": "{\n  \"_vue-i18n-6b73e0ca.js\": {\n    \"file\": \"assets/vue-i18n-6b73e0ca.js\"\n  },\n  \"entrypoints/admin.js\": {\n    \"file\": \"a"
  },
  {
    "path": "storage/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/application_system_test_case.rb",
    "chars": 157,
    "preview": "require \"test_helper\"\n\nclass ApplicationSystemTestCase < ActionDispatch::SystemTestCase\n  driven_by :selenium, using: :c"
  },
  {
    "path": "test/controllers/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/fixtures/files/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/helpers/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/javascript/app.test.js",
    "chars": 152,
    "preview": "const chai = require('chai')\nconst expect = chai.expect\n\ndescribe('first test', () => {\n  it('is true', () => {\n    expe"
  },
  {
    "path": "test/mailers/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/models/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/system/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/test_helper.rb",
    "chars": 1529,
    "preview": "require File.expand_path('../../config/environment', __FILE__)\nrequire 'rails/test_help'\n\nENV['RAILS_ENV'] ||= 'test'\n\nr"
  },
  {
    "path": "tmp/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "vendor/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "vendor/javascript/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "vite.config.ts",
    "chars": 582,
    "preview": "import { defineConfig } from 'vite'\n\nimport RubyPlugin from 'vite-plugin-ruby'\nimport Vue from '@vitejs/plugin-vue'\nimpo"
  }
]

About this extraction

This page contains the full source code of the gbarillot/rails-vue-demo-app GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 162 files (342.8 KB), approximately 116.7k tokens, and a symbol index with 725 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!