Skip to content

Commit

Permalink
✨ introducing the charts view for the details page
Browse files Browse the repository at this point in the history
💄 the dashboard feature has been overhauled to look nicer
⬆️ regular package dependency upgrades
♻️ refactoring user profile specific code to follow the same pattern we use for global settings
🐛 admin users will now see the administration page after logging in (no more manual refresh needed)
  • Loading branch information
faburem committed Mar 3, 2020
1 parent 24b1cc2 commit 9ef6209
Show file tree
Hide file tree
Showing 29 changed files with 304 additions and 281 deletions.
2 changes: 1 addition & 1 deletion .meteor/versions
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ [email protected]
[email protected]
[email protected]
[email protected]
ostrio:[email protected].3
ostrio:[email protected].4
ostrio:[email protected]
ostrio:[email protected]
[email protected]
Expand Down
9 changes: 5 additions & 4 deletions imports/api/dashboards/methods.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Dashboards } from './dashboards'
import { periodToDates } from '../../utils/periodHelpers.js'
import { checkAuthentication } from '../../utils/server_method_helpers.js'
import { getGlobalSetting } from '../../utils/frontend_helpers'

Meteor.methods({
addDashboard({
Expand All @@ -13,13 +14,13 @@ Meteor.methods({
checkAuthentication(this)
const { startDate, endDate } = periodToDates(timePeriod)
const meteorUser = Meteor.users.findOne({ _id: this.userId })
let timeunit = 'h'
let hoursToDays = 8
let timeunit = getGlobalSetting('timeunit')
let hoursToDays = getGlobalSetting('hoursToDays')
if (meteorUser.profile.timeunit) {
({ timeunit } = meteorUser.profile.timeunit)
timeunit = meteorUser.profile.timeunit
}
if (meteorUser.profile.hoursToDays) {
({ hoursToDays } = meteorUser.profile.hoursToDays)
hoursToDays = meteorUser.profile.hoursToDays
}
const _id = Random.id()
Dashboards.insert({
Expand Down
6 changes: 1 addition & 5 deletions imports/api/globalsettings/server/publications.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import { checkAuthentication } from '../../../utils/server_method_helpers.js'
import { Globalsettings } from '../globalsettings.js'

Meteor.publish('globalsettings', function globalsettings() {
checkAuthentication(this)
return Globalsettings.find()
})
Meteor.publish('globalsettings', () => Globalsettings.find())
6 changes: 2 additions & 4 deletions imports/api/timecards/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { periodToDates, timeInUserUnit } from '../../utils/periodHelpers.js'
import {
checkAuthentication,
getProjectListByCustomer,
totalHoursForPeriodMapper,
dailyTimecardMapper,
buildTotalHoursForPeriodSelector,
buildDailyHoursSelector,
buildworkingTimeSelector,
Expand Down Expand Up @@ -266,7 +264,7 @@ Meteor.methods({
.aggregate(buildDailyHoursSelector(projectId, period, userId, customer, 0))
.toArray()).length
const dailyHours = Promise.await(Timecards.rawCollection().aggregate(aggregationSelector)
.toArray()).map(dailyTimecardMapper)
.toArray())
dailyHoursObject.dailyHours = dailyHours
dailyHoursObject.totalEntries = totalEntries
return dailyHoursObject
Expand All @@ -292,7 +290,7 @@ Meteor.methods({
.aggregate(buildTotalHoursForPeriodSelector(projectId, period, userId, customer, 0))
.toArray()).length
const totalHours = Promise.await(Timecards.rawCollection().aggregate(aggregationSelector)
.toArray()).map(totalHoursForPeriodMapper)
.toArray())
totalHoursObject.totalHours = totalHours
totalHoursObject.totalEntries = totalEntries
return totalHoursObject
Expand Down
16 changes: 8 additions & 8 deletions imports/startup/client/startup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import isDarkMode from 'is-dark'
import i18next from 'i18next'
import * as bs4notify from 'bootstrap4-notify'
import Projects from '../../api/projects/projects.js'
import { timeInUserUnit, emojify, getGlobalSetting } from '../../utils/frontend_helpers.js'
import { timeInUserUnit, emojify, getGlobalSetting, getUserSetting } from '../../utils/frontend_helpers.js'

$.notifyDefaults({
type: 'success',
Expand Down Expand Up @@ -79,9 +79,9 @@ Meteor.startup(() => {
if (!Meteor.loggingIn() && Meteor.user()
&& Meteor.user().profile) {
Meteor.subscribe('globalsettings')
if (Meteor.user().profile.theme === 'dark') {
if (getUserSetting('theme') === 'dark') {
import('../../ui/styles/dark.scss')
} else if (Meteor.user().profile.theme === 'light') {
} else if (getUserSetting('theme') === 'light') {
import('../../ui/styles/light.scss')
} else if (isDarkMode()) {
import('../../ui/styles/dark.scss')
Expand All @@ -94,8 +94,8 @@ Meteor.startup(() => {
import('../../ui/styles/light.scss')
}
if (!Meteor.loggingIn() && Meteor.user() && Meteor.user().profile) {
if (Meteor.user().profile.language) {
language = Meteor.user().profile.language === 'auto' ? navigator.language.substring(0, 2) : Meteor.user().profile.language
if (getUserSetting('language')) {
language = getUserSetting('theme') === 'auto' ? navigator.language.substring(0, 2) : getUserSetting('theme')
}
loadLanguage(language)
import('popper.js').then((Popper) => {
Expand Down Expand Up @@ -138,7 +138,7 @@ Meteor.startup(() => {
Template.registerHelper('i18nextReady', () => i18nextReady.get())
Template.registerHelper('unit', () => {
if (!Meteor.loggingIn() && Meteor.user() && Meteor.user().profile) {
return Meteor.user().profile.unit ? Meteor.user().profile.unit : getGlobalSetting('unit')
return getUserSetting('unit') ? getUserSetting('unit') : getGlobalSetting('unit')
}
return false
})
Expand All @@ -150,7 +150,7 @@ Template.registerHelper('emojify', (text) => {
})
Template.registerHelper('timeunit', () => {
if (!Meteor.loggingIn() && Meteor.user() && Meteor.user().profile && i18nextReady.get()) {
switch (Meteor.user().profile.timeunit) {
switch (getUserSetting('timeunit')) {
case 'h':
return i18next.t('globals.unit_hour_short')
case 'd':
Expand All @@ -163,7 +163,7 @@ Template.registerHelper('timeunit', () => {
})
Template.registerHelper('timetrackview', () => {
if (!Meteor.loggingIn() && Meteor.user() && Meteor.user().profile) {
return Meteor.user().profile.timetrackview ? Meteor.user().profile.timetrackview : getGlobalSetting('timetrackview')
return getUserSetting('timetrackview') ? getUserSetting('timetrackview') : getGlobalSetting('timetrackview')
}
return false
})
Expand Down
21 changes: 9 additions & 12 deletions imports/ui/components/allprojectschart.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './allprojectschart.html'
import hex2rgba from '../../utils/hex2rgba.js'
import { getGlobalSetting } from '../../utils/frontend_helpers'
import { getGlobalSetting, getUserSetting } from '../../utils/frontend_helpers'

Template.allprojectschart.onCreated(function allprojectschartCreated() {
// this.resources = new ReactiveVar()
Expand Down Expand Up @@ -36,10 +36,7 @@ Template.allprojectschart.helpers({
})
Template.allprojectschart.onRendered(function allprojectschartRendered() {
const templateInstance = Template.instance()
let precision = getGlobalSetting('precision')
if (!Meteor.loggingIn() && Meteor.user() && Meteor.user().profile) {
precision = Meteor.user().profile.precision ? Meteor.user().profile.precision : getGlobalSetting('precision')
}
const precision = getUserSetting('precision') ? getUserSetting('precision') : getGlobalSetting('precision')
import('chart.js').then((chartModule) => {
const Chart = chartModule.default
this.autorun(() => {
Expand All @@ -48,20 +45,20 @@ Template.allprojectschart.onRendered(function allprojectschartRendered() {
this.$('.js-hour-chart').remove()
this.$('.js-chart-container').html('<canvas class="js-hour-chart"></canvas>')
const stats = templateInstance.projectStats.get()
if (Meteor.user().profile.timeunit === 'd') {
if (getUserSetting('timeunit') === 'd') {
stats.beforePreviousMonthHours
/= Meteor.user().profile.hoursToDays
? Meteor.user().profile.hoursToDays : getGlobalSetting('hoursToDays')
/= getUserSetting('hoursToDays')
? getUserSetting('hoursToDays') : getGlobalSetting('hoursToDays')
stats.beforePreviousMonthHours = Number(stats.beforePreviousMonthHours)
.toFixed(precision)
stats.previousMonthHours
/= Meteor.user().profile.hoursToDays
? Meteor.user().profile.hoursToDays : getGlobalSetting('hoursToDays')
/= getUserSetting('hoursToDays')
? getUserSetting('hoursToDays') : getGlobalSetting('hoursToDays')
stats.previousMonthHours = Number(stats.previousMonthHours)
.toFixed(precision)
stats.currentMonthHours
/= Meteor.user().profile.hoursToDays
? Meteor.user().profile.hoursToDays : getGlobalSetting('hoursToDays')
/= getUserSetting('hoursToDays')
? getUserSetting('hoursToDays') : getGlobalSetting('hoursToDays')
stats.currentMonthHours = Number(stats.currentMonthHours).toFixed(precision)
}
if (this.$('.js-hour-chart')[0]) {
Expand Down
11 changes: 6 additions & 5 deletions imports/ui/components/dailytimetable.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import './dailytimetable.html'
import './pagination.js'
import './limitpicker.js'
import i18nextReady from '../../startup/client/startup.js'
import { getGlobalSetting, numberWithUserPrecision } from '../../utils/frontend_helpers'
import { getGlobalSetting, numberWithUserPrecision, getUserSetting } from '../../utils/frontend_helpers'
import { dailyTimecardMapper } from '../../utils/server_method_helpers'

Template.dailytimetable.onCreated(function dailytimetablecreated() {
this.dailyTimecards = new ReactiveVar()
Expand Down Expand Up @@ -43,7 +44,7 @@ Template.dailytimetable.onRendered(() => {
if (i18nextReady.get()) {
let data = []
if (templateInstance.dailyTimecards.get()) {
data = templateInstance.dailyTimecards.get()
data = templateInstance.dailyTimecards.get().map(dailyTimecardMapper)
.map((entry) => Object.entries(entry)
.map((key) => { if (key[1] instanceof Date) { return dayjs(key[1]).format(getGlobalSetting('dateformat')) } return key[1] }))
}
Expand All @@ -57,7 +58,7 @@ Template.dailytimetable.onRendered(() => {
{ name: i18next.t('globals.project'), editable: false, width: 2 },
{ name: i18next.t('globals.resource'), editable: false, width: 2 },
{
name: Meteor.user() && Meteor.user().profile.timeunit === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural'),
name: Meteor.user() && getUserSetting('timeunit') === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural'),
editable: false,
width: 1,
format: numberWithUserPrecision,
Expand Down Expand Up @@ -112,7 +113,7 @@ Template.dailytimetable.events({
event.preventDefault()
let unit = i18next.t('globals.hour_plural')
if (Meteor.user()) {
unit = Meteor.user().profile.timeunit === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')
unit = getUserSetting('timeunit') === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')
}
const csvArray = [`\uFEFF${i18next.t('globals.date')},${i18next.t('globals.project')},${i18next.t('globals.resource')},${unit}\r\n`]
for (const timeEntry of templateInstance.dailyTimecards.get()) {
Expand All @@ -124,7 +125,7 @@ Template.dailytimetable.events({
event.preventDefault()
let unit = i18next.t('globals.hour_plural')
if (Meteor.user()) {
unit = Meteor.user().profile.timeunit === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')
unit = getUserSetting('timeunit') === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')
}
const data = [[i18next.t('globals.date'), i18next.t('globals.project'), i18next.t('globals.resource'), unit]]
for (const timeEntry of templateInstance.dailyTimecards.get()) {
Expand Down
15 changes: 8 additions & 7 deletions imports/ui/components/detailtimetable.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
timeInUserUnit,
getGlobalSetting,
numberWithUserPrecision,
getUserSetting,
} from '../../utils/frontend_helpers'
import projectUsers from '../../api/users/users.js'
import Projects from '../../api/projects/projects'
Expand Down Expand Up @@ -104,7 +105,7 @@ Template.detailtimetable.onRendered(() => {
{ name: i18next.t('globals.task'), editable: false, format: addToolTipToTableCell },
{ name: i18next.t('globals.resource'), editable: false, format: addToolTipToTableCell },
{
name: Meteor.user() && Meteor.user().profile && Meteor.user().profile.timeunit === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural'),
name: Meteor.user() && Meteor.user().profile && getUserSetting('timeunit') === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural'),
editable: false,
format: numberWithUserPrecision,
},
Expand Down Expand Up @@ -252,7 +253,7 @@ Template.detailtimetable.helpers({
return Template.instance().totalDetailTimeEntries
},
tcid() { return Template.instance().tcid },
showInvoiceButton: () => (Meteor.user() && Meteor.user().profile && Meteor.user().profile.siwappurl) || getGlobalSetting('useState'),
showInvoiceButton: () => (getUserSetting('siwappurl')) || getGlobalSetting('useState'),
})
Template.detailtimetable.events({
'click .js-export-csv': (event, templateInstance) => {
Expand All @@ -270,9 +271,9 @@ Template.detailtimetable.events({
delete selector[1].skip
let csvArray
if (getGlobalSetting('useState')) {
csvArray = [`\uFEFF${i18next.t('globals.project')},${i18next.t('globals.date')},${i18next.t('globals.task')},${i18next.t('globals.resource')},${Meteor.user() && Meteor.user().profile.timeunit === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')},${i18next.t('details.state')}\r\n`]
csvArray = [`\uFEFF${i18next.t('globals.project')},${i18next.t('globals.date')},${i18next.t('globals.task')},${i18next.t('globals.resource')},${Meteor.user() && getUserSetting('timeunit') === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')},${i18next.t('details.state')}\r\n`]
} else {
csvArray = [`\uFEFF${i18next.t('globals.project')},${i18next.t('globals.date')},${i18next.t('globals.task')},${i18next.t('globals.resource')},${Meteor.user() && Meteor.user().profile.timeunit === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')}\r\n`]
csvArray = [`\uFEFF${i18next.t('globals.project')},${i18next.t('globals.date')},${i18next.t('globals.task')},${i18next.t('globals.resource')},${Meteor.user() && getUserSetting('timeunit') === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')}\r\n`]
}
for (const timeEntry of Timecards.find(selector[0], selector[1]).fetch()
.map(detailedDataTableMapper)) {
Expand Down Expand Up @@ -305,9 +306,9 @@ Template.detailtimetable.events({
delete selector[1].skip
let data
if (getGlobalSetting('useState')) {
data = [[i18next.t('globals.project'), i18next.t('globals.date'), i18next.t('globals.task'), i18next.t('globals.resource'), Meteor.user() && Meteor.user().profile.timeunit === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural'), i18next.t('details.state')]]
data = [[i18next.t('globals.project'), i18next.t('globals.date'), i18next.t('globals.task'), i18next.t('globals.resource'), Meteor.user() && getUserSetting('timeunit') === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural'), i18next.t('details.state')]]
} else {
data = [[i18next.t('globals.project'), i18next.t('globals.date'), i18next.t('globals.task'), i18next.t('globals.resource'), Meteor.user() && Meteor.user().profile.timeunit === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')]]
data = [[i18next.t('globals.project'), i18next.t('globals.date'), i18next.t('globals.task'), i18next.t('globals.resource'), Meteor.user() && getUserSetting('timeunit') === 'd' ? i18next.t('globals.day_plural') : i18next.t('globals.hour_plural')]]
}
for (const timeEntry of Timecards.find(selector[0], selector[1]).fetch()
.map(detailedDataTableMapper)) {
Expand Down Expand Up @@ -352,7 +353,7 @@ Template.detailtimetable.events({
},
'click .js-invoice': (event, templateInstance) => {
event.preventDefault()
if (Meteor.user().profile.siwappurl && Meteor.user().profile.siwapptoken) {
if (getUserSetting('siwappurl') && getUserSetting('siwapptoken')) {
Meteor.call('sendToSiwapp', {
projectId: $('.js-target-project').val(), timePeriod: $('#period').val(), userId: $('#resourceselect').val(), customer: $('#customerselect').val(),
}, (error, result) => {
Expand Down
10 changes: 7 additions & 3 deletions imports/ui/components/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@
<div class="dropdown-menu mt-0 bg-dark" aria-labelledby="navbarDropdownMenuLink" style="left:auto;right:-1rem;">
<a class="dropdown-item text-white" href="/profile"><i class="fa fa-user-circle"></i> {{t "navigation.profile"}}</a>
<a class="dropdown-item text-white" href="/settings"><i class="fa fa-cogs"></i> {{t "navigation.settings"}}</a>
{{#if currentUser.isAdmin}}
<a class="dropdown-item text-white" href="/admin"><i class="fa fa-lock"></i> {{t "navigation.administration"}}</a>
{{/if}}
{{#unless currentUser.loggingIn}}
{{#if Template.subscriptionsReady}}
{{#if currentUser.isAdmin}}
<a class="dropdown-item text-white" href="/admin"><i class="fa fa-lock"></i> {{t "navigation.administration"}}</a>
{{/if}}
{{/if}}
{{/unless}}
<div class="dropdown-divider"></div>
<a class="dropdown-item text-white" href="/about"><i class="fa fa-info-circle"></i> {{t "navigation.about"}}</a>
</div>
Expand Down
6 changes: 5 additions & 1 deletion imports/ui/components/navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { displayUserAvatar } from '../../utils/frontend_helpers'
import './navbar.html'

Template.navbar.onCreated(function navbarCreated() {
this.subscribe('userRoles')
this.autorun(() => {
if (Meteor.user()) {
this.subscribe('userRoles')
}
})
})

Template.navbar.helpers({
Expand Down
Loading

0 comments on commit 9ef6209

Please sign in to comment.