mirror of
https://github.com/KevinMidboe/seasoned.git
synced 2026-03-11 11:55:38 +00:00
Refactor: Modernize Activity page UI to match site design
Update page structure:
- Rename wrapper class to 'activity' (matches AdminPage pattern)
- Update h1 to activity__title with consistent styling
- Organize content with BEM naming convention
Redesign controls:
- Replace basic input with styled input-wrapper component
- Add input-suffix "days" label inside input container
- Custom number input styling with hover/focus states
- Better ToggleButton integration with control-label
- Responsive flex layout with proper mobile handling
Enhance chart presentation:
- Wrap each graph in chart-card component
- Add background, borders, and rounded corners
- Clear chart-card__title headers
- Fixed height charts (35vh desktop, 30vh mobile)
- Minimum height prevents squashing (300px/250px)
- Consistent spacing and padding
Improve top content section:
- Grid layout for top content items (3 cols → 1 col mobile)
- Card-based items with borders and hover effects
- Hover: border highlight + translateY animation
- Better visual hierarchy with section-title
Styling details:
- Use CSS variables (--background-ui, --text-color-50, etc.)
- Match AdminPage typography (2rem title, 300 weight)
- Consistent border-radius (12px cards, 8px inputs)
- Proper mobile-only responsive breakpoints
- Remove old commented-out code
Result: Professional, cohesive design matching rest of site ✨
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="plexUserId && plexUsername" class="wrapper">
|
<div v-if="plexUserId && plexUsername" class="activity">
|
||||||
<h1>Your watch activity</h1>
|
<h1 class="activity__title">Watch Activity</h1>
|
||||||
|
|
||||||
<!-- Stats Overview -->
|
<!-- Stats Overview -->
|
||||||
<div v-if="watchStats" class="stats-overview">
|
<div v-if="watchStats" class="stats-overview">
|
||||||
@@ -22,75 +22,82 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="display: flex; flex-direction: row">
|
<div class="controls">
|
||||||
<label class="filter" for="dayinput">
|
<div class="control-group">
|
||||||
<span>Days:</span>
|
<label class="control-label">Time Range</label>
|
||||||
<input
|
<div class="input-wrapper">
|
||||||
id="dayinput"
|
<input
|
||||||
v-model="days"
|
v-model.number="days"
|
||||||
class="dayinput"
|
class="days-input"
|
||||||
placeholder="days"
|
type="number"
|
||||||
type="number"
|
min="1"
|
||||||
pattern="[0-9]*"
|
max="365"
|
||||||
@change="fetchChartData"
|
@change="fetchChartData"
|
||||||
/>
|
/>
|
||||||
</label>
|
<span class="input-suffix">days</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="filter">
|
<div class="control-group">
|
||||||
<span>Data sorted by:</span>
|
<label class="control-label">View Mode</label>
|
||||||
<toggle-button
|
<toggle-button
|
||||||
v-model:selected="graphViewMode"
|
v-model:selected="graphViewMode"
|
||||||
class="filter-item"
|
|
||||||
:options="[GraphTypes.Plays, GraphTypes.Duration]"
|
:options="[GraphTypes.Plays, GraphTypes.Duration]"
|
||||||
@change="fetchChartData"
|
@change="fetchChartData"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="chart-section">
|
<div class="activity__charts">
|
||||||
<h3 class="chart-header">Activity per day:</h3>
|
<div class="chart-card">
|
||||||
<div class="graph">
|
<h3 class="chart-card__title">Daily Activity</h3>
|
||||||
<Graph
|
<div class="chart-card__graph">
|
||||||
v-if="playsByDayData"
|
<Graph
|
||||||
:data="playsByDayData"
|
v-if="playsByDayData"
|
||||||
type="line"
|
:data="playsByDayData"
|
||||||
:stacked="false"
|
type="line"
|
||||||
:dataset-description-suffix="`watch last ${days} days`"
|
:stacked="false"
|
||||||
:tooltip-description-suffix="selectedGraphViewMode.tooltipLabel"
|
:dataset-description-suffix="`watch last ${days} days`"
|
||||||
:graph-value-type="selectedGraphViewMode.valueType"
|
:tooltip-description-suffix="selectedGraphViewMode.tooltipLabel"
|
||||||
/>
|
:graph-value-type="selectedGraphViewMode.valueType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="chart-header">Activity by media type:</h3>
|
<div class="chart-card">
|
||||||
<div class="graph">
|
<h3 class="chart-card__title">Activity by Media Type</h3>
|
||||||
<Graph
|
<div class="chart-card__graph">
|
||||||
v-if="playsByDayofweekData"
|
<Graph
|
||||||
:data="playsByDayofweekData"
|
v-if="playsByDayofweekData"
|
||||||
type="bar"
|
:data="playsByDayofweekData"
|
||||||
:stacked="true"
|
type="bar"
|
||||||
:dataset-description-suffix="`watch last ${days} days`"
|
:stacked="true"
|
||||||
tooltip-description-suffix="plays"
|
:dataset-description-suffix="`watch last ${days} days`"
|
||||||
:graph-value-type="GraphValueTypes.Number"
|
tooltip-description-suffix="plays"
|
||||||
/>
|
:graph-value-type="GraphValueTypes.Number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="chart-header">Watch time by hour:</h3>
|
<div class="chart-card">
|
||||||
<div class="graph">
|
<h3 class="chart-card__title">Viewing Patterns by Hour</h3>
|
||||||
<Graph
|
<div class="chart-card__graph">
|
||||||
v-if="hourlyData"
|
<Graph
|
||||||
:data="hourlyData"
|
v-if="hourlyData"
|
||||||
type="bar"
|
:data="hourlyData"
|
||||||
:stacked="false"
|
type="bar"
|
||||||
:dataset-description-suffix="`last ${days} days`"
|
:stacked="false"
|
||||||
tooltip-description-suffix="plays"
|
:dataset-description-suffix="`last ${days} days`"
|
||||||
:graph-value-type="GraphValueTypes.Number"
|
tooltip-description-suffix="plays"
|
||||||
/>
|
:graph-value-type="GraphValueTypes.Number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Top Content -->
|
<!-- Top Content -->
|
||||||
<div v-if="topContent.length > 0" class="top-content-section">
|
<div v-if="topContent.length > 0" class="activity__top-content">
|
||||||
<h3 class="chart-header">Most watched:</h3>
|
<h3 class="section-title">Most Watched</h3>
|
||||||
<div class="top-content-list">
|
<div class="top-content-list">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in topContent"
|
v-for="(item, index) in topContent"
|
||||||
@@ -262,38 +269,41 @@
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "scss/variables";
|
@import "scss/variables";
|
||||||
|
@import "scss/media-queries";
|
||||||
|
|
||||||
.wrapper {
|
.activity {
|
||||||
padding: 2rem;
|
padding: 3rem;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
@include mobile-only {
|
@include mobile-only {
|
||||||
padding: 0 0.8rem;
|
padding: 0.75rem;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: column;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
|
|
||||||
&:not(:first-of-type) {
|
|
||||||
margin-left: 1.25rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
&__title {
|
||||||
width: 100%;
|
margin: 0 0 2rem 0;
|
||||||
font-size: inherit;
|
font-size: 2rem;
|
||||||
max-width: 6rem;
|
|
||||||
background-color: $background-ui;
|
|
||||||
color: $text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: inherit;
|
|
||||||
line-height: 1;
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
|
color: $text-color;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
@include mobile-only {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__charts {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
|
||||||
|
@include mobile-only {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__top-content {
|
||||||
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,27 +332,121 @@
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
.chart-section {
|
.chart-card {
|
||||||
display: flex;
|
background: var(--background-ui);
|
||||||
flex-wrap: wrap;
|
border-radius: 12px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border: 1px solid var(--text-color-50);
|
||||||
|
|
||||||
.graph {
|
@include mobile-only {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-color;
|
||||||
|
|
||||||
|
@include mobile-only {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__graph {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 35vh;
|
height: 35vh;
|
||||||
width: 90vw;
|
min-height: 300px;
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-header {
|
@include mobile-only {
|
||||||
font-weight: 300;
|
height: 30vh;
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
@include mobile-only {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
min-width: 200px;
|
||||||
|
|
||||||
|
@include mobile-only {
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-label {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-color-60);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--background-ui);
|
||||||
|
border: 1px solid var(--text-color-50);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-within {
|
||||||
|
border-color: var(--text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.days-input {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: $text-color;
|
||||||
|
outline: none;
|
||||||
|
width: 80px;
|
||||||
|
|
||||||
|
&::-webkit-inner-spin-button,
|
||||||
|
&::-webkit-outer-spin-button {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-suffix {
|
||||||
|
padding: 0 1rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-color-60);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
.stats-overview {
|
.stats-overview {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
margin: 2rem 0;
|
margin-bottom: 2rem;
|
||||||
|
|
||||||
@include mobile-only {
|
@include mobile-only {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
@@ -389,14 +493,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-content-section {
|
|
||||||
margin-top: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-content-list {
|
.top-content-list {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
gap: 0.75rem;
|
gap: 1rem;
|
||||||
|
|
||||||
|
@include mobile-only {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-content-item {
|
.top-content-item {
|
||||||
@@ -406,10 +510,12 @@
|
|||||||
background: var(--background-ui);
|
background: var(--background-ui);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
transition: background 0.2s;
|
border: 1px solid var(--text-color-50);
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--background-40);
|
border-color: var(--text-color);
|
||||||
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user