Skip to content

Commit

Permalink
Add vue frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
aurelien-reeves-scalingo authored Aug 23, 2024
1 parent 82c7e68 commit 01bae0d
Show file tree
Hide file tree
Showing 29 changed files with 1,586 additions and 75 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@

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

# Vite Ruby
/public/vite*
node_modules
# Vite uses dotenv and suggests to ignore local-only env files. See
# https://vitejs.dev/guide/env-and-mode.html#env-files
*.local

3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ Style/Documentation:
Metrics/BlockLength:
Exclude:
- '**/*_spec.rb'

Metrics/MethodLength:
Max: 15
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ RUN bundle config set --local path "/rails/vendor/bundle" && \
bundle install && \
bundle exec bootsnap precompile --gemfile

RUN gem install foreman

# Copy application code
COPY . .

Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ GEM
factory_bot_rails (6.4.3)
factory_bot (~> 6.4)
railties (>= 5.0.0)
foreman (0.88.1)
globalid (1.2.1)
activesupport (>= 6.1)
i18n (1.14.5)
Expand Down Expand Up @@ -315,6 +316,7 @@ DEPENDENCIES
dockerfile-rails (>= 1.2)
dotenv-rails
factory_bot_rails
foreman
jbuilder
jsbundling-rails
pg (~> 1.5)
Expand Down
3 changes: 3 additions & 0 deletions Procfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

vite: bin/vite dev
web: bin/rails s -p 3000 -b 0.0.0.0
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ RAILS_ENV=test
You'll need also to build the image and setup the app:

```shell
docker compose run build
docker compose build
docker compose run --rm web bundle install
docker compose run --rm web bundle exec rails db:prepare
```
Expand Down
29 changes: 23 additions & 6 deletions app/controllers/applications_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,45 @@ def create
@application = Application.new(application_params)

if @application.save
redirect_to applications_path
respond_to do |format|
format.html { redirect_to applications_path }
format.json { render :show, status: :ok }
end
else
index
render :index
respond_to do |format|
format.html do
index
render :index
end
format.json { render json: { errors: @application.errors }, status: :unprocessable_content }
end
end
end

def update
@application = current_application

if @application.update(application_params)
redirect_to @application
respond_to do |format|
format.html { redirect_to @application }
format.json { render :show, status: :ok }
end
else
render :edit, status: :unprocessable_content
respond_to do |format|
format.html { render :edit, status: :unprocessable_content }
format.json { render json: { errors: @application.errors }, status: :unprocessable_content }
end
end
end

def destroy
application = Application.find(params[:id])
ApplicationDestroyerService.new(application).call

redirect_to applications_path
respond_to do |format|
format.html { redirect_to applications_path }
format.json { render json: {}, status: :ok }
end
end

private
Expand Down
1 change: 1 addition & 0 deletions app/controllers/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

# rubocop:disable Rails/ApplicationController
class BaseController < ActionController::Base
layout 'application'
end
# rubocop:enable Rails/ApplicationController
13 changes: 13 additions & 0 deletions app/frontend/components/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<!-- <h1>Hello App!</h1>
<p>
<strong>Current route path:</strong> {{ $route.fullPath }}
</p>
<nav>
<RouterLink to="/">Go to Home</RouterLink>
<RouterLink to="/about">Go to About</RouterLink>
</nav> -->
<main>
<RouterView />
</main>
</template>
66 changes: 66 additions & 0 deletions app/frontend/components/ApplicationEditView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script setup>
import router from '../entrypoints/router';
import ErrorMessage from './ErrorMessage.vue'
defineProps({
id: String
})
</script>

<script>
export default {
data() {
return {
app: {
id: 0,
name: '',
description: '',
},
error: {},
}
},
created() {
this.fetchApp()
},
methods: {
async fetchApp() {
const response = await fetch(`/applications/${this.id}.json`)
this.app = await response.json()
},
async onSubmit() {
this.error = {}
const response = await fetch(`/applications/${this.id}.json`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
},
body: JSON.stringify(this.app),
})
if (response.ok) {
router.push({ name: 'Application', params: { id: this.id } })
return
}
this.error = await response.json()
}
},
}
</script>

<template>
<form @submit.prevent="onSubmit">
<div>
<label for="name">Name</label>
<input type="text" id="name" v-model="app.name" />
</div>
<div>
<label for="description">Description</label>
<textarea id="description" v-model="app.description"></textarea>
</div>
<button type="submit">Save</button>
<p><router-link :to="{ name: 'Application', params: { id: app.id } }">Cancel</router-link></p>
</form>
<ErrorMessage :error="error"></ErrorMessage>
</template>
58 changes: 58 additions & 0 deletions app/frontend/components/ApplicationView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script setup>
defineProps({
id: String
})
</script>

