mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 09:36:19 -05:00
UX: Show badges as cards on the badges index and show page
This commit is contained in:
parent
fc9519af52
commit
27c793a990
10 changed files with 193 additions and 95 deletions
|
@ -0,0 +1,15 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
size: 'medium',
|
||||
classNameBindings: [':badge-card', 'size'],
|
||||
|
||||
@computed('size')
|
||||
summary(size) {
|
||||
if (size === 'large') {
|
||||
return Discourse.Emoji.unescape(this.get('badge.long_description') || '');
|
||||
}
|
||||
return this.get('badge.translatedDescription');
|
||||
}
|
||||
|
||||
});
|
|
@ -1,11 +1,16 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'i',
|
||||
classNameBindings: [':fa', 'iconClass'],
|
||||
tagName: 'span',
|
||||
classNameBindings: [':check-display', 'status'],
|
||||
|
||||
@computed('checked')
|
||||
iconClass(checked) {
|
||||
return checked ? 'fa-check' : 'fa-times';
|
||||
status(checked) {
|
||||
return checked ? 'status-checked' : 'status-unchecked';
|
||||
},
|
||||
|
||||
render(buffer) {
|
||||
const icon = this.get('checked') ? 'check' : 'times';
|
||||
buffer.push(`<i class='fa fa-${icon}'></i>`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -44,11 +44,6 @@ export default Ember.Controller.extend({
|
|||
@observes('canLoadMore')
|
||||
_showFooter() {
|
||||
this.set("controllers.application.showFooter", !this.get("canLoadMore"));
|
||||
},
|
||||
|
||||
@computed('model.long_description')
|
||||
longDescription(modelLongDesc) {
|
||||
return Discourse.Emoji.unescape(modelLongDesc || '');
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -33,13 +33,6 @@ const Badge = RestModel.extend({
|
|||
return I18n.t(i18nKey, {defaultValue: this.get('name')});
|
||||
}.property('name', 'i18nNameKey'),
|
||||
|
||||
/**
|
||||
The i18n translated description for this badge. Returns the null if no
|
||||
translation exists.
|
||||
|
||||
@property translatedDescription
|
||||
@type {String}
|
||||
**/
|
||||
translatedDescription: function() {
|
||||
const i18nKey = "badges.badge." + this.get('i18nNameKey') + ".description";
|
||||
let translation = I18n.t(i18nKey);
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
<div class='container badges'>
|
||||
<h1>{{i18n 'badges.title'}}</h1>
|
||||
|
||||
<table class='badges-listing'>
|
||||
<tbody>
|
||||
{{#each bg in badgeGroups}}
|
||||
<tr class='title'>
|
||||
<td colspan=4><h3>{{bg.badgeGrouping.displayName}}</h3></td>
|
||||
</tr>
|
||||
{{#each b in bg.badges}}
|
||||
<tr>
|
||||
<td class='granted'>{{#if b.has_badge}}<i class='fa fa-check'></i>{{/if}}</td>
|
||||
<td class='badge'>{{user-badge badge=b}}</td>
|
||||
<td class='description'>{{{b.displayDescriptionHtml}}}</td>
|
||||
<td class='grant-count'><span title="{{i18n 'badges.granted' count=b.grant_count}}">{{b.grant_count}}</span></td>
|
||||
</tr>
|
||||
<div class='badge-groups'>
|
||||
{{#each bg in badgeGroups}}
|
||||
<div class='badge-grouping'>
|
||||
<div class='title'>
|
||||
<h3>{{bg.badgeGrouping.displayName}}</h3>
|
||||
</div>
|
||||
|
||||
{{#each bg.badges as |b|}}
|
||||
{{badge-card badge=b}}
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,18 +5,16 @@
|
|||
{{model.displayName}}
|
||||
</h1>
|
||||
|
||||
{{#if longDescription}}
|
||||
<div class='long-description banner'>
|
||||
{{{longDescription}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class='badges-listing'>
|
||||
<div class='row'>
|
||||
<div class='grant-count'>{{i18n 'badges.granted' count=grantCount}}</div>
|
||||
<div class='info'>
|
||||
{{i18n 'badges.allow_title'}} {{check-mark checked=model.allow_title}}<br>
|
||||
{{i18n 'badges.multiple_grant'}} {{check-mark checked=model.multiple_grant}}
|
||||
<div class='show-badge-details'>
|
||||
{{badge-card badge=model size="large"}}
|
||||
<div class='badge-grant-info'>
|
||||
<div>
|
||||
<div class='grant-info-item'>
|
||||
{{check-mark checked=model.allow_title}} {{i18n 'badges.allow_title'}}
|
||||
</div>
|
||||
<div class='grant-info-item'>
|
||||
{{check-mark checked=model.multiple_grant}} {{i18n 'badges.multiple_grant'}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
{{#link-to 'badges.show' badge}}
|
||||
{{#if badge.grant_count}}
|
||||
<span class='grant-count' title={{i18n 'badges.granted' count=badge.grant_count}}>{{badge.grant_count}}</span>
|
||||
{{/if}}
|
||||
{{#if badge.has_badge}}
|
||||
<span class='check-display status-checked'>{{fa-icon "check"}}</span>
|
||||
{{/if}}
|
||||
<div class='badge-contents'>
|
||||
<div class='badge-icon {{badge.badgeTypeClassName}}'>
|
||||
{{icon-or-image badge.icon}}
|
||||
</div>
|
||||
<div class='badge-info'>
|
||||
<div class='badge-info-item'>
|
||||
<h3>{{badge.displayName}}</h3>
|
||||
<div class='badge-summary'>{{{summary}}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/link-to}}
|
|
@ -32,6 +32,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* User badge listing. */
|
||||
.user-badges-list {
|
||||
text-align: center;
|
||||
|
@ -64,55 +65,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Badge listing in /badges. */
|
||||
.badges-listing {
|
||||
|
||||
margin: 20px 0;
|
||||
|
||||
tr.title {
|
||||
border-bottom: 2px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
width: 90%;
|
||||
padding: 10px;
|
||||
display: table;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
|
||||
|
||||
.row {
|
||||
display: table-row;
|
||||
> div {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.user-badge {
|
||||
font-size: $base-font-size;
|
||||
}
|
||||
|
||||
.grant-count {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.badge, .grant-count {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 0.9em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.description {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media all and (max-width: 750px) {
|
||||
.show-badge .user-badge-with-posts .badge-user a.post-link {
|
||||
width: auto;
|
||||
|
@ -202,3 +154,127 @@
|
|||
margin-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.badge-card {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
background-color: dark-light-diff($primary, $secondary, 95%, -65%);
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 1px 1px 3px rgba(0.0, 0.0, 0.0, 0.2);
|
||||
|
||||
.check-display {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.grant-count {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
font-weight: bold;
|
||||
color: dark-light-diff($primary, $secondary, 50%, -65%);
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.badge-contents {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-height: 128px;
|
||||
|
||||
.badge-icon {
|
||||
min-width: 90px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: dark-light-diff($primary, $secondary, 92%, -60%);
|
||||
font-size: 3em;
|
||||
|
||||
&.badge-type-gold .fa {
|
||||
color: #ffd700 !important;
|
||||
}
|
||||
|
||||
&.badge-type-silver .fa {
|
||||
color: #c0c0c0 !important;
|
||||
}
|
||||
|
||||
&.badge-type-bronze .fa {
|
||||
color: #cd7f32 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
color: $primary;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.badge-card.medium {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.badge-card.large {
|
||||
width: 750px;
|
||||
a {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.badge-groups {
|
||||
margin: 20px 0;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
|
||||
h3 {
|
||||
margin-bottom: 1.0em;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-grouping {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.show-badge-details {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 2em;
|
||||
margin-top: 1em;
|
||||
|
||||
.badge-grant-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 1em;
|
||||
}
|
||||
.grant-info-item {
|
||||
margin-bottom: 1em;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
|
||||
}
|
||||
}
|
||||
|
||||
.check-display {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
.fa {
|
||||
font-size: 0.9em;
|
||||
color: $secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.check-display.status-checked {
|
||||
background-color: dark-light-diff($primary, $secondary, 40%, -60%);
|
||||
}
|
||||
|
||||
.check-display.status-unchecked {
|
||||
background-color: $danger;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,4 +9,5 @@ class BadgeIndexSerializer < BadgeSerializer
|
|||
def has_badge
|
||||
@options[:user_badges].include?(object.id)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -5,12 +5,12 @@ acceptance("Badges");
|
|||
test("Visit Badge Pages", () => {
|
||||
visit("/badges");
|
||||
andThen(() => {
|
||||
ok(exists('.badges-listing tr'), "has a list of badges");
|
||||
ok(exists('.badge-groups .badge-card'), "has a list of badges");
|
||||
});
|
||||
|
||||
visit("/badges/9/autobiographer");
|
||||
andThen(() => {
|
||||
ok(exists('.badges-listing div'), "has the badge in the listing");
|
||||
ok(exists('.badge-card'), "has the badge in the listing");
|
||||
ok(exists('.user-info'), "has the list of users with that badge");
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue