diff --git a/addons/rose/addon/components/rose/layout/page.js b/addons/rose/addon/components/rose/layout/page.js
new file mode 100644
index 000000000..10190bcba
--- /dev/null
+++ b/addons/rose/addon/components/rose/layout/page.js
@@ -0,0 +1,7 @@
+import Component from '@ember/component';
+import layout from '../../../templates/components/rose/layout/page';
+
+export default Component.extend({
+ layout,
+ tagName: '',
+});
diff --git a/addons/rose/addon/components/rose/layout/page.stories.mdx b/addons/rose/addon/components/rose/layout/page.stories.mdx
new file mode 100644
index 000000000..ae54e41f8
--- /dev/null
+++ b/addons/rose/addon/components/rose/layout/page.stories.mdx
@@ -0,0 +1,31 @@
+import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
+import { hbs } from 'ember-cli-htmlbars';
+import { action } from '@storybook/addon-actions';
+import { text, boolean, select } from '@storybook/addon-knobs';
+
+
+
+# Layout|Page
+
+A page layout/positioning component that allows three distinct sections to be configured: header, sidebar, and main.
+Sidebar's content width determines it's column width.
+
+```htmlbars
+
+
+
+
+
+```
+
+
+ {{
+ template: hbs`
+
+ Header
+ Main
+ Sidebar
+
+ `,
+ }}
+
diff --git a/addons/rose/addon/components/rose/layout/page/header.js b/addons/rose/addon/components/rose/layout/page/header.js
new file mode 100644
index 000000000..45f4c6861
--- /dev/null
+++ b/addons/rose/addon/components/rose/layout/page/header.js
@@ -0,0 +1,7 @@
+import Component from '@ember/component';
+import layout from '../../../../templates/components/rose/layout/page/header';
+
+export default Component.extend({
+ layout,
+ tagName: '',
+});
diff --git a/addons/rose/addon/components/rose/layout/page/main.js b/addons/rose/addon/components/rose/layout/page/main.js
new file mode 100644
index 000000000..83b4d6dcb
--- /dev/null
+++ b/addons/rose/addon/components/rose/layout/page/main.js
@@ -0,0 +1,7 @@
+import Component from '@ember/component';
+import layout from '../../../../templates/components/rose/layout/page/main';
+
+export default Component.extend({
+ layout,
+ tagName: '',
+});
diff --git a/addons/rose/addon/components/rose/layout/page/sidebar.js b/addons/rose/addon/components/rose/layout/page/sidebar.js
new file mode 100644
index 000000000..9df366f6d
--- /dev/null
+++ b/addons/rose/addon/components/rose/layout/page/sidebar.js
@@ -0,0 +1,7 @@
+import Component from '@ember/component';
+import layout from '../../../../templates/components/rose/layout/page/sidebar';
+
+export default Component.extend({
+ layout,
+ tagName: '',
+});
diff --git a/addons/rose/addon/styles/rose/components/_index.scss b/addons/rose/addon/styles/rose/components/_index.scss
index e1518b033..e6d3eefd1 100644
--- a/addons/rose/addon/styles/rose/components/_index.scss
+++ b/addons/rose/addon/styles/rose/components/_index.scss
@@ -1,4 +1,5 @@
@import 'button';
@import 'form';
@import 'header';
+@import 'layout';
@import 'notification';
diff --git a/addons/rose/addon/styles/rose/components/layout/_index.scss b/addons/rose/addon/styles/rose/components/layout/_index.scss
new file mode 100644
index 000000000..958cc197d
--- /dev/null
+++ b/addons/rose/addon/styles/rose/components/layout/_index.scss
@@ -0,0 +1,36 @@
+@use '../../variables/media';
+
+.rose-layout-page {
+ display: grid;
+ grid-template-areas:
+ 'header header header header'
+ 'sidebar main main main';
+ // Define static grid column for sidebar
+ grid-template-columns: 12rem auto 1fr;
+ grid-template-rows: auto 1fr;
+ height: 100vh;
+}
+
+.rose-layout-header {
+ grid-area: header;
+}
+
+.rose-layout-main {
+ grid-area: main;
+}
+
+.rose-layout-sidebar {
+ grid-area: sidebar;
+}
+
+@media (max-width: media.width(small)) {
+ .rose-layout-page {
+ grid-template-columns: 1fr;
+ grid-template-rows: 0.1fr auto 1fr;
+ grid-template-areas:
+ 'header'
+ 'sidebar'
+ 'main';
+ padding-bottom: 0;
+ }
+}
diff --git a/addons/rose/addon/templates/components/rose/header.hbs b/addons/rose/addon/templates/components/rose/header.hbs
index 2d355f398..075ea7494 100644
--- a/addons/rose/addon/templates/components/rose/header.hbs
+++ b/addons/rose/addon/templates/components/rose/header.hbs
@@ -1,4 +1,4 @@
-
+
diff --git a/addons/rose/addon/templates/components/rose/layout/page.hbs b/addons/rose/addon/templates/components/rose/layout/page.hbs
new file mode 100644
index 000000000..73a8b205f
--- /dev/null
+++ b/addons/rose/addon/templates/components/rose/layout/page.hbs
@@ -0,0 +1,7 @@
+
+ {{yield (hash
+ header=(component 'rose/layout/page/header')
+ main=(component 'rose/layout/page/main')
+ sidebar=(component 'rose/layout/page/sidebar')
+ )}}
+
diff --git a/addons/rose/addon/templates/components/rose/layout/page/header.hbs b/addons/rose/addon/templates/components/rose/layout/page/header.hbs
new file mode 100644
index 000000000..545ac6c22
--- /dev/null
+++ b/addons/rose/addon/templates/components/rose/layout/page/header.hbs
@@ -0,0 +1,3 @@
+
diff --git a/addons/rose/addon/templates/components/rose/layout/page/main.hbs b/addons/rose/addon/templates/components/rose/layout/page/main.hbs
new file mode 100644
index 000000000..1752414ce
--- /dev/null
+++ b/addons/rose/addon/templates/components/rose/layout/page/main.hbs
@@ -0,0 +1,3 @@
+
+ {{yield}}
+
diff --git a/addons/rose/addon/templates/components/rose/layout/page/sidebar.hbs b/addons/rose/addon/templates/components/rose/layout/page/sidebar.hbs
new file mode 100644
index 000000000..07f519a2b
--- /dev/null
+++ b/addons/rose/addon/templates/components/rose/layout/page/sidebar.hbs
@@ -0,0 +1,3 @@
+
diff --git a/addons/rose/app/components/rose/layout/page.js b/addons/rose/app/components/rose/layout/page.js
new file mode 100644
index 000000000..a88273d71
--- /dev/null
+++ b/addons/rose/app/components/rose/layout/page.js
@@ -0,0 +1 @@
+export { default } from 'rose/components/rose/layout/page';
diff --git a/addons/rose/app/components/rose/layout/page/header.js b/addons/rose/app/components/rose/layout/page/header.js
new file mode 100644
index 000000000..77437dd12
--- /dev/null
+++ b/addons/rose/app/components/rose/layout/page/header.js
@@ -0,0 +1 @@
+export { default } from 'rose/components/rose/layout/page/header';
diff --git a/addons/rose/app/components/rose/layout/page/main.js b/addons/rose/app/components/rose/layout/page/main.js
new file mode 100644
index 000000000..3bde34d59
--- /dev/null
+++ b/addons/rose/app/components/rose/layout/page/main.js
@@ -0,0 +1 @@
+export { default } from 'rose/components/rose/layout/page/main';
diff --git a/addons/rose/app/components/rose/layout/page/sidebar.js b/addons/rose/app/components/rose/layout/page/sidebar.js
new file mode 100644
index 000000000..aa6735e84
--- /dev/null
+++ b/addons/rose/app/components/rose/layout/page/sidebar.js
@@ -0,0 +1 @@
+export { default } from 'rose/components/rose/layout/page/sidebar';
diff --git a/addons/rose/tests/integration/components/rose/layout/page-test.js b/addons/rose/tests/integration/components/rose/layout/page-test.js
new file mode 100644
index 000000000..f1f7feb87
--- /dev/null
+++ b/addons/rose/tests/integration/components/rose/layout/page-test.js
@@ -0,0 +1,47 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, find } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+
+module('Integration | Component | rose/layout/page', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('div'));
+ assert.ok(find('.rose-layout-page'));
+ });
+
+ test('it renders with attributes', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('#page'));
+ });
+
+ test('it renders with content', async function (assert) {
+ await render(hbs`
+
+ `);
+ assert.ok(find('#content'));
+ });
+
+ test('it renders with header', async function (assert) {
+ await render(hbs`
+
+ `);
+ assert.ok(find('.rose-layout-header'));
+ });
+
+ test('it renders with main', async function (assert) {
+ await render(hbs`
+
+ `);
+ assert.ok(find('.rose-layout-main'));
+ });
+
+ test('it renders with sidebar', async function (assert) {
+ await render(hbs`
+
+ `);
+ assert.ok(find('.rose-layout-sidebar'));
+ });
+});
diff --git a/addons/rose/tests/integration/components/rose/layout/page/header-test.js b/addons/rose/tests/integration/components/rose/layout/page/header-test.js
new file mode 100644
index 000000000..8976dd726
--- /dev/null
+++ b/addons/rose/tests/integration/components/rose/layout/page/header-test.js
@@ -0,0 +1,26 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, find } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+
+module('Integration | Component | rose/layout/page/header', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('header'));
+ assert.ok(find('.rose-layout-header'));
+ });
+
+ test('it renders with attributes', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('#header'));
+ });
+
+ test('it renders with content', async function (assert) {
+ await render(hbs`
+
+ `);
+ assert.ok(find('#content'));
+ });
+});
diff --git a/addons/rose/tests/integration/components/rose/layout/page/main-test.js b/addons/rose/tests/integration/components/rose/layout/page/main-test.js
new file mode 100644
index 000000000..9d677c85c
--- /dev/null
+++ b/addons/rose/tests/integration/components/rose/layout/page/main-test.js
@@ -0,0 +1,26 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, find } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+
+module('Integration | Component | rose/layout/page/main', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('main'));
+ assert.ok(find('.rose-layout-main'));
+ });
+
+ test('it renders with attributes', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('#main'));
+ });
+
+ test('it renders with content', async function (assert) {
+ await render(hbs`
+
+ `);
+ assert.ok(find('#content'));
+ });
+});
diff --git a/addons/rose/tests/integration/components/rose/layout/page/sidebar-test.js b/addons/rose/tests/integration/components/rose/layout/page/sidebar-test.js
new file mode 100644
index 000000000..e09886c0c
--- /dev/null
+++ b/addons/rose/tests/integration/components/rose/layout/page/sidebar-test.js
@@ -0,0 +1,26 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, find } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+
+module('Integration | Component | rose/layout/page/sidebar', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('aside'));
+ assert.ok(find('.rose-layout-sidebar'));
+ });
+
+ test('it renders with attributes', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('#sidebar'));
+ });
+
+ test('it renders with content', async function (assert) {
+ await render(hbs`
+
+ `);
+ assert.ok(find('#content'));
+ });
+});
diff --git a/ui/core/app/routes/orgs/org/projects/index.js b/ui/core/app/routes/orgs/org/projects/index.js
index be2068f6d..717ffbac5 100644
--- a/ui/core/app/routes/orgs/org/projects/index.js
+++ b/ui/core/app/routes/orgs/org/projects/index.js
@@ -1,3 +1,9 @@
import Route from '@ember/routing/route';
+import { action } from '@ember/object';
-export default class OrgsOrgProjectsIndexRoute extends Route {}
+export default class OrgsOrgProjectsIndexRoute extends Route {
+ @action
+ new() {
+ this.transitionTo('orgs.org.projects.new');
+ }
+}
diff --git a/ui/core/app/styles/app.scss b/ui/core/app/styles/app.scss
index 5271833cf..8604fb63b 100644
--- a/ui/core/app/styles/app.scss
+++ b/ui/core/app/styles/app.scss
@@ -38,10 +38,9 @@
box-sizing: border-box;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
border-radius: 2px;
- position: absolute;
- right: 10px;
- top: 70px;
- display: inline-block;
+ position: relative;
+ float: right;
+ top: -50px;
}
.project-overview,
diff --git a/ui/core/app/templates/application.hbs b/ui/core/app/templates/application.hbs
index 1ac60bbce..ff8c801fe 100644
--- a/ui/core/app/templates/application.hbs
+++ b/ui/core/app/templates/application.hbs
@@ -1,8 +1,14 @@
-
-{{title (app-name)}}
+
+
+
+
+
+
-
-
-
+
+ {{title (app-name)}}
+ {{outlet}}
+
-{{outlet}}
+
+
diff --git a/ui/core/app/templates/orgs/org/projects/index.hbs b/ui/core/app/templates/orgs/org/projects/index.hbs
index dd364442c..c873611a3 100644
--- a/ui/core/app/templates/orgs/org/projects/index.hbs
+++ b/ui/core/app/templates/orgs/org/projects/index.hbs
@@ -1,7 +1,13 @@
{{t "resources.projects"}}
-
-
{{t "titles.new"}}
+
+ {{t 'titles.new'}}
+
{{#each @model as |project|}}