<script>
export default {
data() {
return {
app: {
name: '',
description: '',
},
error: {},
}
},
created() {
this.fetchApp()
},
methods: {
async fetchApp() {
const response = await fetch(`/applications/${this.id}.json`)
this.app = await response.json()
},
async onDestroy() {
this.error = {}
if (!confirm('Are you sure?')) {
return
}
const response = await fetch(`/applications/${this.id}.json`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
},
})
if (response.ok) {
this.$router.push('/')
return
}
this.error = await response.json()
},
},
}
</script>

<template>
<h2>{{ app.name }}</h2>
<p>{{ app.description }}</p>
<p><router-link :to="{ name: 'ApplicationEdit', params: { id: app.id } }">Edit</router-link></p>
<p><button @click="onDestroy">Destroy</button></p>
<ErrorMessage :error="error"></ErrorMessage>
<p><router-link to="/">back</router-link></p>
</template>
13 changes: 13 additions & 0 deletions app/frontend/components/ErrorMessage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup>
defineProps({
error: Object
})
</script>

<template>
<ul v-if="error.errors">
<li v-for="(errors, attributeName) in error.errors">
{{ attributeName }}: {{ errors.join(",") }}
</li>
</ul>
</template>
70 changes: 70 additions & 0 deletions app/frontend/components/HomeView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<script setup>
import ErrorMessage from './ErrorMessage.vue'
</script>

<script>
export default {
data() {
return {
apps: [],
newApp: {
name: '',
description: '',
},
error: {},
}
},
created() {
this.fetchApps()
},
methods: {
async fetchApps() {
const response = await fetch('/applications.json')
this.apps = await response.json()
},
async onSubmitNewApp() {
this.error = {}
const response = await fetch(`/applications.json`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
},
body: JSON.stringify(this.newApp),
})
if (response.ok) {
this.apps.push(await response.json())
this.newApp = { name: '', description: '' }
return
}
this.error = await response.json()
},
},
}
</script>

<template>
<h2>Applications</h2>
<ul>
<li v-for="app in apps" :key="app.id">
<router-link :to="{ name: 'Application', params: { id: app.id } }">
{{ app.name }}
</router-link>
</li>
</ul>
<h3>New application</h3>
<form @submit.prevent="onSubmitNewApp">
<div>
<label for="name">Name</label>
<input type="text" id="name" v-model="newApp.name" />
</div>
<div>
<label for="description">Description</label>
<textarea id="description" v-model="newApp.description"></textarea>
</div>
<button type="submit">Save</button>
</form>
<ErrorMessage :error="error"></ErrorMessage>
</template>
9 changes: 9 additions & 0 deletions app/frontend/entrypoints/application.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createApp } from 'vue/dist/vue.esm-bundler';
import router from './router'
import App from '../components/App.vue';

const app = createApp(App)
.use(router)
.mount('#app');

console.log("app", app);
18 changes: 18 additions & 0 deletions app/frontend/entrypoints/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { createWebHistory, createRouter } from 'vue-router'

import HomeView from '../components/HomeView.vue'
import ApplicationView from '../components/ApplicationView.vue'
import ApplicationEditView from '../components/ApplicationEditView.vue'

const routes = [
{ path: '/', component: HomeView },
{ path: '/applications/:id', name: 'Application', component: ApplicationView, props: true },
{ path: '/applications/:id/edit', name: 'ApplicationEdit', component: ApplicationEditView, props: true },
]

const router = createRouter({
history: createWebHistory(),
routes,
})

export default router
22 changes: 0 additions & 22 deletions app/views/applications/edit.html.erb
Original file line number Diff line number Diff line change
@@ -1,22 +0,0 @@
<%= form_with model: @application do |form| %>
<div>
<%= form.label :name %><br>
<%= form.text_field :name %>
<% @application.errors.full_messages_for(:name).each do |message| %>
<div><%= message %></div>
<% end %>
</div>

<div>
<%= form.label :description %><br>
<%= form.text_area :description %>
<% @application.errors.full_messages_for(:description).each do |message| %>
<div><%= message %></div>
<% end %>
</div>

<div>
<%= form.submit %>
</div>
<% end %>
Loading

0 comments on commit 01bae0d

Please sign in to comment.