12 Commits

32 changed files with 4162 additions and 7 deletions

71
.gitignore vendored
View File

@ -1,8 +1,67 @@
# ---> Composer # ---> Ruby
composer.phar *.gem
/vendor/ *.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control # Used by dotenv library to load environment variables.
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file # .env
# composer.lock
# Ignore Byebug command history file.
.byebug_history
## Specific to RubyMotion:
.dat*
.repl_history
build/
*.bridgesupport
build-iPhoneOS/
build-iPhoneSimulator/
## Specific to RubyMotion (use of CocoaPods):
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# vendor/Pods/
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
## Environment normalization:
/.bundle/
/vendor/bundle
/lib/bundler/man/
# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
# .ruby-version
# .ruby-gemset
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
# .rubocop-https?--*
# Local database storage
data/raven.db
# Node modules for Grunt.js
node_modules/
# Compiled CSS and JS
public/css/
public/js/

11
Gemfile Normal file
View File

@ -0,0 +1,11 @@
source 'https://rubygems.org'
gem 'sinatra', '~> 3.0'
gem 'puma', '~> 6.0'
gem 'sequel', '~> 5.63'
gem 'sqlite3', '~> 1.5'
# Use rerun gem to auto-reload app
gem 'rerun'

45
Gemfile.lock Normal file
View File

@ -0,0 +1,45 @@
GEM
remote: https://rubygems.org/
specs:
ffi (1.15.5)
ffi (1.15.5-x64-mingw-ucrt)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mustermann (3.0.0)
ruby2_keywords (~> 0.0.1)
nio4r (2.5.8)
puma (6.0.0)
nio4r (~> 2.0)
rack (2.2.4)
rack-protection (3.0.4)
rack
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rerun (0.13.1)
listen (~> 3.0)
ruby2_keywords (0.0.5)
sequel (5.63.0)
sinatra (3.0.4)
mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.0.4)
tilt (~> 2.0)
sqlite3 (1.5.4-x64-mingw-ucrt)
sqlite3 (1.5.4-x86_64-linux)
tilt (2.0.11)
PLATFORMS
x64-mingw-ucrt
x86_64-linux
DEPENDENCIES
puma (~> 6.0)
rerun
sequel (~> 5.63)
sinatra (~> 3.0)
sqlite3 (~> 1.5)
BUNDLED WITH
2.3.7

41
Gruntfile.js Normal file
View File

@ -0,0 +1,41 @@
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
dist: {
options: {
style: 'compressed'
},
files: [{
expand: true,
cwd: 'assets/styles',
src: ['**/*.scss'],
dest: 'public/css',
ext: '.css'
}]
}
},
watch: {
css: {
files: ['assets/styles/**/*.scss'],
tasks: ['sass'],
options: {
atBegin: true,
spawn: false
}
}
}
});
// Load plugins.
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-sass');
// CLI tasks.
grunt.registerTask('default', ['sass']);
};

View File

@ -1,4 +1,4 @@
Copyright (c) <year> <owner> Copyright (c) 2022 Metaunix.net
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

20
Rakefile Normal file
View File

@ -0,0 +1,20 @@
require 'bundler/setup'
require 'sequel'
require 'sqlite3'
namespace :db do
task :migrate do
%x{sequel -m 'db/migrations/' 'sqlite://data/raven.db'}
end
end
namespace :server do
task :dev do
%x{ruby raven.rb}
end
task :reload do
%x{rerun --no-notify 'ruby raven.rb'}
end
end

80
assets/styles/kraken.scss Normal file
View File

@ -0,0 +1,80 @@
$nav-width: 200px;
$box-shadow-1: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
$box-shadow-2: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
body{
background: lightgrey;
padding-left: $nav-width;
}
#main-nav{
position: fixed;
top: 0;
left: 0;
width: $nav-width;
height: 100%;
padding: 20px 0;
background: #212121;
color: white;
box-shadow: $box-shadow-1;
box-sizing: border-box;
h3{
text-align: center;
}
ul{
list-style: none;
li{
margin: 0;
border-bottom: 1px solid #999;
&:first-child{
border-top: 1px solid #999;
}
}
a{
display: block;
box-sizing: border-box;
width: $nav-width;
padding: 10px 15px;
color: limegreen;
font-size: 2.5rem;
text-decoration: none;
transition: all 230ms ease-in-out;
&:hover{
background: rgba(255, 255, 255, 0.1);
}
}
}
}
#main-wrapper{
max-width: 1200px;
margin-top: 25px;
padding: 20px 30px;
background: white;
border-radius: 5px;
box-shadow: $box-shadow-2;
}
#item-header,
#license-header{
.item-name,
.license-name{
margin-bottom: 5px;
}
.item-created,
.item-updated,
.license-created,
.license-updated{
color: #666;
font-size: 1.75rem;
font-style: italic;
}
}

3
data/defaults.yaml Normal file
View File

@ -0,0 +1,3 @@
database:
adapter: 'sqlite'
database: 'data/raven.db'

View File

@ -0,0 +1,22 @@
Sequel.migration do
up do
create_table(:items) do
primary_key :id
String :name, null: false
String :manufacturer
String :serial_number
String :sku_number
String :type
String :purchased_from
DateTime :purchased_at
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
end
end
down do
drop_table(:items)
end
end

View File

@ -0,0 +1,20 @@
Sequel.migration do
up do
create_table(:item_comments) do
primary_key :id
String :body, null: false
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
end
alter_table(:item_comments) do
add_foreign_key :item_id, :items
end
end
down do
drop_table(:item_comments)
end
end

View File

@ -0,0 +1,22 @@
Sequel.migration do
up do
create_table(:licenses) do
primary_key :id
String :name, null: false
String :key, null: false
String :manufacturer
Integer :seats_used, default: 0
Integer :seats_total, default: 1
String :purchased_from
DateTime :purchased_at
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
end
end
down do
drop_table(:licenses)
end
end

View File

@ -0,0 +1,20 @@
Sequel.migration do
up do
create_table(:license_comments) do
primary_key :id
String :body, null: false
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
end
alter_table(:license_comments) do
add_foreign_key :license_id, :licenses
end
end
down do
drop_table(:license_comments)
end
end

20
lib/config.rb Normal file
View File

@ -0,0 +1,20 @@
require 'yaml'
class Config
def initialize(config_path)
@data = YAML::load_file(config_path)
end
def get(key, depth = 0)
bits = key.split('.')
value = @data
bits.each do |bit|
value = value[bit]
end
return value
end
end

21
lib/helpers.rb Normal file
View File

@ -0,0 +1,21 @@
helpers do
def nullable(value)
if (value) and (value != '')
return value
else
return 'N/a'
end
end
def date_format(date)
dt = date.to_datetime
return dt.strftime('%B %d, %Y @ %I:%M:%S %p %Z')
end
def date_format_input(date)
dt = date.to_datetime
return dt.strftime('%Y-%m-%dT%H:%M:%S')
end
end

13
lib/models/item.rb Normal file
View File

@ -0,0 +1,13 @@
class Item < Sequel::Model
one_to_many :item_comments
def type_selected?(option)
if self.type == option
return 'selected'
else
return ''
end
end
end

View File

@ -0,0 +1,5 @@
class ItemComment < Sequel::Model
many_to_one :items
end

5
lib/models/license.rb Normal file
View File

@ -0,0 +1,5 @@
class License < Sequel::Model
one_to_many :license_comments
end

View File

@ -0,0 +1,5 @@
class LicenseComment < Sequel::Model
many_to_one :licenses
end

163
lib/routes.rb Normal file
View File

@ -0,0 +1,163 @@
get '/' do
items = Item.reverse(:updated_at).limit(10).all()
erb :index, :locals => {
:title => 'Dashboard',
:items => items
}
end
get '/item' do
redirect '/item/list'
end
get '/item/list' do
items = Item.reverse(:updated_at).all()
erb :'item/list', :locals => {
:title => 'List of Items',
:items => items
}
end
get '/item/create' do
erb :'item/create', :locals => {
:title => 'Create New Item'
}
end
post '/item/create' do
item = Item.create(
name: params[:item_name],
serial_number: params[:item_serial],
sku_number: params[:item_sku],
purchased_from: params[:item_purchase_from],
purchased_at: params[:item_purchase_date],
manufacturer: params[:item_manufacturer],
type: params[:item_type]
)
redirect "/item/#{item.id}"
end
get '/item/:item_id' do
item = Item.where(id: params[:item_id]).first()
puts "#{item.name}"
erb :'item/view', :locals => {
:title => item.name,
:item => item
}
end
get '/item/:item_id/edit' do
item = Item.where(id: params[:item_id]).first()
puts "#{item.name}"
erb :'item/edit', :locals => {
:title => "Editing: #{item.name}",
:item => item
}
end
post '/item/:item_id/edit' do
item = Item.where(id: params[:item_id]).first()
item.name = params[:item_name]
item.serial_number = params[:item_serial]
item.sku_number = params[:item_sku]
item.purchased_from = params[:item_purchase_from]
item.purchased_at = params[:item_purchase_date]
item.manufacturer = params[:item_manufacturer]
item.type = params[:item_type]
item.save()
redirect "/item/#{item.id}"
end
get '/item/:item_id/delete' do
item = Item.where(id: params[:item_id]).first()
item.delete()
redirect '/item/list'
end
post '/item/:item_id/comment' do
item = Item.first(id: params[:item_id])
comment = ItemComment.create(body: params[:comment_body])
item.add_item_comment(comment)
redirect "/item/#{item.id}"
end
get '/license' do
redirect '/license/list'
end
get '/license/list' do
licenses = License.reverse(:updated_at).all()
erb :'license/list', :locals => {
:title => 'List of Licenses',
:licenses => licenses
}
end
get '/license/create' do
erb :'license/create', :locals => {
:title => 'Create New License'
}
end
post '/license/create' do
license = License.create(
name: params[:license_name],
key: params[:license_key],
manufacturer: params[:license_manufacturer],
seats_used: params[:license_seats_used],
seats_total: params[:license_seats_total],
purchased_from: params[:license_purchase_from],
purchased_at: params[:license_purchase_date]
)
redirect "/license/#{license.id}"
end
get '/license/:license_id' do
license = License.where(id: params[:license_id]).first()
puts "#{license.name}"
erb :'license/view', :locals => {
:title => license.name,
:license => license
}
end
get '/license/:license_id/edit' do
license = License.where(id: params[:license_id]).first()
puts "#{license.name}"
erb :'license/edit', :locals => {
:title => "Editing: #{license.name}",
:license => license
}
end
post '/license/:license_id/edit' do
license = License.where(id: params[:license_id]).first()
license.name = params[:license_name]
license.key = params[:license_key]
license.manufacturer = params[:license_manufacturer]
license.seats_used = params[:license_seats_used]
license.seats_total = params[:license_seats_total]
license.purchased_from = params[:license_purchase_from]
license.purchased_at = params[:license_purchase_date]
license.save()
redirect "/license/#{license.id}"
end
get '/license/:license_id/delete' do
license = License.where(id: params[:license_id]).first()
license.delete()
redirect '/license/list'
end
post '/license/:license_id/comment' do
license = License.first(id: params[:license_id])
comment = LicenseComment.create(body: params[:comment_body])
license.add_license_comment(comment)
redirect "/license/#{license.id}"
end

3064
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

33
package.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "raven",
"version": "0.1.0",
"description": "Self-hosted inventory tracker",
"main": "index.js",
"scripts": {
"grunt": "grunt",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "gitea@git.metaunix.net:Metaunix/raven.git"
},
"keywords": [
"inventory",
"tracking"
],
"author": "Gregory Ballanine <gballantine@metaunix.net>",
"uploaders": [
{
"name": "Gregory Ballantine",
"email": "gballantine@metaunix.net"
}
],
"license": "BSD-2-Clause",
"devDependencies": {
"grunt": "^1.5.3",
"grunt-cli": "^1.4.3",
"grunt-contrib-sass": "^2.0.0",
"grunt-contrib-watch": "^1.1.0",
"sass": "^1.55.0"
}
}

33
raven.rb Normal file
View File

@ -0,0 +1,33 @@
require 'logger'
require 'sequel'
require 'sqlite3'
require 'sinatra'
require_relative 'lib/config.rb'
set :public_folder, __dir__ + '/public'
set :views, settings.root + '/views'
# Load configuration file
conf = Config.new(File.join(__dir__, 'data/defaults.yaml'))
# Initialize logging
logger = Logger.new(STDOUT)
logger.level = Logger::INFO
# Load the Sequel timestamps plugin
Sequel::Model.plugin :timestamps
# Initialize Sequel gem for database actions
DB = Sequel.connect(adapter: conf.get('database.adapter'), database: conf.get('database.database'))
# Load models
require_relative 'lib/models/item.rb'
require_relative 'lib/models/item_comment.rb'
require_relative 'lib/models/license.rb'
require_relative 'lib/models/license_comment.rb'
# Load helper functions
require_relative 'lib/helpers.rb'
# Register route handlers
require_relative 'lib/routes.rb'

11
views/index.erb Normal file
View File

@ -0,0 +1,11 @@
<p>This is a test.</p>
<% if items.length > 0 %>
<ul>
<% items.each do |item| %>
<li><%= item.name %></li>
<% end %>
</ul>
<% else %>
<p>There are no items to display.</p>
<% end %>

64
views/item/create.erb Normal file
View File

@ -0,0 +1,64 @@
<div class="row">
<div class="twelve columns">
<h1>Create new item</h1>
</div>
</div>
<div class="row">
<div class="twelve columns">
<form action="/item/create" method="POST" class="u-full-width">
<div class="row">
<div class="columns twelve">
<label for="item_name">Item name:</label>
<input class="u-full-width" type="text" placeholder="My new item" id="item_name" name="item_name" required>
</div>
</div>
<div class="row">
<div class="six columns">
<label for="item_serial">Serial number:</label>
<input class="u-full-width" type="text" placeholder="0123456789" id="item_serial" name="item_serial">
</div>
<div class="six columns">
<label for="item_sku">SKU number:</label>
<input class="u-full-width" type="text" placeholder="ABC12345678" id="item_sku" name="item_sku">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="item_purchase_from">Purchased from:</label>
<input class="u-full-width" type="text" placeholder="Newegg" id="item_purchase_from" name="item_purchase_from">
</div>
<div class="six columns">
<label for="item_purchase_date">Purchased at:</label>
<input class="u-full-width" type="datetime-local" id="item_purchase_date" name="item_purchase_date">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="item_manufacturer">Manufacturer:</label>
<input class="u-full-width" type="text" placeholder="Manufacturer" id="item_manufacturer" name="item_manufacturer">
</div>
<div class="six columns">
<label for="item_type">Item type</label>
<select class="u-full-width" id="item_type" name="item_type">
<option value="cpu">Processor</option>
<option value="motherboard">Motherboard</option>
<option value="memory">Memory (RAM)</option>
<option value="psu">Power Supply</option>
<option value="case">Case</option>
<option value="storage">Storage Device</option>
<option value="gpu">Graphics Card</option>
</select>
</div>
</div>
<input class="button-primary u-full-width" type="submit" value="Submit">
</form>
</div>
</div>

