Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions app/components/application/detail-header.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<div class="detail-header" data-test-detail-header>
<div class="detail-header-left" data-test-header-profile>
<div class="header-profile">
<img
src={{this.imageUrl}}
alt="Profile"
class="profile-image"
data-test-profile-image
/>
<div class="profile-info">
<div class="profile-name-row">
<h2 class="user-name" data-test-user-name>
{{this.fullName}}
</h2>
<Application::StatusBadge @status={{this.status}} />
</div>
<p class="profile-role">{{this.role}}</p>
<div class="profile-meta">
<div class="meta-item">
<IconifyIcon @icon="mdi:map-marker" />
{{this.location}}
</div>
<div class="meta-item">
<IconifyIcon @icon="mdi:lightbulb-on" />
{{this.skills}}
</div>
</div>
</div>
</div>

<div class="social-links-container" data-test-social-links-container>
{{#each this.socialLinks as |link|}}
<Application::SocialLinkPill
@platform={{link.platform}}
@userName={{link.userName}}
data-test-social-link
/>
{{/each}}
</div>
</div>

<div class="detail-header-right">
<div class="header-score-nudge" data-test-header-score-nudge>
<div class="score-display" data-test-score-display>
<span class="score-value" data-test-score-value>{{this.score}}</span>
<span class="score-label">Score</span>
</div>
<div class="score-display" data-test-nudge-details>
<span class="score-value">{{this.nudgeCount}}</span>
<span class="score-label">Nudges</span>
</div>
</div>
<div class="header-actions" data-test-header-actions>
{{#if @isAdmin}}
<Reusables::Button
@text="Dashboard"
@variant="dark"
@class="btn--xs"
@onClick={{this.navigateToDashboard}}
@test="navigate-button"
/>
{{else}}
<Reusables::Button
@text="Nudge"
@variant="dark"
@class="btn--xs"
@disabled={{this.isNudgeDisabled}}
@onClick={{this.nudgeApplication}}
@test="nudge-button"
/>
<Reusables::Button
@text="Edit"
@variant="light"
@class="btn--xs"
@onClick={{this.editApplication}}
@test="edit-button"
/>
{{/if}}
</div>
</div>
</div>
99 changes: 99 additions & 0 deletions app/components/application/detail-header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class DetailHeader extends Component {
get application() {
return this.args.application;
}

get userDetails() {
return this.args.userDetails;
}

get fullName() {
const { firstName, lastName } = this.application?.biodata || {};
return firstName || lastName ? `${firstName} ${lastName}` : null;
}

get imageUrl() {
return this.application?.imageUrl ?? '';
}

get role() {
return this.application?.role ?? 'N/A';
}

get status() {
return this.application?.status ?? 'pending';
}

get skills() {
return this.application?.professional?.skills ?? 'N/A';
}

get location() {
const { city, state, country } = this.application?.location || {};
return [city, state, country].filter(Boolean).join(', ') || 'N/A';
}

get score() {
return this.application?.score ?? 'N/A';
}

get nudgeCount() {
return this.application?.nudgeCount ?? 0;
}

get isNudgeDisabled() {
if (this.status !== 'pending') {
return true;
}
if (!this.application?.lastNudgedAt) {
return false;
}
const now = Date.now();
const lastNudgeTime = new Date(this.application.lastNudgedAt).getTime();
const TWENTY_FOUR_HOURS = 24 * 60 * 60 * 1000;
return now - lastNudgeTime < TWENTY_FOUR_HOURS;
}

get socialLinks() {
const links = [];
const social = this.application?.socialLink || {};

if (social.github)
links.push({ platform: 'GitHub', userName: social.github });
if (social.linkedin)
links.push({ platform: 'LinkedIn', userName: social.linkedin });
if (social.twitter)
links.push({ platform: 'Twitter', userName: social.twitter });
if (social.instagram)
links.push({ platform: 'Instagram', userName: social.instagram });
if (social.peerlist)
links.push({ platform: 'Peerlist', userName: social.peerlist });
if (social.dribbble)
links.push({ platform: 'Dribbble', userName: social.dribbble });
if (social.behance)
links.push({ platform: 'Behance', userName: social.behance });

return links;
}

@action
nudgeApplication() {
//ToDo: Implement logic for callling nudge API here
console.log('nudge application');
}

@action
editApplication() {
//ToDo: Implement logic for edit application here
console.log('edit application');
}

@action
navigateToDashboard() {
//ToDo: Navigate to dashboard site for admin actions
console.log('navigate to dashboard');
}
}
14 changes: 14 additions & 0 deletions app/components/application/feedback-card.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<div class="feedback-card" data-test-feedback-card>
<div class="feedback-header" data-test-feedback-header>
<Application::StatusBadge @status={{@status}} data-test-status-badge />
<span
class="feedback-date"
data-test-feedback-date
>{{this.formattedDate}}</span>
</div>
<div class="feedback-content" data-test-feedback-content>
<p class="feedback-text" data-test-feedback-text>{{@feedbackText}}</p>
<p class="feedback-reviewer" data-test-feedback-reviewer>by
{{@reviewerName}}</p>
</div>
</div>
11 changes: 11 additions & 0 deletions app/components/application/feedback-card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Component from '@glimmer/component';

export default class FeedbackCard extends Component {
get formattedDate() {
if (!this.args.createdAt) {
return 'N/A';
}
const date = new Date(this.args.createdAt);
return isNaN(date.getTime()) ? 'N/A' : date.toLocaleDateString();
}
}
11 changes: 11 additions & 0 deletions app/components/application/info-card.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div class="info-card" data-test-info-card>
<h3 class="info-card-title" data-test-info-card-title>{{@title}}</h3>
<div class="info-card-content" data-test-info-card-content>
{{#each @sections as |section|}}
<div class="info-section" data-test-info-section>
<label class="info-label">{{section.label}}</label>
<p class="info-value">{{section.value}}</p>
</div>
{{/each}}
</div>
</div>
11 changes: 11 additions & 0 deletions app/components/application/social-link-pill.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<a
href={{this.redirectUrl}}
target="_blank"
rel="noopener noreferrer"
class="social-link-pill"
data-test-social-link
data-test-platform={{@platform}}
>
<IconifyIcon @icon={{this.icon}} />
<span>{{@platform}}</span>
</a>
16 changes: 16 additions & 0 deletions app/components/application/social-link-pill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Component from '@glimmer/component';
import { mapSocialIcons, mapSocialUrls } from '../../constants/applications';

export default class SocialLinkPill extends Component {
get platform() {
return this.args.platform?.toLowerCase();
}

get icon() {
return mapSocialIcons[this.platform] || 'mdi:link';
}

get redirectUrl() {
return `${mapSocialUrls[this.platform]}/${this.args.userName}`;
}
}
3 changes: 3 additions & 0 deletions app/components/application/status-badge.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<span class="status-badge status-badge--{{@status}}" data-test-status-badge>
{{this.applicationStatus}}
</span>
11 changes: 11 additions & 0 deletions app/components/application/status-badge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Component from '@glimmer/component';
import { mapApplicationStatus } from '../../constants/applications';

export default class StatusBadge extends Component {
get applicationStatus() {
const status = this.args.status?.toUpperCase();
return (
mapApplicationStatus[status] || mapApplicationStatus.PENDING
)?.toUpperCase();
}
}
2 changes: 1 addition & 1 deletion app/components/header.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<img
data-test-home-img
class="nav__home__img"
src="assets/images/rds-logo.png"
src="/assets/images/rds-logo.png"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is required, since the assets weren't loading correctly for nested routes

alt="Real_Dev_Squad"
/>
</LinkTo>
Expand Down
2 changes: 1 addition & 1 deletion app/components/scroll-to-top.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{{#unless this.isLive}}
{{#if this.isScrollToTopVisible}}
<button class="btn__scroll" type="button" {{on "click" this.scrollToTop}}>
<img src="assets/icons/arrowup-icon.svg" alt="arrow up" />
<img src="/assets/icons/arrowup-icon.svg" alt="arrow up" />
</button>
{{/if}}
{{/unless}}
39 changes: 39 additions & 0 deletions app/constants/applications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export const mapApplicationStatus = {
ACCEPTED: 'accepted',
REJECTED: 'rejected',
CHANGES_REQUESTED: 'Changes Requested',
PENDING: 'pending',
};

export const mapSocialIcons = {
github: 'mdi:github',
linkedin: 'mdi:linkedin',
twitter: 'mdi:twitter',
instagram: 'mdi:instagram',
peerlist: 'mdi:account-circle',
};

export const mapSocialUrls = {
github: 'https://github.com',
linkedin: 'https://linkedin.com/in',
twitter: 'https://twitter.com',
instagram: 'https://instagram.com',
peerlist: 'https://peerlist.io',
dribbble: 'https://dribbble.com',
behance: 'https://behance.net',
};

export function adminMessage(status) {
switch (status) {
case 'pending':
return 'Admins are reviewing your applications, please hold up and keep on nudging.';
case 'accepted':
return 'Your application is approved, go back to join page to get join discord invite.';
case 'rejected':
return 'Your application has been rejected. Look at the feedback for more information.';
case 'changes_requested':
return 'Admin has requested changes on your application, please edit and submit again for review.';
default:
return 'Unknown status';
}
}
44 changes: 44 additions & 0 deletions app/controllers/applications/detail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Controller from '@ember/controller';
import { adminMessage } from '../../constants/applications';

export default class ApplicationsDetailController extends Controller {
get application() {
return this.model?.application;
}

get currentUser() {
return this.model?.currentUser;
}

get isAdmin() {
return this.currentUser?.roles?.super_user === true;
}

get isApplicant() {
return this.currentUser?.id === this.application?.userId;
}

get canAccessApplication() {
return this.isAdmin || this.isApplicant;
}

get aboutYouSections() {
return [
{
label: 'Introduction',
value: this.application?.intro?.introduction || 'N/A',
},
{ label: 'Fun Fact', value: this.application?.intro?.funFact || 'N/A' },
{ label: 'For Fun', value: this.application?.intro?.forFun || 'N/A' },
{ label: 'Why Join Us', value: this.application?.intro?.whyRds || 'N/A' },
];
}

get hasFeedback() {
return this.application?.feedback?.length > 0;
}

get showAdminMessage() {
return adminMessage(this.application?.status);
}
}
Loading