DataInventory
Changes
app/Controllers/Http/ElementController.js 130(+130 -0)
app/Controllers/Http/ThemeController.js 124(+124 -0)
app/Models/Element.js 14(+14 -0)
app/Models/Theme.js 14(+14 -0)
package.json 1(+1 -0)
public/logo.png 0(+0 -0)
resources/views/elements/create.edge 53(+53 -0)
resources/views/elements/index.edge 44(+44 -0)
resources/views/inc/nav.edge 23(+18 -5)
resources/views/layouts/main.edge 21(+18 -3)
resources/views/themes/create.edge 29(+29 -0)
resources/views/themes/index.edge 38(+38 -0)
start/app.js 3(+2 -1)
start/routes.js 6(+5 -1)
Details
app/Controllers/Http/ElementController.js 130(+130 -0)
diff --git a/app/Controllers/Http/ElementController.js b/app/Controllers/Http/ElementController.js
new file mode 100644
index 0000000..beb2c94
--- /dev/null
+++ b/app/Controllers/Http/ElementController.js
@@ -0,0 +1,130 @@
+'use strict'
+
+/** @typedef {import('@adonisjs/framework/src/Request')} Request */
+/** @typedef {import('@adonisjs/framework/src/Response')} Response */
+/** @typedef {import('@adonisjs/framework/src/View')} View */
+const Element = use('App/Models/Element')
+const Theme = use('App/Models/Theme')
+const { validate } = use('Validator')
+
+/**
+ * Resourceful controller for interacting with elements
+ */
+class ElementController {
+ /**
+ * Show a list of all elements.
+ * GET elements
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ * @param {View} ctx.view
+ */
+ async index ({ request, response, view }) {
+ const elements = await Element.query().with('theme').orderBy('name', 'asc').fetch()
+ return view.render('elements.index', {
+ elements: elements.toJSON()
+ })
+ }
+
+ /**
+ * Render a form to be used for creating a new element.
+ * GET elements/create
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ * @param {View} ctx.view
+ */
+ async create ({ request, response, view }) {
+ const themes = await Theme.all()
+ return view.render('elements.create', {
+ themes: themes.toJSON()
+ })
+ }
+
+ /**
+ * Create/save a new element.
+ * POST elements
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ */
+ async store ({ request, response }) {
+ const data = request.only(['name', 'theme_id', 'priority', 'refresh_rate'])
+
+ const validation = await validate(request.all(), {
+ name: 'required|max:255',
+ theme_id: 'required',
+ priority: 'required',
+ refresh_rate: 'required'
+ }, {
+ 'name.required': `Theme name is required`,
+ 'theme_id.required': `Theme is required`,
+ 'priority.required': `Priority is required`,
+ 'refresh_rate.required': `Refresh rate is required`,
+ })
+
+ if(validation.fails()) {
+ session.withErrors(validation.messages()).flashAll()
+ return response.redirect('back')
+ }
+
+ await Element.create(data)
+
+ return response.redirect('/elements')
+ }
+
+ /**
+ * Display a single element.
+ * GET elements/:id
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ * @param {View} ctx.view
+ */
+ async show ({ params, request, response, view }) {
+ }
+
+ /**
+ * Render a form to update an existing element.
+ * GET elements/:id/edit
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ * @param {View} ctx.view
+ */
+ async edit ({ params, request, response, view }) {
+ }
+
+ /**
+ * Update element details.
+ * PUT or PATCH elements/:id
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ */
+ async update ({ params, request, response }) {
+ }
+
+ /**
+ * Delete a element with id.
+ * DELETE elements/:id
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ */
+ async destroy ({ params, request, response }) {
+ const element = await Element.findOrFail(params.id)
+ await element.delete()
+
+ return response.redirect('/elements')
+ }
+}
+
+module.exports = ElementController
app/Controllers/Http/ThemeController.js 124(+124 -0)
diff --git a/app/Controllers/Http/ThemeController.js b/app/Controllers/Http/ThemeController.js
new file mode 100644
index 0000000..ed8f288
--- /dev/null
+++ b/app/Controllers/Http/ThemeController.js
@@ -0,0 +1,124 @@
+'use strict'
+
+/** @typedef {import('@adonisjs/framework/src/Request')} Request */
+/** @typedef {import('@adonisjs/framework/src/Response')} Response */
+/** @typedef {import('@adonisjs/framework/src/View')} View */
+
+const Theme = use('App/Models/Theme')
+const { validate } = use('Validator')
+
+/**
+ * Resourceful controller for interacting with themes
+ */
+class ThemeController {
+ /**
+ * Show a list of all themes.
+ * GET themes
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ * @param {View} ctx.view
+ */
+ async index ({ request, response, view }) {
+ const themes = await Theme.query().orderBy('name', 'asc').fetch()
+
+ return view.render('themes.index', {
+ themes: themes.toJSON()
+ })
+ }
+
+ /**
+ * Render a form to be used for creating a new theme.
+ * GET themes/create
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ * @param {View} ctx.view
+ */
+ async create ({ request, response, view }) {
+ return view.render('themes.create')
+ }
+
+ /**
+ * Create/save a new theme.
+ * POST themes
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ */
+ async store ({ session, request, response }) {
+
+ const data = request.only(['name'])
+
+ const validation = await validate(request.all(), {
+ name: 'required|max:255',
+ }, {
+ 'name.required': `Theme name is required`,
+ })
+
+ if(validation.fails()) {
+ session.withErrors(validation.messages()).flashAll()
+ return response.redirect('back')
+ }
+
+ await Theme.create(data)
+
+ return response.redirect('/themes')
+
+ }
+
+ /**
+ * Display a single theme.
+ * GET themes/:id
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ * @param {View} ctx.view
+ */
+ async show ({ params, request, response, view }) {
+ }
+
+ /**
+ * Render a form to update an existing theme.
+ * GET themes/:id/edit
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ * @param {View} ctx.view
+ */
+ async edit ({ params, request, response, view }) {
+ }
+
+ /**
+ * Update theme details.
+ * PUT or PATCH themes/:id
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ */
+ async update ({ params, request, response }) {
+ }
+
+ /**
+ * Delete a theme with id.
+ * DELETE themes/:id
+ *
+ * @param {object} ctx
+ * @param {Request} ctx.request
+ * @param {Response} ctx.response
+ */
+ async destroy ({ params, request, response }) {
+ const theme = await Theme.findOrFail(params.id)
+ await theme.delete()
+
+ return response.redirect('/themes')
+ }
+}
+
+module.exports = ThemeController
app/Models/Element.js 14(+14 -0)
diff --git a/app/Models/Element.js b/app/Models/Element.js
new file mode 100644
index 0000000..0729410
--- /dev/null
+++ b/app/Models/Element.js
@@ -0,0 +1,14 @@
+'use strict'
+
+/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
+const Model = use('Model')
+
+class Element extends Model {
+
+ theme() {
+ return this.belongsTo('App/Models/Theme')
+ }
+
+}
+
+module.exports = Element
app/Models/Theme.js 14(+14 -0)
diff --git a/app/Models/Theme.js b/app/Models/Theme.js
new file mode 100644
index 0000000..3a63cae
--- /dev/null
+++ b/app/Models/Theme.js
@@ -0,0 +1,14 @@
+'use strict'
+
+/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
+const Model = use('Model')
+
+class Theme extends Model {
+
+ elements() {
+ return this.hadMany('App/Models/Element')
+ }
+
+}
+
+module.exports = Theme
diff --git a/database/migrations/1575321261885_themes_schema.js b/database/migrations/1575321261885_themes_schema.js
index bdc0da5..14b9bc6 100644
--- a/database/migrations/1575321261885_themes_schema.js
+++ b/database/migrations/1575321261885_themes_schema.js
@@ -8,6 +8,7 @@ class ThemesSchema extends Schema {
this.create('themes', (table) => {
table.increments('id')
table.string('name').notNullable().unique()
+ table.timestamps()
})
}
diff --git a/database/migrations/1575321574938_elements_schema.js b/database/migrations/1575321574938_elements_schema.js
index a7f54f7..4043b49 100644
--- a/database/migrations/1575321574938_elements_schema.js
+++ b/database/migrations/1575321574938_elements_schema.js
@@ -11,6 +11,7 @@ class ElementsSchema extends Schema {
table.integer('theme_id').notNullable().unsigned().references('id').inTable('themes')
table.integer('priority').nullable().unsigned()
table.integer('refresh_rate').nullable()
+ table.timestamps()
})
}
diff --git a/database/migrations/1575322085612_standards_schema.js b/database/migrations/1575322085612_standards_schema.js
index 31b756a..3b76a9c 100644
--- a/database/migrations/1575322085612_standards_schema.js
+++ b/database/migrations/1575322085612_standards_schema.js
@@ -10,8 +10,8 @@ class StandardsSchema extends Schema {
table.integer('element_id').notNullable().unsigned().references('id').inTable('elements')
table.string('document').notNullable()
table.float('version', 2).unsigned().notNullable()
- table.timestamps()
table.date('retired').nullable()
+ table.timestamps()
})
}
diff --git a/database/migrations/1575322481931_organization_types_schema.js b/database/migrations/1575322481931_organization_types_schema.js
index ac46e44..47fce01 100644
--- a/database/migrations/1575322481931_organization_types_schema.js
+++ b/database/migrations/1575322481931_organization_types_schema.js
@@ -8,6 +8,7 @@ class OrganizationTypesSchema extends Schema {
this.create('organization_types', (table) => {
table.increments('id')
table.string('type').notNullable()
+ table.timestamps()
})
}
diff --git a/database/migrations/1575323434337_elements_stewards_schema.js b/database/migrations/1575323434337_elements_stewards_schema.js
index 1077dd7..07638ea 100644
--- a/database/migrations/1575323434337_elements_stewards_schema.js
+++ b/database/migrations/1575323434337_elements_stewards_schema.js
@@ -8,6 +8,7 @@ class ElementsStewardsSchema extends Schema {
this.create('elements_stewards', (table) => {
table.integer('element_id').references('id').inTable('elements')
table.integer('steward_id').references('id').inTable('stewards')
+ table.timestamps()
})
}
package.json 1(+1 -0)
diff --git a/package.json b/package.json
index d4743c3..9cb4309 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"@adonisjs/lucid": "^6.1.3",
"@adonisjs/session": "^1.0.27",
"@adonisjs/shield": "^1.0.8",
+ "@adonisjs/validator": "^5.0.6",
"pg": "^7.14.0"
},
"devDependencies": {},
public/logo.png 0(+0 -0)
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 0000000..64dd6bc
Binary files /dev/null and b/public/logo.png differ
resources/views/elements/create.edge 53(+53 -0)
diff --git a/resources/views/elements/create.edge b/resources/views/elements/create.edge
new file mode 100644
index 0000000..0808db7
--- /dev/null
+++ b/resources/views/elements/create.edge
@@ -0,0 +1,53 @@
+@layout('layouts.main')
+
+@section('content')
+
+<form method="POST" action="{{ route('/elements.store') }}">
+ {{ csrfField() }}
+ <div class="field">
+ <label class="label">Element Name</label>
+ <input class="input" type="text" name="name" placeholder="Zoning" value="{{ old('name', '') }}" />
+ {{ elIf('<span class="has-text-danger">$self</span>', getErrorFor('name'), hasErrorFor('name')) }}
+ </div>
+
+ <div class="field">
+ <label class="label">Theme</label>
+ <div class="select">
+ <select name="theme_id">
+ <option>Select dropdown</option>
+ @each(theme in themes)
+ <option value="{{theme.id}}">{{theme.name}}</option>
+ @endeach
+ </select>
+ </div>
+ </div>
+
+ <div class="field">
+ <label class="label">Priority</label>
+ <input class="input" type="text" name="priority" placeholder="1 (lowest) - 3 (highest)" value="{{ old('priority', '') }}" />
+ {{ elIf('<span class="has-text-danger">$self</span>', getErrorFor('priority'), hasErrorFor('priority')) }}
+ </div>
+
+ <div class="field">
+ <label class="label">Refresh Rate (days)</label>
+ <input class="input" type="text" name="refresh_rate" placeholder="90" value="{{ old('refresh_rate', '') }}" />
+ {{ elIf('<span class="has-text-danger">$self</span>', getErrorFor('refresh_rate'), hasErrorFor('refresh_rate')) }}
+ </div>
+
+ <div class="field is-grouped">
+ <div class="control">
+ <button class="button is-link ss-submit" type="submit">Submit</button>
+ </div>
+ <div class="control">
+ <button class="button is-text"><a href="{{ route('/themes.index') }}">Cancel</a></button>
+ </div>
+ </div>
+ <!--
+ <div class="notification is-warning">
+ <button class="delete"></button>
+ Building Snapshots for some items may take a couple minutes. Please be patient!
+ </div>
+ -->
+</form>
+
+@endsection
\ No newline at end of file
resources/views/elements/index.edge 44(+44 -0)
diff --git a/resources/views/elements/index.edge b/resources/views/elements/index.edge
new file mode 100644
index 0000000..a8a48ae
--- /dev/null
+++ b/resources/views/elements/index.edge
@@ -0,0 +1,44 @@
+@layout('layouts.main')
+
+@section('content')
+
+<section class="box is-full-width">
+ <h3 class="is-size-3">Theme Actions</h3>
+ <ul>
+ <li><a href="{{ route('/elements/create') }}">Add Element</a></li>
+ </ul>
+</section>
+
+<p>There are {{ elements.length }} total elements.</p>
+
+<section class="box">
+ <table class="table">
+ <thead class="thead">
+ <tr>
+ <th>Element Name</th>
+ <th>Theme</th>
+ <th>Priority</th>
+ <th>Refresh Rate</th>
+ <th>Action</th>
+ </tr>
+ </thead>
+ <tbody class="tbody">
+ @each(element in elements)
+ <tr>
+ <td>{{ element.name }}</td>
+ <td>{{ element.theme.name }}</td>
+ <td>{{ element.priority }}</td>
+ <td>{{ element.refresh_rate }}</td>
+ <td>
+ <form action="{{ route('/elements.destroy', { id: element.id }) + '?_method=DELETE'}}" method="post">
+ {{ csrfField() }}
+ <button type="submit" name="button" class="is-small button is-danger" onclick="return confirm('Are you sure you want to delete this element?');">Delete</button>
+ </form>
+ </td>
+ </tr>
+ @endeach
+ </tbody>
+ </table>
+</section>
+
+@endsection
\ No newline at end of file
resources/views/inc/nav.edge 23(+18 -5)
diff --git a/resources/views/inc/nav.edge b/resources/views/inc/nav.edge
index 2737bf1..9bac3e0 100644
--- a/resources/views/inc/nav.edge
+++ b/resources/views/inc/nav.edge
@@ -1,6 +1,19 @@
-<section class="box">
- <h1 class="is-size-4">Data Inventory Tracking</h1>
+<section class="content box">
+ <h2 class="is-size-5">Main Links</h2>
+ <ul>
+ <li>Main link...</li>
+ </ul>
</section>
-<ul>
- <li>Navigation Link</li>
-</ul>
\ No newline at end of file
+
+<section class="content box">
+ <h2 class="is-size-5">Admin Links</h2>
+ <ul>
+ <li>
+ <a href="{{ route('/themes.index') }}">Themes</a>
+ </li>
+ <li>
+ <a href="{{ route('/elements.index') }}">Elements</a>
+ </li>
+ </ul>
+</section>
+
resources/views/layouts/main.edge 21(+18 -3)
diff --git a/resources/views/layouts/main.edge b/resources/views/layouts/main.edge
index 9d391bd..e00123c 100644
--- a/resources/views/layouts/main.edge
+++ b/resources/views/layouts/main.edge
@@ -13,12 +13,27 @@
</head>
<body class='container'>
<div class="content">
+ <nav class="navbar" role="navigation" aria-label="main navigation">
+ <div class="navbar-brand">
+ <a class="navbar-item" href="/">
+ <img src="{{ assetsUrl('logo.png') }}"" alt="Bulma: Free, open source, and modern CSS framework based on Flexbox" width="40" height="100%"> Oregon Geospatial Enterprise Office - Data Inventory Tracking
+ </a>
+
+ <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
+ <span aria-hidden="true"></span>
+ <span aria-hidden="true"></span>
+ <span aria-hidden="true"></span>
+ </a>
+ </div>
+ </nav>
+ </div>
+ <div class="content">
<div class="columns">
- <div class="column is-3">
+ <div class="column is-3 container">
<!-- Navigation Left Column -->
@include('inc.nav')
</div>
- <div class="column is-9">
+ <div class="column is-9 container">
<!-- Main Right Column -->
@!section('content')
</div>
@@ -26,6 +41,6 @@
@include('inc.footer')
</div>
@!section('scripts')
-{{ script('js/main.js') }}
+{{-- {{ script('js/main.js') }} --}}
</body>
</html>
\ No newline at end of file
resources/views/themes/create.edge 29(+29 -0)
diff --git a/resources/views/themes/create.edge b/resources/views/themes/create.edge
new file mode 100644
index 0000000..b7999fb
--- /dev/null
+++ b/resources/views/themes/create.edge
@@ -0,0 +1,29 @@
+@layout('layouts.main')
+
+@section('content')
+
+<form method="POST" action="{{ route('/themes.store') }}">
+ {{ csrfField() }}
+ <div class="field">
+ <label class="label">Theme Name</label>
+ <input class="input" type="text" name="name" placeholder="Admin boundaries" value="{{ old('name', '') }}" />
+ {{ elIf('<span class="has-text-danger">$self</span>', getErrorFor('name'), hasErrorFor('name')) }}
+ </div>
+
+ <div class="field is-grouped">
+ <div class="control">
+ <button class="button is-link ss-submit" type="submit">Submit</button>
+ </div>
+ <div class="control">
+ <button class="button is-text"><a href="{{ route('/themes.index') }}">Cancel</a></button>
+ </div>
+ </div>
+ <!--
+ <div class="notification is-warning">
+ <button class="delete"></button>
+ Building Snapshots for some items may take a couple minutes. Please be patient!
+ </div>
+ -->
+</form>
+
+@endsection
\ No newline at end of file
resources/views/themes/index.edge 38(+38 -0)
diff --git a/resources/views/themes/index.edge b/resources/views/themes/index.edge
new file mode 100644
index 0000000..7d97b00
--- /dev/null
+++ b/resources/views/themes/index.edge
@@ -0,0 +1,38 @@
+@layout('layouts.main')
+
+@section('content')
+
+<section class="box is-full-width">
+ <h3 class="is-size-3">Theme Actions</h3>
+ <ul>
+ <li><a href="{{ route('/themes/create') }}">Add Theme</a></li>
+ </ul>
+</section>
+
+<p>There are {{ themes.length }} total themes.</p>
+
+<section class="box">
+ <table class="table">
+ <thead class="thead">
+ <tr>
+ <th>Theme Name</th>
+ <th>Action</th>
+ </tr>
+ </thead>
+ <tbody class="tbody">
+ @each(theme in themes)
+ <tr>
+ <td>{{ theme.name }}</td>
+ <td>
+ <form action="{{ route('/themes.destroy', { id: theme.id }) + '?_method=DELETE'}}" method="post">
+ {{ csrfField() }}
+ <button type="submit" name="button" class="is-small button is-danger" onclick="return confirm('Are you sure you want to delete this theme?');">Delete</button>
+ </form>
+ </td>
+ </tr>
+ @endeach
+ </tbody>
+ </table>
+</section>
+
+@endsection
\ No newline at end of file
start/app.js 3(+2 -1)
diff --git a/start/app.js b/start/app.js
index e42f331..eb9b807 100644
--- a/start/app.js
+++ b/start/app.js
@@ -18,7 +18,8 @@ const providers = [
'@adonisjs/cors/providers/CorsProvider',
'@adonisjs/shield/providers/ShieldProvider',
'@adonisjs/session/providers/SessionProvider',
- '@adonisjs/auth/providers/AuthProvider'
+ '@adonisjs/auth/providers/AuthProvider',
+ '@adonisjs/validator/providers/ValidatorProvider'
]
/*
start/routes.js 6(+5 -1)
diff --git a/start/routes.js b/start/routes.js
index c6ad938..8fa8e76 100644
--- a/start/routes.js
+++ b/start/routes.js
@@ -17,4 +17,8 @@
const Route = use('Route')
// Main routes
-Route.on('/').render('index')
\ No newline at end of file
+Route.on('/').render('index')
+
+// Resource Routes (FULL CRUD)
+Route.resource('/themes','ThemeController')
+Route.resource('/elements', 'ElementController')
\ No newline at end of file