64
views/item/edit.erb Normal file
View File

@ -0,0 +1,64 @@
<div class="row">
<div class="twelve columns">
<h1>Editing item: <%= item.name %></h1>
</div>
</div>
<div class="row">
<div class="twelve columns">
<form action="/item/<%= item.id %>/edit" method="POST" class="u-full-width">
<div class="row">
<div class="columns twelve">
<label for="item_name">Item name:</label>
<input class="u-full-width" type="text" placeholder="My new item" id="item_name" name="item_name" required value="<%= item.name %>">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="item_serial">Serial number:</label>
<input class="u-full-width" type="text" placeholder="0123456789" id="item_serial" name="item_serial" value="<%= item.serial_number %>">
</div>
<div class="six columns">
<label for="item_sku">SKU number:</label>
<input class="u-full-width" type="text" placeholder="ABC12345678" id="item_sku" name="item_sku" value="<%= item.sku_number %>">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="item_purchase_from">Purchased from:</label>
<input class="u-full-width" type="text" placeholder="Newegg" id="item_purchase_from" name="item_purchase_from" value="<%= item.purchased_from %>">
</div>
<div class="six columns">
<label for="item_purchase_date">Purchased at:</label>
<input class="u-full-width" type="datetime-local" id="item_purchase_date" name="item_purchase_date" value="<%= date_format_input(item.purchased_at) %>">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="item_manufacturer">Manufacturer:</label>
<input class="u-full-width" type="text" placeholder="Manufacturer" id="item_manufacturer" name="item_manufacturer" value="<%= item.manufacturer %>">
</div>
<div class="six columns">
<label for="item_type">Item type</label>
<select class="u-full-width" id="item_type" name="item_type">
<option value="cpu" <%= item.type_selected?('cpu') %>>Processor</option>
<option value="motherboard" <%= item.type_selected?('motherboard') %>>Motherboard</option>
<option value="memory" <%= item.type_selected?('memory') %>>Memory (RAM)</option>
<option value="psu" <%= item.type_selected?('psu') %>>Power Supply</option>
<option value="case" <%= item.type_selected?('case') %>>Case</option>
<option value="storage" <%= item.type_selected?('storage') %>>Storage Device</option>
<option value="gpu" <%= item.type_selected?('gpu') %>>Graphics Card</option>
</select>
</div>
</div>
<input class="button-primary u-full-width" type="submit" value="Submit">
</form>
</div>
</div>

21
views/item/list.erb Normal file
View File

@ -0,0 +1,21 @@
<div class="row">
<div class="twelve columns">
<h1>Hardware Inventory List</h1>
</div>
</div>
<div class="row">
<div class="twelve columns">
<p><a href="/item/create">Create new item</a></p>
<% if items.length > 0 %>
<ul>
<% items.each do |item| %>
<li><a href="/item/<%= item.id %>"><%= item.name %></a></li>
<% end %>
</ul>
<% else %>
<p>There is nothing registered in your inventory yet.</p>
<% end %>
</div>
</div>

65
views/item/view.erb Normal file
View File

@ -0,0 +1,65 @@
<div id="item-header" class="row">
<div class="twelve columns">
<h1 class="item-name"><%= item.name %></h1>
<h4 class="item-created">Item added at: <%= date_format(item.created_at) %></h4>
<% if item.updated_at %>
<h4 class="item-updated">Last updated at: <%= date_format(item.updated_at) %></h4>
<% end %>
</div>
</div>
<div class="row">
<div class="twelve columns">
<p class="inventory-actions">
<a href="/item/<%= item.id %>/edit"><i class="fa-solid fa-pen-to-square"></i></a>
<a href="/item/<%= item.id %>/delete"><i class="fa-solid fa-trash"></i></a>
</p>
<table class="u-full-width">
<thead>
<tr>
<th>Item type:</th>
<th>Manufacturer:</th>
<th>Serial number:</th>
<th>SKU number:</th>
<th>Vendor:</th>
<th>Purchase Date:</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= item.type %></td>
<td><%= nullable(item.manufacturer) %></td>
<td><%= nullable(item.serial_number) %></td>
<td><%= nullable(item.sku_number) %></td>
<td><%= nullable(item.purchased_from) %></td>
<td><%= nullable(date_format(item.purchased_at)) %></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="twelve columns">
<% if item.item_comments.length > 0 %>
<ul class="u-full-width">
<% item.item_comments.each do |comment| %>
<li><%= comment.body %></li>
<% end %>
</ul>
<% else %>
<p>There are no comments to display at this time.</p>
<% end %>
</div>
</div>
<div class="row">
<div class="twelve columns">
<form action="/item/<%= item.id %>/comment" method="POST" class="u-full-width">
<label for="comment_body">Add a comment:</label>
<textarea name="comment_body" id="comment_body" class="u-full-width" cols="30" rows="10"></textarea>
<input class="button button-primary" type="submit" value="Submit">
</form>
</div>
</div>

27
views/layout.erb Normal file
View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %> | Raven</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css">
<link rel="stylesheet" href="/css/kraken.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
<!-- Main navigation -->
<nav id="main-nav">
<h3>Raven</h3>
<ul>
<li><a href="/">Dashboard</a></li>
<li><a href="/item/list">Items</a></li>
<li><a href="/license/list">Licenses</a></li>
</ul>
</nav>
<div id="main-wrapper" class="container">
<%= yield %>
</div>
</body>
</html>

56
views/license/create.erb Normal file
View File

@ -0,0 +1,56 @@
<div class="row">
<div class="twelve columns">
<h1>Create new license</h1>
</div>
</div>
<div class="row">
<div class="twelve columns">
<form action="/license/create" method="POST" class="u-full-width">
<div class="row">
<div class="columns twelve">
<label for="license_name">Item name:</label>
<input class="u-full-width" type="text" placeholder="My new license" id="license_name" name="license_name" required>
</div>
</div>
<div class="row">
<div class="six columns">
<label for="license_key">License key:</label>
<input class="u-full-width" type="text" placeholder="acbd1234efgh5678" id="license_key" required name="license_key">
</div>
<div class="six columns">
<label for="license_manufacturer">Manufacturer:</label>
<input class="u-full-width" type="text" placeholder="Manufacturer" id="license_manufacturer" name="license_manufacturer">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="license_seats_used">Seats used on license:</label>
<input class="u-full-width" type="number" placeholder="0" id="license_seats_used" name="license_seats_used">
</div>
<div class="six columns">
<label for="license_seats_total">Total seats on license:</label>
<input class="u-full-width" type="number" placeholder="1" id="license_seats_total" name="license_seats_total">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="license_purchase_from">Purchased from:</label>
<input class="u-full-width" type="text" placeholder="Newegg" id="license_purchase_from" name="license_purchase_from">
</div>
<div class="six columns">
<label for="license_purchase_date">Purchased at:</label>
<input class="u-full-width" type="datetime-local" id="license_purchase_date" name="license_purchase_date">
</div>
</div>
<input class="button-primary u-full-width" type="submit" value="Submit">
</form>
</div>
</div>

56
views/license/edit.erb Normal file
View File

