start implementing reports

This commit is contained in:
Joffrey JAFFEUX 2024-12-20 18:56:02 +01:00
parent bc80558e2d
commit 3af6a12a55
20 changed files with 418 additions and 42 deletions

View File

@ -24,6 +24,7 @@ module DiscourseRewind
.where(posts: { user_id: user.id })
.where(created_at: date)
.group(:reaction_value)
.limit(5)
.count
end

View File

@ -0,0 +1,61 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import concatClass from "discourse/helpers/concat-class";
const ROWS = 7;
const COLS = 53;
export default class ActivityCalendar extends Component {
get rowsArray() {
const data = this.args.report.data;
let rowsArray = [];
for (let r = 0; r < ROWS; r++) {
let rowData = [];
for (let c = 0; c < COLS; c++) {
const index = c * ROWS + r;
rowData.push(data[index] ? data[index] : "");
}
rowsArray.push(rowData);
}
return rowsArray;
}
@action
computeClass(count) {
if (!count) {
return "-empty";
} else if (count < 10) {
return "-low";
} else if (count < 20) {
return "-medium";
} else {
return "-high";
}
}
<template>
<div class="rewind-report-page -activity-calendar">
<div class="rewind-card">
<table class="rewind-calendar">
<tbody>
{{#each this.rowsArray as |row|}}
<tr>
{{#each row as |cell|}}
<td
data-date={{cell.date}}
class={{concatClass
"rewind-calendar-cell"
(this.computeClass cell.post_count)
}}
></td>
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</template>
}

View File

@ -0,0 +1,9 @@
import Component from "@glimmer/component";
export default class BestPosts extends Component {
<template>
<div class="rewind-report-page">
Best posts
</div>
</template>
}

View File

@ -0,0 +1,9 @@
import Component from "@glimmer/component";
export default class BestTopics extends Component {
<template>
<div class="rewind-report-page">
BestTopics
</div>
</template>
}

View File

@ -0,0 +1,9 @@
import Component from "@glimmer/component";
export default class FavoriteCategories extends Component {
<template>
<div class="rewind-report-page">
FavoriteCategories
</div>
</template>
}

View File

@ -0,0 +1,9 @@
import Component from "@glimmer/component";
export default class FavoriteTags extends Component {
<template>
<div class="rewind-report-page">
FavoriteTags
</div>
</template>
}

View File

@ -0,0 +1,13 @@
import Component from "@glimmer/component";
export default class FBFF extends Component {
<template>
<div class="rewind-report-page">
FBFF
</div>
<div class="rewind-report-page">
page 2
</div>
</template>
}

View File

@ -1,13 +1,60 @@
import Component from "@glimmer/component";
import { concat } from "@ember/helper";
import { action } from "@ember/object";
import { htmlSafe } from "@ember/template";
import replaceEmoji from "discourse/helpers/replace-emoji";
export default class Reactions extends Component {
@action
cleanEmoji(emojiName) {
return emojiName.replaceAll(/_/g, " ");
}
get totalPostUsedReactions() {
return Object.values(
this.args.report.data.post_used_reactions ?? {}
).reduce((acc, count) => acc + count, 0);
}
@action
computePercentage(count) {
return `${((count / this.totalPostUsedReactions) * 100).toFixed(2)}%`;
}
@action
computePercentageStyle(count) {
return htmlSafe(`width: ${this.computePercentage(count)}`);
}
<template>
<div class="rewind-report-page">
Let's take a look at the reactions you've used this year!
<div class="rewind-report-page -post-received-reactions">
{{#each-in @report.data.post_received_reactions as |emojiName count|}}
<div class="rewind-card">
<span>{{replaceEmoji (concat ":" emojiName ":")}}</span>
<span>{{this.cleanEmoji emojiName}}</span>
<span>{{count}} times</span>
</div>
{{/each-in}}
</div>
<div class="rewind-report-page">
page 2
<div class="rewind-report-page -post-used-reactions">
<div class="rewind-card">
<div class="rewind-reactions-chart">
{{#each-in @report.data.post_used_reactions as |emojiName count|}}
<div class="rewind-reactions-row">
<span>{{replaceEmoji (concat ":" emojiName ":")}}</span>
<span>{{this.computePercentage count}}</span>
<div
class="rewind-reactions-bar"
style={{this.computePercentageStyle count}}
></div>
</div>
{{/each-in}}
<span class="rewind-total-reactions">Total number of reactions:
{{this.totalPostUsedReactions}}</span>
</div>
</div>
</div>
</template>
}

View File

@ -0,0 +1,13 @@
import Component from "@glimmer/component";
export default class ReadingTime extends Component {
<template>
<div class="rewind-report-page">
Reading time
</div>
<div class="rewind-report-page">
page 2
</div>
</template>
}

View File

@ -0,0 +1,9 @@
import Component from "@glimmer/component";
export default class WordCloud extends Component {
<template>
<div class="rewind-report-page">
Word cloud
</div>
</template>
}

View File

@ -1,25 +1,44 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { schedule } from "@ember/runloop";
import { eq } from "truth-helpers";
import DButton from "discourse/components/d-button";
import concatClass from "discourse/helpers/concat-class";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import ActivityCalendar from "discourse/plugins/discourse-rewind/discourse/components/reports/activity-calendar";
import BestPosts from "discourse/plugins/discourse-rewind/discourse/components/reports/best-posts";
import BestTopics from "discourse/plugins/discourse-rewind/discourse/components/reports/best-topics";
import FavoriteCategories from "discourse/plugins/discourse-rewind/discourse/components/reports/favorite-categories";
import FavoriteTags from "discourse/plugins/discourse-rewind/discourse/components/reports/favorite-tags";
import FBFF from "discourse/plugins/discourse-rewind/discourse/components/reports/fbff";
import Reactions from "discourse/plugins/discourse-rewind/discourse/components/reports/reactions";
import ReadingTime from "discourse/plugins/discourse-rewind/discourse/components/reports/reading-time";
import WordCloud from "discourse/plugins/discourse-rewind/discourse/components/reports/word-cloud";
export default class Rewind extends Component {
@tracked rewind = [];
@tracked fullScreen = true;
@tracked loadingRewind = false;
@action
registerScrollWrapper(element) {
this.scrollWrapper = element;
}
@action
async loadRewind() {
try {
this.loadingRewind = true;
this.rewind = await ajax("/rewinds");
} catch (e) {
popupAjaxError(e);
} finally {
this.loadingRewind = false;
}
}
@ -28,30 +47,86 @@ export default class Rewind extends Component {
this.fullScreen = !this.fullScreen;
}
reportComponentForIdentifier(identifier) {
if (identifier === "reactions") {
return Reactions;
@action
handleEscape(event) {
if (this.fullScreen && event.key === "Escape") {
this.fullScreen = false;
}
}
<template>
<div
class={{concatClass "rewind" (if this.fullScreen "-fullscreen")}}
class={{concatClass
"rewind-container"
(if this.fullScreen "-fullscreen")
}}
{{didInsert this.loadRewind}}
{{on "keydown" this.handleEscape}}
tabindex="0"
>
<DButton
class="rewind__exit-fullscreen-btn"
@icon={{if this.fullScreen "discourse-compress" "discourse-expand"}}
title="Toggle fullscreen"
@action={{this.toggleFullScreen}}
/>
{{#each this.rewind as |report|}}
{{#if (eq report.identifier "reactions")}}
<div class="rewind-report">
<Reactions @report={{report}} />
<div class="rewind">
{{#if this.loadingRewind}}
<div class="rewind-loader">
<div class="spinner small"></div>
<div class="rewind-loader__text">Crunching your data...</div>
</div>
{{else}}
<DButton
class="rewind__exit-fullscreen-btn"
@icon={{if this.fullScreen "discourse-compress" "discourse-expand"}}
@action={{this.toggleFullScreen}}
/>
<div
class="rewind__scroll-wrapper"
{{didInsert this.registerScrollWrapper}}
>
{{#each this.rewind as |report|}}
{{log report.identifier}}
<div class="rewind-report">
{{#if (eq report.identifier "reactions")}}
<Reactions @report={{report}} />
{{else if (eq report.identifier "activity-calendar")}}
<ActivityCalendar @report={{report}} />
{{/if}}
{{!-- {{else if (eq report.identifier "fbff")}}
<FBFF @report={{report}} />
{{else if (eq report.identifier "word-cloud")}}
<WordCloud @report={{report}} />
{{else if (eq report.identifier "activity-calendar")}}
<ActivityCalendar @report={{report}} />
{{else if (eq report.identifier "best-posts")}}
<BestPosts @report={{report}} />
{{else if (eq report.identifier "best-topics")}}
<BestTopics @report={{report}} />
{{else if (eq report.identifier "favorite-tags")}}
<FavoriteTags @report={{report}} />
{{else if (eq report.identifier "favorite-categories")}}
<FavoriteCategories @report={{report}} />
{{else if (eq report.identifier "reading-time")}}
<ReadingTime @report={{report}} />
{{/if}} --}}
</div>
{{/each}}
</div>
{{#if this.showPrev}}
<DButton
class="rewind__prev-btn"
@icon="chevron-left"
@action={{this.prev}}
/>
{{/if}}
{{#if this.showNext}}
<DButton
class="rewind__next-btn"
@icon="chevron-right"
@action={{this.next}}
/>
{{/if}}
{{/if}}
{{/each}}
</div>
</div>
</template>
}

View File

@ -0,0 +1,25 @@
.rewind-report-page.-activity-calendar {
.rewind-calendar {
tr {
border: none;
}
}
.rewind-calendar-cell {
height: 10px;
width: 10px;
&.-empty {
background: var(--tertiary-very-low);
}
&.-low {
background: var(--tertiary-low);
}
&.-medium {
background: var(--tertiary-medium);
}
&.-high {
background: var(--tertiary-hig);
}
}
}

View File

@ -1,2 +1,11 @@
.rewind-card {
box-shadow: 0 0 0 1px var(--primary-300), 0 0 0 4px var(--primary-100);
border-radius: calc(var(--d-border-radius) / 2);
display: flex;
flex-direction: column;
text-align: center;
padding: 1em;
box-sizing: border-box;
align-items: center;
justify-content: center;
}

View File

@ -1,3 +1,6 @@
@import "rewind";
@import "report";
@import "card";
@import "post-received-reactions";
@import "post-used-reactions";
@import "activity-calendar";

View File

@ -0,0 +1,16 @@
.rewind-report-page.-post-received-reactions {
.rewind-reactions-chart {
display: flex;
gap: 1em;
}
.rewind-card {
width: 177px;
height: 190px;
.emoji {
width: 72px;
height: 72px;
}
}
}

View File

@ -0,0 +1,20 @@
.rewind-report-page.-post-used-reactions {
display: flex;
gap: 1em;
.rewind-reactions-chart {
display: flex;
flex-direction: column;
gap: 1em;
}
.rewind-reactions-row {
display: flex;
gap: 1em;
}
.rewind-reactions-bar {
background: var(--tertiary);
height: 50px;
}
}

View File

@ -9,6 +9,4 @@
box-sizing: border-box;
font-weight: 700;
font-size: var(--font-up-2);
height: 100cqh;
width: 100cqw;
}

View File

@ -1,14 +1,11 @@
.rewind {
width: 280px;
height: 400px;
background-color: var(--secondary);
.rewind-container {
border-radius: var(--d-border-radius);
overflow-y: auto;
overflow-x: hidden;
border: 1px solid var(--primary-very-low);
box-sizing: border-box;
position: relative;
container-type: size;
display: flex;
align-items: center;
justify-content: center;
padding: 2em 4em;
&.-fullscreen {
top: 0;
@ -16,14 +13,69 @@
right: 0;
bottom: 0;
z-index: 9999;
width: 100vw;
width: calc(100vw - (100vw - 100%) + 2px);
height: 100vh;
position: fixed;
background: rgba(255, 255, 255, 0.5);
border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(4.9px);
-webkit-backdrop-filter: blur(4.9px);
}
}
.rewind__exit-fullscreen-btn {
position: sticky;
top: 5px;
left: calc(100% - 45px);
.rewind {
width: 100vw;
height: 100vh;
max-height: 100%;
max-width: 100%;
background-color: var(--secondary);
box-shadow: 0 0 0 1px var(--primary-300), 0 0 0 4px var(--primary-100);
border-radius: calc(var(--d-border-radius) / 2);
container-type: size;
position: relative;
}
.rewind__scroll-wrapper {
overflow-y: auto;
overflow-x: hidden;
height: 100%;
width: 100%;
position: relative;
}
.rewind__exit-fullscreen-btn {
position: absolute;
top: 5px;
right: 20px;
z-index: 1;
}
.rewind__prev-btn {
position: absolute;
bottom: 5px;
left: 5px;
z-index: 1;
}
.rewind__next-btn {
position: absolute;
bottom: 5px;
right: 5px;
z-index: 1;
}
.rewind-loader {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100cqh;
gap: 1em;
box-sizing: border-box;
&__text {
font-weight: 700;
font-size: var(--font-up-2);
}
}

View File

@ -1,4 +1,2 @@
.report-page {
width: 100vw;
height: 100vh;
}

View File

@ -1,7 +1,7 @@
.rewind {
background-color: var(--secondary);
border-radius: var(--d-border-radius);
overflow-y: auto;
overflow-x: hidden;
border: 1px solid var(--primary-very-low);
.rewind-container {
padding: 1em;
box-sizing: border-box;
}
.rewind {
}