diff --git a/app/components/header.hbs b/app/components/header.hbs
index 54224285..f263aacd 100644
--- a/app/components/header.hbs
+++ b/app/components/header.hbs
@@ -51,7 +51,7 @@
class="nav__element"
href={{this.STATUS_URL}}
>Status
- {{#if @dev}}
+ {{#if @dev}}
{{! TODO - remove query for dev when it goes to production }}
{{else}}
-
- Sign In
- with GitHub
-
+
+ {{#if @dev}}
+ Sign In
+ {{else}}
+ Sign In
+ with GitHub
+
+ {{/if}}
{{/if}}
{{/if}}
diff --git a/app/components/header.js b/app/components/header.js
index f268a922..d2961913 100644
--- a/app/components/header.js
+++ b/app/components/header.js
@@ -43,6 +43,6 @@ export default class HeaderComponent extends Component {
this.fastboot.request.host +
this.fastboot.request.path
: window.location.href;
- return `${AUTH.SIGN_IN}?redirectURL=${currentURL}`;
+ return `${AUTH.GITHUB_SIGN_IN}?redirectURL=${currentURL}`;
}
}
diff --git a/app/components/login.hbs b/app/components/login.hbs
new file mode 100644
index 00000000..f8848d8c
--- /dev/null
+++ b/app/components/login.hbs
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Sign In to
+
+ Real Dev Squad
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/login.js b/app/components/login.js
new file mode 100644
index 00000000..eca4ee6b
--- /dev/null
+++ b/app/components/login.js
@@ -0,0 +1,30 @@
+import Component from '@glimmer/component';
+import { AUTH } from '../constants/urls';
+import { service } from '@ember/service';
+
+export default class LoginComponent extends Component {
+ @service router;
+ @service fastboot;
+
+ AUTH_URL = this.generateAuthURL();
+
+ generateAuthURL() {
+ let currentURL = this.fastboot.isFastBoot
+ ? this.fastboot.request.protocol +
+ '//' +
+ this.fastboot.request.host +
+ this.fastboot.request.path
+ : window.location.href;
+
+ if (currentURL) {
+ currentURL = currentURL.includes('login')
+ ? currentURL.replace('login', '')
+ : currentURL;
+ }
+
+ return {
+ GITHUB: `${AUTH.GITHUB_SIGN_IN}?redirectURL=${currentURL}`,
+ GOOGLE: `${AUTH.GOOGLE_SIGN_IN}&redirectURL=${currentURL}`,
+ };
+ }
+}
diff --git a/app/components/reusables/login-link.hbs b/app/components/reusables/login-link.hbs
new file mode 100644
index 00000000..69e062a8
--- /dev/null
+++ b/app/components/reusables/login-link.hbs
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/app/constants/urls.js b/app/constants/urls.js
index e4eaf6e0..019846c3 100644
--- a/app/constants/urls.js
+++ b/app/constants/urls.js
@@ -67,7 +67,8 @@ export const ABOUT = {
};
export const AUTH = {
- SIGN_IN: `${APPS.API_BACKEND}/auth/github/login`,
+ GITHUB_SIGN_IN: `${APPS.API_BACKEND}/auth/github/login`,
+ GOOGLE_SIGN_IN: `${APPS.API_BACKEND}/auth/google/login?dev=true`,
SIGN_UP: `${APPS.PROFILE}/new-signup`,
};
diff --git a/app/router.js b/app/router.js
index a78344ed..47338ac7 100644
--- a/app/router.js
+++ b/app/router.js
@@ -16,4 +16,5 @@ Router.map(function () {
this.route('events');
this.route('debug');
this.route('subscribe');
+ this.route('login');
});
diff --git a/app/routes/application.js b/app/routes/application.js
index 473ef158..3d7c4032 100644
--- a/app/routes/application.js
+++ b/app/routes/application.js
@@ -1,2 +1,23 @@
import Route from '@ember/routing/route';
-export default class ApplicationRoute extends Route {}
+import { service } from '@ember/service';
+import { TOAST_OPTIONS } from '../constants/toast-options';
+
+export default class ApplicationRoute extends Route {
+ @service router;
+ @service toast;
+
+ showToast(transition) {
+ this.toast.error(transition.to.queryParams.error, 'Error', TOAST_OPTIONS);
+ }
+
+ beforeModel(transition) {
+ if (
+ transition?.to?.queryParams?.dev === 'true' &&
+ transition?.to?.queryParams?.error
+ ) {
+ if (transition?.to?.queryParams?.error !== '') {
+ this.showToast(transition);
+ }
+ }
+ }
+}
diff --git a/app/routes/login.js b/app/routes/login.js
new file mode 100644
index 00000000..43fbe9d5
--- /dev/null
+++ b/app/routes/login.js
@@ -0,0 +1,13 @@
+import Route from '@ember/routing/route';
+import { inject as service } from '@ember/service';
+
+export default class LoginRoute extends Route {
+ @service router;
+ @service login;
+
+ beforeModel(transition) {
+ if (transition?.to?.queryParams?.dev !== 'true') {
+ this.router.transitionTo('/page-not-found');
+ }
+ }
+}
diff --git a/app/styles/app.css b/app/styles/app.css
index 48c90b21..2ce11118 100644
--- a/app/styles/app.css
+++ b/app/styles/app.css
@@ -43,6 +43,7 @@
@import url("unauthenticated.module.css");
@import url("subscribe.module.css");
@import url("phone-input.module.css");
+@import url("login.module.css");
* {
margin: 0;
diff --git a/app/styles/login.module.css b/app/styles/login.module.css
new file mode 100644
index 00000000..4bfeba7a
--- /dev/null
+++ b/app/styles/login.module.css
@@ -0,0 +1,105 @@
+.login__container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 80%;
+ background: white;
+ z-index: 10;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.login__container .logo__img {
+ position: absolute;
+ top: 2.5rem;
+ left: 3rem;
+ height: 5rem;
+ width: 5rem;
+}
+
+.login__container h3 {
+ text-align: center;
+ margin: 0.8rem 0;
+ font-size: 2.25rem;
+}
+
+.link__container {
+ margin-top: 1rem;
+}
+
+.link__container .join__link {
+ margin-top: 4px;
+ transition:
+ background-color 0.3s ease,
+ transform 0.3s ease;
+}
+
+.link__container .join__link:hover {
+ background-color: var(--color-pink-light);
+}
+
+.link__container .join__link:active {
+ transform: scale(0.95);
+}
+
+.link__container small {
+ font-size: large;
+ font-weight: 500;
+ margin: 12px;
+}
+
+.login__container h3 span {
+ font-size: 2.5rem;
+ color: var(--color-pink);
+}
+
+.login__container a {
+ display: flex;
+ gap: 8px;
+}
+
+@media (width <= 625px) {
+ .logo__img {
+ width: 5rem;
+ }
+
+ .login__container h3 {
+ font-size: 1.75rem;
+ }
+
+ .login__container h3 span {
+ font-size: 2.25rem;
+ }
+
+ .link__container small {
+ font-size: medium;
+ margin: 0.5rem;
+ }
+
+ .login__container a img {
+ height: 20px;
+ width: 20px;
+ }
+}
+
+@media (width <= 425px) {
+ .login__container h3 {
+ font-size: 1.25rem;
+ }
+
+ .login__container h3 span {
+ font-size: 1.8rem;
+ }
+
+ .link__container {
+ margin-top: 1rem;
+ }
+
+ .login__container a img {
+ height: 12px;
+ width: 12px;
+ }
+}
diff --git a/app/styles/variables.css b/app/styles/variables.css
index 2e7c4cfe..e571b478 100644
--- a/app/styles/variables.css
+++ b/app/styles/variables.css
@@ -16,6 +16,7 @@
--color-blackshadow2: #3c404326;
--color-backdrop: #0006;
--color-pink: #e30162;
+ --color-pink-light: #e32476;
--color-carmine: #a10829;
--color-soft-magenta: #f5c4f1;
--color-pink-low-opacity: #e301632e;
diff --git a/app/templates/login.hbs b/app/templates/login.hbs
new file mode 100644
index 00000000..9208246d
--- /dev/null
+++ b/app/templates/login.hbs
@@ -0,0 +1,2 @@
+{{page-title "Login"}}
+
\ No newline at end of file
diff --git a/public/assets/icons/google-logo.png b/public/assets/icons/google-logo.png
new file mode 100644
index 00000000..aa026d5f
Binary files /dev/null and b/public/assets/icons/google-logo.png differ
diff --git a/tests/constants/urls.js b/tests/constants/urls.js
index eaf0a096..399018bc 100644
--- a/tests/constants/urls.js
+++ b/tests/constants/urls.js
@@ -8,6 +8,7 @@ export const APPS = {
TASKS: 'https://my.realdevsquad.com/tasks',
IDENTITY: 'https://my.realdevsquad.com/identity',
MY_STATUS: 'https://my.realdevsquad.com',
+ STAGING_API_BACKEND: 'https://staging-api.realdevsquad.com',
};
export const ABOUT = {
diff --git a/tests/integration/components/header-test.js b/tests/integration/components/header-test.js
index 93058f6d..426f7ed3 100644
--- a/tests/integration/components/header-test.js
+++ b/tests/integration/components/header-test.js
@@ -8,11 +8,12 @@ module('Integration | Component | header', function (hooks) {
setupRenderingTest(hooks);
test('header elements renders', async function (assert) {
- assert.expect(13);
+ assert.expect(15);
this.setProperties({
isLoggedIn: false,
isLoading: false,
+ dev: false,
});
this.set('signOut', () => {
@@ -24,6 +25,7 @@ module('Integration | Component | header', function (hooks) {
@isLoggedIn={{this.isLoggedIn}}
@isLoading={{this.isLoading}}
@signOut={{this.signOut}}
+ @dev={{this.dev}}
/>
`);
@@ -52,6 +54,10 @@ module('Integration | Component | header', function (hooks) {
*/
// assert.dom('[data-test-login]').hasAttribute('href', AUTH.SIGN_IN);
assert.dom('[data-test-login-img]').exists();
+
+ this.set('dev', true);
+ assert.dom('[data-test-login]').hasAttribute('href', '/login?dev=true');
+ assert.dom('[data-test-login]').hasText('Sign In');
});
test('toggle nav menu in mobile view', async function (assert) {
diff --git a/tests/integration/components/login-link-test.js b/tests/integration/components/login-link-test.js
new file mode 100644
index 00000000..a81aeecc
--- /dev/null
+++ b/tests/integration/components/login-link-test.js
@@ -0,0 +1,49 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'website-www/tests/helpers';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | login-link', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders content', async function (assert) {
+ this.set('label', 'For Developers:');
+ this.set('auth', 'github');
+ this.set('signInText', 'Sign In with GitHub');
+ this.set('iconSrc', 'assets/icons/github-logo.png');
+ this.set('altText', 'GitHub');
+
+ await render(hbs`
+ `);
+
+ assert.dom('small').exists('Label element exists');
+ assert.dom('small').hasText('For Developers:', 'Label text is correct');
+
+ assert
+ .dom('.join__link span')
+ .hasText('Sign In with GitHub', 'Sign in text is correct');
+
+ assert.dom('.login__logo').exists('Logo image exists');
+ assert
+ .dom('.login__logo')
+ .hasAttribute(
+ 'src',
+ 'assets/icons/github-logo.png',
+ 'Image has correct src',
+ );
+ assert
+ .dom('.login__logo')
+ .hasAttribute('alt', 'GitHub', 'Image has correct alt text');
+ assert
+ .dom('.login__logo')
+ .hasAttribute('height', '24px', 'Image has correct height');
+ assert
+ .dom('.login__logo')
+ .hasAttribute('width', '24px', 'Image has correct width');
+ });
+});
diff --git a/tests/integration/components/login-test.js b/tests/integration/components/login-test.js
new file mode 100644
index 00000000..eb861ff9
--- /dev/null
+++ b/tests/integration/components/login-test.js
@@ -0,0 +1,31 @@
+import { module, test } from 'qunit';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+import { setupRenderingTest } from 'website-www/tests/helpers';
+import { APPS } from '../../constants/urls';
+
+module('Integration | Component | login', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('renders content & generates correct auth URLs', async function (assert) {
+ await render(hbs``);
+
+ assert.dom('.login__container').exists();
+ assert.dom('.login__container h3').hasText('Sign In to Real Dev Squad');
+
+ assert
+ .dom('[data-test-login=github]')
+ .exists()
+ .hasAttribute(
+ 'href',
+ `${APPS.STAGING_API_BACKEND}/auth/github/login?redirectURL=${window.location.href}`,
+ );
+ assert
+ .dom('[data-test-login=google]')
+ .exists()
+ .hasAttribute(
+ 'href',
+ `${APPS.STAGING_API_BACKEND}/auth/google/login?dev=true&redirectURL=${window.location.href}`,
+ );
+ });
+});
diff --git a/tests/unit/routes/login-test.js b/tests/unit/routes/login-test.js
new file mode 100644
index 00000000..253d0cec
--- /dev/null
+++ b/tests/unit/routes/login-test.js
@@ -0,0 +1,32 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'website-www/tests/helpers';
+import { visit, currentURL } from '@ember/test-helpers';
+
+module('Unit | Route | login', function (hooks) {
+ setupTest(hooks);
+
+ test('it exists', function (assert) {
+ let route = this.owner.lookup('route:login');
+ assert.ok(route);
+ });
+
+ test('it can be only visited with dev flag', async function (assert) {
+ await visit('/login?dev=true');
+
+ assert.strictEqual(
+ currentURL(),
+ '/login?dev=true',
+ 'User can access /login with dev flag only',
+ );
+ });
+
+ test('it cannot be visited without dev flag', async function (assert) {
+ await visit('/login');
+
+ assert.strictEqual(
+ currentURL(),
+ '/page-not-found',
+ 'User cannot access /login without dev flag',
+ );
+ });
+});