@ -0,0 +1,56 @@
<div class="row">
<div class="twelve columns">
<h1>Editing: <%= license.name %></h1>
</div>
</div>
<div class="row">
<div class="twelve columns">
<form action="/license/<%= license.id %>/edit" method="POST" class="u-full-width">
<div class="row">
<div class="columns twelve">
<label for="license_name">Item name:</label>
<input class="u-full-width" type="text" placeholder="My new license" id="license_name" name="license_name" required value="<%= license.name %>">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="license_key">License key:</label>
<input class="u-full-width" type="text" placeholder="acbd1234efgh5678" id="license_key" name="license_key" required value="<%= license.key %>">
</div>
<div class="six columns">
<label for="license_manufacturer">Manufacturer:</label>
<input class="u-full-width" type="text" placeholder="Manufacturer" id="license_manufacturer" name="license_manufacturer" value="<%= license.manufacturer %>">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="license_seats_used">Seats used on license:</label>
<input class="u-full-width" type="number" placeholder="0" id="license_seats_used" name="license_seats_used" value="<%= license.seats_used %>">
</div>
<div class="six columns">
<label for="license_seats_total">Total seats on license:</label>
<input class="u-full-width" type="number" placeholder="1" id="license_seats_total" name="license_seats_total" value="<%= license.seats_total %>">
</div>
</div>
<div class="row">
<div class="six columns">
<label for="license_purchase_from">Purchased from:</label>
<input class="u-full-width" type="text" placeholder="Newegg" id="license_purchase_from" name="license_purchase_from" value="<%= license.purchased_from %>">
</div>
<div class="six columns">
<label for="license_purchase_date">Purchased at:</label>
<input class="u-full-width" type="datetime-local" id="license_purchase_date" name="license_purchase_date" value="<%= date_format_input(license.purchased_at) %>">
</div>
</div>
<input class="button-primary u-full-width" type="submit" value="Submit">
</form>
</div>
</div>

21
views/license/list.erb Normal file
View File

@ -0,0 +1,21 @@
<div class="row">
<div class="twelve columns">
<h1>Software Licenses List</h1>
</div>
</div>
<div class="row">
<div class="twelve columns">
<p><a href="/license/create">Create new license</a></p>
<% if licenses.length > 0 %>
<ul>
<% licenses.each do |license| %>
<li><a href="/license/<%= license.id %>"><%= license.name %></a></li>
<% end %>
</ul>
<% else %>
<p>There is nothing registered in your inventory yet.</p>
<% end %>
</div>
</div>

65
views/license/view.erb Normal file
View File

@ -0,0 +1,65 @@
<div id="license-header" class="row">
<div class="twelve columns">
<h1 class="license-name"><%= license.name %></h1>
<h4 class="license-created">License added at: <%= date_format(license.created_at) %></h4>
<% if license.updated_at %>
<h4 class="license-updated">Last updated at: <%= date_format(license.updated_at) %></h4>
<% end %>
</div>
</div>
<div class="row">
<div class="twelve columns">
<p class="inventory-actions">
<a href="/license/<%= license.id %>/edit"><i class="fa-solid fa-pen-to-square"></i></a>
<a href="/license/<%= license.id %>/delete"><i class="fa-solid fa-trash"></i></a>
</p>
<table class="u-full-width">
<thead>
<tr>
<th>License key:</th>
<th>Manufacturer:</th>
<th>Seats used:</th>
<th>Seats total:</th>
<th>Vendor:</th>
<th>Purchase Date:</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= license.key %></td>
<td><%= nullable(license.manufacturer) %></td>
<td><%= nullable(license.seats_used) %></td>
<td><%= nullable(license.seats_total) %></td>
<td><%= nullable(license.purchased_from) %></td>
<td><%= nullable(date_format(license.purchased_at)) %></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="twelve columns">
<% if license.license_comments.length > 0 %>
<ul class="u-full-width">
<% license.license_comments.each do |comment| %>
<li><%= comment.body %></li>
<% end %>
</ul>
<% else %>
<p>There are no comments to display at this time.</p>
<% end %>
</div>
</div>
<div class="row">
<div class="twelve columns">
<form action="/license/<%= license.id %>/comment" method="POST" class="u-full-width">
<label for="comment_body">Add a comment:</label>
<textarea name="comment_body" id="comment_body" class="u-full-width" cols="30" rows="10"></textarea>
<input class="button button-primary" type="submit" value="Submit">
</form>
</div>
</div>