Skip to content
Next
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
Minds Frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Insights
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Locked Files
Issues
877
Issues
877
List
Boards
Labels
Service Desk
Milestones
Merge Requests
45
Merge Requests
45
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Security & Compliance
Security & Compliance
Dependency List
Packages
Packages
List
Container Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Minds
Minds Frontend
Commits
1b70d572
Commit
1b70d572
authored
1 minute ago
by
Olivia Madrid
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
(feat): analytics v2 channel search
parent
680f081b
feat/entity-centric-metrics
1 merge request
!579
WIP: Entity centric metrics (analytics v2)
Pipeline
#87227140
running with stages
Changes
18
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
611 additions
and
68 deletions
+611
-68
src/app/modules/analytics/analytics.module.ts
src/app/modules/analytics/analytics.module.ts
+12
-6
src/app/modules/analytics/v2/components/chart/chart.component.ts
.../modules/analytics/v2/components/chart/chart.component.ts
+12
-22
src/app/modules/analytics/v2/components/filter/filter.component.html
...ules/analytics/v2/components/filter/filter.component.html
+5
-1
src/app/modules/analytics/v2/components/filter/filter.component.scss
...ules/analytics/v2/components/filter/filter.component.scss
+7
-1
src/app/modules/analytics/v2/components/metrics/metrics.component.scss
...es/analytics/v2/components/metrics/metrics.component.scss
+2
-7
src/app/modules/analytics/v2/components/search-suggestions/search-suggestions.component.html
...ents/search-suggestions/search-suggestions.component.html
+36
-0
src/app/modules/analytics/v2/components/search-suggestions/search-suggestions.component.scss
...ents/search-suggestions/search-suggestions.component.scss
+47
-0
src/app/modules/analytics/v2/components/search-suggestions/search-suggestions.component.spec.ts
...s/search-suggestions/search-suggestions.component.spec.ts
+24
-0
src/app/modules/analytics/v2/components/search-suggestions/search-suggestions.component.ts
...onents/search-suggestions/search-suggestions.component.ts
+95
-0
src/app/modules/analytics/v2/components/search/search.component.html
...ules/analytics/v2/components/search/search.component.html
+21
-0
src/app/modules/analytics/v2/components/search/search.component.scss
...ules/analytics/v2/components/search/search.component.scss
+188
-0
src/app/modules/analytics/v2/components/search/search.component.spec.ts
...s/analytics/v2/components/search/search.component.spec.ts
+24
-0
src/app/modules/analytics/v2/components/search/search.component.ts
...odules/analytics/v2/components/search/search.component.ts
+98
-0
src/app/modules/analytics/v2/dashboard.component.html
src/app/modules/analytics/v2/dashboard.component.html
+7
-11
src/app/modules/analytics/v2/dashboard.component.scss
src/app/modules/analytics/v2/dashboard.component.scss
+8
-5
src/app/modules/analytics/v2/dashboard.component.ts
src/app/modules/analytics/v2/dashboard.component.ts
+23
-12
src/app/modules/analytics/v2/dashboard.service.ts
src/app/modules/analytics/v2/dashboard.service.ts
+2
-2
src/app/modules/analytics/v2/layouts/layout-chart/layout-chart.component.scss
...ytics/v2/layouts/layout-chart/layout-chart.component.scss
+0
-1
No files found.
src/app/modules/analytics/analytics.module.ts
View file @
1b70d572
...
...
@@ -59,6 +59,10 @@ import { AnalyticsFilterComponent } from './v2/components/filter/filter.componen
import
{
AnalyticsChartComponent
}
from
'
./v2/components/chart/chart.component
'
;
import
{
AnalyticsTableComponent
}
from
'
./v2/components/table/table.component
'
;
import
{
AnalyticsDashboardService
}
from
'
./v2/dashboard.service
'
;
import
{
SearchModule
}
from
'
../search/search.module
'
;
import
{
AnalyticsSearchComponent
}
from
'
./v2/components/search/search.component
'
;
import
{
FormsModule
}
from
'
@angular/forms
'
;
import
{
AnalyticsSearchSuggestionsComponent
}
from
'
./v2/components/search-suggestions/search-suggestions.component
'
;
PlotlyModule
.
plotlyjs
=
PlotlyJS
;
...
...
@@ -88,13 +92,11 @@ const routes: Routes = [
],
},
{
path
:
'
dashboard
'
,
component
:
AnalyticsDashboardComponent
,
children
:
[
{
path
:
''
,
redirectTo
:
'
traffic
'
,
pathMatch
:
'
full
'
},
{
path
:
'
:category
'
,
component
:
AnalyticsDashboardComponent
},
],
path
:
'
dashboard/
'
,
redirectTo
:
'
dashboard/traffic
'
,
pathMatch
:
'
full
'
,
},
{
path
:
'
dashboard/:category
'
,
component
:
AnalyticsDashboardComponent
},
],
},
];
...
...
@@ -105,6 +107,8 @@ const routes: Routes = [
CommonModule
,
RouterModule
.
forChild
(
routes
),
PlotlyModule
,
SearchModule
,
FormsModule
,
],
exports
:
[
AdminAnalyticsComponent
,
...
...
@@ -165,6 +169,8 @@ const routes: Routes = [
AnalyticsFilterComponent
,
AnalyticsChartComponent
,
AnalyticsTableComponent
,
AnalyticsSearchComponent
,
AnalyticsSearchSuggestionsComponent
,
],
providers
:
[
AnalyticsDashboardService
],
})
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/chart/chart.component.ts
View file @
1b70d572
...
...
@@ -101,7 +101,10 @@ export class AnalyticsChartComponent implements OnInit, OnDestroy {
}
});
this
.
themeService
.
isDark$
.
subscribe
(
isDark
=>
(
this
.
isDark
=
isDark
));
this
.
themeSubscription
=
this
.
themeService
.
isDark$
.
subscribe
(
isDark
=>
{
this
.
isDark
=
isDark
;
// TODO: relayout and restyle when theme changes
});
this
.
graphDiv
=
document
.
getElementById
(
'
graphDiv
'
);
...
...
@@ -124,10 +127,6 @@ export class AnalyticsChartComponent implements OnInit, OnDestroy {
console
.
log
(
'
segments
'
,
this
.
segments
);
this
.
segmentLength
=
this
.
segments
[
0
].
buckets
.
length
;
// this.timespan = this.vm.timespans.find(
// timespan => timespan.id === this.vm.timespan
// );
// ----------------------------------------------
for
(
let
i
=
0
;
i
<
this
.
segmentLength
;
i
++
)
{
this
.
markerOpacities
[
i
]
=
0
;
...
...
@@ -136,9 +135,9 @@ export class AnalyticsChartComponent implements OnInit, OnDestroy {
type
:
'
line
'
,
layer
:
'
below
'
,
x0
:
this
.
segments
[
0
].
buckets
[
i
].
date
.
slice
(
0
,
10
),
y0
:
0
,
y0
:
0
,
// this should be graph y min
x1
:
this
.
segments
[
0
].
buckets
[
i
].
date
.
slice
(
0
,
10
),
y1
:
this
.
segments
[
0
].
buckets
[
i
].
value
,
y1
:
this
.
segments
[
0
].
buckets
[
i
].
value
,
// this should be graph y max
line
:
{
color
:
this
.
getColor
(
'
m-transparent
'
),
width
:
2
,
...
...
@@ -259,13 +258,13 @@ export class AnalyticsChartComponent implements OnInit, OnDestroy {
}
relayout
()
{
//const layoutUpdate = this.layout;
//
const layoutUpdate = this.layout;
//Plotly.relayout('graphDiv', layoutUpdate);
this
.
cd
.
markForCheck
();
this
.
cd
.
detectChanges
();
}
drawGraph
()
{}
//
drawGraph() {}
// UTILITY \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
unpack
(
rows
,
key
)
{
...
...
@@ -302,7 +301,6 @@ export class AnalyticsChartComponent implements OnInit, OnDestroy {
}
setLineHeights
()
{
console
.
log
(
this
.
graphDiv
.
layout
);
this
.
shapes
.
forEach
(
shape
=>
{
shape
.
y0
=
this
.
graphDiv
.
layout
.
yaxis
.
range
[
0
];
shape
.
y1
=
this
.
graphDiv
.
layout
.
yaxis
.
range
[
1
];
...
...
@@ -310,11 +308,11 @@ export class AnalyticsChartComponent implements OnInit, OnDestroy {
}
onHover
(
$event
)
{
console
.
log
(
'
hovering
'
);
console
.
log
(
$event
);
//
console.log('hovering');
//
console.log($event);
this
.
hoverPoint
=
$event
.
points
[
0
].
pointIndex
;
console
.
log
(
this
.
shapes
);
//
console.log(this.shapes);
this
.
markerOpacities
[
this
.
hoverPoint
]
=
1
;
// SHOW VERTICAL LINE
...
...
@@ -326,12 +324,7 @@ export class AnalyticsChartComponent implements OnInit, OnDestroy {
onUnhover
(
$event
)
{
// HIDE VERTICAL LINE
this
.
shapes
[
this
.
hoverPoint
].
line
.
color
=
this
.
getColor
(
'
m-grey-50-transparent
'
);
// this.layout.shapes[this.hoverPoint].line.color = this.getColor(
// 'm-grey-50-transparent'
// );
this
.
shapes
[
this
.
hoverPoint
].
line
.
color
=
this
.
getColor
(
'
m-transparent
'
);
// HIDE MARKER
this
.
hoverInfoDiv
.
style
.
opacity
=
0
;
...
...
@@ -348,7 +341,6 @@ export class AnalyticsChartComponent implements OnInit, OnDestroy {
@
HostListener
(
'
window:resize
'
)
applyDimensions
()
{
console
.
log
(
'
windowresize
'
);
// this.layout = this.relayout();
// this.setLineHeights();
// this.layout = {
...
...
@@ -361,8 +353,6 @@ export class AnalyticsChartComponent implements OnInit, OnDestroy {
detectChanges
()
{
this
.
cd
.
markForCheck
();
this
.
cd
.
detectChanges
();
console
.
log
(
'
change detected
'
);
// this.updateGraph(); // Does this run every time a change is made to vm$ as well?
}
ngOnDestroy
()
{
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/filter/filter.component.html
View file @
1b70d572
<div
class=
"filterWrapper"
[ngClass]=
"{ expanded: expanded }"
>
<div
class=
"filterWrapper"
[ngClass]=
"{ expanded: expanded }"
(blur)=
"expanded = false"
>
<div
class=
"filterHeader"
(click)=
"expanded = !expanded"
>
<span
class=
"filterLabel"
>
{{ filter.label }}
</span>
<span
class=
"option option--selected"
>
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/filter/filter.component.scss
View file @
1b70d572
...
...
@@ -8,7 +8,7 @@ m-analytics__filter {
cursor
:
pointer
;
border-radius
:
3px
;
@include
m-theme
()
{
background-color
:
white
;
background-color
:
themed
(
$m-white
)
;
border
:
1px
solid
themed
(
$m-grey-100
);
color
:
rgba
(
themed
(
$m-grey-200
)
,
0
.7
);
}
...
...
@@ -58,6 +58,7 @@ m-analytics__filter {
}
}
.option--selected
{
border-radius
:
3px
;
@include
m-theme
()
{
color
:
themed
(
$m-grey-500
);
}
...
...
@@ -76,4 +77,9 @@ m-analytics__filter {
border-top
:
1px
solid
themed
(
$m-grey-100
);
background-color
:
themed
(
$m-white
);
}
.option
:last-child
{
border-bottom-left-radius
:
3px
;
border-bottom-right-radius
:
3px
;
}
}
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/metrics/metrics.component.scss
View file @
1b70d572
m-analytics__metrics
{
position
:
relative
;
z-index
:
999
;
}
.metricsContainer
{
display
:
flex
;
// position: relative;
width
:
95%
;
padding
:
0
16px
;
@include
m-theme
()
{
box-shadow
:
0
px
0px
10px
-3px
rgba
(
themed
(
$m-black-always
)
,
0
.3
);
box-shadow
:
0
7px
15px
-7px
rgba
(
themed
(
$m-black-always
)
,
0
.1
);
}
.metric
{
width
:
20%
;
padding
:
20px
;
padding
:
2
4px
20px
20px
2
0px
;
font-size
:
12px
;
box-sizing
:
border-box
;
@include
m-theme
()
{
// border-bottom: 5px solid themed($m-white);
// border-bottom: 5px solid transparent;
color
:
themed
(
$m-grey-200
);
}
&
.active
{
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/search-suggestions/search-suggestions.component.html
0 → 100644
View file @
1b70d572
<div
class=
"m-analytics__searchSuggestions__list"
[hidden]=
"disabled || !active"
(mousedown)=
"mousedown($event)"
*ngIf=
"session.isLoggedIn()"
>
<ng-container
*ngIf=
"!q"
>
<ng-container
*ngFor=
"let suggestion of recent"
>
<a
class=
"m-analytics__searchSuggestions__suggestion"
*ngIf=
"suggestion.type == 'user'"
(click)=
"applyChannelFilter(suggestion)"
>
<img
src=
"icon/{{ suggestion.guid }}/small"
/>
<div>
<div>
{{ suggestion.name }}
</div>
<div>
@{{ suggestion.username }}
</div>
</div>
</a>
</ng-container>
</ng-container>
<ng-container
*ngIf=
"q"
>
<a
class=
"m-analytics__searchSuggestions__suggestion"
*ngFor=
"let suggestion of suggestions"
[routerLink]=
"['/', suggestion.username]"
>
<img
src=
"icon/{{ suggestion.guid }}/small"
/>
<div>
<div>
{{ suggestion.name }}
</div>
<div>
@{{ suggestion.username }}
</div>
</div>
</a>
</ng-container>
</div>
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/search-suggestions/search-suggestions.component.scss
0 → 100644
View file @
1b70d572
m-analytics__searchSuggestions
{
display
:
block
;
.m-analytics__searchSuggestions__list
{
padding
:
0
;
margin
:
0
;
position
:
absolute
;
z-index
:
5000
;
box-sizing
:
border-box
;
width
:
100%
;
@include
m-theme
()
{
background-color
:
themed
(
$m-white
);
border
:
1px
solid
themed
(
$m-grey-50
);
}
.m-analytics__searchSuggestions__suggestion
{
cursor
:
pointer
;
padding
:
4px
;
display
:
block
;
text-decoration
:
none
;
font-size
:
14px
;
font-weight
:
600
;
letter-spacing
:
0
.5px
;
-webkit-font-smoothing
:
antialiased
;
text-rendering
:
optimizeLegibility
;
@include
m-theme
()
{
border-bottom
:
1px
solid
themed
(
$m-grey-50
);
color
:
themed
(
$m-grey-700
);
}
a
{
display
:
flex
;
flex-direction
:
row
;
}
img
{
border-radius
:
50%
;
margin
:
0
8px
0
4px
;
width
:
36px
;
height
:
36px
;
@include
m-theme
()
{
background-color
:
themed
(
$m-grey-800
);
}
}
}
}
}
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/search-suggestions/search-suggestions.component.spec.ts
0 → 100644
View file @
1b70d572
import
{
async
,
ComponentFixture
,
TestBed
}
from
'
@angular/core/testing
'
;
import
{
AnalyticsSearchSuggestionsComponent
}
from
'
./analytics-search-suggestions.component
'
;
describe
(
'
AnalyticsSearchSuggestionsComponent
'
,
()
=>
{
let
component
:
AnalyticsSearchSuggestionsComponent
;
let
fixture
:
ComponentFixture
<
AnalyticsSearchSuggestionsComponent
>
;
beforeEach
(
async
(()
=>
{
TestBed
.
configureTestingModule
({
declarations
:
[
AnalyticsSearchSuggestionsComponent
],
}).
compileComponents
();
}));
beforeEach
(()
=>
{
fixture
=
TestBed
.
createComponent
(
AnalyticsSearchSuggestionsComponent
);
component
=
fixture
.
componentInstance
;
fixture
.
detectChanges
();
});
it
(
'
should create
'
,
()
=>
{
expect
(
component
).
toBeTruthy
();
});
});
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/search-suggestions/search-suggestions.component.ts
0 → 100644
View file @
1b70d572
import
{
Component
,
OnInit
,
Input
,
ChangeDetectorRef
}
from
'
@angular/core
'
;
import
{
AnalyticsDashboardService
,
Filter
,
Option
,
}
from
'
../../dashboard.service
'
;
import
{
RecentService
}
from
'
../../../../../services/ux/recent
'
;
import
{
Session
}
from
'
../../../../../services/session
'
;
import
{
Client
}
from
'
../../../../../services/api
'
;
@
Component
({
selector
:
'
m-analytics__searchSuggestions
'
,
templateUrl
:
'
./search-suggestions.component.html
'
,
})
export
class
AnalyticsSearchSuggestionsComponent
implements
OnInit
{
suggestions
:
Array
<
any
>
=
[];
recent
:
any
[];
q
:
string
=
''
;
private
searchTimeout
;
@
Input
()
active
:
boolean
;
@
Input
(
'
q
'
)
set
_q
(
value
:
string
)
{
if
(
this
.
searchTimeout
)
{
clearTimeout
(
this
.
searchTimeout
);
}
this
.
q
=
value
||
''
;
if
(
!
value
)
{
this
.
loadRecent
();
this
.
suggestions
=
[];
return
;
}
this
.
searchTimeout
=
setTimeout
(
async
()
=>
{
this
.
loadRecent
();
try
{
const
response
:
any
=
await
this
.
client
.
get
(
'
api/v2/search/suggest
'
,
{
q
:
value
,
limit
:
4
,
});
this
.
suggestions
=
response
.
entities
;
console
.
log
(
response
.
entities
);
}
catch
(
e
)
{
console
.
error
(
e
);
this
.
suggestions
=
[];
}
},
300
);
}
constructor
(
private
analyticsService
:
AnalyticsDashboardService
,
private
session
:
Session
,
public
client
:
Client
,
public
recentService
:
RecentService
,
private
cd
:
ChangeDetectorRef
)
{}
ngOnInit
()
{
this
.
loadRecent
();
}
applyChannelFilter
(
suggestion
)
{
// TODO: remove dummy data
let
selection
=
suggestion
.
guid
||
'
test
'
;
const
selectedFilterStr
=
`channel::
${
selection
}
`
;
// TODO: enable admin gate
// if (this.session.isAdmin()) {
this
.
analyticsService
.
updateFilter
(
selectedFilterStr
);
// }
}
loadRecent
()
{
if
(
this
.
session
.
getLoggedInUser
())
{
// TODO: Q - Does this only store channels?
this
.
recent
=
this
.
recentService
.
fetch
(
'
recent
'
,
6
);
}
}
mousedown
(
e
)
{
e
.
preventDefault
();
setTimeout
(()
=>
{
this
.
active
=
false
;
this
.
detectChanges
();
},
300
);
}
detectChanges
()
{
this
.
cd
.
markForCheck
();
this
.
cd
.
detectChanges
();
}
}
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/search/search.component.html
0 → 100644
View file @
1b70d572
<div
class=
"mdl-textfield mdl-js-textfield"
>
<i
class=
"material-icons"
(click)=
"setFocus()"
>
search
</i>
<input
[(ngModel)]=
"q"
(focus)=
"focus()"
(blur)=
"blur()"
name=
"q"
class=
"mdl-textfield__input"
type=
"text"
id=
"search"
autocomplete=
"off"
placeholder=
"Search for a channel"
#searchInput
/>
<!-- <label class="mdl-textfield__label" for="search">abc</label> -->
<m-analytics
__searchSuggestions
[q]=
"q"
[active]=
"active"
></m-analytics
__searchSuggestions
>
</div>
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/search/search.component.scss
0 → 100644
View file @
1b70d572
@import
'defaults'
;
m-analytics__search
{
width
:
200px
;
.mdl-textfield
{
width
:
100%
;
.material-icons
{
position
:
absolute
;
margin
:
6px
;
font-size
:
20px
;
}
input
{
@include
m-theme
()
{
background-color
:
themed
(
$m-white
);
border
:
1px
solid
rgba
(
themed
(
$m-black
)
,
0
.12
);
}
&
:placeholder
{
@include
m-theme
()
{
color
:
themed
(
$m-grey-200
);
font-weight
:
200
;
}
}
}
input
,
label
{
padding
:
$minds-padding
$minds-padding
$minds-padding
$minds-padding
*
4
;
box-sizing
:
border-box
;
}
}
.m-search-bar--context
{
display
:
none
;
}
.mdl-textfield
.mdl-textfield__input
{
height
:
32px
;
border-radius
:
18px
;
//text-indent: 22px;
font-family
:
'Roboto'
,
sans-serif
;
font-size
:
14px
;
letter-spacing
:
0
.5px
;
font-weight
:
600
;
//line-height: 38px;
text-rendering
:
optimizeLegibility
;
-webkit-font-smoothing
:
antialiased
;
}
// @media screen and (min-width: 769px) {
// .mdl-textfield.m-search-bar--has-context {
// display: flex;
// .m-search-bar--context {
// display: block;
// flex-grow: 1;
// align-self: center;
// max-width: 25em;
// height: 32px;
// border-radius: 3px;
// padding: 0 0 0 32px;
// font-family: Roboto, sans-serif;
// font-size: 12px;
// letter-spacing: 1.25px;
// //line-height: 38px;
// white-space: nowrap;
// text-transform: uppercase;
// border-right: none;
// box-sizing: border-box;
// text-rendering: optimizeLegibility;
// -webkit-font-smoothing: antialiased;
// @include m-theme() {
// border: 1px solid rgba(themed($m-black), 0.12);
// color: rgba(themed($m-grey-800), 0.5);
// }
// }
// .mdl-textfield__input {
// padding: 0 $minds-padding;
// //border-left: none;
// box-sizing: border-box;
// appearance: none;
// @include m-theme() {
// border: 1px solid rgba(themed($m-black), 0.12);
// }
// }
// }
// }
// }
// Search
// m-search {
// display: block;
// min-height: 100vh;
// padding-top: 42px;
// .m-toolbar {
// position: fixed;
// top: 0;
// position: fixed;
// top: 50px;
// width: 100%;
// z-index: 500;
// @include m-theme() {
// background-color: rgba(themed($m-grey-50), 0.8);
// }
// }
// .m-topbar--navigation--more {
// position: relative;
// cursor: pointer;
// .minds-dropdown-menu {
// width: auto;
// max-width: 300px;
// .mdl-menu__item {
// display: flex;
// align-items: center;
// overflow: visible;
// font-size: 12px;
// .m-dropdown--spacer {
// flex: 1;
// }
// > label.mdl-switch {
// width: 36px;
// }
// .m-tooltip i {
// font-size: 16px;
// margin: 0 8px;
// @include m-theme() {
// color: themed($m-grey-400);
// }
// }
// }
// }
// @media screen and (max-width: $max-mobile) {
// flex: 1;
// }
// }
// }
// Search Lists
// .m-search--list {
// max-width: 1280px;
// padding: $minds-padding * 2;
// margin: auto;
// .m-search--list-section {
// margin-bottom: $minds-padding * 3;
// &:last-child {
// margin-bottom: 0;
// }
// }
// .m-search--list-title {
// margin: 0;
// padding: 0 $minds-padding * 2;
// font-size: 14px;
// font-weight: 600;
// line-height: 1.25;
// letter-spacing: 2px;
// text-transform: uppercase;
// @include m-theme() {
// color: rgba(themed($m-black), 0.6);
// }
// }
// .m-search--list-entities {
// minds-card {
// background: transparent;
// }
// }
// }
}
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/search/search.component.spec.ts
0 → 100644
View file @
1b70d572
import
{
async
,
ComponentFixture
,
TestBed
}
from
'
@angular/core/testing
'
;
import
{
AnalyticsSearchComponent
}
from
'
./search.component
'
;
describe
(
'
AnalyticsSearchComponent
'
,
()
=>
{
let
component
:
AnalyticsSearchComponent
;
let
fixture
:
ComponentFixture
<
AnalyticsSearchComponent
>
;
beforeEach
(
async
(()
=>
{
TestBed
.
configureTestingModule
({
declarations
:
[
AnalyticsSearchComponent
],
}).
compileComponents
();
}));
beforeEach
(()
=>
{
fixture
=
TestBed
.
createComponent
(
AnalyticsSearchComponent
);
component
=
fixture
.
componentInstance
;
fixture
.
detectChanges
();
});
it
(
'
should create
'
,
()
=>
{
expect
(
component
).
toBeTruthy
();
});
});
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/components/search/search.component.ts
0 → 100644
View file @
1b70d572
import
{
Component
,
OnInit
,
ViewChild
,
Input
,
ElementRef
}
from
'
@angular/core
'
;
// import { Observable, Subscription } from 'rxjs';
import
{
AnalyticsDashboardService
,
Filter
,
Option
,
}
from
'
../../dashboard.service
'
;
import
{
Session
}
from
'
../../../../../services/session
'
;
@
Component
({
selector
:
'
m-analytics__search
'
,
templateUrl
:
'
./search.component.html
'
,
})
export
class
AnalyticsSearchComponent
implements
OnInit
{
active
:
boolean
;
q
:
string
;
id
:
string
;
hasSearchContext
:
boolean
=
true
;
searchContext
:
string
|
Promise
<
string
>
=
''
;
@
ViewChild
(
'
searchInput
'
,
{
static
:
true
})
searchInput
:
ElementRef
;
constructor
(
private
analyticsService
:
AnalyticsDashboardService
,
private
session
:
Session
)
{}
ngOnInit
()
{}
search
()
{
// const qs: { q; ref; id? } = { q: this.q, ref: 'top' };
// if (this.id) {
// qs.id = this.id;
// }
// if (this.featureService.has('top-feeds')) {
// this.router.navigate([
// '/newsfeed/global/top',
// { query: this.q, period: '24h' },
// ]);
// } else {
// this.router.navigate(['search', qs]);
// }
}
keyup
(
e
)
{
if
(
e
.
keyCode
===
13
&&
this
.
session
.
isLoggedIn
())
{
this
.
search
();
this
.
unsetFocus
();
}
// TODO: allow to tab through suggestions?
}
focus
()
{
this
.
active
=
true
;
}
blur
()
{
setTimeout
(()
=>
(
this
.
active
=
false
),
100
);
}
setFocus
()
{
if
(
this
.
searchInput
.
nativeElement
)
{
this
.
searchInput
.
nativeElement
.
focus
();
}
}
unsetFocus
()
{
if
(
this
.
searchInput
.
nativeElement
)
{
this
.
searchInput
.
nativeElement
.
blur
();
}
}
// protected getActiveSearchContext(fragments: string[]) {
// this.searchContext = ''; // this would be 'channels'
// this.id = ''; //this would be a guid
// fragments.forEach((fragment: string) => {
// let param = fragment.split('=');
// if (param[0] === 'q') {
// this.q = decodeURIComponent(param[1]);
// }
// if (param[0] === 'id') {
// this.id = param[1];
// this.searchContext = this.context.resolveLabel(
// decodeURIComponent(param[1])
// );
// }
// if (param[0] == 'type' && !this.searchContext) {
// this.searchContext = this.context.resolveStaticLabel(
// decodeURIComponent(param[1])
// );
// }
// });
// }
}
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/dashboard.component.html
View file @
1b70d572
<div
class=
"page"
[ngClass]=
"{ isMobile: isMobile }"
>
<section
class=
"sidebar"
>
<h3>
Analytics
</h3>
<div
*ngIf=
"isMobile"
>
{{ category$ | async }}
</div>
<div
*ngIf=
"isMobile"
>
{{ category$ | async
| titlecase
}}
</div>
<div
class=
"catContainer"
>
<i
class=
"material-icons"
>
menu
</i>
<div
class=
"cat"
*ngFor=
"let cat of cats"
>
<a
[routerLink]=
"'./' + cat.id"
routerLinkActive=
"selected"
>
{{
<a
[routerLink]=
"'.
.
/' + cat.id"
routerLinkActive=
"selected"
>
{{
cat?.label
}}
</a>
</div>
...
...
@@ -15,20 +15,16 @@
<section
class=
"main"
>
<div
class=
"mainHeader"
>
<h3
class=
"selectedCatLabel"
>
{{ category$ | async }}
{{ category$ | async
| titlecase
}}
</h3>
<div
class=
"globalFilters"
>
<!-- TODO enable only show to admins -->
<!-- TODO: ngIf for global_filter bool -->
<!-- TODO: enable only show to admins -->
<!-- <div *ngIf="session.isAdmin()" class="channelSearch"> -->
<div
class=
"channelSearch"
>
(todo: Channel search)
<!-- <input
type="text"
[formControl]="channelSearch"
placeholder="Search for a channel"
/> -->
<m-analytics
__search
></m-analytics
__search
>
</div>
<!-- TODO: timespan filter will be a date filter -->
<div
class=
"timespanFilter"
>
<m-analytics
__filter
[filter]=
"timespanFilter"
></m-analytics
__filter
>
</div>
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/dashboard.component.scss
View file @
1b70d572
...
...
@@ -47,7 +47,7 @@
.mainHeader
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
flex-
end
;
align-items
:
flex-
start
;
}
.globalFilters
{
...
...
@@ -55,7 +55,8 @@
display
:
flex
;
align-items
:
baseline
;
.channelSearch
{
border-radius
:
5px
;
// border-radius: 5px;
margin-right
:
8px
;
@include
m-theme
()
{
border
:
themed
(
$m-grey-50
);
color
:
themed
(
$m-grey-100
);
...
...
@@ -74,13 +75,15 @@
}
}
}
.m-analytics__layout
{
position
:
relative
;
.layoutWrapper
{
@include
m-theme
()
{
box-shadow
:
0px
0px
10px
-3px
rgba
(
themed
(
$m-black-always
)
,
0
.3
);
}
}
.m-analytics__layout
{
position
:
relative
;
}
}
// *************************************
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/dashboard.component.ts
View file @
1b70d572
...
...
@@ -7,7 +7,12 @@ import {
ChangeDetectorRef
,
}
from
'
@angular/core
'
;
import
{
FormControl
}
from
'
@angular/forms
'
;
import
{
ActivatedRoute
,
Router
}
from
'
@angular/router
'
;
import
{
ActivatedRoute
,
Router
,
ParamMap
,
RoutesRecognized
,
}
from
'
@angular/router
'
;
import
{
Subscription
,
Observable
}
from
'
rxjs
'
;
import
{
MindsTitle
}
from
'
../../../services/ux/title
'
;
...
...
@@ -29,7 +34,6 @@ import {
UserState
,
}
from
'
./dashboard.service
'
;
// import fakeData from './fake';
import
categories
from
'
./categories.default
'
;
import
isMobileOrTablet
from
'
../../../helpers/is-mobile-or-tablet
'
;
...
...
@@ -46,7 +50,7 @@ export class AnalyticsDashboardComponent implements OnInit, OnDestroy {
subscription
:
Subscription
;
paramsSubscription
:
Subscription
;
category$
=
this
.
analyticsService
.
category$
;
selectedCat
:
Category
;
selectedCat
:
string
;
selectedTimespan
;
//string? or Timespan?
timespanFilter
:
Filter
=
{
...
...
@@ -74,8 +78,8 @@ export class AnalyticsDashboardComponent implements OnInit, OnDestroy {
this
.
isMobile
=
isMobileOrTablet
();
this
.
title
.
setTitle
(
'
Analytics
'
);
this
.
route
.
param
s
.
subscribe
(
params
=>
{
this
.
updateCategory
(
params
[
'
category
'
]
);
this
.
route
.
param
Map
.
subscribe
((
params
:
ParamMap
)
=>
{
this
.
updateCategory
(
params
.
get
(
'
category
'
)
);
});
// TODO: implement channel filter
...
...
@@ -85,10 +89,19 @@ export class AnalyticsDashboardComponent implements OnInit, OnDestroy {
this
.
paramsSubscription
=
this
.
route
.
queryParams
.
subscribe
(
params
=>
{
// TODO: do the same filter, metric, channel
//if (params['timespan'] && params['timespan'] !== this.vm.timespan) {
// this.updateTimespan(params['timespan']);
//}
//this.selectedCat = this.cats.find(cat => cat.id === this.vm.category);
if
(
params
[
'
timespan
'
]
&&
params
[
'
timespan
'
]
!==
'
bork
'
)
{
// this.updateTimespan(params['timespan']);
console
.
log
(
'
bork
'
);
}
// TODO: if (there's no channel filter in the url) {
if
(
!
this
.
session
.
isAdmin
())
{
const
selection
=
'
self
'
;
}
else
{
const
selection
=
'
all
'
;
}
// }
});
this
.
analyticsService
.
timespans$
.
subscribe
(
timespans
=>
{
...
...
@@ -104,9 +117,7 @@ export class AnalyticsDashboardComponent implements OnInit, OnDestroy {
}
updateCategory
(
categoryId
)
{
// TODO
// update url
// this.analyticsService.updateCategory(categoryId);
this
.
analyticsService
.
updateCategory
(
categoryId
);
}
detectChanges
()
{
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/dashboard.service.ts
View file @
1b70d572
...
...
@@ -379,9 +379,9 @@ export class AnalyticsDashboardService {
vm
$
:
Observable
<
UserState
>
=
new
BehaviorSubject
(
_state
);
/**
* Watch
5
streams to trigger user loads and state updates
* Watch
4
streams to trigger user loads and state updates
*/
// TODO: remove one of these later
// TODO: remove one of these
clients
later
constructor
(
private
client
:
Client
,
private
httpClient
:
HttpClient
)
{
this
.
loadFromRemote
();
}
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/analytics/v2/layouts/layout-chart/layout-chart.component.scss
View file @
1b70d572
...
...
@@ -4,6 +4,5 @@
width
:
95%
;
@include
m-theme
()
{
border-top
:
1px
solid
themed
(
$m-grey-50
);
box-shadow
:
0px
0px
10px
-3px
rgba
(
themed
(
$m-black-always
)
,
0
.3
);
}
}
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment